ide_db/syntax_helpers/
suggest_name.rs

1//! This module contains functions to suggest names for expressions, functions and other items
2
3use std::{collections::hash_map::Entry, str::FromStr};
4
5use hir::{Semantics, SemanticsScope};
6use itertools::Itertools;
7use rustc_hash::FxHashMap;
8use stdx::to_lower_snake_case;
9use syntax::{
10    AstNode, Edition, SmolStr, SmolStrBuilder, ToSmolStr,
11    ast::{self, HasName},
12    match_ast,
13};
14
15use crate::RootDatabase;
16
17/// Trait names, that will be ignored when in `impl Trait` and `dyn Trait`
18const USELESS_TRAITS: &[&str] = &["Send", "Sync", "Copy", "Clone", "Eq", "PartialEq"];
19
20/// Identifier names that won't be suggested, ever
21///
22/// **NOTE**: they all must be snake lower case
23const USELESS_NAMES: &[&str] =
24    &["new", "default", "option", "some", "none", "ok", "err", "str", "string", "from", "into"];
25
26const USELESS_NAME_PREFIXES: &[&str] = &["from_", "with_", "into_"];
27
28/// Generic types replaced by their first argument
29///
30/// # Examples
31/// `Option<Name>` -> `Name`
32/// `Result<User, Error>` -> `User`
33const WRAPPER_TYPES: &[&str] = &["Box", "Arc", "Rc", "Option", "Result"];
34
35/// Generic types replaced by a plural of their first argument.
36///
37/// # Examples
38/// `Vec<Name>` -> "names"
39const SEQUENCE_TYPES: &[&str] = &["Vec", "VecDeque", "LinkedList"];
40
41/// Prefixes to strip from methods names
42///
43/// # Examples
44/// `vec.as_slice()` -> `slice`
45/// `args.into_config()` -> `config`
46/// `bytes.to_vec()` -> `vec`
47const USELESS_METHOD_PREFIXES: &[&str] = &["into_", "as_", "to_"];
48
49/// Useless methods that are stripped from expression
50///
51/// # Examples
52/// `var.name().to_string()` -> `var.name()`
53const USELESS_METHODS: &[&str] = &[
54    "to_string",
55    "as_str",
56    "to_owned",
57    "as_ref",
58    "clone",
59    "cloned",
60    "expect",
61    "expect_none",
62    "unwrap",
63    "unwrap_none",
64    "unwrap_or",
65    "unwrap_or_default",
66    "unwrap_or_else",
67    "unwrap_unchecked",
68    "iter",
69    "into_iter",
70    "iter_mut",
71    "into_future",
72];
73
74/// Generator for new names
75///
76/// The generator keeps track of existing names and suggests new names that do
77/// not conflict with existing names.
78///
79/// The generator will try to resolve conflicts by adding a numeric suffix to
80/// the name, e.g. `a`, `a1`, `a2`, ...
81///
82/// # Examples
83///
84/// ```
85/// # use ide_db::syntax_helpers::suggest_name::NameGenerator;
86/// let mut generator = NameGenerator::default();
87/// assert_eq!(generator.suggest_name("a"), "a");
88/// assert_eq!(generator.suggest_name("a"), "a1");
89///
90/// assert_eq!(generator.suggest_name("b2"), "b2");
91/// assert_eq!(generator.suggest_name("b"), "b3");
92/// ```
93#[derive(Debug, Default)]
94pub struct NameGenerator {
95    pool: FxHashMap<SmolStr, usize>,
96}
97
98impl NameGenerator {
99    /// Create a new generator with existing names. When suggesting a name, it will
100    /// avoid conflicts with existing names.
101    pub fn new_with_names<'a>(existing_names: impl Iterator<Item = &'a str>) -> Self {
102        let mut generator = Self::default();
103        existing_names.for_each(|name| generator.insert(name));
104        generator
105    }
106
107    pub fn new_from_scope_locals(scope: Option<SemanticsScope<'_>>) -> Self {
108        let mut generator = Self::default();
109        if let Some(scope) = scope {
110            scope.process_all_names(&mut |name, scope| {
111                if let hir::ScopeDef::Local(_) = scope {
112                    generator.insert(name.as_str());
113                }
114            });
115        }
116
117        generator
118    }
119
120    /// Suggest a name without conflicts. If the name conflicts with existing names,
121    /// it will try to resolve the conflict by adding a numeric suffix.
122    pub fn suggest_name(&mut self, name: &str) -> SmolStr {
123        let (prefix, suffix) = Self::split_numeric_suffix(name);
124        let prefix = SmolStr::new(prefix);
125        let suffix = suffix.unwrap_or(0);
126
127        match self.pool.entry(prefix.clone()) {
128            Entry::Vacant(entry) => {
129                entry.insert(suffix);
130                SmolStr::from_str(name).unwrap()
131            }
132            Entry::Occupied(mut entry) => {
133                let count = entry.get_mut();
134                *count = (*count + 1).max(suffix);
135
136                let mut new_name = SmolStrBuilder::new();
137                new_name.push_str(&prefix);
138                new_name.push_str(count.to_string().as_str());
139                new_name.finish()
140            }
141        }
142    }
143
144    /// Suggest a name for given type.
145    ///
146    /// The function will strip references first, and suggest name from the inner type.
147    ///
148    /// - If `ty` is an ADT, it will suggest the name of the ADT.
149    ///   + If `ty` is wrapped in `Box`, `Option` or `Result`, it will suggest the name from the inner type.
150    /// - If `ty` is a trait, it will suggest the name of the trait.
151    /// - If `ty` is an `impl Trait`, it will suggest the name of the first trait.
152    ///
153    /// If the suggested name conflicts with reserved keywords, it will return `None`.
154    pub fn for_type<'db>(
155        &mut self,
156        ty: &hir::Type<'db>,
157        db: &'db RootDatabase,
158        edition: Edition,
159    ) -> Option<SmolStr> {
160        let name = name_of_type(ty, db, edition)?;
161        Some(self.suggest_name(&name))
162    }
163
164    /// Suggest name of impl trait type
165    ///
166    /// # Current implementation
167    ///
168    /// In current implementation, the function tries to get the name from the first
169    /// character of the name for the first type bound.
170    ///
171    /// If the name conflicts with existing generic parameters, it will try to
172    /// resolve the conflict with `for_unique_generic_name`.
173    pub fn for_impl_trait_as_generic(&mut self, ty: &ast::ImplTraitType) -> SmolStr {
174        let c = ty
175            .type_bound_list()
176            .and_then(|bounds| bounds.syntax().text().char_at(0.into()))
177            .unwrap_or('T');
178
179        self.suggest_name(&c.to_string())
180    }
181
182    /// Suggest name of variable for given expression
183    ///
184    /// In current implementation, the function tries to get the name from
185    /// the following sources:
186    ///
187    /// * if expr is an argument to function/method, use parameter name
188    /// * if expr is a function/method call, use function name
189    /// * expression type name if it exists (E.g. `()`, `fn() -> ()` or `!` do not have names)
190    /// * fallback: `var_name`
191    ///
192    /// It also applies heuristics to filter out less informative names
193    ///
194    /// Currently it sticks to the first name found.
195    pub fn for_variable(
196        &mut self,
197        expr: &ast::Expr,
198        sema: &Semantics<'_, RootDatabase>,
199    ) -> SmolStr {
200        // `from_param` does not benefit from stripping it need the largest
201        // context possible so we check firstmost
202        if let Some(name) = from_param(expr, sema) {
203            return self.suggest_name(&name);
204        }
205
206        let mut next_expr = Some(expr.clone());
207        while let Some(expr) = next_expr {
208            let name = from_call(&expr)
209                .or_else(|| from_type(&expr, sema))
210                .or_else(|| from_field_name(&expr));
211            if let Some(name) = name {
212                return self.suggest_name(&name);
213            }
214
215            match expr {
216                ast::Expr::RefExpr(inner) => next_expr = inner.expr(),
217                ast::Expr::AwaitExpr(inner) => next_expr = inner.expr(),
218                // ast::Expr::BlockExpr(block) => expr = block.tail_expr(),
219                ast::Expr::CastExpr(inner) => next_expr = inner.expr(),
220                ast::Expr::MethodCallExpr(method) if is_useless_method(&method) => {
221                    next_expr = method.receiver();
222                }
223                ast::Expr::ParenExpr(inner) => next_expr = inner.expr(),
224                ast::Expr::TryExpr(inner) => next_expr = inner.expr(),
225                ast::Expr::PrefixExpr(prefix) if prefix.op_kind() == Some(ast::UnaryOp::Deref) => {
226                    next_expr = prefix.expr()
227                }
228                _ => break,
229            }
230        }
231
232        self.suggest_name("var_name")
233    }
234
235    /// Insert a name into the pool
236    fn insert(&mut self, name: &str) {
237        let (prefix, suffix) = Self::split_numeric_suffix(name);
238        let prefix = SmolStr::new(prefix);
239        let suffix = suffix.unwrap_or(0);
240
241        match self.pool.entry(prefix) {
242            Entry::Vacant(entry) => {
243                entry.insert(suffix);
244            }
245            Entry::Occupied(mut entry) => {
246                let count = entry.get_mut();
247                *count = (*count).max(suffix);
248            }
249        }
250    }
251
252    /// Remove the numeric suffix from the name
253    ///
254    /// # Examples
255    /// `a1b2c3` -> `a1b2c`
256    fn split_numeric_suffix(name: &str) -> (&str, Option<usize>) {
257        let pos =
258            name.rfind(|c: char| !c.is_numeric()).expect("Name cannot be empty or all-numeric");
259        let (prefix, suffix) = name.split_at(pos + 1);
260        (prefix, suffix.parse().ok())
261    }
262}
263
264fn normalize(name: &str) -> Option<SmolStr> {
265    let name = to_lower_snake_case(name).to_smolstr();
266
267    if USELESS_NAMES.contains(&name.as_str()) {
268        return None;
269    }
270
271    if USELESS_NAME_PREFIXES.iter().any(|prefix| name.starts_with(prefix)) {
272        return None;
273    }
274
275    if !is_valid_name(&name) {
276        return None;
277    }
278
279    Some(name)
280}
281
282fn is_valid_name(name: &str) -> bool {
283    matches!(
284        super::LexedStr::single_token(syntax::Edition::CURRENT_FIXME, name),
285        Some((syntax::SyntaxKind::IDENT, _error))
286    )
287}
288
289fn is_useless_method(method: &ast::MethodCallExpr) -> bool {
290    let ident = method.name_ref().and_then(|it| it.ident_token());
291
292    match ident {
293        Some(ident) => USELESS_METHODS.contains(&ident.text()),
294        None => false,
295    }
296}
297
298fn from_call(expr: &ast::Expr) -> Option<SmolStr> {
299    from_func_call(expr).or_else(|| from_method_call(expr))
300}
301
302fn from_func_call(expr: &ast::Expr) -> Option<SmolStr> {
303    let call = match expr {
304        ast::Expr::CallExpr(call) => call,
305        _ => return None,
306    };
307    let func = match call.expr()? {
308        ast::Expr::PathExpr(path) => path,
309        _ => return None,
310    };
311    let ident = func.path()?.segment()?.name_ref()?.ident_token()?;
312    normalize(ident.text())
313}
314
315fn from_method_call(expr: &ast::Expr) -> Option<SmolStr> {
316    let method = match expr {
317        ast::Expr::MethodCallExpr(call) => call,
318        _ => return None,
319    };
320    let ident = method.name_ref()?.ident_token()?;
321    let mut name = ident.text();
322
323    if USELESS_METHODS.contains(&name) {
324        return None;
325    }
326
327    for prefix in USELESS_METHOD_PREFIXES {
328        if let Some(suffix) = name.strip_prefix(prefix) {
329            name = suffix;
330            break;
331        }
332    }
333
334    normalize(name)
335}
336
337fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<SmolStr> {
338    let arg_list = expr.syntax().parent().and_then(ast::ArgList::cast)?;
339    let args_parent = arg_list.syntax().parent()?;
340    let func = match_ast! {
341        match args_parent {
342            ast::CallExpr(call) => {
343                let func = call.expr()?;
344                let func_ty = sema.type_of_expr(&func)?.adjusted();
345                func_ty.as_callable(sema.db)?
346            },
347            ast::MethodCallExpr(method) => sema.resolve_method_call_as_callable(&method)?,
348            _ => return None,
349        }
350    };
351
352    let (idx, _) = arg_list.args().find_position(|it| it == expr).unwrap();
353    let param = func.params().into_iter().nth(idx)?;
354    let pat = sema.source(param)?.value.right()?.pat()?;
355    let name = var_name_from_pat(&pat)?;
356    normalize(&name.to_smolstr())
357}
358
359fn var_name_from_pat(pat: &ast::Pat) -> Option<ast::Name> {
360    match pat {
361        ast::Pat::IdentPat(var) => var.name(),
362        ast::Pat::RefPat(ref_pat) => var_name_from_pat(&ref_pat.pat()?),
363        ast::Pat::BoxPat(box_pat) => var_name_from_pat(&box_pat.pat()?),
364        _ => None,
365    }
366}
367
368fn from_type(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<SmolStr> {
369    let ty = sema.type_of_expr(expr)?.adjusted();
370    let ty = ty.remove_ref().unwrap_or(ty);
371    let edition = sema.scope(expr.syntax())?.krate().edition(sema.db);
372
373    name_of_type(&ty, sema.db, edition)
374}
375
376fn name_of_type<'db>(
377    ty: &hir::Type<'db>,
378    db: &'db RootDatabase,
379    edition: Edition,
380) -> Option<SmolStr> {
381    let name = if let Some(adt) = ty.as_adt() {
382        let name = adt.name(db).display(db, edition).to_string();
383
384        if WRAPPER_TYPES.contains(&name.as_str()) {
385            let inner_ty = ty.type_arguments().next()?;
386            return name_of_type(&inner_ty, db, edition);
387        }
388
389        if SEQUENCE_TYPES.contains(&name.as_str()) {
390            let inner_ty = ty.type_arguments().next();
391            return Some(sequence_name(inner_ty.as_ref(), db, edition));
392        }
393
394        name
395    } else if let Some(trait_) = ty.as_dyn_trait() {
396        trait_name(&trait_, db, edition)?
397    } else if let Some(traits) = ty.as_impl_traits(db) {
398        let mut iter = traits.filter_map(|t| trait_name(&t, db, edition));
399        let name = iter.next()?;
400        if iter.next().is_some() {
401            return None;
402        }
403        name
404    } else if let Some(inner_ty) = ty.remove_ref() {
405        return name_of_type(&inner_ty, db, edition);
406    } else if let Some(inner_ty) = ty.as_slice() {
407        return Some(sequence_name(Some(&inner_ty), db, edition));
408    } else {
409        return None;
410    };
411    normalize(&name)
412}
413
414fn sequence_name<'db>(
415    inner_ty: Option<&hir::Type<'db>>,
416    db: &'db RootDatabase,
417    edition: Edition,
418) -> SmolStr {
419    let items_str = SmolStr::new_static("items");
420    let Some(inner_ty) = inner_ty else {
421        return items_str;
422    };
423    let Some(name) = name_of_type(inner_ty, db, edition) else {
424        return items_str;
425    };
426
427    if name.ends_with(['s', 'x', 'y']) {
428        // Given a type called e.g. "Boss", "Fox" or "Story", don't try to
429        // create a plural.
430        items_str
431    } else {
432        SmolStr::new(format!("{name}s"))
433    }
434}
435
436fn trait_name(trait_: &hir::Trait, db: &RootDatabase, edition: Edition) -> Option<String> {
437    let name = trait_.name(db).display(db, edition).to_string();
438    if USELESS_TRAITS.contains(&name.as_str()) {
439        return None;
440    }
441    Some(name)
442}
443
444fn from_field_name(expr: &ast::Expr) -> Option<SmolStr> {
445    let field = match expr {
446        ast::Expr::FieldExpr(field) => field,
447        _ => return None,
448    };
449    let ident = field.name_ref()?.ident_token()?;
450    normalize(ident.text())
451}
452
453#[cfg(test)]
454mod tests {
455    use hir::FileRange;
456    use test_fixture::WithFixture;
457
458    use super::*;
459
460    #[track_caller]
461    fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expected: &str) {
462        let (db, file_id, range_or_offset) = RootDatabase::with_range_or_offset(ra_fixture);
463        let frange = FileRange { file_id, range: range_or_offset.into() };
464        let sema = Semantics::new(&db);
465
466        let source_file = sema.parse(frange.file_id);
467
468        let element = source_file.syntax().covering_element(frange.range);
469        let expr =
470            element.ancestors().find_map(ast::Expr::cast).expect("selection is not an expression");
471        assert_eq!(
472            expr.syntax().text_range(),
473            frange.range,
474            "selection is not an expression(yet contained in one)"
475        );
476        let name = NameGenerator::default().for_variable(&expr, &sema);
477        assert_eq!(&name, expected);
478    }
479
480    #[test]
481    fn no_args() {
482        check(r#"fn foo() { $0bar()$0 }"#, "bar");
483        check(r#"fn foo() { $0bar.frobnicate()$0 }"#, "frobnicate");
484    }
485
486    #[test]
487    fn single_arg() {
488        check(r#"fn foo() { $0bar(1)$0 }"#, "bar");
489    }
490
491    #[test]
492    fn many_args() {
493        check(r#"fn foo() { $0bar(1, 2, 3)$0 }"#, "bar");
494    }
495
496    #[test]
497    fn path() {
498        check(r#"fn foo() { $0i32::bar(1, 2, 3)$0 }"#, "bar");
499    }
500
501    #[test]
502    fn generic_params() {
503        check(r#"fn foo() { $0bar::<i32>(1, 2, 3)$0 }"#, "bar");
504        check(r#"fn foo() { $0bar.frobnicate::<i32, u32>()$0 }"#, "frobnicate");
505    }
506
507    #[test]
508    fn to_name() {
509        check(
510            r#"
511struct Args;
512struct Config;
513impl Args {
514    fn to_config(&self) -> Config {}
515}
516fn foo() {
517    $0Args.to_config()$0;
518}
519"#,
520            "config",
521        );
522    }
523
524    #[test]
525    fn plain_func() {
526        check(
527            r#"
528fn bar(n: i32, m: u32);
529fn foo() { bar($01$0, 2) }
530"#,
531            "n",
532        );
533    }
534
535    #[test]
536    fn mut_param() {
537        check(
538            r#"
539fn bar(mut n: i32, m: u32);
540fn foo() { bar($01$0, 2) }
541"#,
542            "n",
543        );
544    }
545
546    #[test]
547    fn func_does_not_exist() {
548        check(r#"fn foo() { bar($01$0, 2) }"#, "var_name");
549    }
550
551    #[test]
552    fn unnamed_param() {
553        check(
554            r#"
555fn bar(_: i32, m: u32);
556fn foo() { bar($01$0, 2) }
557"#,
558            "var_name",
559        );
560    }
561
562    #[test]
563    fn tuple_pat() {
564        check(
565            r#"
566fn bar((n, k): (i32, i32), m: u32);
567fn foo() {
568    bar($0(1, 2)$0, 3)
569}
570"#,
571            "var_name",
572        );
573    }
574
575    #[test]
576    fn ref_pat() {
577        check(
578            r#"
579fn bar(&n: &i32, m: u32);
580fn foo() { bar($0&1$0, 3) }
581"#,
582            "n",
583        );
584    }
585
586    #[test]
587    fn box_pat() {
588        check(
589            r#"
590fn bar(box n: &i32, m: u32);
591fn foo() { bar($01$0, 3) }
592"#,
593            "n",
594        );
595    }
596
597    #[test]
598    fn param_out_of_index() {
599        check(
600            r#"
601fn bar(n: i32, m: u32);
602fn foo() { bar(1, 2, $03$0) }
603"#,
604            "var_name",
605        );
606    }
607
608    #[test]
609    fn generic_param_resolved() {
610        check(
611            r#"
612fn bar<T>(n: T, m: u32);
613fn foo() { bar($01$0, 2) }
614"#,
615            "n",
616        );
617    }
618
619    #[test]
620    fn generic_param_unresolved() {
621        check(
622            r#"
623fn bar<T>(n: T, m: u32);
624fn foo<T>(x: T) { bar($0x$0, 2) }
625"#,
626            "n",
627        );
628    }
629
630    #[test]
631    fn method() {
632        check(
633            r#"
634struct S;
635impl S { fn bar(&self, n: i32, m: u32); }
636fn foo() { S.bar($01$0, 2) }
637"#,
638            "n",
639        );
640    }
641
642    #[test]
643    fn method_on_impl_trait() {
644        check(
645            r#"
646struct S;
647trait T {
648    fn bar(&self, n: i32, m: u32);
649}
650impl T for S { fn bar(&self, n: i32, m: u32); }
651fn foo() { S.bar($01$0, 2) }
652"#,
653            "n",
654        );
655    }
656
657    #[test]
658    fn method_ufcs() {
659        check(
660            r#"
661struct S;
662impl S { fn bar(&self, n: i32, m: u32); }
663fn foo() { S::bar(&S, $01$0, 2) }
664"#,
665            "n",
666        );
667    }
668
669    #[test]
670    fn method_self() {
671        check(
672            r#"
673struct S;
674impl S { fn bar(&self, n: i32, m: u32); }
675fn foo() { S::bar($0&S$0, 1, 2) }
676"#,
677            "s",
678        );
679    }
680
681    #[test]
682    fn method_self_named() {
683        check(
684            r#"
685struct S;
686impl S { fn bar(strukt: &Self, n: i32, m: u32); }
687fn foo() { S::bar($0&S$0, 1, 2) }
688"#,
689            "strukt",
690        );
691    }
692
693    #[test]
694    fn i32() {
695        check(r#"fn foo() { let _: i32 = $01$0; }"#, "var_name");
696    }
697
698    #[test]
699    fn u64() {
700        check(r#"fn foo() { let _: u64 = $01$0; }"#, "var_name");
701    }
702
703    #[test]
704    fn bool() {
705        check(r#"fn foo() { let _: bool = $0true$0; }"#, "var_name");
706    }
707
708    #[test]
709    fn struct_unit() {
710        check(
711            r#"
712struct Seed;
713fn foo() { let _ = $0Seed$0; }
714"#,
715            "seed",
716        );
717    }
718
719    #[test]
720    fn struct_unit_to_snake() {
721        check(
722            r#"
723struct SeedState;
724fn foo() { let _ = $0SeedState$0; }
725"#,
726            "seed_state",
727        );
728    }
729
730    #[test]
731    fn struct_single_arg() {
732        check(
733            r#"
734struct Seed(u32);
735fn foo() { let _ = $0Seed(0)$0; }
736"#,
737            "seed",
738        );
739    }
740
741    #[test]
742    fn struct_with_fields() {
743        check(
744            r#"
745struct Seed { value: u32 }
746fn foo() { let _ = $0Seed { value: 0 }$0; }
747"#,
748            "seed",
749        );
750    }
751
752    #[test]
753    fn enum_() {
754        check(
755            r#"
756enum Kind { A, B }
757fn foo() { let _ = $0Kind::A$0; }
758"#,
759            "kind",
760        );
761    }
762
763    #[test]
764    fn enum_generic_resolved() {
765        check(
766            r#"
767enum Kind<T> { A { x: T }, B }
768fn foo() { let _ = $0Kind::A { x:1 }$0; }
769"#,
770            "kind",
771        );
772    }
773
774    #[test]
775    fn enum_generic_unresolved() {
776        check(
777            r#"
778enum Kind<T> { A { x: T }, B }
779fn foo<T>(x: T) { let _ = $0Kind::A { x }$0; }
780"#,
781            "kind",
782        );
783    }
784
785    #[test]
786    fn dyn_trait() {
787        check(
788            r#"
789trait DynHandler {}
790fn bar() -> dyn DynHandler {}
791fn foo() { $0(bar())$0; }
792"#,
793            "dyn_handler",
794        );
795    }
796
797    #[test]
798    fn impl_trait() {
799        check(
800            r#"
801trait StaticHandler {}
802fn bar() -> impl StaticHandler {}
803fn foo() { $0(bar())$0; }
804"#,
805            "static_handler",
806        );
807    }
808
809    #[test]
810    fn impl_trait_plus_clone() {
811        check(
812            r#"
813trait StaticHandler {}
814trait Clone {}
815fn bar() -> impl StaticHandler + Clone {}
816fn foo() { $0(bar())$0; }
817"#,
818            "static_handler",
819        );
820    }
821
822    #[test]
823    fn impl_trait_plus_lifetime() {
824        check(
825            r#"
826trait StaticHandler {}
827trait Clone {}
828fn bar<'a>(&'a i32) -> impl StaticHandler + 'a {}
829fn foo() { $0(bar(&1))$0; }
830"#,
831            "static_handler",
832        );
833    }
834
835    #[test]
836    fn impl_trait_plus_trait() {
837        check(
838            r#"
839trait Handler {}
840trait StaticHandler {}
841fn bar() -> impl StaticHandler + Handler {}
842fn foo() { $0(bar())$0; }
843"#,
844            "bar",
845        );
846    }
847
848    #[test]
849    fn ref_value() {
850        check(
851            r#"
852struct Seed;
853fn bar() -> &Seed {}
854fn foo() { $0(bar())$0; }
855"#,
856            "seed",
857        );
858    }
859
860    #[test]
861    fn box_value() {
862        check(
863            r#"
864struct Box<T>(*const T);
865struct Seed;
866fn bar() -> Box<Seed> {}
867fn foo() { $0(bar())$0; }
868"#,
869            "seed",
870        );
871    }
872
873    #[test]
874    fn box_generic() {
875        check(
876            r#"
877struct Box<T>(*const T);
878fn bar<T>() -> Box<T> {}
879fn foo<T>() { $0(bar::<T>())$0; }
880"#,
881            "bar",
882        );
883    }
884
885    #[test]
886    fn option_value() {
887        check(
888            r#"
889enum Option<T> { Some(T) }
890struct Seed;
891fn bar() -> Option<Seed> {}
892fn foo() { $0(bar())$0; }
893"#,
894            "seed",
895        );
896    }
897
898    #[test]
899    fn result_value() {
900        check(
901            r#"
902enum Result<T, E> { Ok(T), Err(E) }
903struct Seed;
904struct Error;
905fn bar() -> Result<Seed, Error> {}
906fn foo() { $0(bar())$0; }
907"#,
908            "seed",
909        );
910    }
911
912    #[test]
913    fn arc_value() {
914        check(
915            r#"
916struct Arc<T>(*const T);
917struct Seed;
918fn bar() -> Arc<Seed> {}
919fn foo() { $0(bar())$0; }
920"#,
921            "seed",
922        );
923    }
924
925    #[test]
926    fn rc_value() {
927        check(
928            r#"
929struct Rc<T>(*const T);
930struct Seed;
931fn bar() -> Rc<Seed> {}
932fn foo() { $0(bar())$0; }
933"#,
934            "seed",
935        );
936    }
937
938    #[test]
939    fn vec_value() {
940        check(
941            r#"
942struct Vec<T> {};
943struct Seed;
944fn bar() -> Vec<Seed> {}
945fn foo() { $0(bar())$0; }
946"#,
947            "seeds",
948        );
949    }
950
951    #[test]
952    fn vec_value_ends_with_s() {
953        check(
954            r#"
955struct Vec<T> {};
956struct Boss;
957fn bar() -> Vec<Boss> {}
958fn foo() { $0(bar())$0; }
959"#,
960            "items",
961        );
962    }
963
964    #[test]
965    fn vecdeque_value() {
966        check(
967            r#"
968struct VecDeque<T> {};
969struct Seed;
970fn bar() -> VecDeque<Seed> {}
971fn foo() { $0(bar())$0; }
972"#,
973            "seeds",
974        );
975    }
976
977    #[test]
978    fn slice_value() {
979        check(
980            r#"
981struct Vec<T> {};
982struct Seed;
983fn bar() -> &[Seed] {}
984fn foo() { $0(bar())$0; }
985"#,
986            "seeds",
987        );
988    }
989
990    #[test]
991    fn ref_call() {
992        check(
993            r#"
994fn foo() { $0&bar(1, 3)$0 }
995"#,
996            "bar",
997        );
998    }
999
1000    #[test]
1001    fn name_to_string() {
1002        check(
1003            r#"
1004fn foo() { $0function.name().to_string()$0 }
1005"#,
1006            "name",
1007        );
1008    }
1009
1010    #[test]
1011    fn nested_useless_method() {
1012        check(
1013            r#"
1014fn foo() { $0function.name().as_ref().unwrap().to_string()$0 }
1015"#,
1016            "name",
1017        );
1018    }
1019
1020    #[test]
1021    fn struct_field_name() {
1022        check(
1023            r#"
1024struct S<T> {
1025    some_field: T;
1026}
1027fn foo<T>(some_struct: S<T>) { $0some_struct.some_field$0 }
1028"#,
1029            "some_field",
1030        );
1031    }
1032
1033    #[test]
1034    fn from_and_to_func() {
1035        check(
1036            r#"
1037//- minicore: from
1038struct Foo;
1039struct Bar;
1040
1041impl From<Foo> for Bar {
1042    fn from(_: Foo) -> Self {
1043        Bar;
1044    }
1045}
1046
1047fn f(_: Bar) {}
1048
1049fn main() {
1050    let foo = Foo {};
1051    f($0Bar::from(foo)$0);
1052}
1053"#,
1054            "bar",
1055        );
1056
1057        check(
1058            r#"
1059//- minicore: from
1060struct Foo;
1061struct Bar;
1062
1063impl From<Foo> for Bar {
1064    fn from(_: Foo) -> Self {
1065        Bar;
1066    }
1067}
1068
1069fn f(_: Bar) {}
1070
1071fn main() {
1072    let foo = Foo {};
1073    f($0Into::<Bar>::into(foo)$0);
1074}
1075"#,
1076            "bar",
1077        );
1078    }
1079
1080    #[test]
1081    fn useless_name_prefix() {
1082        check(
1083            r#"
1084struct Foo;
1085struct Bar;
1086
1087impl Bar {
1088    fn from_foo(_: Foo) -> Self {
1089        Foo {}
1090    }
1091}
1092
1093fn main() {
1094    let foo = Foo {};
1095    let _ = $0Bar::from_foo(foo)$0;
1096}
1097"#,
1098            "bar",
1099        );
1100
1101        check(
1102            r#"
1103struct Foo;
1104struct Bar;
1105
1106impl Bar {
1107    fn with_foo(_: Foo) -> Self {
1108        Bar {}
1109    }
1110}
1111
1112fn main() {
1113    let foo = Foo {};
1114    let _ = $0Bar::with_foo(foo)$0;
1115}
1116"#,
1117            "bar",
1118        );
1119    }
1120
1121    #[test]
1122    fn conflicts_with_existing_names() {
1123        let mut generator = NameGenerator::default();
1124        assert_eq!(generator.suggest_name("a"), "a");
1125        assert_eq!(generator.suggest_name("a"), "a1");
1126        assert_eq!(generator.suggest_name("a"), "a2");
1127        assert_eq!(generator.suggest_name("a"), "a3");
1128
1129        assert_eq!(generator.suggest_name("b"), "b");
1130        assert_eq!(generator.suggest_name("b2"), "b2");
1131        assert_eq!(generator.suggest_name("b"), "b3");
1132        assert_eq!(generator.suggest_name("b"), "b4");
1133        assert_eq!(generator.suggest_name("b3"), "b5");
1134
1135        // ---------
1136        let mut generator = NameGenerator::new_with_names(["a", "b", "b2", "c4"].into_iter());
1137        assert_eq!(generator.suggest_name("a"), "a1");
1138        assert_eq!(generator.suggest_name("a"), "a2");
1139
1140        assert_eq!(generator.suggest_name("b"), "b3");
1141        assert_eq!(generator.suggest_name("b2"), "b4");
1142
1143        assert_eq!(generator.suggest_name("c"), "c5");
1144    }
1145}