2
2
3
3
use std:: { collections:: hash_map:: Entry , str:: FromStr } ;
4
4
5
- use hir:: Semantics ;
5
+ use hir:: { Semantics , SemanticsScope } ;
6
6
use itertools:: Itertools ;
7
7
use rustc_hash:: FxHashMap ;
8
8
use stdx:: to_lower_snake_case;
9
9
use syntax:: {
10
10
ast:: { self , HasName } ,
11
- match_ast, AstNode , Edition , SmolStr , SmolStrBuilder ,
11
+ match_ast, AstNode , Edition , SmolStr , SmolStrBuilder , ToSmolStr ,
12
12
} ;
13
13
14
14
use crate :: RootDatabase ;
@@ -100,6 +100,19 @@ impl NameGenerator {
100
100
generator
101
101
}
102
102
103
+ pub fn new_from_scope_locals ( scope : Option < SemanticsScope < ' _ > > ) -> Self {
104
+ let mut generator = Self :: new ( ) ;
105
+ if let Some ( scope) = scope {
106
+ scope. process_all_names ( & mut |name, scope| {
107
+ if let hir:: ScopeDef :: Local ( _) = scope {
108
+ generator. insert ( name. as_str ( ) ) ;
109
+ }
110
+ } ) ;
111
+ }
112
+
113
+ generator
114
+ }
115
+
103
116
/// Suggest a name without conflicts. If the name conflicts with existing names,
104
117
/// it will try to resolve the conflict by adding a numeric suffix.
105
118
pub fn suggest_name ( & mut self , name : & str ) -> SmolStr {
@@ -162,6 +175,59 @@ impl NameGenerator {
162
175
self . suggest_name ( & c. to_string ( ) )
163
176
}
164
177
178
+ /// Suggest name of variable for given expression
179
+ ///
180
+ /// In current implementation, the function tries to get the name from
181
+ /// the following sources:
182
+ ///
183
+ /// * if expr is an argument to function/method, use parameter name
184
+ /// * if expr is a function/method call, use function name
185
+ /// * expression type name if it exists (E.g. `()`, `fn() -> ()` or `!` do not have names)
186
+ /// * fallback: `var_name`
187
+ ///
188
+ /// It also applies heuristics to filter out less informative names
189
+ ///
190
+ /// Currently it sticks to the first name found.
191
+ pub fn for_variable (
192
+ & mut self ,
193
+ expr : & ast:: Expr ,
194
+ sema : & Semantics < ' _ , RootDatabase > ,
195
+ ) -> SmolStr {
196
+ // `from_param` does not benefit from stripping it need the largest
197
+ // context possible so we check firstmost
198
+ if let Some ( name) = from_param ( expr, sema) {
199
+ return self . suggest_name ( & name) ;
200
+ }
201
+
202
+ let mut next_expr = Some ( expr. clone ( ) ) ;
203
+ while let Some ( expr) = next_expr {
204
+ let name = from_call ( & expr)
205
+ . or_else ( || from_type ( & expr, sema) )
206
+ . or_else ( || from_field_name ( & expr) ) ;
207
+ if let Some ( name) = name {
208
+ return self . suggest_name ( & name) ;
209
+ }
210
+
211
+ match expr {
212
+ ast:: Expr :: RefExpr ( inner) => next_expr = inner. expr ( ) ,
213
+ ast:: Expr :: AwaitExpr ( inner) => next_expr = inner. expr ( ) ,
214
+ // ast::Expr::BlockExpr(block) => expr = block.tail_expr(),
215
+ ast:: Expr :: CastExpr ( inner) => next_expr = inner. expr ( ) ,
216
+ ast:: Expr :: MethodCallExpr ( method) if is_useless_method ( & method) => {
217
+ next_expr = method. receiver ( ) ;
218
+ }
219
+ ast:: Expr :: ParenExpr ( inner) => next_expr = inner. expr ( ) ,
220
+ ast:: Expr :: TryExpr ( inner) => next_expr = inner. expr ( ) ,
221
+ ast:: Expr :: PrefixExpr ( prefix) if prefix. op_kind ( ) == Some ( ast:: UnaryOp :: Deref ) => {
222
+ next_expr = prefix. expr ( )
223
+ }
224
+ _ => break ,
225
+ }
226
+ }
227
+
228
+ self . suggest_name ( "var_name" )
229
+ }
230
+
165
231
/// Insert a name into the pool
166
232
fn insert ( & mut self , name : & str ) {
167
233
let ( prefix, suffix) = Self :: split_numeric_suffix ( name) ;
@@ -191,63 +257,8 @@ impl NameGenerator {
191
257
}
192
258
}
193
259
194
- /// Suggest name of variable for given expression
195
- ///
196
- /// **NOTE**: it is caller's responsibility to guarantee uniqueness of the name.
197
- /// I.e. it doesn't look for names in scope.
198
- ///
199
- /// # Current implementation
200
- ///
201
- /// In current implementation, the function tries to get the name from
202
- /// the following sources:
203
- ///
204
- /// * if expr is an argument to function/method, use parameter name
205
- /// * if expr is a function/method call, use function name
206
- /// * expression type name if it exists (E.g. `()`, `fn() -> ()` or `!` do not have names)
207
- /// * fallback: `var_name`
208
- ///
209
- /// It also applies heuristics to filter out less informative names
210
- ///
211
- /// Currently it sticks to the first name found.
212
- // FIXME: Microoptimize and return a `SmolStr` here.
213
- pub fn for_variable ( expr : & ast:: Expr , sema : & Semantics < ' _ , RootDatabase > ) -> String {
214
- // `from_param` does not benefit from stripping
215
- // it need the largest context possible
216
- // so we check firstmost
217
- if let Some ( name) = from_param ( expr, sema) {
218
- return name;
219
- }
220
-
221
- let mut next_expr = Some ( expr. clone ( ) ) ;
222
- while let Some ( expr) = next_expr {
223
- let name =
224
- from_call ( & expr) . or_else ( || from_type ( & expr, sema) ) . or_else ( || from_field_name ( & expr) ) ;
225
- if let Some ( name) = name {
226
- return name;
227
- }
228
-
229
- match expr {
230
- ast:: Expr :: RefExpr ( inner) => next_expr = inner. expr ( ) ,
231
- ast:: Expr :: AwaitExpr ( inner) => next_expr = inner. expr ( ) ,
232
- // ast::Expr::BlockExpr(block) => expr = block.tail_expr(),
233
- ast:: Expr :: CastExpr ( inner) => next_expr = inner. expr ( ) ,
234
- ast:: Expr :: MethodCallExpr ( method) if is_useless_method ( & method) => {
235
- next_expr = method. receiver ( ) ;
236
- }
237
- ast:: Expr :: ParenExpr ( inner) => next_expr = inner. expr ( ) ,
238
- ast:: Expr :: TryExpr ( inner) => next_expr = inner. expr ( ) ,
239
- ast:: Expr :: PrefixExpr ( prefix) if prefix. op_kind ( ) == Some ( ast:: UnaryOp :: Deref ) => {
240
- next_expr = prefix. expr ( )
241
- }
242
- _ => break ,
243
- }
244
- }
245
-
246
- "var_name" . to_owned ( )
247
- }
248
-
249
- fn normalize ( name : & str ) -> Option < String > {
250
- let name = to_lower_snake_case ( name) ;
260
+ fn normalize ( name : & str ) -> Option < SmolStr > {
261
+ let name = to_lower_snake_case ( name) . to_smolstr ( ) ;
251
262
252
263
if USELESS_NAMES . contains ( & name. as_str ( ) ) {
253
264
return None ;
@@ -280,11 +291,11 @@ fn is_useless_method(method: &ast::MethodCallExpr) -> bool {
280
291
}
281
292
}
282
293
283
- fn from_call ( expr : & ast:: Expr ) -> Option < String > {
294
+ fn from_call ( expr : & ast:: Expr ) -> Option < SmolStr > {
284
295
from_func_call ( expr) . or_else ( || from_method_call ( expr) )
285
296
}
286
297
287
- fn from_func_call ( expr : & ast:: Expr ) -> Option < String > {
298
+ fn from_func_call ( expr : & ast:: Expr ) -> Option < SmolStr > {
288
299
let call = match expr {
289
300
ast:: Expr :: CallExpr ( call) => call,
290
301
_ => return None ,
@@ -297,7 +308,7 @@ fn from_func_call(expr: &ast::Expr) -> Option<String> {
297
308
normalize ( ident. text ( ) )
298
309
}
299
310
300
- fn from_method_call ( expr : & ast:: Expr ) -> Option < String > {
311
+ fn from_method_call ( expr : & ast:: Expr ) -> Option < SmolStr > {
301
312
let method = match expr {
302
313
ast:: Expr :: MethodCallExpr ( call) => call,
303
314
_ => return None ,
@@ -319,7 +330,7 @@ fn from_method_call(expr: &ast::Expr) -> Option<String> {
319
330
normalize ( name)
320
331
}
321
332
322
- fn from_param ( expr : & ast:: Expr , sema : & Semantics < ' _ , RootDatabase > ) -> Option < String > {
333
+ fn from_param ( expr : & ast:: Expr , sema : & Semantics < ' _ , RootDatabase > ) -> Option < SmolStr > {
323
334
let arg_list = expr. syntax ( ) . parent ( ) . and_then ( ast:: ArgList :: cast) ?;
324
335
let args_parent = arg_list. syntax ( ) . parent ( ) ?;
325
336
let func = match_ast ! {
@@ -338,7 +349,7 @@ fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<St
338
349
let param = func. params ( ) . into_iter ( ) . nth ( idx) ?;
339
350
let pat = sema. source ( param) ?. value . right ( ) ?. pat ( ) ?;
340
351
let name = var_name_from_pat ( & pat) ?;
341
- normalize ( & name. to_string ( ) )
352
+ normalize ( & name. to_smolstr ( ) )
342
353
}
343
354
344
355
fn var_name_from_pat ( pat : & ast:: Pat ) -> Option < ast:: Name > {
@@ -350,15 +361,15 @@ fn var_name_from_pat(pat: &ast::Pat) -> Option<ast::Name> {
350
361
}
351
362
}
352
363
353
- fn from_type ( expr : & ast:: Expr , sema : & Semantics < ' _ , RootDatabase > ) -> Option < String > {
364
+ fn from_type ( expr : & ast:: Expr , sema : & Semantics < ' _ , RootDatabase > ) -> Option < SmolStr > {
354
365
let ty = sema. type_of_expr ( expr) ?. adjusted ( ) ;
355
366
let ty = ty. remove_ref ( ) . unwrap_or ( ty) ;
356
367
let edition = sema. scope ( expr. syntax ( ) ) ?. krate ( ) . edition ( sema. db ) ;
357
368
358
369
name_of_type ( & ty, sema. db , edition)
359
370
}
360
371
361
- fn name_of_type ( ty : & hir:: Type , db : & RootDatabase , edition : Edition ) -> Option < String > {
372
+ fn name_of_type ( ty : & hir:: Type , db : & RootDatabase , edition : Edition ) -> Option < SmolStr > {
362
373
let name = if let Some ( adt) = ty. as_adt ( ) {
363
374
let name = adt. name ( db) . display ( db, edition) . to_string ( ) ;
364
375
@@ -393,7 +404,7 @@ fn trait_name(trait_: &hir::Trait, db: &RootDatabase, edition: Edition) -> Optio
393
404
Some ( name)
394
405
}
395
406
396
- fn from_field_name ( expr : & ast:: Expr ) -> Option < String > {
407
+ fn from_field_name ( expr : & ast:: Expr ) -> Option < SmolStr > {
397
408
let field = match expr {
398
409
ast:: Expr :: FieldExpr ( field) => field,
399
410
_ => return None ,
@@ -424,7 +435,7 @@ mod tests {
424
435
frange. range,
425
436
"selection is not an expression(yet contained in one)"
426
437
) ;
427
- let name = for_variable ( & expr, & sema) ;
438
+ let name = NameGenerator :: new ( ) . for_variable ( & expr, & sema) ;
428
439
assert_eq ! ( & name, expected) ;
429
440
}
430
441
0 commit comments