diff options
author | Vladyslav Katasonov <[email protected]> | 2021-02-05 01:35:41 +0000 |
---|---|---|
committer | Vladyslav Katasonov <[email protected]> | 2021-02-05 01:35:41 +0000 |
commit | 876ca603166dcd2680652b42fb6bdd5358e59aa6 (patch) | |
tree | 074c5f4abd125ce2f8ac9d245090f636b6a7e7aa /crates/assists/src/handlers | |
parent | 271c1cb01325ac252b5153c3729462a4d96a0e0a (diff) |
allow transitive `&mut` access for fields in extract_function
Diffstat (limited to 'crates/assists/src/handlers')
-rw-r--r-- | crates/assists/src/handlers/extract_function.rs | 119 |
1 files changed, 92 insertions, 27 deletions
diff --git a/crates/assists/src/handlers/extract_function.rs b/crates/assists/src/handlers/extract_function.rs index 5d6f5bb26..49ea1c4b3 100644 --- a/crates/assists/src/handlers/extract_function.rs +++ b/crates/assists/src/handlers/extract_function.rs | |||
@@ -503,17 +503,42 @@ fn reference_is_exclusive( | |||
503 | } | 503 | } |
504 | 504 | ||
505 | // we take `&mut` reference to variable: `&mut v` | 505 | // we take `&mut` reference to variable: `&mut v` |
506 | let path = path_element_of_reference(body, reference); | 506 | let path = match path_element_of_reference(body, reference) { |
507 | if is_mut_ref_expr(path.as_ref()).unwrap_or(false) { | 507 | Some(path) => path, |
508 | return true; | 508 | None => return false, |
509 | }; | ||
510 | |||
511 | expr_require_exclusive_access(ctx, &path).unwrap_or(false) | ||
512 | } | ||
513 | |||
514 | /// checks if this expr requires `&mut` access, recurses on field access | ||
515 | fn expr_require_exclusive_access(ctx: &AssistContext, expr: &ast::Expr) -> Option<bool> { | ||
516 | let parent = expr.syntax().parent()?; | ||
517 | |||
518 | if let Some(bin_expr) = ast::BinExpr::cast(parent.clone()) { | ||
519 | if bin_expr.op_kind()?.is_assignment() { | ||
520 | return Some(bin_expr.lhs()?.syntax() == expr.syntax()); | ||
521 | } | ||
522 | return Some(false); | ||
509 | } | 523 | } |
510 | 524 | ||
511 | // we call method with `&mut self` receiver | 525 | if let Some(ref_expr) = ast::RefExpr::cast(parent.clone()) { |
512 | if is_mut_method_call_receiver(ctx, path.as_ref()).unwrap_or(false) { | 526 | return Some(ref_expr.mut_token().is_some()); |
513 | return true; | 527 | } |
528 | |||
529 | if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) { | ||
530 | let func = ctx.sema.resolve_method_call(&method_call)?; | ||
531 | let self_param = func.self_param(ctx.db())?; | ||
532 | let access = self_param.access(ctx.db()); | ||
533 | |||
534 | return Some(matches!(access, hir::Access::Exclusive)); | ||
535 | } | ||
536 | |||
537 | if let Some(field) = ast::FieldExpr::cast(parent) { | ||
538 | return expr_require_exclusive_access(ctx, &field.into()); | ||
514 | } | 539 | } |
515 | 540 | ||
516 | false | 541 | Some(false) |
517 | } | 542 | } |
518 | 543 | ||
519 | /// Container of local varaible usages | 544 | /// Container of local varaible usages |
@@ -567,24 +592,6 @@ fn path_element_of_reference( | |||
567 | Some(path) | 592 | Some(path) |
568 | } | 593 | } |
569 | 594 | ||
570 | fn is_mut_ref_expr(path: Option<&ast::Expr>) -> Option<bool> { | ||
571 | let path = path?; | ||
572 | let ref_expr = path.syntax().parent().and_then(ast::RefExpr::cast)?; | ||
573 | Some(ref_expr.mut_token().is_some()) | ||
574 | } | ||
575 | |||
576 | /// checks if `path` is the receiver in method call that requires `&mut self` access | ||
577 | fn is_mut_method_call_receiver(ctx: &AssistContext, path: Option<&ast::Expr>) -> Option<bool> { | ||
578 | let path = path?; | ||
579 | let method_call = path.syntax().parent().and_then(ast::MethodCallExpr::cast)?; | ||
580 | |||
581 | let func = ctx.sema.resolve_method_call(&method_call)?; | ||
582 | let self_param = func.self_param(ctx.db())?; | ||
583 | let access = self_param.access(ctx.db()); | ||
584 | |||
585 | Some(matches!(access, hir::Access::Exclusive)) | ||
586 | } | ||
587 | |||
588 | /// list local variables defined inside `body` | 595 | /// list local variables defined inside `body` |
589 | fn vars_defined_in_body(body: &FunctionBody, ctx: &AssistContext) -> Vec<Local> { | 596 | fn vars_defined_in_body(body: &FunctionBody, ctx: &AssistContext) -> Vec<Local> { |
590 | // FIXME: this doesn't work well with macros | 597 | // FIXME: this doesn't work well with macros |
@@ -872,7 +879,7 @@ fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode) | |||
872 | .filter_map(|reference| path_element_of_reference(syntax, reference)); | 879 | .filter_map(|reference| path_element_of_reference(syntax, reference)); |
873 | for path in usages { | 880 | for path in usages { |
874 | match path.syntax().ancestors().skip(1).find_map(ast::Expr::cast) { | 881 | match path.syntax().ancestors().skip(1).find_map(ast::Expr::cast) { |
875 | Some(ast::Expr::MethodCallExpr(_)) => { | 882 | Some(ast::Expr::MethodCallExpr(_)) | Some(ast::Expr::FieldExpr(_)) => { |
876 | // do nothing | 883 | // do nothing |
877 | } | 884 | } |
878 | Some(ast::Expr::RefExpr(node)) | 885 | Some(ast::Expr::RefExpr(node)) |
@@ -1673,6 +1680,64 @@ fn $0fun_name(n: &mut i32) { | |||
1673 | } | 1680 | } |
1674 | 1681 | ||
1675 | #[test] | 1682 | #[test] |
1683 | fn mut_field_from_outer_scope() { | ||
1684 | check_assist( | ||
1685 | extract_function, | ||
1686 | r" | ||
1687 | struct C { n: i32 } | ||
1688 | fn foo() { | ||
1689 | let mut c = C { n: 0 }; | ||
1690 | $0c.n += 1;$0 | ||
1691 | let m = c.n + 1; | ||
1692 | }", | ||
1693 | r" | ||
1694 | struct C { n: i32 } | ||
1695 | fn foo() { | ||
1696 | let mut c = C { n: 0 }; | ||
1697 | fun_name(&mut c); | ||
1698 | let m = c.n + 1; | ||
1699 | } | ||
1700 | |||
1701 | fn $0fun_name(c: &mut C) { | ||
1702 | c.n += 1; | ||
1703 | }", | ||
1704 | ); | ||
1705 | } | ||
1706 | |||
1707 | #[test] | ||
1708 | fn mut_nested_field_from_outer_scope() { | ||
1709 | check_assist( | ||
1710 | extract_function, | ||
1711 | r" | ||
1712 | struct P { n: i32} | ||
1713 | struct C { p: P } | ||
1714 | fn foo() { | ||
1715 | let mut c = C { p: P { n: 0 } }; | ||
1716 | let mut v = C { p: P { n: 0 } }; | ||
1717 | let u = C { p: P { n: 0 } }; | ||
1718 | $0c.p.n += u.p.n; | ||
1719 | let r = &mut v.p.n;$0 | ||
1720 | let m = c.p.n + v.p.n + u.p.n; | ||
1721 | }", | ||
1722 | r" | ||
1723 | struct P { n: i32} | ||
1724 | struct C { p: P } | ||
1725 | fn foo() { | ||
1726 | let mut c = C { p: P { n: 0 } }; | ||
1727 | let mut v = C { p: P { n: 0 } }; | ||
1728 | let u = C { p: P { n: 0 } }; | ||
1729 | fun_name(&mut c, &u, &mut v); | ||
1730 | let m = c.p.n + v.p.n + u.p.n; | ||
1731 | } | ||
1732 | |||
1733 | fn $0fun_name(c: &mut C, u: &C, v: &mut C) { | ||
1734 | c.p.n += u.p.n; | ||
1735 | let r = &mut v.p.n; | ||
1736 | }", | ||
1737 | ); | ||
1738 | } | ||
1739 | |||
1740 | #[test] | ||
1676 | fn mut_param_many_usages_stmt() { | 1741 | fn mut_param_many_usages_stmt() { |
1677 | check_assist( | 1742 | check_assist( |
1678 | extract_function, | 1743 | extract_function, |
@@ -1999,7 +2064,7 @@ fn foo() { | |||
1999 | } | 2064 | } |
2000 | 2065 | ||
2001 | fn $0fun_name(c: &Counter) { | 2066 | fn $0fun_name(c: &Counter) { |
2002 | let n = *c.0; | 2067 | let n = c.0; |
2003 | }", | 2068 | }", |
2004 | ); | 2069 | ); |
2005 | } | 2070 | } |