aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_assists
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_assists')
-rw-r--r--crates/ide_assists/src/handlers/generate_function.rs73
1 files changed, 47 insertions, 26 deletions
diff --git a/crates/ide_assists/src/handlers/generate_function.rs b/crates/ide_assists/src/handlers/generate_function.rs
index fd4f2fbed..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
90impl FunctionTemplate { 91impl 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,7 +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,
107 ret_type: Option<ast::RetType>, 108 ret_type: ast::RetType,
109 should_render_snippet: bool,
108 file: FileId, 110 file: FileId,
109 needs_pub: bool, 111 needs_pub: bool,
110} 112}
@@ -132,9 +134,44 @@ impl FunctionBuilder {
132 let target_module = target_module.or_else(|| ctx.sema.scope(target.syntax()).module())?; 134 let target_module = target_module.or_else(|| ctx.sema.scope(target.syntax()).module())?;
133 let fn_name = fn_name(&path)?; 135 let fn_name = fn_name(&path)?;
134 let (type_params, params) = fn_args(ctx, target_module, &call)?; 136 let (type_params, params) = fn_args(ctx, target_module, &call)?;
135 let ret_type = fn_ret_type(ctx, target_module, &call);
136 137
137 Some(Self { target, fn_name, type_params, params, ret_type, 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 })
138 } 175 }
139 176
140 fn render(self) -> FunctionTemplate { 177 fn render(self) -> FunctionTemplate {
@@ -147,7 +184,7 @@ impl FunctionBuilder {
147 self.type_params, 184 self.type_params,
148 self.params, 185 self.params,
149 fn_body, 186 fn_body,
150 Some(self.ret_type.unwrap_or_else(|| make::ret_type(make::ty_unit()))), 187 Some(self.ret_type),
151 ); 188 );
152 let leading_ws; 189 let leading_ws;
153 let trailing_ws; 190 let trailing_ws;
@@ -173,6 +210,7 @@ impl FunctionBuilder {
173 insert_offset, 210 insert_offset,
174 leading_ws, 211 leading_ws,
175 ret_type: fn_def.ret_type().unwrap(), 212 ret_type: fn_def.ret_type().unwrap(),
213 should_render_snippet: self.should_render_snippet,
176 fn_def, 214 fn_def,
177 trailing_ws, 215 trailing_ws,
178 file: self.file, 216 file: self.file,
@@ -225,23 +263,6 @@ fn fn_args(
225 Some((None, make::param_list(None, params))) 263 Some((None, make::param_list(None, params)))
226} 264}
227 265
228fn fn_ret_type(
229 ctx: &AssistContext,
230 target_module: hir::Module,
231 call: &ast::CallExpr,
232) -> Option<ast::RetType> {
233 let ty = ctx.sema.type_of_expr(&ast::Expr::CallExpr(call.clone()))?;
234 if ty.is_unknown() {
235 return None;
236 }
237
238 if let Ok(rendered) = ty.display_source_code(ctx.db(), target_module.into()) {
239 Some(make::ret_type(make::ty(&rendered)))
240 } else {
241 None
242 }
243}
244
245/// Makes duplicate argument names unique by appending incrementing numbers. 266/// Makes duplicate argument names unique by appending incrementing numbers.
246/// 267///
247/// ``` 268/// ```
@@ -565,7 +586,7 @@ impl Baz {
565 } 586 }
566} 587}
567 588
568fn bar(baz: Baz) ${0:-> Baz} { 589fn bar(baz: Baz) -> Baz {
569 todo!() 590 todo!()
570} 591}
571", 592",
@@ -1092,7 +1113,7 @@ fn main() {
1092 let x: u32 = foo(); 1113 let x: u32 = foo();
1093} 1114}
1094 1115
1095fn foo() ${0:-> u32} { 1116fn foo() -> u32 {
1096 todo!() 1117 todo!()
1097} 1118}
1098", 1119",