@@ -20,7 +20,7 @@ use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists};
2020// ->
2121// ```
2222// fn main() {
23- // let $0var_name = ( 1 + 2) ;
23+ // let $0var_name = 1 + 2;
2424// var_name * 4;
2525// }
2626// ```
@@ -58,9 +58,30 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
5858 }
5959
6060 let parent = to_extract. syntax ( ) . parent ( ) . and_then ( ast:: Expr :: cast) ;
61- let needs_adjust = parent
62- . as_ref ( )
63- . map_or ( false , |it| matches ! ( it, ast:: Expr :: FieldExpr ( _) | ast:: Expr :: MethodCallExpr ( _) ) ) ;
61+ // Any expression that autoderefs may need adjustment.
62+ let mut needs_adjust = parent. as_ref ( ) . map_or ( false , |it| match it {
63+ ast:: Expr :: FieldExpr ( _)
64+ | ast:: Expr :: MethodCallExpr ( _)
65+ | ast:: Expr :: CallExpr ( _)
66+ | ast:: Expr :: AwaitExpr ( _) => true ,
67+ ast:: Expr :: IndexExpr ( index) if index. base ( ) . as_ref ( ) == Some ( & to_extract) => true ,
68+ _ => false ,
69+ } ) ;
70+ let mut to_extract_no_ref = peel_parens ( to_extract. clone ( ) ) ;
71+ let needs_ref = needs_adjust
72+ && match & to_extract_no_ref {
73+ ast:: Expr :: FieldExpr ( _)
74+ | ast:: Expr :: IndexExpr ( _)
75+ | ast:: Expr :: MacroExpr ( _)
76+ | ast:: Expr :: ParenExpr ( _)
77+ | ast:: Expr :: PathExpr ( _) => true ,
78+ ast:: Expr :: PrefixExpr ( prefix) if prefix. op_kind ( ) == Some ( ast:: UnaryOp :: Deref ) => {
79+ to_extract_no_ref = prefix. expr ( ) ?;
80+ needs_adjust = false ;
81+ false
82+ }
83+ _ => false ,
84+ } ;
6485
6586 let anchor = Anchor :: from ( & to_extract) ?;
6687 let target = to_extract. syntax ( ) . text_range ( ) ;
@@ -87,22 +108,28 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
87108 Some ( ast:: Expr :: RefExpr ( expr) ) if expr. mut_token ( ) . is_some ( ) => {
88109 make:: ident_pat ( false , true , make:: name ( & var_name) )
89110 }
111+ _ if needs_adjust
112+ && !needs_ref
113+ && ty. as_ref ( ) . is_some_and ( |ty| ty. is_mutable_reference ( ) ) =>
114+ {
115+ make:: ident_pat ( false , true , make:: name ( & var_name) )
116+ }
90117 _ => make:: ident_pat ( false , false , make:: name ( & var_name) ) ,
91118 } ;
92119
93- let to_extract = match ty. as_ref ( ) . filter ( |_| needs_adjust ) {
120+ let to_extract_no_ref = match ty. as_ref ( ) . filter ( |_| needs_ref ) {
94121 Some ( receiver_type) if receiver_type. is_mutable_reference ( ) => {
95- make:: expr_ref ( to_extract , true )
122+ make:: expr_ref ( to_extract_no_ref , true )
96123 }
97124 Some ( receiver_type) if receiver_type. is_reference ( ) => {
98- make:: expr_ref ( to_extract , false )
125+ make:: expr_ref ( to_extract_no_ref , false )
99126 }
100- _ => to_extract ,
127+ _ => to_extract_no_ref ,
101128 } ;
102129
103130 let expr_replace = edit. make_syntax_mut ( expr_replace) ;
104131 let let_stmt =
105- make:: let_stmt ( ident_pat. into ( ) , None , Some ( to_extract ) ) . clone_for_update ( ) ;
132+ make:: let_stmt ( ident_pat. into ( ) , None , Some ( to_extract_no_ref ) ) . clone_for_update ( ) ;
106133 let name_expr = make:: expr_path ( make:: ext:: ident_path ( & var_name) ) . clone_for_update ( ) ;
107134
108135 match anchor {
@@ -202,6 +229,14 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
202229 )
203230}
204231
232+ fn peel_parens ( mut expr : ast:: Expr ) -> ast:: Expr {
233+ while let ast:: Expr :: ParenExpr ( parens) = & expr {
234+ let Some ( expr_inside) = parens. expr ( ) else { break } ;
235+ expr = expr_inside;
236+ }
237+ expr
238+ }
239+
205240/// Check whether the node is a valid expression which can be extracted to a variable.
206241/// In general that's true for any expression, but in some cases that would produce invalid code.
207242fn valid_target_expr ( node : SyntaxNode ) -> Option < ast:: Expr > {
@@ -1220,6 +1255,45 @@ fn foo(s: &S) {
12201255 ) ;
12211256 }
12221257
1258+ #[ test]
1259+ fn test_extract_var_index_deref ( ) {
1260+ check_assist (
1261+ extract_variable,
1262+ r#"
1263+ //- minicore: index
1264+ struct X;
1265+
1266+ impl std::ops::Index<usize> for X {
1267+ type Output = i32;
1268+ fn index(&self) -> &Self::Output { 0 }
1269+ }
1270+
1271+ struct S {
1272+ sub: X
1273+ }
1274+
1275+ fn foo(s: &S) {
1276+ $0s.sub$0[0];
1277+ }"# ,
1278+ r#"
1279+ struct X;
1280+
1281+ impl std::ops::Index<usize> for X {
1282+ type Output = i32;
1283+ fn index(&self) -> &Self::Output { 0 }
1284+ }
1285+
1286+ struct S {
1287+ sub: X
1288+ }
1289+
1290+ fn foo(s: &S) {
1291+ let $0sub = &s.sub;
1292+ sub[0];
1293+ }"# ,
1294+ ) ;
1295+ }
1296+
12231297 #[ test]
12241298 fn test_extract_var_reference_parameter_deep_nesting ( ) {
12251299 check_assist (
@@ -1461,4 +1535,60 @@ fn foo() {
14611535}"# ,
14621536 ) ;
14631537 }
1538+
1539+ #[ test]
1540+ fn generates_no_ref_on_calls ( ) {
1541+ check_assist (
1542+ extract_variable,
1543+ r#"
1544+ struct S;
1545+ impl S {
1546+ fn do_work(&mut self) {}
1547+ }
1548+ fn bar() -> S { S }
1549+ fn foo() {
1550+ $0bar()$0.do_work();
1551+ }"# ,
1552+ r#"
1553+ struct S;
1554+ impl S {
1555+ fn do_work(&mut self) {}
1556+ }
1557+ fn bar() -> S { S }
1558+ fn foo() {
1559+ let mut $0bar = bar();
1560+ bar.do_work();
1561+ }"# ,
1562+ ) ;
1563+ }
1564+
1565+ #[ test]
1566+ fn generates_no_ref_for_deref ( ) {
1567+ check_assist (
1568+ extract_variable,
1569+ r#"
1570+ struct S;
1571+ impl S {
1572+ fn do_work(&mut self) {}
1573+ }
1574+ fn bar() -> S { S }
1575+ fn foo() {
1576+ let v = &mut &mut bar();
1577+ $0(**v)$0.do_work();
1578+ }
1579+ "# ,
1580+ r#"
1581+ struct S;
1582+ impl S {
1583+ fn do_work(&mut self) {}
1584+ }
1585+ fn bar() -> S { S }
1586+ fn foo() {
1587+ let v = &mut &mut bar();
1588+ let $0s = *v;
1589+ s.do_work();
1590+ }
1591+ "# ,
1592+ ) ;
1593+ }
14641594}
0 commit comments