]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs
Auto merge of #107843 - bjorn3:sync_cg_clif-2023-02-09, r=bjorn3
[rust.git] / src / tools / rust-analyzer / crates / ide-assists / src / utils / suggest_name.rs
1 //! This module contains functions to suggest names for expressions, functions and other items
2
3 use hir::Semantics;
4 use ide_db::RootDatabase;
5 use itertools::Itertools;
6 use stdx::to_lower_snake_case;
7 use syntax::{
8     ast::{self, HasName},
9     match_ast, AstNode, SmolStr,
10 };
11
12 /// Trait names, that will be ignored when in `impl Trait` and `dyn Trait`
13 const USELESS_TRAITS: &[&str] = &["Send", "Sync", "Copy", "Clone", "Eq", "PartialEq"];
14
15 /// Identifier names that won't be suggested, ever
16 ///
17 /// **NOTE**: they all must be snake lower case
18 const USELESS_NAMES: &[&str] =
19     &["new", "default", "option", "some", "none", "ok", "err", "str", "string"];
20
21 /// Generic types replaced by their first argument
22 ///
23 /// # Examples
24 /// `Option<Name>` -> `Name`
25 /// `Result<User, Error>` -> `User`
26 const WRAPPER_TYPES: &[&str] = &["Box", "Option", "Result"];
27
28 /// Prefixes to strip from methods names
29 ///
30 /// # Examples
31 /// `vec.as_slice()` -> `slice`
32 /// `args.into_config()` -> `config`
33 /// `bytes.to_vec()` -> `vec`
34 const USELESS_METHOD_PREFIXES: &[&str] = &["into_", "as_", "to_"];
35
36 /// Useless methods that are stripped from expression
37 ///
38 /// # Examples
39 /// `var.name().to_string()` -> `var.name()`
40 const USELESS_METHODS: &[&str] = &[
41     "to_string",
42     "as_str",
43     "to_owned",
44     "as_ref",
45     "clone",
46     "cloned",
47     "expect",
48     "expect_none",
49     "unwrap",
50     "unwrap_none",
51     "unwrap_or",
52     "unwrap_or_default",
53     "unwrap_or_else",
54     "unwrap_unchecked",
55     "iter",
56     "into_iter",
57     "iter_mut",
58     "into_future",
59 ];
60
61 pub(crate) fn for_generic_parameter(ty: &ast::ImplTraitType) -> SmolStr {
62     let c = ty
63         .type_bound_list()
64         .and_then(|bounds| bounds.syntax().text().char_at(0.into()))
65         .unwrap_or('T');
66     c.encode_utf8(&mut [0; 4]).into()
67 }
68
69 /// Suggest name of variable for given expression
70 ///
71 /// **NOTE**: it is caller's responsibility to guarantee uniqueness of the name.
72 /// I.e. it doesn't look for names in scope.
73 ///
74 /// # Current implementation
75 ///
76 /// In current implementation, the function tries to get the name from
77 /// the following sources:
78 ///
79 /// * if expr is an argument to function/method, use parameter name
80 /// * if expr is a function/method call, use function name
81 /// * expression type name if it exists (E.g. `()`, `fn() -> ()` or `!` do not have names)
82 /// * fallback: `var_name`
83 ///
84 /// It also applies heuristics to filter out less informative names
85 ///
86 /// Currently it sticks to the first name found.
87 // FIXME: Microoptimize and return a `SmolStr` here.
88 pub(crate) fn for_variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String {
89     // `from_param` does not benefit from stripping
90     // it need the largest context possible
91     // so we check firstmost
92     if let Some(name) = from_param(expr, sema) {
93         return name;
94     }
95
96     let mut next_expr = Some(expr.clone());
97     while let Some(expr) = next_expr {
98         let name =
99             from_call(&expr).or_else(|| from_type(&expr, sema)).or_else(|| from_field_name(&expr));
100         if let Some(name) = name {
101             return name;
102         }
103
104         match expr {
105             ast::Expr::RefExpr(inner) => next_expr = inner.expr(),
106             ast::Expr::BoxExpr(inner) => next_expr = inner.expr(),
107             ast::Expr::AwaitExpr(inner) => next_expr = inner.expr(),
108             // ast::Expr::BlockExpr(block) => expr = block.tail_expr(),
109             ast::Expr::CastExpr(inner) => next_expr = inner.expr(),
110             ast::Expr::MethodCallExpr(method) if is_useless_method(&method) => {
111                 next_expr = method.receiver();
112             }
113             ast::Expr::ParenExpr(inner) => next_expr = inner.expr(),
114             ast::Expr::TryExpr(inner) => next_expr = inner.expr(),
115             ast::Expr::PrefixExpr(prefix) if prefix.op_kind() == Some(ast::UnaryOp::Deref) => {
116                 next_expr = prefix.expr()
117             }
118             _ => break,
119         }
120     }
121
122     "var_name".to_string()
123 }
124
125 fn normalize(name: &str) -> Option<String> {
126     let name = to_lower_snake_case(name);
127
128     if USELESS_NAMES.contains(&name.as_str()) {
129         return None;
130     }
131
132     if !is_valid_name(&name) {
133         return None;
134     }
135
136     Some(name)
137 }
138
139 fn is_valid_name(name: &str) -> bool {
140     match ide_db::syntax_helpers::LexedStr::single_token(name) {
141         Some((syntax::SyntaxKind::IDENT, _error)) => true,
142         _ => false,
143     }
144 }
145
146 fn is_useless_method(method: &ast::MethodCallExpr) -> bool {
147     let ident = method.name_ref().and_then(|it| it.ident_token());
148
149     match ident {
150         Some(ident) => USELESS_METHODS.contains(&ident.text()),
151         None => false,
152     }
153 }
154
155 fn from_call(expr: &ast::Expr) -> Option<String> {
156     from_func_call(expr).or_else(|| from_method_call(expr))
157 }
158
159 fn from_func_call(expr: &ast::Expr) -> Option<String> {
160     let call = match expr {
161         ast::Expr::CallExpr(call) => call,
162         _ => return None,
163     };
164     let func = match call.expr()? {
165         ast::Expr::PathExpr(path) => path,
166         _ => return None,
167     };
168     let ident = func.path()?.segment()?.name_ref()?.ident_token()?;
169     normalize(ident.text())
170 }
171
172 fn from_method_call(expr: &ast::Expr) -> Option<String> {
173     let method = match expr {
174         ast::Expr::MethodCallExpr(call) => call,
175         _ => return None,
176     };
177     let ident = method.name_ref()?.ident_token()?;
178     let mut name = ident.text();
179
180     if USELESS_METHODS.contains(&name) {
181         return None;
182     }
183
184     for prefix in USELESS_METHOD_PREFIXES {
185         if let Some(suffix) = name.strip_prefix(prefix) {
186             name = suffix;
187             break;
188         }
189     }
190
191     normalize(name)
192 }
193
194 fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<String> {
195     let arg_list = expr.syntax().parent().and_then(ast::ArgList::cast)?;
196     let args_parent = arg_list.syntax().parent()?;
197     let func = match_ast! {
198         match args_parent {
199             ast::CallExpr(call) => {
200                 let func = call.expr()?;
201                 let func_ty = sema.type_of_expr(&func)?.adjusted();
202                 func_ty.as_callable(sema.db)?
203             },
204             ast::MethodCallExpr(method) => sema.resolve_method_call_as_callable(&method)?,
205             _ => return None,
206         }
207     };
208
209     let (idx, _) = arg_list.args().find_position(|it| it == expr).unwrap();
210     let (pat, _) = func.params(sema.db).into_iter().nth(idx)?;
211     let pat = match pat? {
212         either::Either::Right(pat) => pat,
213         _ => return None,
214     };
215     let name = var_name_from_pat(&pat)?;
216     normalize(&name.to_string())
217 }
218
219 fn var_name_from_pat(pat: &ast::Pat) -> Option<ast::Name> {
220     match pat {
221         ast::Pat::IdentPat(var) => var.name(),
222         ast::Pat::RefPat(ref_pat) => var_name_from_pat(&ref_pat.pat()?),
223         ast::Pat::BoxPat(box_pat) => var_name_from_pat(&box_pat.pat()?),
224         _ => None,
225     }
226 }
227
228 fn from_type(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<String> {
229     let ty = sema.type_of_expr(expr)?.adjusted();
230     let ty = ty.remove_ref().unwrap_or(ty);
231
232     name_of_type(&ty, sema.db)
233 }
234
235 fn name_of_type(ty: &hir::Type, db: &RootDatabase) -> Option<String> {
236     let name = if let Some(adt) = ty.as_adt() {
237         let name = adt.name(db).to_string();
238
239         if WRAPPER_TYPES.contains(&name.as_str()) {
240             let inner_ty = ty.type_arguments().next()?;
241             return name_of_type(&inner_ty, db);
242         }
243
244         name
245     } else if let Some(trait_) = ty.as_dyn_trait() {
246         trait_name(&trait_, db)?
247     } else if let Some(traits) = ty.as_impl_traits(db) {
248         let mut iter = traits.filter_map(|t| trait_name(&t, db));
249         let name = iter.next()?;
250         if iter.next().is_some() {
251             return None;
252         }
253         name
254     } else {
255         return None;
256     };
257     normalize(&name)
258 }
259
260 fn trait_name(trait_: &hir::Trait, db: &RootDatabase) -> Option<String> {
261     let name = trait_.name(db).to_string();
262     if USELESS_TRAITS.contains(&name.as_str()) {
263         return None;
264     }
265     Some(name)
266 }
267
268 fn from_field_name(expr: &ast::Expr) -> Option<String> {
269     let field = match expr {
270         ast::Expr::FieldExpr(field) => field,
271         _ => return None,
272     };
273     let ident = field.name_ref()?.ident_token()?;
274     normalize(ident.text())
275 }
276
277 #[cfg(test)]
278 mod tests {
279     use ide_db::base_db::{fixture::WithFixture, FileRange};
280
281     use super::*;
282
283     #[track_caller]
284     fn check(ra_fixture: &str, expected: &str) {
285         let (db, file_id, range_or_offset) = RootDatabase::with_range_or_offset(ra_fixture);
286         let frange = FileRange { file_id, range: range_or_offset.into() };
287
288         let sema = Semantics::new(&db);
289         let source_file = sema.parse(frange.file_id);
290         let element = source_file.syntax().covering_element(frange.range);
291         let expr =
292             element.ancestors().find_map(ast::Expr::cast).expect("selection is not an expression");
293         assert_eq!(
294             expr.syntax().text_range(),
295             frange.range,
296             "selection is not an expression(yet contained in one)"
297         );
298         let name = for_variable(&expr, &sema);
299         assert_eq!(&name, expected);
300     }
301
302     #[test]
303     fn no_args() {
304         check(r#"fn foo() { $0bar()$0 }"#, "bar");
305         check(r#"fn foo() { $0bar.frobnicate()$0 }"#, "frobnicate");
306     }
307
308     #[test]
309     fn single_arg() {
310         check(r#"fn foo() { $0bar(1)$0 }"#, "bar");
311     }
312
313     #[test]
314     fn many_args() {
315         check(r#"fn foo() { $0bar(1, 2, 3)$0 }"#, "bar");
316     }
317
318     #[test]
319     fn path() {
320         check(r#"fn foo() { $0i32::bar(1, 2, 3)$0 }"#, "bar");
321     }
322
323     #[test]
324     fn generic_params() {
325         check(r#"fn foo() { $0bar::<i32>(1, 2, 3)$0 }"#, "bar");
326         check(r#"fn foo() { $0bar.frobnicate::<i32, u32>()$0 }"#, "frobnicate");
327     }
328
329     #[test]
330     fn to_name() {
331         check(
332             r#"
333 struct Args;
334 struct Config;
335 impl Args {
336     fn to_config(&self) -> Config {}
337 }
338 fn foo() {
339     $0Args.to_config()$0;
340 }
341 "#,
342             "config",
343         );
344     }
345
346     #[test]
347     fn plain_func() {
348         check(
349             r#"
350 fn bar(n: i32, m: u32);
351 fn foo() { bar($01$0, 2) }
352 "#,
353             "n",
354         );
355     }
356
357     #[test]
358     fn mut_param() {
359         check(
360             r#"
361 fn bar(mut n: i32, m: u32);
362 fn foo() { bar($01$0, 2) }
363 "#,
364             "n",
365         );
366     }
367
368     #[test]
369     fn func_does_not_exist() {
370         check(r#"fn foo() { bar($01$0, 2) }"#, "var_name");
371     }
372
373     #[test]
374     fn unnamed_param() {
375         check(
376             r#"
377 fn bar(_: i32, m: u32);
378 fn foo() { bar($01$0, 2) }
379 "#,
380             "var_name",
381         );
382     }
383
384     #[test]
385     fn tuple_pat() {
386         check(
387             r#"
388 fn bar((n, k): (i32, i32), m: u32);
389 fn foo() {
390     bar($0(1, 2)$0, 3)
391 }
392 "#,
393             "var_name",
394         );
395     }
396
397     #[test]
398     fn ref_pat() {
399         check(
400             r#"
401 fn bar(&n: &i32, m: u32);
402 fn foo() { bar($0&1$0, 3) }
403 "#,
404             "n",
405         );
406     }
407
408     #[test]
409     fn box_pat() {
410         check(
411             r#"
412 fn bar(box n: &i32, m: u32);
413 fn foo() { bar($01$0, 3) }
414 "#,
415             "n",
416         );
417     }
418
419     #[test]
420     fn param_out_of_index() {
421         check(
422             r#"
423 fn bar(n: i32, m: u32);
424 fn foo() { bar(1, 2, $03$0) }
425 "#,
426             "var_name",
427         );
428     }
429
430     #[test]
431     fn generic_param_resolved() {
432         check(
433             r#"
434 fn bar<T>(n: T, m: u32);
435 fn foo() { bar($01$0, 2) }
436 "#,
437             "n",
438         );
439     }
440
441     #[test]
442     fn generic_param_unresolved() {
443         check(
444             r#"
445 fn bar<T>(n: T, m: u32);
446 fn foo<T>(x: T) { bar($0x$0, 2) }
447 "#,
448             "n",
449         );
450     }
451
452     #[test]
453     fn method() {
454         check(
455             r#"
456 struct S;
457 impl S { fn bar(&self, n: i32, m: u32); }
458 fn foo() { S.bar($01$0, 2) }
459 "#,
460             "n",
461         );
462     }
463
464     #[test]
465     fn method_on_impl_trait() {
466         check(
467             r#"
468 struct S;
469 trait T {
470     fn bar(&self, n: i32, m: u32);
471 }
472 impl T for S { fn bar(&self, n: i32, m: u32); }
473 fn foo() { S.bar($01$0, 2) }
474 "#,
475             "n",
476         );
477     }
478
479     #[test]
480     fn method_ufcs() {
481         check(
482             r#"
483 struct S;
484 impl S { fn bar(&self, n: i32, m: u32); }
485 fn foo() { S::bar(&S, $01$0, 2) }
486 "#,
487             "n",
488         );
489     }
490
491     #[test]
492     fn method_self() {
493         check(
494             r#"
495 struct S;
496 impl S { fn bar(&self, n: i32, m: u32); }
497 fn foo() { S::bar($0&S$0, 1, 2) }
498 "#,
499             "s",
500         );
501     }
502
503     #[test]
504     fn method_self_named() {
505         check(
506             r#"
507 struct S;
508 impl S { fn bar(strukt: &Self, n: i32, m: u32); }
509 fn foo() { S::bar($0&S$0, 1, 2) }
510 "#,
511             "strukt",
512         );
513     }
514
515     #[test]
516     fn i32() {
517         check(r#"fn foo() { let _: i32 = $01$0; }"#, "var_name");
518     }
519
520     #[test]
521     fn u64() {
522         check(r#"fn foo() { let _: u64 = $01$0; }"#, "var_name");
523     }
524
525     #[test]
526     fn bool() {
527         check(r#"fn foo() { let _: bool = $0true$0; }"#, "var_name");
528     }
529
530     #[test]
531     fn struct_unit() {
532         check(
533             r#"
534 struct Seed;
535 fn foo() { let _ = $0Seed$0; }
536 "#,
537             "seed",
538         );
539     }
540
541     #[test]
542     fn struct_unit_to_snake() {
543         check(
544             r#"
545 struct SeedState;
546 fn foo() { let _ = $0SeedState$0; }
547 "#,
548             "seed_state",
549         );
550     }
551
552     #[test]
553     fn struct_single_arg() {
554         check(
555             r#"
556 struct Seed(u32);
557 fn foo() { let _ = $0Seed(0)$0; }
558 "#,
559             "seed",
560         );
561     }
562
563     #[test]
564     fn struct_with_fields() {
565         check(
566             r#"
567 struct Seed { value: u32 }
568 fn foo() { let _ = $0Seed { value: 0 }$0; }
569 "#,
570             "seed",
571         );
572     }
573
574     #[test]
575     fn enum_() {
576         check(
577             r#"
578 enum Kind { A, B }
579 fn foo() { let _ = $0Kind::A$0; }
580 "#,
581             "kind",
582         );
583     }
584
585     #[test]
586     fn enum_generic_resolved() {
587         check(
588             r#"
589 enum Kind<T> { A { x: T }, B }
590 fn foo() { let _ = $0Kind::A { x:1 }$0; }
591 "#,
592             "kind",
593         );
594     }
595
596     #[test]
597     fn enum_generic_unresolved() {
598         check(
599             r#"
600 enum Kind<T> { A { x: T }, B }
601 fn foo<T>(x: T) { let _ = $0Kind::A { x }$0; }
602 "#,
603             "kind",
604         );
605     }
606
607     #[test]
608     fn dyn_trait() {
609         check(
610             r#"
611 trait DynHandler {}
612 fn bar() -> dyn DynHandler {}
613 fn foo() { $0(bar())$0; }
614 "#,
615             "dyn_handler",
616         );
617     }
618
619     #[test]
620     fn impl_trait() {
621         check(
622             r#"
623 trait StaticHandler {}
624 fn bar() -> impl StaticHandler {}
625 fn foo() { $0(bar())$0; }
626 "#,
627             "static_handler",
628         );
629     }
630
631     #[test]
632     fn impl_trait_plus_clone() {
633         check(
634             r#"
635 trait StaticHandler {}
636 trait Clone {}
637 fn bar() -> impl StaticHandler + Clone {}
638 fn foo() { $0(bar())$0; }
639 "#,
640             "static_handler",
641         );
642     }
643
644     #[test]
645     fn impl_trait_plus_lifetime() {
646         check(
647             r#"
648 trait StaticHandler {}
649 trait Clone {}
650 fn bar<'a>(&'a i32) -> impl StaticHandler + 'a {}
651 fn foo() { $0(bar(&1))$0; }
652 "#,
653             "static_handler",
654         );
655     }
656
657     #[test]
658     fn impl_trait_plus_trait() {
659         check(
660             r#"
661 trait Handler {}
662 trait StaticHandler {}
663 fn bar() -> impl StaticHandler + Handler {}
664 fn foo() { $0(bar())$0; }
665 "#,
666             "bar",
667         );
668     }
669
670     #[test]
671     fn ref_value() {
672         check(
673             r#"
674 struct Seed;
675 fn bar() -> &Seed {}
676 fn foo() { $0(bar())$0; }
677 "#,
678             "seed",
679         );
680     }
681
682     #[test]
683     fn box_value() {
684         check(
685             r#"
686 struct Box<T>(*const T);
687 struct Seed;
688 fn bar() -> Box<Seed> {}
689 fn foo() { $0(bar())$0; }
690 "#,
691             "seed",
692         );
693     }
694
695     #[test]
696     fn box_generic() {
697         check(
698             r#"
699 struct Box<T>(*const T);
700 fn bar<T>() -> Box<T> {}
701 fn foo<T>() { $0(bar::<T>())$0; }
702 "#,
703             "bar",
704         );
705     }
706
707     #[test]
708     fn option_value() {
709         check(
710             r#"
711 enum Option<T> { Some(T) }
712 struct Seed;
713 fn bar() -> Option<Seed> {}
714 fn foo() { $0(bar())$0; }
715 "#,
716             "seed",
717         );
718     }
719
720     #[test]
721     fn result_value() {
722         check(
723             r#"
724 enum Result<T, E> { Ok(T), Err(E) }
725 struct Seed;
726 struct Error;
727 fn bar() -> Result<Seed, Error> {}
728 fn foo() { $0(bar())$0; }
729 "#,
730             "seed",
731         );
732     }
733
734     #[test]
735     fn ref_call() {
736         check(
737             r#"
738 fn foo() { $0&bar(1, 3)$0 }
739 "#,
740             "bar",
741         );
742     }
743
744     #[test]
745     fn name_to_string() {
746         check(
747             r#"
748 fn foo() { $0function.name().to_string()$0 }
749 "#,
750             "name",
751         );
752     }
753
754     #[test]
755     fn nested_useless_method() {
756         check(
757             r#"
758 fn foo() { $0function.name().as_ref().unwrap().to_string()$0 }
759 "#,
760             "name",
761         );
762     }
763
764     #[test]
765     fn struct_field_name() {
766         check(
767             r#"
768 struct S<T> {
769     some_field: T;
770 }
771 fn foo<T>(some_struct: S<T>) { $0some_struct.some_field$0 }
772 "#,
773             "some_field",
774         );
775     }
776 }