diff options
Diffstat (limited to 'crates/assists/src')
-rw-r--r-- | crates/assists/src/handlers/early_return.rs | 2 | ||||
-rw-r--r-- | crates/assists/src/handlers/extract_function.rs | 1687 | ||||
-rw-r--r-- | crates/assists/src/handlers/generate_function.rs | 7 | ||||
-rw-r--r-- | crates/assists/src/tests.rs | 5 |
4 files changed, 1463 insertions, 238 deletions
diff --git a/crates/assists/src/handlers/early_return.rs b/crates/assists/src/handlers/early_return.rs index 8bbbb7ed5..6b87c3c05 100644 --- a/crates/assists/src/handlers/early_return.rs +++ b/crates/assists/src/handlers/early_return.rs | |||
@@ -88,7 +88,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) | |||
88 | 88 | ||
89 | let early_expression: ast::Expr = match parent_container.kind() { | 89 | let early_expression: ast::Expr = match parent_container.kind() { |
90 | WHILE_EXPR | LOOP_EXPR => make::expr_continue(), | 90 | WHILE_EXPR | LOOP_EXPR => make::expr_continue(), |
91 | FN => make::expr_return(), | 91 | FN => make::expr_return(None), |
92 | _ => return None, | 92 | _ => return None, |
93 | }; | 93 | }; |
94 | 94 | ||
diff --git a/crates/assists/src/handlers/extract_function.rs b/crates/assists/src/handlers/extract_function.rs index d876eabca..9f34cc725 100644 --- a/crates/assists/src/handlers/extract_function.rs +++ b/crates/assists/src/handlers/extract_function.rs | |||
@@ -1,3 +1,6 @@ | |||
1 | use std::iter; | ||
2 | |||
3 | use ast::make; | ||
1 | use either::Either; | 4 | use either::Either; |
2 | use hir::{HirDisplay, Local}; | 5 | use hir::{HirDisplay, Local}; |
3 | use ide_db::{ | 6 | use ide_db::{ |
@@ -13,9 +16,9 @@ use syntax::{ | |||
13 | edit::{AstNodeEdit, IndentLevel}, | 16 | edit::{AstNodeEdit, IndentLevel}, |
14 | AstNode, | 17 | AstNode, |
15 | }, | 18 | }, |
16 | AstToken, Direction, SyntaxElement, | 19 | SyntaxElement, |
17 | SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR}, | 20 | SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR}, |
18 | SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, T, | 21 | SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T, |
19 | }; | 22 | }; |
20 | use test_utils::mark; | 23 | use test_utils::mark; |
21 | 24 | ||
@@ -81,11 +84,9 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option | |||
81 | // We should not have variables that outlive body if we have expression block | 84 | // We should not have variables that outlive body if we have expression block |
82 | return None; | 85 | return None; |
83 | } | 86 | } |
87 | let control_flow = external_control_flow(ctx, &body)?; | ||
84 | 88 | ||
85 | let target_range = match &body { | 89 | let target_range = body.text_range(); |
86 | FunctionBody::Expr(expr) => expr.syntax().text_range(), | ||
87 | FunctionBody::Span { .. } => ctx.frange.range, | ||
88 | }; | ||
89 | 90 | ||
90 | acc.add( | 91 | acc.add( |
91 | AssistId("extract_function", crate::AssistKind::RefactorExtract), | 92 | AssistId("extract_function", crate::AssistKind::RefactorExtract), |
@@ -98,16 +99,17 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option | |||
98 | name: "fun_name".to_string(), | 99 | name: "fun_name".to_string(), |
99 | self_param: self_param.map(|(_, pat)| pat), | 100 | self_param: self_param.map(|(_, pat)| pat), |
100 | params, | 101 | params, |
102 | control_flow, | ||
101 | ret_ty, | 103 | ret_ty, |
102 | body, | 104 | body, |
103 | vars_defined_in_body_and_outlive, | 105 | vars_defined_in_body_and_outlive, |
104 | }; | 106 | }; |
105 | 107 | ||
106 | builder.replace(target_range, format_replacement(ctx, &fun)); | ||
107 | |||
108 | let new_indent = IndentLevel::from_node(&insert_after); | 108 | let new_indent = IndentLevel::from_node(&insert_after); |
109 | let old_indent = fun.body.indent_level(); | 109 | let old_indent = fun.body.indent_level(); |
110 | 110 | ||
111 | builder.replace(target_range, format_replacement(ctx, &fun, old_indent)); | ||
112 | |||
111 | let fn_def = format_function(ctx, module, &fun, old_indent, new_indent); | 113 | let fn_def = format_function(ctx, module, &fun, old_indent, new_indent); |
112 | let insert_offset = insert_after.text_range().end(); | 114 | let insert_offset = insert_after.text_range().end(); |
113 | builder.insert(insert_offset, fn_def); | 115 | builder.insert(insert_offset, fn_def); |
@@ -115,11 +117,140 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option | |||
115 | ) | 117 | ) |
116 | } | 118 | } |
117 | 119 | ||
120 | fn external_control_flow(ctx: &AssistContext, body: &FunctionBody) -> Option<ControlFlow> { | ||
121 | let mut ret_expr = None; | ||
122 | let mut try_expr = None; | ||
123 | let mut break_expr = None; | ||
124 | let mut continue_expr = None; | ||
125 | let (syntax, text_range) = match body { | ||
126 | FunctionBody::Expr(expr) => (expr.syntax(), expr.syntax().text_range()), | ||
127 | FunctionBody::Span { parent, text_range } => (parent.syntax(), *text_range), | ||
128 | }; | ||
129 | |||
130 | let mut nested_loop = None; | ||
131 | let mut nested_scope = None; | ||
132 | |||
133 | for e in syntax.preorder() { | ||
134 | let e = match e { | ||
135 | WalkEvent::Enter(e) => e, | ||
136 | WalkEvent::Leave(e) => { | ||
137 | if nested_loop.as_ref() == Some(&e) { | ||
138 | nested_loop = None; | ||
139 | } | ||
140 | if nested_scope.as_ref() == Some(&e) { | ||
141 | nested_scope = None; | ||
142 | } | ||
143 | continue; | ||
144 | } | ||
145 | }; | ||
146 | if nested_scope.is_some() { | ||
147 | continue; | ||
148 | } | ||
149 | if !text_range.contains_range(e.text_range()) { | ||
150 | continue; | ||
151 | } | ||
152 | match e.kind() { | ||
153 | SyntaxKind::LOOP_EXPR | SyntaxKind::WHILE_EXPR | SyntaxKind::FOR_EXPR => { | ||
154 | if nested_loop.is_none() { | ||
155 | nested_loop = Some(e); | ||
156 | } | ||
157 | } | ||
158 | SyntaxKind::FN | ||
159 | | SyntaxKind::CONST | ||
160 | | SyntaxKind::STATIC | ||
161 | | SyntaxKind::IMPL | ||
162 | | SyntaxKind::MODULE => { | ||
163 | if nested_scope.is_none() { | ||
164 | nested_scope = Some(e); | ||
165 | } | ||
166 | } | ||
167 | SyntaxKind::RETURN_EXPR => { | ||
168 | ret_expr = Some(ast::ReturnExpr::cast(e).unwrap()); | ||
169 | } | ||
170 | SyntaxKind::TRY_EXPR => { | ||
171 | try_expr = Some(ast::TryExpr::cast(e).unwrap()); | ||
172 | } | ||
173 | SyntaxKind::BREAK_EXPR if nested_loop.is_none() => { | ||
174 | break_expr = Some(ast::BreakExpr::cast(e).unwrap()); | ||
175 | } | ||
176 | SyntaxKind::CONTINUE_EXPR if nested_loop.is_none() => { | ||
177 | continue_expr = Some(ast::ContinueExpr::cast(e).unwrap()); | ||
178 | } | ||
179 | _ => {} | ||
180 | } | ||
181 | } | ||
182 | |||
183 | let kind = match (try_expr, ret_expr, break_expr, continue_expr) { | ||
184 | (Some(e), None, None, None) => { | ||
185 | let func = e.syntax().ancestors().find_map(ast::Fn::cast)?; | ||
186 | let def = ctx.sema.to_def(&func)?; | ||
187 | let ret_ty = def.ret_type(ctx.db()); | ||
188 | let kind = try_kind_of_ty(ret_ty, ctx)?; | ||
189 | |||
190 | Some(FlowKind::Try { kind }) | ||
191 | } | ||
192 | (Some(_), Some(r), None, None) => match r.expr() { | ||
193 | Some(expr) => { | ||
194 | if let Some(kind) = expr_err_kind(&expr, ctx) { | ||
195 | Some(FlowKind::TryReturn { expr, kind }) | ||
196 | } else { | ||
197 | mark::hit!(external_control_flow_try_and_return_non_err); | ||
198 | return None; | ||
199 | } | ||
200 | } | ||
201 | None => return None, | ||
202 | }, | ||
203 | (Some(_), _, _, _) => { | ||
204 | mark::hit!(external_control_flow_try_and_bc); | ||
205 | return None; | ||
206 | } | ||
207 | (None, Some(r), None, None) => match r.expr() { | ||
208 | Some(expr) => Some(FlowKind::ReturnValue(expr)), | ||
209 | None => Some(FlowKind::Return), | ||
210 | }, | ||
211 | (None, Some(_), _, _) => { | ||
212 | mark::hit!(external_control_flow_return_and_bc); | ||
213 | return None; | ||
214 | } | ||
215 | (None, None, Some(_), Some(_)) => { | ||
216 | mark::hit!(external_control_flow_break_and_continue); | ||
217 | return None; | ||
218 | } | ||
219 | (None, None, Some(b), None) => match b.expr() { | ||
220 | Some(expr) => Some(FlowKind::BreakValue(expr)), | ||
221 | None => Some(FlowKind::Break), | ||
222 | }, | ||
223 | (None, None, None, Some(_)) => Some(FlowKind::Continue), | ||
224 | (None, None, None, None) => None, | ||
225 | }; | ||
226 | |||
227 | Some(ControlFlow { kind }) | ||
228 | } | ||
229 | |||
230 | /// Checks is expr is `Err(_)` or `None` | ||
231 | fn expr_err_kind(expr: &ast::Expr, ctx: &AssistContext) -> Option<TryKind> { | ||
232 | let func_name = match expr { | ||
233 | ast::Expr::CallExpr(call_expr) => call_expr.expr()?, | ||
234 | ast::Expr::PathExpr(_) => expr.clone(), | ||
235 | _ => return None, | ||
236 | }; | ||
237 | let text = func_name.syntax().text(); | ||
238 | |||
239 | if text == "Err" { | ||
240 | Some(TryKind::Result { ty: ctx.sema.type_of_expr(expr)? }) | ||
241 | } else if text == "None" { | ||
242 | Some(TryKind::Option) | ||
243 | } else { | ||
244 | None | ||
245 | } | ||
246 | } | ||
247 | |||
118 | #[derive(Debug)] | 248 | #[derive(Debug)] |
119 | struct Function { | 249 | struct Function { |
120 | name: String, | 250 | name: String, |
121 | self_param: Option<ast::SelfParam>, | 251 | self_param: Option<ast::SelfParam>, |
122 | params: Vec<Param>, | 252 | params: Vec<Param>, |
253 | control_flow: ControlFlow, | ||
123 | ret_ty: RetType, | 254 | ret_ty: RetType, |
124 | body: FunctionBody, | 255 | body: FunctionBody, |
125 | vars_defined_in_body_and_outlive: Vec<Local>, | 256 | vars_defined_in_body_and_outlive: Vec<Local>, |
@@ -134,6 +265,11 @@ struct Param { | |||
134 | is_copy: bool, | 265 | is_copy: bool, |
135 | } | 266 | } |
136 | 267 | ||
268 | #[derive(Debug)] | ||
269 | struct ControlFlow { | ||
270 | kind: Option<FlowKind>, | ||
271 | } | ||
272 | |||
137 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 273 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
138 | enum ParamKind { | 274 | enum ParamKind { |
139 | Value, | 275 | Value, |
@@ -142,6 +278,30 @@ enum ParamKind { | |||
142 | MutRef, | 278 | MutRef, |
143 | } | 279 | } |
144 | 280 | ||
281 | #[derive(Debug, Eq, PartialEq)] | ||
282 | enum FunType { | ||
283 | Unit, | ||
284 | Single(hir::Type), | ||
285 | Tuple(Vec<hir::Type>), | ||
286 | } | ||
287 | |||
288 | impl Function { | ||
289 | fn return_type(&self, ctx: &AssistContext) -> FunType { | ||
290 | match &self.ret_ty { | ||
291 | RetType::Expr(ty) if ty.is_unit() => FunType::Unit, | ||
292 | RetType::Expr(ty) => FunType::Single(ty.clone()), | ||
293 | RetType::Stmt => match self.vars_defined_in_body_and_outlive.as_slice() { | ||
294 | [] => FunType::Unit, | ||
295 | [var] => FunType::Single(var.ty(ctx.db())), | ||
296 | vars => { | ||
297 | let types = vars.iter().map(|v| v.ty(ctx.db())).collect(); | ||
298 | FunType::Tuple(types) | ||
299 | } | ||
300 | }, | ||
301 | } | ||
302 | } | ||
303 | } | ||
304 | |||
145 | impl ParamKind { | 305 | impl ParamKind { |
146 | fn is_ref(&self) -> bool { | 306 | fn is_ref(&self) -> bool { |
147 | matches!(self, ParamKind::SharedRef | ParamKind::MutRef) | 307 | matches!(self, ParamKind::SharedRef | ParamKind::MutRef) |
@@ -158,30 +318,121 @@ impl Param { | |||
158 | } | 318 | } |
159 | } | 319 | } |
160 | 320 | ||
161 | fn value_prefix(&self) -> &'static str { | 321 | fn to_arg(&self, ctx: &AssistContext) -> ast::Expr { |
322 | let var = path_expr_from_local(ctx, self.var); | ||
162 | match self.kind() { | 323 | match self.kind() { |
163 | ParamKind::Value | ParamKind::MutValue => "", | 324 | ParamKind::Value | ParamKind::MutValue => var, |
164 | ParamKind::SharedRef => "&", | 325 | ParamKind::SharedRef => make::expr_ref(var, false), |
165 | ParamKind::MutRef => "&mut ", | 326 | ParamKind::MutRef => make::expr_ref(var, true), |
166 | } | 327 | } |
167 | } | 328 | } |
168 | 329 | ||
169 | fn type_prefix(&self) -> &'static str { | 330 | fn to_param(&self, ctx: &AssistContext, module: hir::Module) -> ast::Param { |
170 | match self.kind() { | 331 | let var = self.var.name(ctx.db()).unwrap().to_string(); |
171 | ParamKind::Value | ParamKind::MutValue => "", | 332 | let var_name = make::name(&var); |
172 | ParamKind::SharedRef => "&", | 333 | let pat = match self.kind() { |
173 | ParamKind::MutRef => "&mut ", | 334 | ParamKind::MutValue => make::ident_mut_pat(var_name), |
335 | ParamKind::Value | ParamKind::SharedRef | ParamKind::MutRef => { | ||
336 | make::ident_pat(var_name) | ||
337 | } | ||
338 | }; | ||
339 | |||
340 | let ty = make_ty(&self.ty, ctx, module); | ||
341 | let ty = match self.kind() { | ||
342 | ParamKind::Value | ParamKind::MutValue => ty, | ||
343 | ParamKind::SharedRef => make::ty_ref(ty, false), | ||
344 | ParamKind::MutRef => make::ty_ref(ty, true), | ||
345 | }; | ||
346 | |||
347 | make::param(pat.into(), ty) | ||
348 | } | ||
349 | } | ||
350 | |||
351 | /// Control flow that is exported from extracted function | ||
352 | /// | ||
353 | /// E.g.: | ||
354 | /// ```rust,no_run | ||
355 | /// loop { | ||
356 | /// $0 | ||
357 | /// if 42 == 42 { | ||
358 | /// break; | ||
359 | /// } | ||
360 | /// $0 | ||
361 | /// } | ||
362 | /// ``` | ||
363 | #[derive(Debug, Clone)] | ||
364 | enum FlowKind { | ||
365 | /// Return without value (`return;`) | ||
366 | Return, | ||
367 | /// Return with value (`return $expr;`) | ||
368 | ReturnValue(ast::Expr), | ||
369 | Try { | ||
370 | kind: TryKind, | ||
371 | }, | ||
372 | TryReturn { | ||
373 | expr: ast::Expr, | ||
374 | kind: TryKind, | ||
375 | }, | ||
376 | /// Break without value (`return;`) | ||
377 | Break, | ||
378 | /// Break with value (`break $expr;`) | ||
379 | BreakValue(ast::Expr), | ||
380 | /// Continue | ||
381 | Continue, | ||
382 | } | ||
383 | |||
384 | #[derive(Debug, Clone)] | ||
385 | enum TryKind { | ||
386 | Option, | ||
387 | Result { ty: hir::Type }, | ||
388 | } | ||
389 | |||
390 | impl FlowKind { | ||
391 | fn make_result_handler(&self, expr: Option<ast::Expr>) -> ast::Expr { | ||
392 | match self { | ||
393 | FlowKind::Return | FlowKind::ReturnValue(_) => make::expr_return(expr), | ||
394 | FlowKind::Break | FlowKind::BreakValue(_) => make::expr_break(expr), | ||
395 | FlowKind::Try { .. } | FlowKind::TryReturn { .. } => { | ||
396 | stdx::never!("cannot have result handler with try"); | ||
397 | expr.unwrap_or_else(|| make::expr_return(None)) | ||
398 | } | ||
399 | FlowKind::Continue => { | ||
400 | stdx::always!(expr.is_none(), "continue with value is not possible"); | ||
401 | make::expr_continue() | ||
402 | } | ||
174 | } | 403 | } |
175 | } | 404 | } |
176 | 405 | ||
177 | fn mut_pattern(&self) -> &'static str { | 406 | fn expr_ty(&self, ctx: &AssistContext) -> Option<hir::Type> { |
178 | match self.kind() { | 407 | match self { |
179 | ParamKind::MutValue => "mut ", | 408 | FlowKind::ReturnValue(expr) |
180 | _ => "", | 409 | | FlowKind::BreakValue(expr) |
410 | | FlowKind::TryReturn { expr, .. } => ctx.sema.type_of_expr(expr), | ||
411 | FlowKind::Try { .. } => { | ||
412 | stdx::never!("try does not have defined expr_ty"); | ||
413 | None | ||
414 | } | ||
415 | FlowKind::Return | FlowKind::Break | FlowKind::Continue => None, | ||
181 | } | 416 | } |
182 | } | 417 | } |
183 | } | 418 | } |
184 | 419 | ||
420 | fn try_kind_of_ty(ty: hir::Type, ctx: &AssistContext) -> Option<TryKind> { | ||
421 | if ty.is_unknown() { | ||
422 | // We favour Result for `expr?` | ||
423 | return Some(TryKind::Result { ty }); | ||
424 | } | ||
425 | let adt = ty.as_adt()?; | ||
426 | let name = adt.name(ctx.db()); | ||
427 | // FIXME: use lang items to determine if it is std type or user defined | ||
428 | // E.g. if user happens to define type named `Option`, we would have false positive | ||
429 | match name.to_string().as_str() { | ||
430 | "Option" => Some(TryKind::Option), | ||
431 | "Result" => Some(TryKind::Result { ty }), | ||
432 | _ => None, | ||
433 | } | ||
434 | } | ||
435 | |||
185 | #[derive(Debug)] | 436 | #[derive(Debug)] |
186 | enum RetType { | 437 | enum RetType { |
187 | Expr(hir::Type), | 438 | Expr(hir::Type), |
@@ -195,21 +446,13 @@ impl RetType { | |||
195 | RetType::Stmt => true, | 446 | RetType::Stmt => true, |
196 | } | 447 | } |
197 | } | 448 | } |
198 | |||
199 | fn as_fn_ret(&self) -> Option<&hir::Type> { | ||
200 | match self { | ||
201 | RetType::Stmt => None, | ||
202 | RetType::Expr(ty) if ty.is_unit() => None, | ||
203 | RetType::Expr(ty) => Some(ty), | ||
204 | } | ||
205 | } | ||
206 | } | 449 | } |
207 | 450 | ||
208 | /// Semantically same as `ast::Expr`, but preserves identity when using only part of the Block | 451 | /// Semantically same as `ast::Expr`, but preserves identity when using only part of the Block |
209 | #[derive(Debug)] | 452 | #[derive(Debug)] |
210 | enum FunctionBody { | 453 | enum FunctionBody { |
211 | Expr(ast::Expr), | 454 | Expr(ast::Expr), |
212 | Span { elements: Vec<SyntaxElement>, leading_indent: String }, | 455 | Span { parent: ast::BlockExpr, text_range: TextRange }, |
213 | } | 456 | } |
214 | 457 | ||
215 | impl FunctionBody { | 458 | impl FunctionBody { |
@@ -226,58 +469,28 @@ impl FunctionBody { | |||
226 | } | 469 | } |
227 | } | 470 | } |
228 | 471 | ||
229 | fn from_range(node: &SyntaxNode, range: TextRange) -> Option<FunctionBody> { | 472 | fn from_range(node: SyntaxNode, text_range: TextRange) -> Option<FunctionBody> { |
230 | let mut first = node.token_at_offset(range.start()).left_biased()?; | 473 | let block = ast::BlockExpr::cast(node)?; |
231 | let last = node.token_at_offset(range.end()).right_biased()?; | 474 | Some(Self::Span { parent: block, text_range }) |
232 | |||
233 | let mut leading_indent = String::new(); | ||
234 | |||
235 | let leading_trivia = first | ||
236 | .siblings_with_tokens(Direction::Prev) | ||
237 | .skip(1) | ||
238 | .take_while(|e| e.kind() == SyntaxKind::WHITESPACE && e.as_token().is_some()); | ||
239 | |||
240 | for e in leading_trivia { | ||
241 | let token = e.as_token().unwrap(); | ||
242 | let text = token.text(); | ||
243 | match text.rfind('\n') { | ||
244 | Some(pos) => { | ||
245 | leading_indent = text[pos..].to_owned(); | ||
246 | break; | ||
247 | } | ||
248 | None => first = token.clone(), | ||
249 | } | ||
250 | } | ||
251 | |||
252 | let mut elements: Vec<_> = first | ||
253 | .siblings_with_tokens(Direction::Next) | ||
254 | .take_while(|e| e.as_token() != Some(&last)) | ||
255 | .collect(); | ||
256 | |||
257 | if !(last.kind() == SyntaxKind::WHITESPACE && last.text().lines().count() <= 2) { | ||
258 | elements.push(last.into()); | ||
259 | } | ||
260 | |||
261 | Some(FunctionBody::Span { elements, leading_indent }) | ||
262 | } | 475 | } |
263 | 476 | ||
264 | fn indent_level(&self) -> IndentLevel { | 477 | fn indent_level(&self) -> IndentLevel { |
265 | match &self { | 478 | match &self { |
266 | FunctionBody::Expr(expr) => IndentLevel::from_node(expr.syntax()), | 479 | FunctionBody::Expr(expr) => IndentLevel::from_node(expr.syntax()), |
267 | FunctionBody::Span { elements, .. } => elements | 480 | FunctionBody::Span { parent, .. } => IndentLevel::from_node(parent.syntax()) + 1, |
268 | .iter() | ||
269 | .filter_map(SyntaxElement::as_node) | ||
270 | .map(IndentLevel::from_node) | ||
271 | .min_by_key(|level| level.0) | ||
272 | .expect("body must contain at least one node"), | ||
273 | } | 481 | } |
274 | } | 482 | } |
275 | 483 | ||
276 | fn tail_expr(&self) -> Option<ast::Expr> { | 484 | fn tail_expr(&self) -> Option<ast::Expr> { |
277 | match &self { | 485 | match &self { |
278 | FunctionBody::Expr(expr) => Some(expr.clone()), | 486 | FunctionBody::Expr(expr) => Some(expr.clone()), |
279 | FunctionBody::Span { elements, .. } => { | 487 | FunctionBody::Span { parent, text_range } => { |
280 | elements.iter().rev().find_map(|e| e.as_node()).cloned().and_then(ast::Expr::cast) | 488 | let tail_expr = parent.tail_expr()?; |
489 | if text_range.contains_range(tail_expr.syntax().text_range()) { | ||
490 | Some(tail_expr) | ||
491 | } else { | ||
492 | None | ||
493 | } | ||
281 | } | 494 | } |
282 | } | 495 | } |
283 | } | 496 | } |
@@ -285,11 +498,11 @@ impl FunctionBody { | |||
285 | fn descendants(&self) -> impl Iterator<Item = SyntaxNode> + '_ { | 498 | fn descendants(&self) -> impl Iterator<Item = SyntaxNode> + '_ { |
286 | match self { | 499 | match self { |
287 | FunctionBody::Expr(expr) => Either::Right(expr.syntax().descendants()), | 500 | FunctionBody::Expr(expr) => Either::Right(expr.syntax().descendants()), |
288 | FunctionBody::Span { elements, .. } => Either::Left( | 501 | FunctionBody::Span { parent, text_range } => Either::Left( |
289 | elements | 502 | parent |
290 | .iter() | 503 | .syntax() |
291 | .filter_map(SyntaxElement::as_node) | 504 | .descendants() |
292 | .flat_map(SyntaxNode::descendants), | 505 | .filter(move |it| text_range.contains_range(it.text_range())), |
293 | ), | 506 | ), |
294 | } | 507 | } |
295 | } | 508 | } |
@@ -297,10 +510,7 @@ impl FunctionBody { | |||
297 | fn text_range(&self) -> TextRange { | 510 | fn text_range(&self) -> TextRange { |
298 | match self { | 511 | match self { |
299 | FunctionBody::Expr(expr) => expr.syntax().text_range(), | 512 | FunctionBody::Expr(expr) => expr.syntax().text_range(), |
300 | FunctionBody::Span { elements, .. } => TextRange::new( | 513 | FunctionBody::Span { parent: _, text_range } => *text_range, |
301 | elements.first().unwrap().text_range().start(), | ||
302 | elements.last().unwrap().text_range().end(), | ||
303 | ), | ||
304 | } | 514 | } |
305 | } | 515 | } |
306 | 516 | ||
@@ -321,30 +531,27 @@ impl HasTokenAtOffset for FunctionBody { | |||
321 | fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<SyntaxToken> { | 531 | fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<SyntaxToken> { |
322 | match self { | 532 | match self { |
323 | FunctionBody::Expr(expr) => expr.syntax().token_at_offset(offset), | 533 | FunctionBody::Expr(expr) => expr.syntax().token_at_offset(offset), |
324 | FunctionBody::Span { elements, .. } => { | 534 | FunctionBody::Span { parent, text_range } => { |
325 | stdx::always!(self.text_range().contains(offset)); | 535 | match parent.syntax().token_at_offset(offset) { |
326 | let mut iter = elements | 536 | TokenAtOffset::None => TokenAtOffset::None, |
327 | .iter() | 537 | TokenAtOffset::Single(t) => { |
328 | .filter(|element| element.text_range().contains_inclusive(offset)); | 538 | if text_range.contains_range(t.text_range()) { |
329 | let element1 = iter.next().expect("offset does not fall into body"); | 539 | TokenAtOffset::Single(t) |
330 | let element2 = iter.next(); | 540 | } else { |
331 | stdx::always!(iter.next().is_none(), "> 2 tokens at offset"); | 541 | TokenAtOffset::None |
332 | let t1 = match element1 { | 542 | } |
333 | syntax::NodeOrToken::Node(node) => node.token_at_offset(offset), | 543 | } |
334 | syntax::NodeOrToken::Token(token) => TokenAtOffset::Single(token.clone()), | 544 | TokenAtOffset::Between(a, b) => { |
335 | }; | 545 | match ( |
336 | let t2 = element2.map(|e| match e { | 546 | text_range.contains_range(a.text_range()), |
337 | syntax::NodeOrToken::Node(node) => node.token_at_offset(offset), | 547 | text_range.contains_range(b.text_range()), |
338 | syntax::NodeOrToken::Token(token) => TokenAtOffset::Single(token.clone()), | 548 | ) { |
339 | }); | 549 | (true, true) => TokenAtOffset::Between(a, b), |
340 | 550 | (true, false) => TokenAtOffset::Single(a), | |
341 | match t2 { | 551 | (false, true) => TokenAtOffset::Single(b), |
342 | Some(t2) => match (t1.clone().right_biased(), t2.clone().left_biased()) { | 552 | (false, false) => TokenAtOffset::None, |
343 | (Some(e1), Some(e2)) => TokenAtOffset::Between(e1, e2), | 553 | } |
344 | (Some(_), None) => t1, | 554 | } |
345 | (None, _) => t2, | ||
346 | }, | ||
347 | None => t1, | ||
348 | } | 555 | } |
349 | } | 556 | } |
350 | } | 557 | } |
@@ -389,7 +596,7 @@ fn extraction_target(node: &SyntaxNode, selection_range: TextRange) -> Option<Fu | |||
389 | // we have selected a few statements in a block | 596 | // we have selected a few statements in a block |
390 | // so covering_element returns the whole block | 597 | // so covering_element returns the whole block |
391 | if node.kind() == BLOCK_EXPR { | 598 | if node.kind() == BLOCK_EXPR { |
392 | let body = FunctionBody::from_range(&node, selection_range); | 599 | let body = FunctionBody::from_range(node.clone(), selection_range); |
393 | if body.is_some() { | 600 | if body.is_some() { |
394 | return body; | 601 | return body; |
395 | } | 602 | } |
@@ -400,7 +607,7 @@ fn extraction_target(node: &SyntaxNode, selection_range: TextRange) -> Option<Fu | |||
400 | // so we try to expand covering_element to parent and repeat the previous | 607 | // so we try to expand covering_element to parent and repeat the previous |
401 | if let Some(parent) = node.parent() { | 608 | if let Some(parent) = node.parent() { |
402 | if parent.kind() == BLOCK_EXPR { | 609 | if parent.kind() == BLOCK_EXPR { |
403 | let body = FunctionBody::from_range(&parent, selection_range); | 610 | let body = FunctionBody::from_range(parent, selection_range); |
404 | if body.is_some() { | 611 | if body.is_some() { |
405 | return body; | 612 | return body; |
406 | } | 613 | } |
@@ -642,13 +849,9 @@ fn either_syntax(value: &Either<ast::IdentPat, ast::SelfParam>) -> &SyntaxNode { | |||
642 | 849 | ||
643 | /// checks if local variable is used after(outside of) body | 850 | /// checks if local variable is used after(outside of) body |
644 | fn var_outlives_body(ctx: &AssistContext, body: &FunctionBody, var: &Local) -> bool { | 851 | fn var_outlives_body(ctx: &AssistContext, body: &FunctionBody, var: &Local) -> bool { |
645 | let usages = Definition::Local(*var) | 852 | let usages = LocalUsages::find(ctx, *var); |
646 | .usages(&ctx.sema) | 853 | let has_usages = usages.iter().any(|reference| body.preceedes_range(reference.range)); |
647 | .in_scope(SearchScope::single_file(ctx.frange.file_id)) | 854 | has_usages |
648 | .all(); | ||
649 | let mut usages = usages.iter().flat_map(|(_, rs)| rs.iter()); | ||
650 | |||
651 | usages.any(|reference| body.preceedes_range(reference.range)) | ||
652 | } | 855 | } |
653 | 856 | ||
654 | fn body_return_ty(ctx: &AssistContext, body: &FunctionBody) -> Option<RetType> { | 857 | fn body_return_ty(ctx: &AssistContext, body: &FunctionBody) -> Option<RetType> { |
@@ -675,10 +878,7 @@ enum Anchor { | |||
675 | fn scope_for_fn_insertion(body: &FunctionBody, anchor: Anchor) -> Option<SyntaxNode> { | 878 | fn scope_for_fn_insertion(body: &FunctionBody, anchor: Anchor) -> Option<SyntaxNode> { |
676 | match body { | 879 | match body { |
677 | FunctionBody::Expr(e) => scope_for_fn_insertion_node(e.syntax(), anchor), | 880 | FunctionBody::Expr(e) => scope_for_fn_insertion_node(e.syntax(), anchor), |
678 | FunctionBody::Span { elements, .. } => { | 881 | FunctionBody::Span { parent, .. } => scope_for_fn_insertion_node(parent.syntax(), anchor), |
679 | let node = elements.iter().find_map(|e| e.as_node())?; | ||
680 | scope_for_fn_insertion_node(&node, anchor) | ||
681 | } | ||
682 | } | 882 | } |
683 | } | 883 | } |
684 | 884 | ||
@@ -711,9 +911,24 @@ fn scope_for_fn_insertion_node(node: &SyntaxNode, anchor: Anchor) -> Option<Synt | |||
711 | last_ancestor | 911 | last_ancestor |
712 | } | 912 | } |
713 | 913 | ||
714 | fn format_replacement(ctx: &AssistContext, fun: &Function) -> String { | 914 | fn format_replacement(ctx: &AssistContext, fun: &Function, indent: IndentLevel) -> String { |
715 | let mut buf = String::new(); | 915 | let ret_ty = fun.return_type(ctx); |
916 | |||
917 | let args = fun.params.iter().map(|param| param.to_arg(ctx)); | ||
918 | let args = make::arg_list(args); | ||
919 | let call_expr = if fun.self_param.is_some() { | ||
920 | let self_arg = make::expr_path(make_path_from_text("self")); | ||
921 | make::expr_method_call(self_arg, &fun.name, args) | ||
922 | } else { | ||
923 | let func = make::expr_path(make_path_from_text(&fun.name)); | ||
924 | make::expr_call(func, args) | ||
925 | }; | ||
926 | |||
927 | let handler = FlowHandler::from_ret_ty(fun, &ret_ty); | ||
716 | 928 | ||
929 | let expr = handler.make_call_expr(call_expr).indent(indent); | ||
930 | |||
931 | let mut buf = String::new(); | ||
717 | match fun.vars_defined_in_body_and_outlive.as_slice() { | 932 | match fun.vars_defined_in_body_and_outlive.as_slice() { |
718 | [] => {} | 933 | [] => {} |
719 | [var] => format_to!(buf, "let {} = ", var.name(ctx.db()).unwrap()), | 934 | [var] => format_to!(buf, "let {} = ", var.name(ctx.db()).unwrap()), |
@@ -726,34 +941,131 @@ fn format_replacement(ctx: &AssistContext, fun: &Function) -> String { | |||
726 | buf.push_str(") = "); | 941 | buf.push_str(") = "); |
727 | } | 942 | } |
728 | } | 943 | } |
729 | 944 | format_to!(buf, "{}", expr); | |
730 | if fun.self_param.is_some() { | 945 | if fun.ret_ty.is_unit() |
731 | format_to!(buf, "self."); | 946 | && (!fun.vars_defined_in_body_and_outlive.is_empty() || !expr.is_block_like()) |
732 | } | 947 | { |
733 | format_to!(buf, "{}(", fun.name); | 948 | buf.push(';'); |
734 | format_arg_list_to(&mut buf, fun, ctx); | ||
735 | format_to!(buf, ")"); | ||
736 | |||
737 | if fun.ret_ty.is_unit() { | ||
738 | format_to!(buf, ";"); | ||
739 | } | 949 | } |
740 | |||
741 | buf | 950 | buf |
742 | } | 951 | } |
743 | 952 | ||
744 | fn format_arg_list_to(buf: &mut String, fun: &Function, ctx: &AssistContext) { | 953 | enum FlowHandler { |
745 | let mut it = fun.params.iter(); | 954 | None, |
746 | if let Some(param) = it.next() { | 955 | Try { kind: TryKind }, |
747 | format_arg_to(buf, ctx, param); | 956 | If { action: FlowKind }, |
957 | IfOption { action: FlowKind }, | ||
958 | MatchOption { none: FlowKind }, | ||
959 | MatchResult { err: FlowKind }, | ||
960 | } | ||
961 | |||
962 | impl FlowHandler { | ||
963 | fn from_ret_ty(fun: &Function, ret_ty: &FunType) -> FlowHandler { | ||
964 | match &fun.control_flow.kind { | ||
965 | None => FlowHandler::None, | ||
966 | Some(flow_kind) => { | ||
967 | let action = flow_kind.clone(); | ||
968 | if *ret_ty == FunType::Unit { | ||
969 | match flow_kind { | ||
970 | FlowKind::Return | FlowKind::Break | FlowKind::Continue => { | ||
971 | FlowHandler::If { action } | ||
972 | } | ||
973 | FlowKind::ReturnValue(_) | FlowKind::BreakValue(_) => { | ||
974 | FlowHandler::IfOption { action } | ||
975 | } | ||
976 | FlowKind::Try { kind } | FlowKind::TryReturn { kind, .. } => { | ||
977 | FlowHandler::Try { kind: kind.clone() } | ||
978 | } | ||
979 | } | ||
980 | } else { | ||
981 | match flow_kind { | ||
982 | FlowKind::Return | FlowKind::Break | FlowKind::Continue => { | ||
983 | FlowHandler::MatchOption { none: action } | ||
984 | } | ||
985 | FlowKind::ReturnValue(_) | FlowKind::BreakValue(_) => { | ||
986 | FlowHandler::MatchResult { err: action } | ||
987 | } | ||
988 | FlowKind::Try { kind } | FlowKind::TryReturn { kind, .. } => { | ||
989 | FlowHandler::Try { kind: kind.clone() } | ||
990 | } | ||
991 | } | ||
992 | } | ||
993 | } | ||
994 | } | ||
748 | } | 995 | } |
749 | for param in it { | 996 | |
750 | buf.push_str(", "); | 997 | fn make_call_expr(&self, call_expr: ast::Expr) -> ast::Expr { |
751 | format_arg_to(buf, ctx, param); | 998 | match self { |
999 | FlowHandler::None => call_expr, | ||
1000 | FlowHandler::Try { kind: _ } => make::expr_try(call_expr), | ||
1001 | FlowHandler::If { action } => { | ||
1002 | let action = action.make_result_handler(None); | ||
1003 | let stmt = make::expr_stmt(action); | ||
1004 | let block = make::block_expr(iter::once(stmt.into()), None); | ||
1005 | let condition = make::condition(call_expr, None); | ||
1006 | make::expr_if(condition, block, None) | ||
1007 | } | ||
1008 | FlowHandler::IfOption { action } => { | ||
1009 | let path = make_path_from_text("Some"); | ||
1010 | let value_pat = make::ident_pat(make::name("value")); | ||
1011 | let pattern = make::tuple_struct_pat(path, iter::once(value_pat.into())); | ||
1012 | let cond = make::condition(call_expr, Some(pattern.into())); | ||
1013 | let value = make::expr_path(make_path_from_text("value")); | ||
1014 | let action_expr = action.make_result_handler(Some(value)); | ||
1015 | let action_stmt = make::expr_stmt(action_expr); | ||
1016 | let then = make::block_expr(iter::once(action_stmt.into()), None); | ||
1017 | make::expr_if(cond, then, None) | ||
1018 | } | ||
1019 | FlowHandler::MatchOption { none } => { | ||
1020 | let some_name = "value"; | ||
1021 | |||
1022 | let some_arm = { | ||
1023 | let path = make_path_from_text("Some"); | ||
1024 | let value_pat = make::ident_pat(make::name(some_name)); | ||
1025 | let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); | ||
1026 | let value = make::expr_path(make_path_from_text(some_name)); | ||
1027 | make::match_arm(iter::once(pat.into()), value) | ||
1028 | }; | ||
1029 | let none_arm = { | ||
1030 | let path = make_path_from_text("None"); | ||
1031 | let pat = make::path_pat(path); | ||
1032 | make::match_arm(iter::once(pat), none.make_result_handler(None)) | ||
1033 | }; | ||
1034 | let arms = make::match_arm_list(vec![some_arm, none_arm]); | ||
1035 | make::expr_match(call_expr, arms) | ||
1036 | } | ||
1037 | FlowHandler::MatchResult { err } => { | ||
1038 | let ok_name = "value"; | ||
1039 | let err_name = "value"; | ||
1040 | |||
1041 | let ok_arm = { | ||
1042 | let path = make_path_from_text("Ok"); | ||
1043 | let value_pat = make::ident_pat(make::name(ok_name)); | ||
1044 | let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); | ||
1045 | let value = make::expr_path(make_path_from_text(ok_name)); | ||
1046 | make::match_arm(iter::once(pat.into()), value) | ||
1047 | }; | ||
1048 | let err_arm = { | ||
1049 | let path = make_path_from_text("Err"); | ||
1050 | let value_pat = make::ident_pat(make::name(err_name)); | ||
1051 | let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); | ||
1052 | let value = make::expr_path(make_path_from_text(err_name)); | ||
1053 | make::match_arm(iter::once(pat.into()), err.make_result_handler(Some(value))) | ||
1054 | }; | ||
1055 | let arms = make::match_arm_list(vec![ok_arm, err_arm]); | ||
1056 | make::expr_match(call_expr, arms) | ||
1057 | } | ||
1058 | } | ||
752 | } | 1059 | } |
753 | } | 1060 | } |
754 | 1061 | ||
755 | fn format_arg_to(buf: &mut String, ctx: &AssistContext, param: &Param) { | 1062 | fn make_path_from_text(text: &str) -> ast::Path { |
756 | format_to!(buf, "{}{}", param.value_prefix(), param.var.name(ctx.db()).unwrap()); | 1063 | make::path_unqualified(make::path_segment(make::name_ref(text))) |
1064 | } | ||
1065 | |||
1066 | fn path_expr_from_local(ctx: &AssistContext, var: Local) -> ast::Expr { | ||
1067 | let name = var.name(ctx.db()).unwrap().to_string(); | ||
1068 | make::expr_path(make_path_from_text(&name)) | ||
757 | } | 1069 | } |
758 | 1070 | ||
759 | fn format_function( | 1071 | fn format_function( |
@@ -764,132 +1076,233 @@ fn format_function( | |||
764 | new_indent: IndentLevel, | 1076 | new_indent: IndentLevel, |
765 | ) -> String { | 1077 | ) -> String { |
766 | let mut fn_def = String::new(); | 1078 | let mut fn_def = String::new(); |
767 | format_to!(fn_def, "\n\n{}fn $0{}(", new_indent, fun.name); | 1079 | let params = make_param_list(ctx, module, fun); |
768 | format_function_param_list_to(&mut fn_def, ctx, module, fun); | 1080 | let ret_ty = make_ret_ty(ctx, module, fun); |
769 | fn_def.push(')'); | 1081 | let body = make_body(ctx, old_indent, new_indent, fun); |
770 | format_function_ret_to(&mut fn_def, ctx, module, fun); | 1082 | format_to!(fn_def, "\n\n{}fn $0{}{}", new_indent, fun.name, params); |
771 | fn_def.push_str(" {"); | 1083 | if let Some(ret_ty) = ret_ty { |
772 | format_function_body_to(&mut fn_def, ctx, old_indent, new_indent, fun); | 1084 | format_to!(fn_def, " {}", ret_ty); |
773 | format_to!(fn_def, "{}}}", new_indent); | 1085 | } |
1086 | format_to!(fn_def, " {}", body); | ||
774 | 1087 | ||
775 | fn_def | 1088 | fn_def |
776 | } | 1089 | } |
777 | 1090 | ||
778 | fn format_function_param_list_to( | 1091 | fn make_param_list(ctx: &AssistContext, module: hir::Module, fun: &Function) -> ast::ParamList { |
779 | fn_def: &mut String, | 1092 | let self_param = fun.self_param.clone(); |
780 | ctx: &AssistContext, | 1093 | let params = fun.params.iter().map(|param| param.to_param(ctx, module)); |
781 | module: hir::Module, | 1094 | make::param_list(self_param, params) |
782 | fun: &Function, | ||
783 | ) { | ||
784 | let mut it = fun.params.iter(); | ||
785 | if let Some(self_param) = &fun.self_param { | ||
786 | format_to!(fn_def, "{}", self_param); | ||
787 | } else if let Some(param) = it.next() { | ||
788 | format_param_to(fn_def, ctx, module, param); | ||
789 | } | ||
790 | for param in it { | ||
791 | fn_def.push_str(", "); | ||
792 | format_param_to(fn_def, ctx, module, param); | ||
793 | } | ||
794 | } | ||
795 | |||
796 | fn format_param_to(fn_def: &mut String, ctx: &AssistContext, module: hir::Module, param: &Param) { | ||
797 | format_to!( | ||
798 | fn_def, | ||
799 | "{}{}: {}{}", | ||
800 | param.mut_pattern(), | ||
801 | param.var.name(ctx.db()).unwrap(), | ||
802 | param.type_prefix(), | ||
803 | format_type(¶m.ty, ctx, module) | ||
804 | ); | ||
805 | } | 1095 | } |
806 | 1096 | ||
807 | fn format_function_ret_to( | 1097 | impl FunType { |
808 | fn_def: &mut String, | 1098 | fn make_ty(&self, ctx: &AssistContext, module: hir::Module) -> ast::Type { |
809 | ctx: &AssistContext, | 1099 | match self { |
810 | module: hir::Module, | 1100 | FunType::Unit => make::ty_unit(), |
811 | fun: &Function, | 1101 | FunType::Single(ty) => make_ty(ty, ctx, module), |
812 | ) { | 1102 | FunType::Tuple(types) => match types.as_slice() { |
813 | if let Some(ty) = fun.ret_ty.as_fn_ret() { | 1103 | [] => { |
814 | format_to!(fn_def, " -> {}", format_type(ty, ctx, module)); | 1104 | stdx::never!("tuple type with 0 elements"); |
815 | } else { | 1105 | make::ty_unit() |
816 | match fun.vars_defined_in_body_and_outlive.as_slice() { | ||
817 | [] => {} | ||
818 | [var] => { | ||
819 | format_to!(fn_def, " -> {}", format_type(&var.ty(ctx.db()), ctx, module)); | ||
820 | } | ||
821 | [v0, vs @ ..] => { | ||
822 | format_to!(fn_def, " -> ({}", format_type(&v0.ty(ctx.db()), ctx, module)); | ||
823 | for var in vs { | ||
824 | format_to!(fn_def, ", {}", format_type(&var.ty(ctx.db()), ctx, module)); | ||
825 | } | 1106 | } |
826 | fn_def.push(')'); | 1107 | [ty] => { |
827 | } | 1108 | stdx::never!("tuple type with 1 element"); |
1109 | make_ty(ty, ctx, module) | ||
1110 | } | ||
1111 | types => { | ||
1112 | let types = types.iter().map(|ty| make_ty(ty, ctx, module)); | ||
1113 | make::ty_tuple(types) | ||
1114 | } | ||
1115 | }, | ||
828 | } | 1116 | } |
829 | } | 1117 | } |
830 | } | 1118 | } |
831 | 1119 | ||
832 | fn format_function_body_to( | 1120 | fn make_ret_ty(ctx: &AssistContext, module: hir::Module, fun: &Function) -> Option<ast::RetType> { |
833 | fn_def: &mut String, | 1121 | let fun_ty = fun.return_type(ctx); |
1122 | let handler = FlowHandler::from_ret_ty(fun, &fun_ty); | ||
1123 | let ret_ty = match &handler { | ||
1124 | FlowHandler::None => { | ||
1125 | if matches!(fun_ty, FunType::Unit) { | ||
1126 | return None; | ||
1127 | } | ||
1128 | fun_ty.make_ty(ctx, module) | ||
1129 | } | ||
1130 | FlowHandler::Try { kind: TryKind::Option } => { | ||
1131 | make::ty_generic(make::name_ref("Option"), iter::once(fun_ty.make_ty(ctx, module))) | ||
1132 | } | ||
1133 | FlowHandler::Try { kind: TryKind::Result { ty: parent_ret_ty } } => { | ||
1134 | let handler_ty = parent_ret_ty | ||
1135 | .type_parameters() | ||
1136 | .nth(1) | ||
1137 | .map(|ty| make_ty(&ty, ctx, module)) | ||
1138 | .unwrap_or_else(make::ty_unit); | ||
1139 | make::ty_generic( | ||
1140 | make::name_ref("Result"), | ||
1141 | vec![fun_ty.make_ty(ctx, module), handler_ty], | ||
1142 | ) | ||
1143 | } | ||
1144 | FlowHandler::If { .. } => make::ty("bool"), | ||
1145 | FlowHandler::IfOption { action } => { | ||
1146 | let handler_ty = action | ||
1147 | .expr_ty(ctx) | ||
1148 | .map(|ty| make_ty(&ty, ctx, module)) | ||
1149 | .unwrap_or_else(make::ty_unit); | ||
1150 | make::ty_generic(make::name_ref("Option"), iter::once(handler_ty)) | ||
1151 | } | ||
1152 | FlowHandler::MatchOption { .. } => { | ||
1153 | make::ty_generic(make::name_ref("Option"), iter::once(fun_ty.make_ty(ctx, module))) | ||
1154 | } | ||
1155 | FlowHandler::MatchResult { err } => { | ||
1156 | let handler_ty = | ||
1157 | err.expr_ty(ctx).map(|ty| make_ty(&ty, ctx, module)).unwrap_or_else(make::ty_unit); | ||
1158 | make::ty_generic( | ||
1159 | make::name_ref("Result"), | ||
1160 | vec![fun_ty.make_ty(ctx, module), handler_ty], | ||
1161 | ) | ||
1162 | } | ||
1163 | }; | ||
1164 | Some(make::ret_type(ret_ty)) | ||
1165 | } | ||
1166 | |||
1167 | fn make_body( | ||
834 | ctx: &AssistContext, | 1168 | ctx: &AssistContext, |
835 | old_indent: IndentLevel, | 1169 | old_indent: IndentLevel, |
836 | new_indent: IndentLevel, | 1170 | new_indent: IndentLevel, |
837 | fun: &Function, | 1171 | fun: &Function, |
838 | ) { | 1172 | ) -> ast::BlockExpr { |
839 | match &fun.body { | 1173 | let ret_ty = fun.return_type(ctx); |
1174 | let handler = FlowHandler::from_ret_ty(fun, &ret_ty); | ||
1175 | let block = match &fun.body { | ||
840 | FunctionBody::Expr(expr) => { | 1176 | FunctionBody::Expr(expr) => { |
841 | fn_def.push('\n'); | 1177 | let expr = rewrite_body_segment(ctx, &fun.params, &handler, expr.syntax()); |
842 | let expr = expr.dedent(old_indent).indent(new_indent + 1); | 1178 | let expr = ast::Expr::cast(expr).unwrap(); |
843 | let expr = fix_param_usages(ctx, &fun.params, expr.syntax()); | 1179 | let expr = expr.dedent(old_indent).indent(IndentLevel(1)); |
844 | format_to!(fn_def, "{}{}", new_indent + 1, expr); | 1180 | |
845 | fn_def.push('\n'); | 1181 | make::block_expr(Vec::new(), Some(expr)) |
846 | } | 1182 | } |
847 | FunctionBody::Span { elements, leading_indent } => { | 1183 | FunctionBody::Span { parent, text_range } => { |
848 | format_to!(fn_def, "{}", leading_indent); | 1184 | let mut elements: Vec<_> = parent |
849 | let new_indent_str = format!("\n{}", new_indent + 1); | 1185 | .syntax() |
850 | for mut element in elements { | 1186 | .children() |
851 | let new_ws; | 1187 | .filter(|it| text_range.contains_range(it.text_range())) |
852 | if let Some(ws) = element.as_token().cloned().and_then(ast::Whitespace::cast) { | 1188 | .map(|it| rewrite_body_segment(ctx, &fun.params, &handler, &it)) |
853 | let text = ws.syntax().text(); | 1189 | .collect(); |
854 | if text.contains('\n') { | 1190 | |
855 | let new_text = text.replace(&format!("\n{}", old_indent), &new_indent_str); | 1191 | let mut tail_expr = match elements.pop() { |
856 | new_ws = ast::make::tokens::whitespace(&new_text).into(); | 1192 | Some(node) => ast::Expr::cast(node.clone()).or_else(|| { |
857 | element = &new_ws; | 1193 | elements.push(node); |
858 | } | 1194 | None |
859 | } | 1195 | }), |
1196 | None => None, | ||
1197 | }; | ||
860 | 1198 | ||
861 | match element { | 1199 | if tail_expr.is_none() { |
862 | syntax::NodeOrToken::Node(node) => { | 1200 | match fun.vars_defined_in_body_and_outlive.as_slice() { |
863 | format_to!(fn_def, "{}", fix_param_usages(ctx, &fun.params, node)); | 1201 | [] => {} |
1202 | [var] => { | ||
1203 | tail_expr = Some(path_expr_from_local(ctx, *var)); | ||
864 | } | 1204 | } |
865 | syntax::NodeOrToken::Token(token) => { | 1205 | vars => { |
866 | format_to!(fn_def, "{}", token); | 1206 | let exprs = vars.iter().map(|var| path_expr_from_local(ctx, *var)); |
1207 | let expr = make::expr_tuple(exprs); | ||
1208 | tail_expr = Some(expr); | ||
867 | } | 1209 | } |
868 | } | 1210 | } |
869 | } | 1211 | } |
870 | if !fn_def.ends_with('\n') { | 1212 | |
871 | fn_def.push('\n'); | 1213 | let elements = elements.into_iter().filter_map(|node| match ast::Stmt::cast(node) { |
872 | } | 1214 | Some(stmt) => Some(stmt), |
1215 | None => { | ||
1216 | stdx::never!("block contains non-statement"); | ||
1217 | None | ||
1218 | } | ||
1219 | }); | ||
1220 | |||
1221 | let body_indent = IndentLevel(1); | ||
1222 | let elements = elements.map(|stmt| stmt.dedent(old_indent).indent(body_indent)); | ||
1223 | let tail_expr = tail_expr.map(|expr| expr.dedent(old_indent).indent(body_indent)); | ||
1224 | |||
1225 | make::block_expr(elements, tail_expr) | ||
873 | } | 1226 | } |
874 | } | 1227 | }; |
875 | 1228 | ||
876 | match fun.vars_defined_in_body_and_outlive.as_slice() { | 1229 | let block = match &handler { |
877 | [] => {} | 1230 | FlowHandler::None => block, |
878 | [var] => format_to!(fn_def, "{}{}\n", new_indent + 1, var.name(ctx.db()).unwrap()), | 1231 | FlowHandler::Try { kind } => { |
879 | [v0, vs @ ..] => { | 1232 | let block = with_default_tail_expr(block, make::expr_unit()); |
880 | format_to!(fn_def, "{}({}", new_indent + 1, v0.name(ctx.db()).unwrap()); | 1233 | map_tail_expr(block, |tail_expr| { |
881 | for var in vs { | 1234 | let constructor = match kind { |
882 | format_to!(fn_def, ", {}", var.name(ctx.db()).unwrap()); | 1235 | TryKind::Option => "Some", |
883 | } | 1236 | TryKind::Result { .. } => "Ok", |
884 | fn_def.push_str(")\n"); | 1237 | }; |
1238 | let func = make::expr_path(make_path_from_text(constructor)); | ||
1239 | let args = make::arg_list(iter::once(tail_expr)); | ||
1240 | make::expr_call(func, args) | ||
1241 | }) | ||
1242 | } | ||
1243 | FlowHandler::If { .. } => { | ||
1244 | let lit_false = ast::Literal::cast(make::tokens::literal("false").parent()).unwrap(); | ||
1245 | with_tail_expr(block, lit_false.into()) | ||
885 | } | 1246 | } |
1247 | FlowHandler::IfOption { .. } => { | ||
1248 | let none = make::expr_path(make_path_from_text("None")); | ||
1249 | with_tail_expr(block, none) | ||
1250 | } | ||
1251 | FlowHandler::MatchOption { .. } => map_tail_expr(block, |tail_expr| { | ||
1252 | let some = make::expr_path(make_path_from_text("Some")); | ||
1253 | let args = make::arg_list(iter::once(tail_expr)); | ||
1254 | make::expr_call(some, args) | ||
1255 | }), | ||
1256 | FlowHandler::MatchResult { .. } => map_tail_expr(block, |tail_expr| { | ||
1257 | let ok = make::expr_path(make_path_from_text("Ok")); | ||
1258 | let args = make::arg_list(iter::once(tail_expr)); | ||
1259 | make::expr_call(ok, args) | ||
1260 | }), | ||
1261 | }; | ||
1262 | |||
1263 | block.indent(new_indent) | ||
1264 | } | ||
1265 | |||
1266 | fn map_tail_expr(block: ast::BlockExpr, f: impl FnOnce(ast::Expr) -> ast::Expr) -> ast::BlockExpr { | ||
1267 | let tail_expr = match block.tail_expr() { | ||
1268 | Some(tail_expr) => tail_expr, | ||
1269 | None => return block, | ||
1270 | }; | ||
1271 | make::block_expr(block.statements(), Some(f(tail_expr))) | ||
1272 | } | ||
1273 | |||
1274 | fn with_default_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr { | ||
1275 | match block.tail_expr() { | ||
1276 | Some(_) => block, | ||
1277 | None => make::block_expr(block.statements(), Some(tail_expr)), | ||
886 | } | 1278 | } |
887 | } | 1279 | } |
888 | 1280 | ||
1281 | fn with_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr { | ||
1282 | let stmt_tail = block.tail_expr().map(|expr| make::expr_stmt(expr).into()); | ||
1283 | let stmts = block.statements().chain(stmt_tail); | ||
1284 | make::block_expr(stmts, Some(tail_expr)) | ||
1285 | } | ||
1286 | |||
889 | fn format_type(ty: &hir::Type, ctx: &AssistContext, module: hir::Module) -> String { | 1287 | fn format_type(ty: &hir::Type, ctx: &AssistContext, module: hir::Module) -> String { |
890 | ty.display_source_code(ctx.db(), module.into()).ok().unwrap_or_else(|| "()".to_string()) | 1288 | ty.display_source_code(ctx.db(), module.into()).ok().unwrap_or_else(|| "()".to_string()) |
891 | } | 1289 | } |
892 | 1290 | ||
1291 | fn make_ty(ty: &hir::Type, ctx: &AssistContext, module: hir::Module) -> ast::Type { | ||
1292 | let ty_str = format_type(ty, ctx, module); | ||
1293 | make::ty(&ty_str) | ||
1294 | } | ||
1295 | |||
1296 | fn rewrite_body_segment( | ||
1297 | ctx: &AssistContext, | ||
1298 | params: &[Param], | ||
1299 | handler: &FlowHandler, | ||
1300 | syntax: &SyntaxNode, | ||
1301 | ) -> SyntaxNode { | ||
1302 | let syntax = fix_param_usages(ctx, params, syntax); | ||
1303 | update_external_control_flow(handler, &syntax) | ||
1304 | } | ||
1305 | |||
893 | /// change all usages to account for added `&`/`&mut` for some params | 1306 | /// change all usages to account for added `&`/`&mut` for some params |
894 | fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode) -> SyntaxNode { | 1307 | fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode) -> SyntaxNode { |
895 | let mut rewriter = SyntaxRewriter::default(); | 1308 | let mut rewriter = SyntaxRewriter::default(); |
@@ -919,7 +1332,7 @@ fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode) | |||
919 | rewriter.replace_ast(&node.clone().into(), &node.expr().unwrap()); | 1332 | rewriter.replace_ast(&node.clone().into(), &node.expr().unwrap()); |
920 | } | 1333 | } |
921 | Some(_) | None => { | 1334 | Some(_) | None => { |
922 | rewriter.replace_ast(&path, &ast::make::expr_prefix(T![*], path.clone())); | 1335 | rewriter.replace_ast(&path, &make::expr_prefix(T![*], path.clone())); |
923 | } | 1336 | } |
924 | }; | 1337 | }; |
925 | } | 1338 | } |
@@ -928,6 +1341,98 @@ fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode) | |||
928 | rewriter.rewrite(syntax) | 1341 | rewriter.rewrite(syntax) |
929 | } | 1342 | } |
930 | 1343 | ||
1344 | fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) -> SyntaxNode { | ||
1345 | let mut rewriter = SyntaxRewriter::default(); | ||
1346 | |||
1347 | let mut nested_loop = None; | ||
1348 | let mut nested_scope = None; | ||
1349 | for event in syntax.preorder() { | ||
1350 | let node = match event { | ||
1351 | WalkEvent::Enter(e) => { | ||
1352 | match e.kind() { | ||
1353 | SyntaxKind::LOOP_EXPR | SyntaxKind::WHILE_EXPR | SyntaxKind::FOR_EXPR => { | ||
1354 | if nested_loop.is_none() { | ||
1355 | nested_loop = Some(e.clone()); | ||
1356 | } | ||
1357 | } | ||
1358 | SyntaxKind::FN | ||
1359 | | SyntaxKind::CONST | ||
1360 | | SyntaxKind::STATIC | ||
1361 | | SyntaxKind::IMPL | ||
1362 | | SyntaxKind::MODULE => { | ||
1363 | if nested_scope.is_none() { | ||
1364 | nested_scope = Some(e.clone()); | ||
1365 | } | ||
1366 | } | ||
1367 | _ => {} | ||
1368 | } | ||
1369 | e | ||
1370 | } | ||
1371 | WalkEvent::Leave(e) => { | ||
1372 | if nested_loop.as_ref() == Some(&e) { | ||
1373 | nested_loop = None; | ||
1374 | } | ||
1375 | if nested_scope.as_ref() == Some(&e) { | ||
1376 | nested_scope = None; | ||
1377 | } | ||
1378 | continue; | ||
1379 | } | ||
1380 | }; | ||
1381 | if nested_scope.is_some() { | ||
1382 | continue; | ||
1383 | } | ||
1384 | let expr = match ast::Expr::cast(node) { | ||
1385 | Some(e) => e, | ||
1386 | None => continue, | ||
1387 | }; | ||
1388 | match expr { | ||
1389 | ast::Expr::ReturnExpr(return_expr) if nested_scope.is_none() => { | ||
1390 | let expr = return_expr.expr(); | ||
1391 | if let Some(replacement) = make_rewritten_flow(handler, expr) { | ||
1392 | rewriter.replace_ast(&return_expr.into(), &replacement); | ||
1393 | } | ||
1394 | } | ||
1395 | ast::Expr::BreakExpr(break_expr) if nested_loop.is_none() => { | ||
1396 | let expr = break_expr.expr(); | ||
1397 | if let Some(replacement) = make_rewritten_flow(handler, expr) { | ||
1398 | rewriter.replace_ast(&break_expr.into(), &replacement); | ||
1399 | } | ||
1400 | } | ||
1401 | ast::Expr::ContinueExpr(continue_expr) if nested_loop.is_none() => { | ||
1402 | if let Some(replacement) = make_rewritten_flow(handler, None) { | ||
1403 | rewriter.replace_ast(&continue_expr.into(), &replacement); | ||
1404 | } | ||
1405 | } | ||
1406 | _ => { | ||
1407 | // do nothing | ||
1408 | } | ||
1409 | } | ||
1410 | } | ||
1411 | |||
1412 | rewriter.rewrite(syntax) | ||
1413 | } | ||
1414 | |||
1415 | fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Option<ast::Expr> { | ||
1416 | let value = match handler { | ||
1417 | FlowHandler::None | FlowHandler::Try { .. } => return None, | ||
1418 | FlowHandler::If { .. } => { | ||
1419 | ast::Literal::cast(make::tokens::literal("true").parent()).unwrap().into() | ||
1420 | } | ||
1421 | FlowHandler::IfOption { .. } => { | ||
1422 | let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new())); | ||
1423 | let args = make::arg_list(iter::once(expr)); | ||
1424 | make::expr_call(make::expr_path(make_path_from_text("Some")), args) | ||
1425 | } | ||
1426 | FlowHandler::MatchOption { .. } => make::expr_path(make_path_from_text("None")), | ||
1427 | FlowHandler::MatchResult { .. } => { | ||
1428 | let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new())); | ||
1429 | let args = make::arg_list(iter::once(expr)); | ||
1430 | make::expr_call(make::expr_path(make_path_from_text("Err")), args) | ||
1431 | } | ||
1432 | }; | ||
1433 | Some(make::expr_return(Some(value))) | ||
1434 | } | ||
1435 | |||
931 | #[cfg(test)] | 1436 | #[cfg(test)] |
932 | mod tests { | 1437 | mod tests { |
933 | use crate::tests::{check_assist, check_assist_not_applicable}; | 1438 | use crate::tests::{check_assist, check_assist_not_applicable}; |
@@ -2096,6 +2601,66 @@ fn $0fun_name(c: &Counter) { | |||
2096 | } | 2601 | } |
2097 | 2602 | ||
2098 | #[test] | 2603 | #[test] |
2604 | fn copy_used_after() { | ||
2605 | check_assist( | ||
2606 | extract_function, | ||
2607 | r##" | ||
2608 | #[lang = "copy"] | ||
2609 | pub trait Copy {} | ||
2610 | impl Copy for i32 {} | ||
2611 | fn foo() { | ||
2612 | let n = 0; | ||
2613 | $0let m = n;$0 | ||
2614 | let k = n; | ||
2615 | }"##, | ||
2616 | r##" | ||
2617 | #[lang = "copy"] | ||
2618 | pub trait Copy {} | ||
2619 | impl Copy for i32 {} | ||
2620 | fn foo() { | ||
2621 | let n = 0; | ||
2622 | fun_name(n); | ||
2623 | let k = n; | ||
2624 | } | ||
2625 | |||
2626 | fn $0fun_name(n: i32) { | ||
2627 | let m = n; | ||
2628 | }"##, | ||
2629 | ) | ||
2630 | } | ||
2631 | |||
2632 | #[test] | ||
2633 | fn copy_custom_used_after() { | ||
2634 | check_assist( | ||
2635 | extract_function, | ||
2636 | r##" | ||
2637 | #[lang = "copy"] | ||
2638 | pub trait Copy {} | ||
2639 | struct Counter(i32); | ||
2640 | impl Copy for Counter {} | ||
2641 | fn foo() { | ||
2642 | let c = Counter(0); | ||
2643 | $0let n = c.0;$0 | ||
2644 | let m = c.0; | ||
2645 | }"##, | ||
2646 | r##" | ||
2647 | #[lang = "copy"] | ||
2648 | pub trait Copy {} | ||
2649 | struct Counter(i32); | ||
2650 | impl Copy for Counter {} | ||
2651 | fn foo() { | ||
2652 | let c = Counter(0); | ||
2653 | fun_name(c); | ||
2654 | let m = c.0; | ||
2655 | } | ||
2656 | |||
2657 | fn $0fun_name(c: Counter) { | ||
2658 | let n = c.0; | ||
2659 | }"##, | ||
2660 | ); | ||
2661 | } | ||
2662 | |||
2663 | #[test] | ||
2099 | fn indented_stmts() { | 2664 | fn indented_stmts() { |
2100 | check_assist( | 2665 | check_assist( |
2101 | extract_function, | 2666 | extract_function, |
@@ -2156,4 +2721,658 @@ mod bar { | |||
2156 | }", | 2721 | }", |
2157 | ); | 2722 | ); |
2158 | } | 2723 | } |
2724 | |||
2725 | #[test] | ||
2726 | fn break_loop() { | ||
2727 | check_assist( | ||
2728 | extract_function, | ||
2729 | r##" | ||
2730 | enum Option<T> { | ||
2731 | #[lang = "None"] None, | ||
2732 | #[lang = "Some"] Some(T), | ||
2733 | } | ||
2734 | use Option::*; | ||
2735 | fn foo() { | ||
2736 | loop { | ||
2737 | let n = 1; | ||
2738 | $0let m = n + 1; | ||
2739 | break; | ||
2740 | let k = 2;$0 | ||
2741 | let h = 1 + k; | ||
2742 | } | ||
2743 | }"##, | ||
2744 | r##" | ||
2745 | enum Option<T> { | ||
2746 | #[lang = "None"] None, | ||
2747 | #[lang = "Some"] Some(T), | ||
2748 | } | ||
2749 | use Option::*; | ||
2750 | fn foo() { | ||
2751 | loop { | ||
2752 | let n = 1; | ||
2753 | let k = match fun_name(n) { | ||
2754 | Some(value) => value, | ||
2755 | None => break, | ||
2756 | }; | ||
2757 | let h = 1 + k; | ||
2758 | } | ||
2759 | } | ||
2760 | |||
2761 | fn $0fun_name(n: i32) -> Option<i32> { | ||
2762 | let m = n + 1; | ||
2763 | return None; | ||
2764 | let k = 2; | ||
2765 | Some(k) | ||
2766 | }"##, | ||
2767 | ); | ||
2768 | } | ||
2769 | |||
2770 | #[test] | ||
2771 | fn return_to_parent() { | ||
2772 | check_assist( | ||
2773 | extract_function, | ||
2774 | r##" | ||
2775 | #[lang = "copy"] | ||
2776 | pub trait Copy {} | ||
2777 | impl Copy for i32 {} | ||
2778 | enum Result<T, E> { | ||
2779 | #[lang = "Ok"] Ok(T), | ||
2780 | #[lang = "Err"] Err(E), | ||
2781 | } | ||
2782 | use Result::*; | ||
2783 | fn foo() -> i64 { | ||
2784 | let n = 1; | ||
2785 | $0let m = n + 1; | ||
2786 | return 1; | ||
2787 | let k = 2;$0 | ||
2788 | (n + k) as i64 | ||
2789 | }"##, | ||
2790 | r##" | ||
2791 | #[lang = "copy"] | ||
2792 | pub trait Copy {} | ||
2793 | impl Copy for i32 {} | ||
2794 | enum Result<T, E> { | ||
2795 | #[lang = "Ok"] Ok(T), | ||
2796 | #[lang = "Err"] Err(E), | ||
2797 | } | ||
2798 | use Result::*; | ||
2799 | fn foo() -> i64 { | ||
2800 | let n = 1; | ||
2801 | let k = match fun_name(n) { | ||
2802 | Ok(value) => value, | ||
2803 | Err(value) => return value, | ||
2804 | }; | ||
2805 | (n + k) as i64 | ||
2806 | } | ||
2807 | |||
2808 | fn $0fun_name(n: i32) -> Result<i32, i64> { | ||
2809 | let m = n + 1; | ||
2810 | return Err(1); | ||
2811 | let k = 2; | ||
2812 | Ok(k) | ||
2813 | }"##, | ||
2814 | ); | ||
2815 | } | ||
2816 | |||
2817 | #[test] | ||
2818 | fn break_and_continue() { | ||
2819 | mark::check!(external_control_flow_break_and_continue); | ||
2820 | check_assist_not_applicable( | ||
2821 | extract_function, | ||
2822 | r##" | ||
2823 | fn foo() { | ||
2824 | loop { | ||
2825 | let n = 1; | ||
2826 | $0let m = n + 1; | ||
2827 | break; | ||
2828 | let k = 2; | ||
2829 | continue; | ||
2830 | let k = k + 1;$0 | ||
2831 | let r = n + k; | ||
2832 | } | ||
2833 | }"##, | ||
2834 | ); | ||
2835 | } | ||
2836 | |||
2837 | #[test] | ||
2838 | fn return_and_break() { | ||
2839 | mark::check!(external_control_flow_return_and_bc); | ||
2840 | check_assist_not_applicable( | ||
2841 | extract_function, | ||
2842 | r##" | ||
2843 | fn foo() { | ||
2844 | loop { | ||
2845 | let n = 1; | ||
2846 | $0let m = n + 1; | ||
2847 | break; | ||
2848 | let k = 2; | ||
2849 | return; | ||
2850 | let k = k + 1;$0 | ||
2851 | let r = n + k; | ||
2852 | } | ||
2853 | }"##, | ||
2854 | ); | ||
2855 | } | ||
2856 | |||
2857 | #[test] | ||
2858 | fn break_loop_with_if() { | ||
2859 | check_assist( | ||
2860 | extract_function, | ||
2861 | r##" | ||
2862 | fn foo() { | ||
2863 | loop { | ||
2864 | let mut n = 1; | ||
2865 | $0let m = n + 1; | ||
2866 | break; | ||
2867 | n += m;$0 | ||
2868 | let h = 1 + n; | ||
2869 | } | ||
2870 | }"##, | ||
2871 | r##" | ||
2872 | fn foo() { | ||
2873 | loop { | ||
2874 | let mut n = 1; | ||
2875 | if fun_name(&mut n) { | ||
2876 | break; | ||
2877 | } | ||
2878 | let h = 1 + n; | ||
2879 | } | ||
2880 | } | ||
2881 | |||
2882 | fn $0fun_name(n: &mut i32) -> bool { | ||
2883 | let m = *n + 1; | ||
2884 | return true; | ||
2885 | *n += m; | ||
2886 | false | ||
2887 | }"##, | ||
2888 | ); | ||
2889 | } | ||
2890 | |||
2891 | #[test] | ||
2892 | fn break_loop_nested() { | ||
2893 | check_assist( | ||
2894 | extract_function, | ||
2895 | r##" | ||
2896 | fn foo() { | ||
2897 | loop { | ||
2898 | let mut n = 1; | ||
2899 | $0let m = n + 1; | ||
2900 | if m == 42 { | ||
2901 | break; | ||
2902 | }$0 | ||
2903 | let h = 1; | ||
2904 | } | ||
2905 | }"##, | ||
2906 | r##" | ||
2907 | fn foo() { | ||
2908 | loop { | ||
2909 | let mut n = 1; | ||
2910 | if fun_name(n) { | ||
2911 | break; | ||
2912 | } | ||
2913 | let h = 1; | ||
2914 | } | ||
2915 | } | ||
2916 | |||
2917 | fn $0fun_name(n: i32) -> bool { | ||
2918 | let m = n + 1; | ||
2919 | if m == 42 { | ||
2920 | return true; | ||
2921 | } | ||
2922 | false | ||
2923 | }"##, | ||
2924 | ); | ||
2925 | } | ||
2926 | |||
2927 | #[test] | ||
2928 | fn return_from_nested_loop() { | ||
2929 | check_assist( | ||
2930 | extract_function, | ||
2931 | r##" | ||
2932 | fn foo() { | ||
2933 | loop { | ||
2934 | let n = 1; | ||
2935 | $0 | ||
2936 | let k = 1; | ||
2937 | loop { | ||
2938 | return; | ||
2939 | } | ||
2940 | let m = k + 1;$0 | ||
2941 | let h = 1 + m; | ||
2942 | } | ||
2943 | }"##, | ||
2944 | r##" | ||
2945 | fn foo() { | ||
2946 | loop { | ||
2947 | let n = 1; | ||
2948 | let m = match fun_name() { | ||
2949 | Some(value) => value, | ||
2950 | None => return, | ||
2951 | }; | ||
2952 | let h = 1 + m; | ||
2953 | } | ||
2954 | } | ||
2955 | |||
2956 | fn $0fun_name() -> Option<i32> { | ||
2957 | let k = 1; | ||
2958 | loop { | ||
2959 | return None; | ||
2960 | } | ||
2961 | let m = k + 1; | ||
2962 | Some(m) | ||
2963 | }"##, | ||
2964 | ); | ||
2965 | } | ||
2966 | |||
2967 | #[test] | ||
2968 | fn break_from_nested_loop() { | ||
2969 | check_assist( | ||
2970 | extract_function, | ||
2971 | r##" | ||
2972 | fn foo() { | ||
2973 | loop { | ||
2974 | let n = 1; | ||
2975 | $0let k = 1; | ||
2976 | loop { | ||
2977 | break; | ||
2978 | } | ||
2979 | let m = k + 1;$0 | ||
2980 | let h = 1 + m; | ||
2981 | } | ||
2982 | }"##, | ||
2983 | r##" | ||
2984 | fn foo() { | ||
2985 | loop { | ||
2986 | let n = 1; | ||
2987 | let m = fun_name(); | ||
2988 | let h = 1 + m; | ||
2989 | } | ||
2990 | } | ||
2991 | |||
2992 | fn $0fun_name() -> i32 { | ||
2993 | let k = 1; | ||
2994 | loop { | ||
2995 | break; | ||
2996 | } | ||
2997 | let m = k + 1; | ||
2998 | m | ||
2999 | }"##, | ||
3000 | ); | ||
3001 | } | ||
3002 | |||
3003 | #[test] | ||
3004 | fn break_from_nested_and_outer_loops() { | ||
3005 | check_assist( | ||
3006 | extract_function, | ||
3007 | r##" | ||
3008 | fn foo() { | ||
3009 | loop { | ||
3010 | let n = 1; | ||
3011 | $0let k = 1; | ||
3012 | loop { | ||
3013 | break; | ||
3014 | } | ||
3015 | if k == 42 { | ||
3016 | break; | ||
3017 | } | ||
3018 | let m = k + 1;$0 | ||
3019 | let h = 1 + m; | ||
3020 | } | ||
3021 | }"##, | ||
3022 | r##" | ||
3023 | fn foo() { | ||
3024 | loop { | ||
3025 | let n = 1; | ||
3026 | let m = match fun_name() { | ||
3027 | Some(value) => value, | ||
3028 | None => break, | ||
3029 | }; | ||
3030 | let h = 1 + m; | ||
3031 | } | ||
3032 | } | ||
3033 | |||
3034 | fn $0fun_name() -> Option<i32> { | ||
3035 | let k = 1; | ||
3036 | loop { | ||
3037 | break; | ||
3038 | } | ||
3039 | if k == 42 { | ||
3040 | return None; | ||
3041 | } | ||
3042 | let m = k + 1; | ||
3043 | Some(m) | ||
3044 | }"##, | ||
3045 | ); | ||
3046 | } | ||
3047 | |||
3048 | #[test] | ||
3049 | fn return_from_nested_fn() { | ||
3050 | check_assist( | ||
3051 | extract_function, | ||
3052 | r##" | ||
3053 | fn foo() { | ||
3054 | loop { | ||
3055 | let n = 1; | ||
3056 | $0let k = 1; | ||
3057 | fn test() { | ||
3058 | return; | ||
3059 | } | ||
3060 | let m = k + 1;$0 | ||
3061 | let h = 1 + m; | ||
3062 | } | ||
3063 | }"##, | ||
3064 | r##" | ||
3065 | fn foo() { | ||
3066 | loop { | ||
3067 | let n = 1; | ||
3068 | let m = fun_name(); | ||
3069 | let h = 1 + m; | ||
3070 | } | ||
3071 | } | ||
3072 | |||
3073 | fn $0fun_name() -> i32 { | ||
3074 | let k = 1; | ||
3075 | fn test() { | ||
3076 | return; | ||
3077 | } | ||
3078 | let m = k + 1; | ||
3079 | m | ||
3080 | }"##, | ||
3081 | ); | ||
3082 | } | ||
3083 | |||
3084 | #[test] | ||
3085 | fn break_with_value() { | ||
3086 | check_assist( | ||
3087 | extract_function, | ||
3088 | r##" | ||
3089 | fn foo() -> i32 { | ||
3090 | loop { | ||
3091 | let n = 1; | ||
3092 | $0let k = 1; | ||
3093 | if k == 42 { | ||
3094 | break 3; | ||
3095 | } | ||
3096 | let m = k + 1;$0 | ||
3097 | let h = 1; | ||
3098 | } | ||
3099 | }"##, | ||
3100 | r##" | ||
3101 | fn foo() -> i32 { | ||
3102 | loop { | ||
3103 | let n = 1; | ||
3104 | if let Some(value) = fun_name() { | ||
3105 | break value; | ||
3106 | } | ||
3107 | let h = 1; | ||
3108 | } | ||
3109 | } | ||
3110 | |||
3111 | fn $0fun_name() -> Option<i32> { | ||
3112 | let k = 1; | ||
3113 | if k == 42 { | ||
3114 | return Some(3); | ||
3115 | } | ||
3116 | let m = k + 1; | ||
3117 | None | ||
3118 | }"##, | ||
3119 | ); | ||
3120 | } | ||
3121 | |||
3122 | #[test] | ||
3123 | fn break_with_value_and_return() { | ||
3124 | check_assist( | ||
3125 | extract_function, | ||
3126 | r##" | ||
3127 | fn foo() -> i64 { | ||
3128 | loop { | ||
3129 | let n = 1; | ||
3130 | $0 | ||
3131 | let k = 1; | ||
3132 | if k == 42 { | ||
3133 | break 3; | ||
3134 | } | ||
3135 | let m = k + 1;$0 | ||
3136 | let h = 1 + m; | ||
3137 | } | ||
3138 | }"##, | ||
3139 | r##" | ||
3140 | fn foo() -> i64 { | ||
3141 | loop { | ||
3142 | let n = 1; | ||
3143 | let m = match fun_name() { | ||
3144 | Ok(value) => value, | ||
3145 | Err(value) => break value, | ||
3146 | }; | ||
3147 | let h = 1 + m; | ||
3148 | } | ||
3149 | } | ||
3150 | |||
3151 | fn $0fun_name() -> Result<i32, i64> { | ||
3152 | let k = 1; | ||
3153 | if k == 42 { | ||
3154 | return Err(3); | ||
3155 | } | ||
3156 | let m = k + 1; | ||
3157 | Ok(m) | ||
3158 | }"##, | ||
3159 | ); | ||
3160 | } | ||
3161 | |||
3162 | #[test] | ||
3163 | fn try_option() { | ||
3164 | check_assist( | ||
3165 | extract_function, | ||
3166 | r##" | ||
3167 | enum Option<T> { None, Some(T), } | ||
3168 | use Option::*; | ||
3169 | fn bar() -> Option<i32> { None } | ||
3170 | fn foo() -> Option<()> { | ||
3171 | let n = bar()?; | ||
3172 | $0let k = foo()?; | ||
3173 | let m = k + 1;$0 | ||
3174 | let h = 1 + m; | ||
3175 | Some(()) | ||
3176 | }"##, | ||
3177 | r##" | ||
3178 | enum Option<T> { None, Some(T), } | ||
3179 | use Option::*; | ||
3180 | fn bar() -> Option<i32> { None } | ||
3181 | fn foo() -> Option<()> { | ||
3182 | let n = bar()?; | ||
3183 | let m = fun_name()?; | ||
3184 | let h = 1 + m; | ||
3185 | Some(()) | ||
3186 | } | ||
3187 | |||
3188 | fn $0fun_name() -> Option<i32> { | ||
3189 | let k = foo()?; | ||
3190 | let m = k + 1; | ||
3191 | Some(m) | ||
3192 | }"##, | ||
3193 | ); | ||
3194 | } | ||
3195 | |||
3196 | #[test] | ||
3197 | fn try_option_unit() { | ||
3198 | check_assist( | ||
3199 | extract_function, | ||
3200 | r##" | ||
3201 | enum Option<T> { None, Some(T), } | ||
3202 | use Option::*; | ||
3203 | fn foo() -> Option<()> { | ||
3204 | let n = 1; | ||
3205 | $0let k = foo()?; | ||
3206 | let m = k + 1;$0 | ||
3207 | let h = 1 + n; | ||
3208 | Some(()) | ||
3209 | }"##, | ||
3210 | r##" | ||
3211 | enum Option<T> { None, Some(T), } | ||
3212 | use Option::*; | ||
3213 | fn foo() -> Option<()> { | ||
3214 | let n = 1; | ||
3215 | fun_name()?; | ||
3216 | let h = 1 + n; | ||
3217 | Some(()) | ||
3218 | } | ||
3219 | |||
3220 | fn $0fun_name() -> Option<()> { | ||
3221 | let k = foo()?; | ||
3222 | let m = k + 1; | ||
3223 | Some(()) | ||
3224 | }"##, | ||
3225 | ); | ||
3226 | } | ||
3227 | |||
3228 | #[test] | ||
3229 | fn try_result() { | ||
3230 | check_assist( | ||
3231 | extract_function, | ||
3232 | r##" | ||
3233 | enum Result<T, E> { Ok(T), Err(E), } | ||
3234 | use Result::*; | ||
3235 | fn foo() -> Result<(), i64> { | ||
3236 | let n = 1; | ||
3237 | $0let k = foo()?; | ||
3238 | let m = k + 1;$0 | ||
3239 | let h = 1 + m; | ||
3240 | Ok(()) | ||
3241 | }"##, | ||
3242 | r##" | ||
3243 | enum Result<T, E> { Ok(T), Err(E), } | ||
3244 | use Result::*; | ||
3245 | fn foo() -> Result<(), i64> { | ||
3246 | let n = 1; | ||
3247 | let m = fun_name()?; | ||
3248 | let h = 1 + m; | ||
3249 | Ok(()) | ||
3250 | } | ||
3251 | |||
3252 | fn $0fun_name() -> Result<i32, i64> { | ||
3253 | let k = foo()?; | ||
3254 | let m = k + 1; | ||
3255 | Ok(m) | ||
3256 | }"##, | ||
3257 | ); | ||
3258 | } | ||
3259 | |||
3260 | #[test] | ||
3261 | fn try_option_with_return() { | ||
3262 | check_assist( | ||
3263 | extract_function, | ||
3264 | r##" | ||
3265 | enum Option<T> { None, Some(T) } | ||
3266 | use Option::*; | ||
3267 | fn foo() -> Option<()> { | ||
3268 | let n = 1; | ||
3269 | $0let k = foo()?; | ||
3270 | if k == 42 { | ||
3271 | return None; | ||
3272 | } | ||
3273 | let m = k + 1;$0 | ||
3274 | let h = 1 + m; | ||
3275 | Some(()) | ||
3276 | }"##, | ||
3277 | r##" | ||
3278 | enum Option<T> { None, Some(T) } | ||
3279 | use Option::*; | ||
3280 | fn foo() -> Option<()> { | ||
3281 | let n = 1; | ||
3282 | let m = fun_name()?; | ||
3283 | let h = 1 + m; | ||
3284 | Some(()) | ||
3285 | } | ||
3286 | |||
3287 | fn $0fun_name() -> Option<i32> { | ||
3288 | let k = foo()?; | ||
3289 | if k == 42 { | ||
3290 | return None; | ||
3291 | } | ||
3292 | let m = k + 1; | ||
3293 | Some(m) | ||
3294 | }"##, | ||
3295 | ); | ||
3296 | } | ||
3297 | |||
3298 | #[test] | ||
3299 | fn try_result_with_return() { | ||
3300 | check_assist( | ||
3301 | extract_function, | ||
3302 | r##" | ||
3303 | enum Result<T, E> { Ok(T), Err(E), } | ||
3304 | use Result::*; | ||
3305 | fn foo() -> Result<(), i64> { | ||
3306 | let n = 1; | ||
3307 | $0let k = foo()?; | ||
3308 | if k == 42 { | ||
3309 | return Err(1); | ||
3310 | } | ||
3311 | let m = k + 1;$0 | ||
3312 | let h = 1 + m; | ||
3313 | Ok(()) | ||
3314 | }"##, | ||
3315 | r##" | ||
3316 | enum Result<T, E> { Ok(T), Err(E), } | ||
3317 | use Result::*; | ||
3318 | fn foo() -> Result<(), i64> { | ||
3319 | let n = 1; | ||
3320 | let m = fun_name()?; | ||
3321 | let h = 1 + m; | ||
3322 | Ok(()) | ||
3323 | } | ||
3324 | |||
3325 | fn $0fun_name() -> Result<i32, i64> { | ||
3326 | let k = foo()?; | ||
3327 | if k == 42 { | ||
3328 | return Err(1); | ||
3329 | } | ||
3330 | let m = k + 1; | ||
3331 | Ok(m) | ||
3332 | }"##, | ||
3333 | ); | ||
3334 | } | ||
3335 | |||
3336 | #[test] | ||
3337 | fn try_and_break() { | ||
3338 | mark::check!(external_control_flow_try_and_bc); | ||
3339 | check_assist_not_applicable( | ||
3340 | extract_function, | ||
3341 | r##" | ||
3342 | enum Option<T> { None, Some(T) } | ||
3343 | use Option::*; | ||
3344 | fn foo() -> Option<()> { | ||
3345 | loop { | ||
3346 | let n = Some(1); | ||
3347 | $0let m = n? + 1; | ||
3348 | break; | ||
3349 | let k = 2; | ||
3350 | let k = k + 1;$0 | ||
3351 | let r = n + k; | ||
3352 | } | ||
3353 | Some(()) | ||
3354 | }"##, | ||
3355 | ); | ||
3356 | } | ||
3357 | |||
3358 | #[test] | ||
3359 | fn try_and_return_ok() { | ||
3360 | mark::check!(external_control_flow_try_and_return_non_err); | ||
3361 | check_assist_not_applicable( | ||
3362 | extract_function, | ||
3363 | r##" | ||
3364 | enum Result<T, E> { Ok(T), Err(E), } | ||
3365 | use Result::*; | ||
3366 | fn foo() -> Result<(), i64> { | ||
3367 | let n = 1; | ||
3368 | $0let k = foo()?; | ||
3369 | if k == 42 { | ||
3370 | return Ok(1); | ||
3371 | } | ||
3372 | let m = k + 1;$0 | ||
3373 | let h = 1 + m; | ||
3374 | Ok(()) | ||
3375 | }"##, | ||
3376 | ); | ||
3377 | } | ||
2159 | } | 3378 | } |
diff --git a/crates/assists/src/handlers/generate_function.rs b/crates/assists/src/handlers/generate_function.rs index 1805c1dfd..959824981 100644 --- a/crates/assists/src/handlers/generate_function.rs +++ b/crates/assists/src/handlers/generate_function.rs | |||
@@ -215,8 +215,11 @@ fn fn_args( | |||
215 | }); | 215 | }); |
216 | } | 216 | } |
217 | deduplicate_arg_names(&mut arg_names); | 217 | deduplicate_arg_names(&mut arg_names); |
218 | let params = arg_names.into_iter().zip(arg_types).map(|(name, ty)| make::param(name, ty)); | 218 | let params = arg_names |
219 | Some((None, make::param_list(params))) | 219 | .into_iter() |
220 | .zip(arg_types) | ||
221 | .map(|(name, ty)| make::param(make::ident_pat(make::name(&name)).into(), make::ty(&ty))); | ||
222 | Some((None, make::param_list(None, params))) | ||
220 | } | 223 | } |
221 | 224 | ||
222 | /// Makes duplicate argument names unique by appending incrementing numbers. | 225 | /// Makes duplicate argument names unique by appending incrementing numbers. |
diff --git a/crates/assists/src/tests.rs b/crates/assists/src/tests.rs index 5b9992f15..720f561a1 100644 --- a/crates/assists/src/tests.rs +++ b/crates/assists/src/tests.rs | |||
@@ -195,6 +195,7 @@ fn assist_order_if_expr() { | |||
195 | let assists = Assist::get(&db, &TEST_CONFIG, false, frange); | 195 | let assists = Assist::get(&db, &TEST_CONFIG, false, frange); |
196 | let mut assists = assists.iter(); | 196 | let mut assists = assists.iter(); |
197 | 197 | ||
198 | assert_eq!(assists.next().expect("expected assist").label, "Extract into function"); | ||
198 | assert_eq!(assists.next().expect("expected assist").label, "Extract into variable"); | 199 | assert_eq!(assists.next().expect("expected assist").label, "Extract into variable"); |
199 | assert_eq!(assists.next().expect("expected assist").label, "Replace with match"); | 200 | assert_eq!(assists.next().expect("expected assist").label, "Replace with match"); |
200 | } | 201 | } |
@@ -220,6 +221,7 @@ fn assist_filter_works() { | |||
220 | let assists = Assist::get(&db, &cfg, false, frange); | 221 | let assists = Assist::get(&db, &cfg, false, frange); |
221 | let mut assists = assists.iter(); | 222 | let mut assists = assists.iter(); |
222 | 223 | ||
224 | assert_eq!(assists.next().expect("expected assist").label, "Extract into function"); | ||
223 | assert_eq!(assists.next().expect("expected assist").label, "Extract into variable"); | 225 | assert_eq!(assists.next().expect("expected assist").label, "Extract into variable"); |
224 | assert_eq!(assists.next().expect("expected assist").label, "Replace with match"); | 226 | assert_eq!(assists.next().expect("expected assist").label, "Replace with match"); |
225 | } | 227 | } |
@@ -228,9 +230,10 @@ fn assist_filter_works() { | |||
228 | let mut cfg = TEST_CONFIG; | 230 | let mut cfg = TEST_CONFIG; |
229 | cfg.allowed = Some(vec![AssistKind::RefactorExtract]); | 231 | cfg.allowed = Some(vec![AssistKind::RefactorExtract]); |
230 | let assists = Assist::get(&db, &cfg, false, frange); | 232 | let assists = Assist::get(&db, &cfg, false, frange); |
231 | assert_eq!(assists.len(), 1); | 233 | assert_eq!(assists.len(), 2); |
232 | 234 | ||
233 | let mut assists = assists.iter(); | 235 | let mut assists = assists.iter(); |
236 | assert_eq!(assists.next().expect("expected assist").label, "Extract into function"); | ||
234 | assert_eq!(assists.next().expect("expected assist").label, "Extract into variable"); | 237 | assert_eq!(assists.next().expect("expected assist").label, "Extract into variable"); |
235 | } | 238 | } |
236 | 239 | ||