Skip to main content

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