diff options
-rw-r--r-- | crates/ide_assists/src/handlers/generate_function.rs | 73 |
1 files changed, 67 insertions, 6 deletions
diff --git a/crates/ide_assists/src/handlers/generate_function.rs b/crates/ide_assists/src/handlers/generate_function.rs index 3870b7e75..6f95b1a07 100644 --- a/crates/ide_assists/src/handlers/generate_function.rs +++ b/crates/ide_assists/src/handlers/generate_function.rs | |||
@@ -83,17 +83,18 @@ struct FunctionTemplate { | |||
83 | leading_ws: String, | 83 | leading_ws: String, |
84 | fn_def: ast::Fn, | 84 | fn_def: ast::Fn, |
85 | ret_type: ast::RetType, | 85 | ret_type: ast::RetType, |
86 | should_render_snippet: bool, | ||
86 | trailing_ws: String, | 87 | trailing_ws: String, |
87 | file: FileId, | 88 | file: FileId, |
88 | } | 89 | } |
89 | 90 | ||
90 | impl FunctionTemplate { | 91 | impl FunctionTemplate { |
91 | fn to_string(&self, cap: Option<SnippetCap>) -> String { | 92 | fn to_string(&self, cap: Option<SnippetCap>) -> String { |
92 | let f = match cap { | 93 | let f = match (cap, self.should_render_snippet) { |
93 | Some(cap) => { | 94 | (Some(cap), true) => { |
94 | render_snippet(cap, self.fn_def.syntax(), Cursor::Replace(self.ret_type.syntax())) | 95 | render_snippet(cap, self.fn_def.syntax(), Cursor::Replace(self.ret_type.syntax())) |
95 | } | 96 | } |
96 | None => self.fn_def.to_string(), | 97 | _ => self.fn_def.to_string(), |
97 | }; | 98 | }; |
98 | format!("{}{}{}", self.leading_ws, f, self.trailing_ws) | 99 | format!("{}{}{}", self.leading_ws, f, self.trailing_ws) |
99 | } | 100 | } |
@@ -104,6 +105,8 @@ struct FunctionBuilder { | |||
104 | fn_name: ast::Name, | 105 | fn_name: ast::Name, |
105 | type_params: Option<ast::GenericParamList>, | 106 | type_params: Option<ast::GenericParamList>, |
106 | params: ast::ParamList, | 107 | params: ast::ParamList, |
108 | ret_type: ast::RetType, | ||
109 | should_render_snippet: bool, | ||
107 | file: FileId, | 110 | file: FileId, |
108 | needs_pub: bool, | 111 | needs_pub: bool, |
109 | } | 112 | } |
@@ -132,7 +135,43 @@ impl FunctionBuilder { | |||
132 | let fn_name = fn_name(&path)?; | 135 | let fn_name = fn_name(&path)?; |
133 | let (type_params, params) = fn_args(ctx, target_module, &call)?; | 136 | let (type_params, params) = fn_args(ctx, target_module, &call)?; |
134 | 137 | ||
135 | Some(Self { target, fn_name, type_params, params, file, needs_pub }) | 138 | // should_render_snippet intends to express a rough level of confidence about |
139 | // the correctness of the return type. | ||
140 | // | ||
141 | // If we are able to infer some return type, and that return type is not unit, we | ||
142 | // don't want to render the snippet. The assumption here is in this situation the | ||
143 | // return type is just as likely to be correct as any other part of the generated | ||
144 | // function. | ||
145 | // | ||
146 | // In the case where the return type is inferred as unit it is likely that the | ||
147 | // user does in fact intend for this generated function to return some non unit | ||
148 | // type, but that the current state of their code doesn't allow that return type | ||
149 | // to be accurately inferred. | ||
150 | let (ret_ty, should_render_snippet) = { | ||
151 | match ctx.sema.type_of_expr(&ast::Expr::CallExpr(call.clone())) { | ||
152 | Some(ty) if ty.is_unknown() || ty.is_unit() => (make::ty_unit(), true), | ||
153 | Some(ty) => { | ||
154 | let rendered = ty.display_source_code(ctx.db(), target_module.into()); | ||
155 | match rendered { | ||
156 | Ok(rendered) => (make::ty(&rendered), false), | ||
157 | Err(_) => (make::ty_unit(), true), | ||
158 | } | ||
159 | } | ||
160 | None => (make::ty_unit(), true), | ||
161 | } | ||
162 | }; | ||
163 | let ret_type = make::ret_type(ret_ty); | ||
164 | |||
165 | Some(Self { | ||
166 | target, | ||
167 | fn_name, | ||
168 | type_params, | ||
169 | params, | ||
170 | ret_type, | ||
171 | should_render_snippet, | ||
172 | file, | ||
173 | needs_pub, | ||
174 | }) | ||
136 | } | 175 | } |
137 | 176 | ||
138 | fn render(self) -> FunctionTemplate { | 177 | fn render(self) -> FunctionTemplate { |
@@ -145,7 +184,7 @@ impl FunctionBuilder { | |||
145 | self.type_params, | 184 | self.type_params, |
146 | self.params, | 185 | self.params, |
147 | fn_body, | 186 | fn_body, |
148 | Some(make::ret_type(make::ty_unit())), | 187 | Some(self.ret_type), |
149 | ); | 188 | ); |
150 | let leading_ws; | 189 | let leading_ws; |
151 | let trailing_ws; | 190 | let trailing_ws; |
@@ -171,6 +210,7 @@ impl FunctionBuilder { | |||
171 | insert_offset, | 210 | insert_offset, |
172 | leading_ws, | 211 | leading_ws, |
173 | ret_type: fn_def.ret_type().unwrap(), | 212 | ret_type: fn_def.ret_type().unwrap(), |
213 | should_render_snippet: self.should_render_snippet, | ||
174 | fn_def, | 214 | fn_def, |
175 | trailing_ws, | 215 | trailing_ws, |
176 | file: self.file, | 216 | file: self.file, |
@@ -546,7 +586,7 @@ impl Baz { | |||
546 | } | 586 | } |
547 | } | 587 | } |
548 | 588 | ||
549 | fn bar(baz: Baz) ${0:-> ()} { | 589 | fn bar(baz: Baz) -> Baz { |
550 | todo!() | 590 | todo!() |
551 | } | 591 | } |
552 | ", | 592 | ", |
@@ -1060,6 +1100,27 @@ pub(crate) fn bar() ${0:-> ()} { | |||
1060 | } | 1100 | } |
1061 | 1101 | ||
1062 | #[test] | 1102 | #[test] |
1103 | fn add_function_with_return_type() { | ||
1104 | check_assist( | ||
1105 | generate_function, | ||
1106 | r" | ||
1107 | fn main() { | ||
1108 | let x: u32 = foo$0(); | ||
1109 | } | ||
1110 | ", | ||
1111 | r" | ||
1112 | fn main() { | ||
1113 | let x: u32 = foo(); | ||
1114 | } | ||
1115 | |||
1116 | fn foo() -> u32 { | ||
1117 | todo!() | ||
1118 | } | ||
1119 | ", | ||
1120 | ) | ||
1121 | } | ||
1122 | |||
1123 | #[test] | ||
1063 | fn add_function_not_applicable_if_function_already_exists() { | 1124 | fn add_function_not_applicable_if_function_already_exists() { |
1064 | check_assist_not_applicable( | 1125 | check_assist_not_applicable( |
1065 | generate_function, | 1126 | generate_function, |