@@ -20,7 +20,7 @@ use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists};
20
20
// ->
21
21
// ```
22
22
// fn main() {
23
- // let $0var_name = ( 1 + 2) ;
23
+ // let $0var_name = 1 + 2;
24
24
// var_name * 4;
25
25
// }
26
26
// ```
@@ -58,9 +58,30 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
58
58
}
59
59
60
60
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
+ } ;
64
85
65
86
let anchor = Anchor :: from ( & to_extract) ?;
66
87
let target = to_extract. syntax ( ) . text_range ( ) ;
@@ -87,22 +108,28 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
87
108
Some ( ast:: Expr :: RefExpr ( expr) ) if expr. mut_token ( ) . is_some ( ) => {
88
109
make:: ident_pat ( false , true , make:: name ( & var_name) )
89
110
}
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
+ }
90
117
_ => make:: ident_pat ( false , false , make:: name ( & var_name) ) ,
91
118
} ;
92
119
93
- let to_extract = match ty. as_ref ( ) . filter ( |_| needs_adjust ) {
120
+ let to_extract_no_ref = match ty. as_ref ( ) . filter ( |_| needs_ref ) {
94
121
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 )
96
123
}
97
124
Some ( receiver_type) if receiver_type. is_reference ( ) => {
98
- make:: expr_ref ( to_extract , false )
125
+ make:: expr_ref ( to_extract_no_ref , false )
99
126
}
100
- _ => to_extract ,
127
+ _ => to_extract_no_ref ,
101
128
} ;
102
129
103
130
let expr_replace = edit. make_syntax_mut ( expr_replace) ;
104
131
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 ( ) ;
106
133
let name_expr = make:: expr_path ( make:: ext:: ident_path ( & var_name) ) . clone_for_update ( ) ;
107
134
108
135
match anchor {
@@ -202,6 +229,14 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
202
229
)
203
230
}
204
231
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
+
205
240
/// Check whether the node is a valid expression which can be extracted to a variable.
206
241
/// In general that's true for any expression, but in some cases that would produce invalid code.
207
242
fn valid_target_expr ( node : SyntaxNode ) -> Option < ast:: Expr > {
@@ -1220,6 +1255,45 @@ fn foo(s: &S) {
1220
1255
) ;
1221
1256
}
1222
1257
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
+
1223
1297
#[ test]
1224
1298
fn test_extract_var_reference_parameter_deep_nesting ( ) {
1225
1299
check_assist (
@@ -1461,4 +1535,60 @@ fn foo() {
1461
1535
}"# ,
1462
1536
) ;
1463
1537
}
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
+ }
1464
1594
}
0 commit comments