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