22
33use std:: { collections:: hash_map:: Entry , str:: FromStr } ;
44
5- use hir:: Semantics ;
5+ use hir:: { Semantics , SemanticsScope } ;
66use itertools:: Itertools ;
77use rustc_hash:: FxHashMap ;
88use stdx:: to_lower_snake_case;
99use syntax:: {
1010 ast:: { self , HasName } ,
11- match_ast, AstNode , Edition , SmolStr , SmolStrBuilder ,
11+ match_ast, AstNode , Edition , SmolStr , SmolStrBuilder , ToSmolStr ,
1212} ;
1313
1414use crate :: RootDatabase ;
@@ -100,6 +100,19 @@ impl NameGenerator {
100100 generator
101101 }
102102
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+
103116 /// Suggest a name without conflicts. If the name conflicts with existing names,
104117 /// it will try to resolve the conflict by adding a numeric suffix.
105118 pub fn suggest_name ( & mut self , name : & str ) -> SmolStr {
@@ -162,6 +175,59 @@ impl NameGenerator {
162175 self . suggest_name ( & c. to_string ( ) )
163176 }
164177
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+
165231 /// Insert a name into the pool
166232 fn insert ( & mut self , name : & str ) {
167233 let ( prefix, suffix) = Self :: split_numeric_suffix ( name) ;
@@ -191,63 +257,8 @@ impl NameGenerator {
191257 }
192258}
193259
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 ( ) ;
251262
252263 if USELESS_NAMES . contains ( & name. as_str ( ) ) {
253264 return None ;
@@ -280,11 +291,11 @@ fn is_useless_method(method: &ast::MethodCallExpr) -> bool {
280291 }
281292}
282293
283- fn from_call ( expr : & ast:: Expr ) -> Option < String > {
294+ fn from_call ( expr : & ast:: Expr ) -> Option < SmolStr > {
284295 from_func_call ( expr) . or_else ( || from_method_call ( expr) )
285296}
286297
287- fn from_func_call ( expr : & ast:: Expr ) -> Option < String > {
298+ fn from_func_call ( expr : & ast:: Expr ) -> Option < SmolStr > {
288299 let call = match expr {
289300 ast:: Expr :: CallExpr ( call) => call,
290301 _ => return None ,
@@ -297,7 +308,7 @@ fn from_func_call(expr: &ast::Expr) -> Option<String> {
297308 normalize ( ident. text ( ) )
298309}
299310
300- fn from_method_call ( expr : & ast:: Expr ) -> Option < String > {
311+ fn from_method_call ( expr : & ast:: Expr ) -> Option < SmolStr > {
301312 let method = match expr {
302313 ast:: Expr :: MethodCallExpr ( call) => call,
303314 _ => return None ,
@@ -319,7 +330,7 @@ fn from_method_call(expr: &ast::Expr) -> Option<String> {
319330 normalize ( name)
320331}
321332
322- fn from_param ( expr : & ast:: Expr , sema : & Semantics < ' _ , RootDatabase > ) -> Option < String > {
333+ fn from_param ( expr : & ast:: Expr , sema : & Semantics < ' _ , RootDatabase > ) -> Option < SmolStr > {
323334 let arg_list = expr. syntax ( ) . parent ( ) . and_then ( ast:: ArgList :: cast) ?;
324335 let args_parent = arg_list. syntax ( ) . parent ( ) ?;
325336 let func = match_ast ! {
@@ -338,7 +349,7 @@ fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<St
338349 let param = func. params ( ) . into_iter ( ) . nth ( idx) ?;
339350 let pat = sema. source ( param) ?. value . right ( ) ?. pat ( ) ?;
340351 let name = var_name_from_pat ( & pat) ?;
341- normalize ( & name. to_string ( ) )
352+ normalize ( & name. to_smolstr ( ) )
342353}
343354
344355fn 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> {
350361 }
351362}
352363
353- fn from_type ( expr : & ast:: Expr , sema : & Semantics < ' _ , RootDatabase > ) -> Option < String > {
364+ fn from_type ( expr : & ast:: Expr , sema : & Semantics < ' _ , RootDatabase > ) -> Option < SmolStr > {
354365 let ty = sema. type_of_expr ( expr) ?. adjusted ( ) ;
355366 let ty = ty. remove_ref ( ) . unwrap_or ( ty) ;
356367 let edition = sema. scope ( expr. syntax ( ) ) ?. krate ( ) . edition ( sema. db ) ;
357368
358369 name_of_type ( & ty, sema. db , edition)
359370}
360371
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 > {
362373 let name = if let Some ( adt) = ty. as_adt ( ) {
363374 let name = adt. name ( db) . display ( db, edition) . to_string ( ) ;
364375
@@ -393,7 +404,7 @@ fn trait_name(trait_: &hir::Trait, db: &RootDatabase, edition: Edition) -> Optio
393404 Some ( name)
394405}
395406
396- fn from_field_name ( expr : & ast:: Expr ) -> Option < String > {
407+ fn from_field_name ( expr : & ast:: Expr ) -> Option < SmolStr > {
397408 let field = match expr {
398409 ast:: Expr :: FieldExpr ( field) => field,
399410 _ => return None ,
@@ -424,7 +435,7 @@ mod tests {
424435 frange. range,
425436 "selection is not an expression(yet contained in one)"
426437 ) ;
427- let name = for_variable ( & expr, & sema) ;
438+ let name = NameGenerator :: new ( ) . for_variable ( & expr, & sema) ;
428439 assert_eq ! ( & name, expected) ;
429440 }
430441
0 commit comments