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