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