1
1
use either:: Either ;
2
- use hir:: PathResolution ;
2
+ use hir:: { PathResolution , Semantics } ;
3
3
use ide_db:: {
4
+ base_db:: { FileId , FileRange } ,
4
5
defs:: Definition ,
5
6
search:: { FileReference , UsageSearchResult } ,
7
+ RootDatabase ,
6
8
} ;
7
9
use syntax:: {
8
10
ast:: { self , AstNode , AstToken , NameOwner } ,
@@ -31,8 +33,15 @@ use crate::{
31
33
// }
32
34
// ```
33
35
pub ( crate ) fn inline_local_variable ( acc : & mut Assists , ctx : & AssistContext ) -> Option < ( ) > {
36
+ let FileRange { file_id, range } = ctx. frange ;
34
37
let InlineData { let_stmt, delete_let, references, target } =
35
- inline_let ( ctx) . or_else ( || inline_usage ( ctx) ) ?;
38
+ if let Some ( let_stmt) = ctx. find_node_at_offset :: < ast:: LetStmt > ( ) {
39
+ inline_let ( & ctx. sema , let_stmt, range, file_id)
40
+ } else if let Some ( path_expr) = ctx. find_node_at_offset :: < ast:: PathExpr > ( ) {
41
+ inline_usage ( & ctx. sema , path_expr, range, file_id)
42
+ } else {
43
+ None
44
+ } ?;
36
45
let initializer_expr = let_stmt. initializer ( ) ?;
37
46
38
47
let delete_range = delete_let. then ( || {
@@ -139,8 +148,12 @@ struct InlineData {
139
148
references : Vec < FileReference > ,
140
149
}
141
150
142
- fn inline_let ( ctx : & AssistContext ) -> Option < InlineData > {
143
- let let_stmt = ctx. find_node_at_offset :: < ast:: LetStmt > ( ) ?;
151
+ fn inline_let (
152
+ sema : & Semantics < RootDatabase > ,
153
+ let_stmt : ast:: LetStmt ,
154
+ range : TextRange ,
155
+ file_id : FileId ,
156
+ ) -> Option < InlineData > {
144
157
let bind_pat = match let_stmt. pat ( ) ? {
145
158
ast:: Pat :: IdentPat ( pat) => pat,
146
159
_ => return None ,
@@ -149,14 +162,14 @@ fn inline_let(ctx: &AssistContext) -> Option<InlineData> {
149
162
cov_mark:: hit!( test_not_inline_mut_variable) ;
150
163
return None ;
151
164
}
152
- if !bind_pat. syntax ( ) . text_range ( ) . contains_inclusive ( ctx . offset ( ) ) {
165
+ if !bind_pat. syntax ( ) . text_range ( ) . contains_range ( range ) {
153
166
cov_mark:: hit!( not_applicable_outside_of_bind_pat) ;
154
167
return None ;
155
168
}
156
169
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 ) {
170
+ let local = sema. to_def ( & bind_pat) ?;
171
+ let UsageSearchResult { mut references } = Definition :: Local ( local) . usages ( sema) . all ( ) ;
172
+ match references. remove ( & file_id) {
160
173
Some ( references) => Some ( InlineData {
161
174
let_stmt,
162
175
delete_let : true ,
@@ -170,29 +183,37 @@ fn inline_let(ctx: &AssistContext) -> Option<InlineData> {
170
183
}
171
184
}
172
185
173
- fn inline_usage ( ctx : & AssistContext ) -> Option < InlineData > {
174
- let path_expr = ctx. find_node_at_offset :: < ast:: PathExpr > ( ) ?;
186
+ fn inline_usage (
187
+ sema : & Semantics < RootDatabase > ,
188
+ path_expr : ast:: PathExpr ,
189
+ range : TextRange ,
190
+ file_id : FileId ,
191
+ ) -> Option < InlineData > {
175
192
let path = path_expr. path ( ) ?;
176
193
let name = path. as_single_name_ref ( ) ?;
194
+ if !name. syntax ( ) . text_range ( ) . contains_range ( range) {
195
+ cov_mark:: hit!( test_not_inline_selection_too_broad) ;
196
+ return None ;
197
+ }
177
198
178
- let local = match ctx . sema . resolve_path ( & path) ? {
199
+ let local = match sema. resolve_path ( & path) ? {
179
200
PathResolution :: Local ( local) => local,
180
201
_ => return None ,
181
202
} ;
182
- if local. is_mut ( ctx . db ( ) ) {
203
+ if local. is_mut ( sema . db ) {
183
204
cov_mark:: hit!( test_not_inline_mut_variable_use) ;
184
205
return None ;
185
206
}
186
207
187
- let bind_pat = match local. source ( ctx . db ( ) ) . value {
208
+ let bind_pat = match local. source ( sema . db ) . value {
188
209
Either :: Left ( ident) => ident,
189
210
_ => return None ,
190
211
} ;
191
212
192
213
let let_stmt = ast:: LetStmt :: cast ( bind_pat. syntax ( ) . parent ( ) ?) ?;
193
214
194
- let UsageSearchResult { mut references } = Definition :: Local ( local) . usages ( & ctx . sema ) . all ( ) ;
195
- let mut references = references. remove ( & ctx . frange . file_id ) ?;
215
+ let UsageSearchResult { mut references } = Definition :: Local ( local) . usages ( sema) . all ( ) ;
216
+ let mut references = references. remove ( & file_id) ?;
196
217
let delete_let = references. len ( ) == 1 ;
197
218
references. retain ( |fref| fref. name . as_name_ref ( ) == Some ( & name) ) ;
198
219
@@ -875,6 +896,21 @@ fn f() {
875
896
let xyz$0 = 0;
876
897
m!(xyz); // replacing it would break the macro
877
898
}
899
+ "# ,
900
+ ) ;
901
+ }
902
+
903
+ #[ test]
904
+ fn test_not_inline_selection_too_broad ( ) {
905
+ cov_mark:: check!( test_not_inline_selection_too_broad) ;
906
+ check_assist_not_applicable (
907
+ inline_local_variable,
908
+ r#"
909
+ fn f() {
910
+ let foo = 0;
911
+ let bar = 0;
912
+ $0foo + bar$0;
913
+ }
878
914
"# ,
879
915
) ;
880
916
}
0 commit comments