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