aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_assists/src/handlers/generate_function.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_assists/src/handlers/generate_function.rs')
-rw-r--r--crates/ide_assists/src/handlers/generate_function.rs73
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
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,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
549fn bar(baz: Baz) ${0:-> ()} { 589fn 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"
1107fn main() {
1108 let x: u32 = foo$0();
1109}
1110",
1111 r"
1112fn main() {
1113 let x: u32 = foo();
1114}
1115
1116fn 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,