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