1use 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
17const USELESS_TRAITS: &[&str] = &["Send", "Sync", "Copy", "Clone", "Eq", "PartialEq"];
19
20const 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
28const WRAPPER_TYPES: &[&str] = &["Box", "Arc", "Rc", "Option", "Result"];
34
35const SEQUENCE_TYPES: &[&str] = &["Vec", "VecDeque", "LinkedList"];
40
41const USELESS_METHOD_PREFIXES: &[&str] = &["try_into_", "into_", "as_", "to_"];
48
49const 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#[derive(Debug, Default)]
100pub struct NameGenerator {
101 pool: FxHashMap<SmolStr, usize>,
102}
103
104impl NameGenerator {
105 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 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 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 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 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 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 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::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 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 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 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 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 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}