1
1
use either:: Either ;
2
2
use hir:: PathResolution ;
3
- use ide_db:: { base_db:: FileId , defs:: Definition , search:: FileReference } ;
4
- use rustc_hash:: FxHashMap ;
3
+ use ide_db:: {
4
+ defs:: Definition ,
5
+ search:: { FileReference , UsageSearchResult } ,
6
+ } ;
5
7
use syntax:: {
6
8
ast:: { self , AstNode , AstToken , NameOwner } ,
7
- TextRange ,
9
+ SyntaxElement , TextRange ,
8
10
} ;
9
11
10
12
use crate :: {
@@ -14,7 +16,7 @@ use crate::{
14
16
15
17
// Assist: inline_local_variable
16
18
//
17
- // Inlines local variable.
19
+ // Inlines a local variable.
18
20
//
19
21
// ```
20
22
// fn main() {
@@ -29,76 +31,77 @@ use crate::{
29
31
// }
30
32
// ```
31
33
pub ( crate ) fn inline_local_variable ( acc : & mut Assists , ctx : & AssistContext ) -> Option < ( ) > {
32
- let InlineData { let_stmt, delete_let, replace_usages , target } =
34
+ let InlineData { let_stmt, delete_let, references , target } =
33
35
inline_let ( ctx) . or_else ( || inline_usage ( ctx) ) ?;
34
36
let initializer_expr = let_stmt. initializer ( ) ?;
35
37
36
- let delete_range = if delete_let {
38
+ let delete_range = delete_let. then ( || {
37
39
if let Some ( whitespace) = let_stmt
38
40
. syntax ( )
39
41
. next_sibling_or_token ( )
40
- . and_then ( |it| ast:: Whitespace :: cast ( it. as_token ( ) ?. clone ( ) ) )
42
+ . and_then ( SyntaxElement :: into_token)
43
+ . and_then ( ast:: Whitespace :: cast)
41
44
{
42
- Some ( TextRange :: new (
45
+ TextRange :: new (
43
46
let_stmt. syntax ( ) . text_range ( ) . start ( ) ,
44
47
whitespace. syntax ( ) . text_range ( ) . end ( ) ,
45
- ) )
48
+ )
46
49
} else {
47
- Some ( let_stmt. syntax ( ) . text_range ( ) )
50
+ let_stmt. syntax ( ) . text_range ( )
48
51
}
49
- } else {
50
- None
51
- } ;
52
+ } ) ;
52
53
53
- let wrap_in_parens = replace_usages
54
- . iter ( )
55
- . map ( |( & file_id, refs) | {
56
- refs. iter ( )
57
- . map ( |& FileReference { range, .. } | {
58
- let usage_node = ctx
59
- . covering_node_for_range ( range)
60
- . ancestors ( )
61
- . find_map ( ast:: PathExpr :: cast) ?;
62
- let usage_parent_option =
63
- usage_node. syntax ( ) . parent ( ) . and_then ( ast:: Expr :: cast) ;
64
- let usage_parent = match usage_parent_option {
65
- Some ( u) => u,
66
- None => return Some ( false ) ,
67
- } ;
68
- let initializer = matches ! (
69
- initializer_expr,
70
- ast:: Expr :: CallExpr ( _)
71
- | ast:: Expr :: IndexExpr ( _)
72
- | ast:: Expr :: MethodCallExpr ( _)
73
- | ast:: Expr :: FieldExpr ( _)
74
- | ast:: Expr :: TryExpr ( _)
75
- | ast:: Expr :: RefExpr ( _)
76
- | ast:: Expr :: Literal ( _)
77
- | ast:: Expr :: TupleExpr ( _)
78
- | ast:: Expr :: ArrayExpr ( _)
79
- | ast:: Expr :: ParenExpr ( _)
80
- | ast:: Expr :: PathExpr ( _)
81
- | ast:: Expr :: BlockExpr ( _)
82
- | ast:: Expr :: EffectExpr ( _) ,
83
- ) ;
84
- let parent = matches ! (
85
- usage_parent,
86
- ast:: Expr :: CallExpr ( _)
87
- | ast:: Expr :: TupleExpr ( _)
88
- | ast:: Expr :: ArrayExpr ( _)
89
- | ast:: Expr :: ParenExpr ( _)
90
- | ast:: Expr :: ForExpr ( _)
91
- | ast:: Expr :: WhileExpr ( _)
92
- | ast:: Expr :: BreakExpr ( _)
93
- | ast:: Expr :: ReturnExpr ( _)
94
- | ast:: Expr :: MatchExpr ( _)
95
- ) ;
96
- Some ( !( initializer || parent) )
97
- } )
98
- . collect :: < Option < _ > > ( )
99
- . map ( |b| ( file_id, b) )
54
+ let wrap_in_parens = references
55
+ . into_iter ( )
56
+ . filter_map ( |FileReference { range, name, .. } | match name {
57
+ ast:: NameLike :: NameRef ( name) => Some ( ( range, name) ) ,
58
+ _ => None ,
59
+ } )
60
+ . map ( |( range, name_ref) | {
61
+ if range != name_ref. syntax ( ) . text_range ( ) {
62
+ // Do not rename inside macros
63
+ // FIXME: This feels like a bad heuristic for macros
64
+ return None ;
65
+ }
66
+ let usage_node =
67
+ name_ref. syntax ( ) . ancestors ( ) . find ( |it| ast:: PathExpr :: can_cast ( it. kind ( ) ) ) ;
68
+ let usage_parent_option =
69
+ usage_node. and_then ( |it| it. parent ( ) ) . and_then ( ast:: Expr :: cast) ;
70
+ let usage_parent = match usage_parent_option {
71
+ Some ( u) => u,
72
+ None => return Some ( ( range, name_ref, false ) ) ,
73
+ } ;
74
+ let initializer = matches ! (
75
+ initializer_expr,
76
+ ast:: Expr :: CallExpr ( _)
77
+ | ast:: Expr :: IndexExpr ( _)
78
+ | ast:: Expr :: MethodCallExpr ( _)
79
+ | ast:: Expr :: FieldExpr ( _)
80
+ | ast:: Expr :: TryExpr ( _)
81
+ | ast:: Expr :: RefExpr ( _)
82
+ | ast:: Expr :: Literal ( _)
83
+ | ast:: Expr :: TupleExpr ( _)
84
+ | ast:: Expr :: ArrayExpr ( _)
85
+ | ast:: Expr :: ParenExpr ( _)
86
+ | ast:: Expr :: PathExpr ( _)
87
+ | ast:: Expr :: BlockExpr ( _)
88
+ | ast:: Expr :: EffectExpr ( _) ,
89
+ ) ;
90
+ let parent = matches ! (
91
+ usage_parent,
92
+ ast:: Expr :: CallExpr ( _)
93
+ | ast:: Expr :: TupleExpr ( _)
94
+ | ast:: Expr :: ArrayExpr ( _)
95
+ | ast:: Expr :: ParenExpr ( _)
96
+ | ast:: Expr :: ForExpr ( _)
97
+ | ast:: Expr :: WhileExpr ( _)
98
+ | ast:: Expr :: BreakExpr ( _)
99
+ | ast:: Expr :: ReturnExpr ( _)
100
+ | ast:: Expr :: MatchExpr ( _)
101
+ ) ;
102
+ Some ( ( range, name_ref, !( initializer || parent) ) )
100
103
} )
101
- . collect :: < Option < FxHashMap < _ , Vec < _ > > > > ( ) ?;
104
+ . collect :: < Option < Vec < _ > > > ( ) ?;
102
105
103
106
let init_str = initializer_expr. syntax ( ) . text ( ) . to_string ( ) ;
104
107
let init_in_paren = format ! ( "({})" , & init_str) ;
@@ -116,19 +119,13 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
116
119
if let Some ( range) = delete_range {
117
120
builder. delete ( range) ;
118
121
}
119
- for ( file_id, references) in replace_usages {
120
- for ( & should_wrap, reference) in wrap_in_parens[ & file_id] . iter ( ) . zip ( references) {
121
- let replacement =
122
- if should_wrap { init_in_paren. clone ( ) } else { init_str. clone ( ) } ;
123
- match reference. name . as_name_ref ( ) {
124
- Some ( name_ref)
125
- if ast:: RecordExprField :: for_field_name ( name_ref) . is_some ( ) =>
126
- {
127
- cov_mark:: hit!( inline_field_shorthand) ;
128
- builder. insert ( reference. range . end ( ) , format ! ( ": {}" , replacement) ) ;
129
- }
130
- _ => builder. replace ( reference. range , replacement) ,
131
- }
122
+ for ( range, name, should_wrap) in wrap_in_parens {
123
+ let replacement = if should_wrap { & init_in_paren } else { & init_str } ;
124
+ if ast:: RecordExprField :: for_field_name ( & name) . is_some ( ) {
125
+ cov_mark:: hit!( inline_field_shorthand) ;
126
+ builder. insert ( range. end ( ) , format ! ( ": {}" , replacement) ) ;
127
+ } else {
128
+ builder. replace ( range, replacement. clone ( ) )
132
129
}
133
130
}
134
131
} ,
@@ -139,7 +136,7 @@ struct InlineData {
139
136
let_stmt : ast:: LetStmt ,
140
137
delete_let : bool ,
141
138
target : ast:: NameOrNameRef ,
142
- replace_usages : FxHashMap < FileId , Vec < FileReference > > ,
139
+ references : Vec < FileReference > ,
143
140
}
144
141
145
142
fn inline_let ( ctx : & AssistContext ) -> Option < InlineData > {
@@ -157,35 +154,32 @@ fn inline_let(ctx: &AssistContext) -> Option<InlineData> {
157
154
return None ;
158
155
}
159
156
160
- let def = ctx. sema . to_def ( & bind_pat) ?;
161
- let def = Definition :: Local ( def ) ;
162
- let usages = def . usages ( & ctx. sema ) . all ( ) ;
163
- if usages . is_empty ( ) {
164
- cov_mark :: hit! ( test_not_applicable_if_variable_unused ) ;
165
- return None ;
166
- } ;
167
-
168
- Some ( InlineData {
169
- let_stmt ,
170
- delete_let : true ,
171
- target : ast :: NameOrNameRef :: Name ( bind_pat . name ( ) ? ) ,
172
- replace_usages : usages . references ,
173
- } )
157
+ let local = ctx. sema . to_def ( & bind_pat) ?;
158
+ let UsageSearchResult { mut references } = Definition :: Local ( local ) . usages ( & ctx . sema ) . all ( ) ;
159
+ match references . remove ( & ctx. frange . file_id ) {
160
+ Some ( references ) => Some ( InlineData {
161
+ let_stmt ,
162
+ delete_let : true ,
163
+ target : ast :: NameOrNameRef :: Name ( bind_pat . name ( ) ? ) ,
164
+ references ,
165
+ } ) ,
166
+ None => {
167
+ cov_mark :: hit! ( test_not_applicable_if_variable_unused ) ;
168
+ None
169
+ }
170
+ }
174
171
}
175
172
176
173
fn inline_usage ( ctx : & AssistContext ) -> Option < InlineData > {
177
174
let path_expr = ctx. find_node_at_offset :: < ast:: PathExpr > ( ) ?;
178
175
let path = path_expr. path ( ) ?;
179
- let name = match path. as_single_segment ( ) ?. kind ( ) ? {
180
- ast:: PathSegmentKind :: Name ( name) => name,
181
- _ => return None ,
182
- } ;
176
+ let name = path. as_single_name_ref ( ) ?;
183
177
184
178
let local = match ctx. sema . resolve_path ( & path) ? {
185
179
PathResolution :: Local ( local) => local,
186
180
_ => return None ,
187
181
} ;
188
- if local. is_mut ( ctx. sema . db ) {
182
+ if local. is_mut ( ctx. db ( ) ) {
189
183
cov_mark:: hit!( test_not_inline_mut_variable_use) ;
190
184
return None ;
191
185
}
@@ -197,21 +191,12 @@ fn inline_usage(ctx: &AssistContext) -> Option<InlineData> {
197
191
198
192
let let_stmt = ast:: LetStmt :: cast ( bind_pat. syntax ( ) . parent ( ) ?) ?;
199
193
200
- let def = Definition :: Local ( local) ;
201
- let mut usages = def. usages ( & ctx. sema ) . all ( ) ;
202
-
203
- let delete_let = usages. references . values ( ) . map ( |v| v. len ( ) ) . sum :: < usize > ( ) == 1 ;
204
-
205
- for references in usages. references . values_mut ( ) {
206
- references. retain ( |reference| reference. name . as_name_ref ( ) == Some ( & name) ) ;
207
- }
194
+ let UsageSearchResult { mut references } = Definition :: Local ( local) . usages ( & ctx. sema ) . all ( ) ;
195
+ let mut references = references. remove ( & ctx. frange . file_id ) ?;
196
+ let delete_let = references. len ( ) == 1 ;
197
+ references. retain ( |fref| fref. name . as_name_ref ( ) == Some ( & name) ) ;
208
198
209
- Some ( InlineData {
210
- let_stmt,
211
- delete_let,
212
- target : ast:: NameOrNameRef :: NameRef ( name) ,
213
- replace_usages : usages. references ,
214
- } )
199
+ Some ( InlineData { let_stmt, delete_let, target : ast:: NameOrNameRef :: NameRef ( name) , references } )
215
200
}
216
201
217
202
#[ cfg( test) ]
0 commit comments