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.strip_reference();
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.as_reference() {
449 return name_of_type(&inner_ty, db, edition);
450 } else {
451 let inner_ty = ty.as_slice()?;
452 return Some(sequence_name(Some(&inner_ty), db, edition));
453 };
454 normalize(&name, edition)
455}
456
457fn sequence_name<'db>(
458 inner_ty: Option<&hir::Type<'db>>,
459 db: &'db RootDatabase,
460 edition: Edition,
461) -> SmolStr {
462 let items_str = SmolStr::new_static("items");
463 let Some(inner_ty) = inner_ty else {
464 return items_str;
465 };
466 let Some(name) = name_of_type(inner_ty, db, edition) else {
467 return items_str;
468 };
469
470 if name.ends_with(['s', 'x', 'y']) {
471 items_str
474 } else {
475 SmolStr::new(format!("{name}s"))
476 }
477}
478
479fn trait_name(trait_: &hir::Trait, db: &RootDatabase, edition: Edition) -> Option<String> {
480 let name = trait_.name(db).display(db, edition).to_string();
481 if USELESS_TRAITS.contains(&name.as_str()) {
482 return None;
483 }
484 Some(name)
485}
486
487fn from_field_name(expr: &ast::Expr, edition: syntax::Edition) -> Option<SmolStr> {
488 let field = match expr {
489 ast::Expr::FieldExpr(field) => field,
490 _ => return None,
491 };
492 let ident = field.name_ref()?.ident_token()?;
493 normalize(ident.text(), edition)
494}
495
496#[cfg(test)]
497mod tests {
498 use hir::FileRange;
499 use test_fixture::WithFixture;
500
501 use super::*;
502
503 #[track_caller]
504 fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expected: &str) {
505 let (db, file_id, range_or_offset) = RootDatabase::with_range_or_offset(ra_fixture);
506 let frange = FileRange { file_id, range: range_or_offset.into() };
507 let sema = Semantics::new(&db);
508
509 let source_file = sema.parse(frange.file_id);
510
511 let element = source_file.syntax().covering_element(frange.range);
512 let expr =
513 element.ancestors().find_map(ast::Expr::cast).expect("selection is not an expression");
514 assert_eq!(
515 expr.syntax().text_range(),
516 frange.range,
517 "selection is not an expression(yet contained in one)"
518 );
519 let name = hir::attach_db(sema.db, || NameGenerator::default().for_variable(&expr, &sema));
520 assert_eq!(&name, expected);
521 }
522
523 #[test]
524 fn no_args() {
525 check(r#"fn foo() { $0bar()$0 }"#, "bar");
526 check(r#"fn foo() { $0bar.frobnicate()$0 }"#, "frobnicate");
527 }
528
529 #[test]
530 fn single_arg() {
531 check(r#"fn foo() { $0bar(1)$0 }"#, "bar");
532 }
533
534 #[test]
535 fn many_args() {
536 check(r#"fn foo() { $0bar(1, 2, 3)$0 }"#, "bar");
537 }
538
539 #[test]
540 fn path() {
541 check(r#"fn foo() { $0i32::bar(1, 2, 3)$0 }"#, "bar");
542 }
543
544 #[test]
545 fn generic_params() {
546 check(r#"fn foo() { $0bar::<i32>(1, 2, 3)$0 }"#, "bar");
547 check(r#"fn foo() { $0bar.frobnicate::<i32, u32>()$0 }"#, "frobnicate");
548 }
549
550 #[test]
551 fn to_name() {
552 check(
553 r#"
554struct Args;
555struct Config;
556impl Args {
557 fn to_config(&self) -> Config {}
558}
559fn foo() {
560 $0Args.to_config()$0;
561}
562"#,
563 "config",
564 );
565 }
566
567 #[test]
568 fn plain_func() {
569 check(
570 r#"
571fn bar(n: i32, m: u32);
572fn foo() { bar($01$0, 2) }
573"#,
574 "n",
575 );
576 }
577
578 #[test]
579 fn mut_param() {
580 check(
581 r#"
582fn bar(mut n: i32, m: u32);
583fn foo() { bar($01$0, 2) }
584"#,
585 "n",
586 );
587 }
588
589 #[test]
590 fn func_does_not_exist() {
591 check(r#"fn foo() { bar($01$0, 2) }"#, "var_name");
592 }
593
594 #[test]
595 fn unnamed_param() {
596 check(
597 r#"
598fn bar(_: i32, m: u32);
599fn foo() { bar($01$0, 2) }
600"#,
601 "var_name",
602 );
603 }
604
605 #[test]
606 fn tuple_pat() {
607 check(
608 r#"
609fn bar((n, k): (i32, i32), m: u32);
610fn foo() {
611 bar($0(1, 2)$0, 3)
612}
613"#,
614 "var_name",
615 );
616 }
617
618 #[test]
619 fn ref_pat() {
620 check(
621 r#"
622fn bar(&n: &i32, m: u32);
623fn foo() { bar($0&1$0, 3) }
624"#,
625 "n",
626 );
627 }
628
629 #[test]
630 fn box_pat() {
631 check(
632 r#"
633fn bar(box n: &i32, m: u32);
634fn foo() { bar($01$0, 3) }
635"#,
636 "n",
637 );
638 }
639
640 #[test]
641 fn param_out_of_index() {
642 check(
643 r#"
644fn bar(n: i32, m: u32);
645fn foo() { bar(1, 2, $03$0) }
646"#,
647 "var_name",
648 );
649 }
650
651 #[test]
652 fn generic_param_resolved() {
653 check(
654 r#"
655fn bar<T>(n: T, m: u32);
656fn foo() { bar($01$0, 2) }
657"#,
658 "n",
659 );
660 }
661
662 #[test]
663 fn generic_param_unresolved() {
664 check(
665 r#"
666fn bar<T>(n: T, m: u32);
667fn foo<T>(x: T) { bar($0x$0, 2) }
668"#,
669 "n",
670 );
671 }
672
673 #[test]
674 fn method() {
675 check(
676 r#"
677struct S;
678impl S { fn bar(&self, n: i32, m: u32); }
679fn foo() { S.bar($01$0, 2) }
680"#,
681 "n",
682 );
683 }
684
685 #[test]
686 fn method_on_impl_trait() {
687 check(
688 r#"
689struct S;
690trait T {
691 fn bar(&self, n: i32, m: u32);
692}
693impl T for S { fn bar(&self, n: i32, m: u32); }
694fn foo() { S.bar($01$0, 2) }
695"#,
696 "n",
697 );
698 }
699
700 #[test]
701 fn method_ufcs() {
702 check(
703 r#"
704struct S;
705impl S { fn bar(&self, n: i32, m: u32); }
706fn foo() { S::bar(&S, $01$0, 2) }
707"#,
708 "n",
709 );
710 }
711
712 #[test]
713 fn method_self() {
714 check(
715 r#"
716struct S;
717impl S { fn bar(&self, n: i32, m: u32); }
718fn foo() { S::bar($0&S$0, 1, 2) }
719"#,
720 "s",
721 );
722 }
723
724 #[test]
725 fn method_self_named() {
726 check(
727 r#"
728struct S;
729impl S { fn bar(strukt: &Self, n: i32, m: u32); }
730fn foo() { S::bar($0&S$0, 1, 2) }
731"#,
732 "strukt",
733 );
734 }
735
736 #[test]
737 fn i32() {
738 check(r#"fn foo() { let _: i32 = $01$0; }"#, "var_name");
739 }
740
741 #[test]
742 fn u64() {
743 check(r#"fn foo() { let _: u64 = $01$0; }"#, "var_name");
744 }
745
746 #[test]
747 fn bool() {
748 check(r#"fn foo() { let _: bool = $0true$0; }"#, "var_name");
749 }
750
751 #[test]
752 fn struct_unit() {
753 check(
754 r#"
755struct Seed;
756fn foo() { let _ = $0Seed$0; }
757"#,
758 "seed",
759 );
760 }
761
762 #[test]
763 fn struct_unit_to_snake() {
764 check(
765 r#"
766struct SeedState;
767fn foo() { let _ = $0SeedState$0; }
768"#,
769 "seed_state",
770 );
771 }
772
773 #[test]
774 fn struct_single_arg() {
775 check(
776 r#"
777struct Seed(u32);
778fn foo() { let _ = $0Seed(0)$0; }
779"#,
780 "seed",
781 );
782 }
783
784 #[test]
785 fn struct_with_fields() {
786 check(
787 r#"
788struct Seed { value: u32 }
789fn foo() { let _ = $0Seed { value: 0 }$0; }
790"#,
791 "seed",
792 );
793 }
794
795 #[test]
796 fn enum_() {
797 check(
798 r#"
799enum Kind { A, B }
800fn foo() { let _ = $0Kind::A$0; }
801"#,
802 "kind",
803 );
804 }
805
806 #[test]
807 fn enum_generic_resolved() {
808 check(
809 r#"
810enum Kind<T> { A { x: T }, B }
811fn foo() { let _ = $0Kind::A { x:1 }$0; }
812"#,
813 "kind",
814 );
815 }
816
817 #[test]
818 fn enum_generic_unresolved() {
819 check(
820 r#"
821enum Kind<T> { A { x: T }, B }
822fn foo<T>(x: T) { let _ = $0Kind::A { x }$0; }
823"#,
824 "kind",
825 );
826 }
827
828 #[test]
829 fn dyn_trait() {
830 check(
831 r#"
832trait DynHandler {}
833fn bar() -> dyn DynHandler {}
834fn foo() { $0(bar())$0; }
835"#,
836 "dyn_handler",
837 );
838 }
839
840 #[test]
841 fn impl_trait() {
842 check(
843 r#"
844trait StaticHandler {}
845fn bar() -> impl StaticHandler {}
846fn foo() { $0(bar())$0; }
847"#,
848 "static_handler",
849 );
850 }
851
852 #[test]
853 fn impl_trait_plus_clone() {
854 check(
855 r#"
856trait StaticHandler {}
857trait Clone {}
858fn bar() -> impl StaticHandler + Clone {}
859fn foo() { $0(bar())$0; }
860"#,
861 "static_handler",
862 );
863 }
864
865 #[test]
866 fn impl_trait_plus_lifetime() {
867 check(
868 r#"
869trait StaticHandler {}
870trait Clone {}
871fn bar<'a>(&'a i32) -> impl StaticHandler + 'a {}
872fn foo() { $0(bar(&1))$0; }
873"#,
874 "static_handler",
875 );
876 }
877
878 #[test]
879 fn impl_trait_plus_trait() {
880 check(
881 r#"
882trait Handler {}
883trait StaticHandler {}
884fn bar() -> impl StaticHandler + Handler {}
885fn foo() { $0(bar())$0; }
886"#,
887 "bar",
888 );
889 }
890
891 #[test]
892 fn ref_value() {
893 check(
894 r#"
895struct Seed;
896fn bar() -> &Seed {}
897fn foo() { $0(bar())$0; }
898"#,
899 "seed",
900 );
901 }
902
903 #[test]
904 fn box_value() {
905 check(
906 r#"
907struct Box<T>(*const T);
908struct Seed;
909fn bar() -> Box<Seed> {}
910fn foo() { $0(bar())$0; }
911"#,
912 "seed",
913 );
914 }
915
916 #[test]
917 fn box_generic() {
918 check(
919 r#"
920struct Box<T>(*const T);
921fn bar<T>() -> Box<T> {}
922fn foo<T>() { $0(bar::<T>())$0; }
923"#,
924 "bar",
925 );
926 }
927
928 #[test]
929 fn option_value() {
930 check(
931 r#"
932enum Option<T> { Some(T) }
933struct Seed;
934fn bar() -> Option<Seed> {}
935fn foo() { $0(bar())$0; }
936"#,
937 "seed",
938 );
939 }
940
941 #[test]
942 fn result_value() {
943 check(
944 r#"
945enum Result<T, E> { Ok(T), Err(E) }
946struct Seed;
947struct Error;
948fn bar() -> Result<Seed, Error> {}
949fn foo() { $0(bar())$0; }
950"#,
951 "seed",
952 );
953 }
954
955 #[test]
956 fn arc_value() {
957 check(
958 r#"
959struct Arc<T>(*const T);
960struct Seed;
961fn bar() -> Arc<Seed> {}
962fn foo() { $0(bar())$0; }
963"#,
964 "seed",
965 );
966 }
967
968 #[test]
969 fn rc_value() {
970 check(
971 r#"
972struct Rc<T>(*const T);
973struct Seed;
974fn bar() -> Rc<Seed> {}
975fn foo() { $0(bar())$0; }
976"#,
977 "seed",
978 );
979 }
980
981 #[test]
982 fn vec_value() {
983 check(
984 r#"
985struct Vec<T> {};
986struct Seed;
987fn bar() -> Vec<Seed> {}
988fn foo() { $0(bar())$0; }
989"#,
990 "seeds",
991 );
992 }
993
994 #[test]
995 fn vec_value_ends_with_s() {
996 check(
997 r#"
998struct Vec<T> {};
999struct Boss;
1000fn bar() -> Vec<Boss> {}
1001fn foo() { $0(bar())$0; }
1002"#,
1003 "items",
1004 );
1005 }
1006
1007 #[test]
1008 fn vecdeque_value() {
1009 check(
1010 r#"
1011struct VecDeque<T> {};
1012struct Seed;
1013fn bar() -> VecDeque<Seed> {}
1014fn foo() { $0(bar())$0; }
1015"#,
1016 "seeds",
1017 );
1018 }
1019
1020 #[test]
1021 fn slice_value() {
1022 check(
1023 r#"
1024struct Vec<T> {};
1025struct Seed;
1026fn bar() -> &[Seed] {}
1027fn foo() { $0(bar())$0; }
1028"#,
1029 "seeds",
1030 );
1031 }
1032
1033 #[test]
1034 fn ref_call() {
1035 check(
1036 r#"
1037fn foo() { $0&bar(1, 3)$0 }
1038"#,
1039 "bar",
1040 );
1041 }
1042
1043 #[test]
1044 fn name_to_string() {
1045 check(
1046 r#"
1047fn foo() { $0function.name().to_string()$0 }
1048"#,
1049 "name",
1050 );
1051 }
1052
1053 #[test]
1054 fn nested_useless_method() {
1055 check(
1056 r#"
1057fn foo() { $0function.name().as_ref().unwrap().to_string()$0 }
1058"#,
1059 "name",
1060 );
1061 }
1062
1063 #[test]
1064 fn struct_field_name() {
1065 check(
1066 r#"
1067struct S<T> {
1068 some_field: T;
1069}
1070fn foo<T>(some_struct: S<T>) { $0some_struct.some_field$0 }
1071"#,
1072 "some_field",
1073 );
1074 }
1075
1076 #[test]
1077 fn from_and_to_func() {
1078 check(
1079 r#"
1080//- minicore: from
1081struct Foo;
1082struct Bar;
1083
1084impl From<Foo> for Bar {
1085 fn from(_: Foo) -> Self {
1086 Bar;
1087 }
1088}
1089
1090fn f(_: Bar) {}
1091
1092fn main() {
1093 let foo = Foo {};
1094 f($0Bar::from(foo)$0);
1095}
1096"#,
1097 "bar",
1098 );
1099
1100 check(
1101 r#"
1102//- minicore: from
1103struct Foo;
1104struct Bar;
1105
1106impl From<Foo> for Bar {
1107 fn from(_: Foo) -> Self {
1108 Bar;
1109 }
1110}
1111
1112fn f(_: Bar) {}
1113
1114fn main() {
1115 let foo = Foo {};
1116 f($0Into::<Bar>::into(foo)$0);
1117}
1118"#,
1119 "bar",
1120 );
1121 }
1122
1123 #[test]
1124 fn useless_name_prefix() {
1125 check(
1126 r#"
1127struct Foo;
1128struct Bar;
1129
1130impl Bar {
1131 fn from_foo(_: Foo) -> Self {
1132 Foo {}
1133 }
1134}
1135
1136fn main() {
1137 let foo = Foo {};
1138 let _ = $0Bar::from_foo(foo)$0;
1139}
1140"#,
1141 "bar",
1142 );
1143
1144 check(
1145 r#"
1146struct Foo;
1147struct Bar;
1148
1149impl Bar {
1150 fn with_foo(_: Foo) -> Self {
1151 Bar {}
1152 }
1153}
1154
1155fn main() {
1156 let foo = Foo {};
1157 let _ = $0Bar::with_foo(foo)$0;
1158}
1159"#,
1160 "bar",
1161 );
1162 }
1163
1164 #[test]
1165 fn conflicts_with_existing_names() {
1166 let mut generator = NameGenerator::default();
1167 assert_eq!(generator.suggest_name("a"), "a");
1168 assert_eq!(generator.suggest_name("a"), "a1");
1169 assert_eq!(generator.suggest_name("a"), "a2");
1170 assert_eq!(generator.suggest_name("a"), "a3");
1171
1172 assert_eq!(generator.suggest_name("b"), "b");
1173 assert_eq!(generator.suggest_name("b2"), "b2");
1174 assert_eq!(generator.suggest_name("b"), "b3");
1175 assert_eq!(generator.suggest_name("b"), "b4");
1176 assert_eq!(generator.suggest_name("b3"), "b5");
1177
1178 let mut generator = NameGenerator::new_with_names(["a", "b", "b2", "c4"].into_iter());
1180 assert_eq!(generator.suggest_name("a"), "a1");
1181 assert_eq!(generator.suggest_name("a"), "a2");
1182
1183 assert_eq!(generator.suggest_name("b"), "b3");
1184 assert_eq!(generator.suggest_name("b2"), "b4");
1185
1186 assert_eq!(generator.suggest_name("c"), "c5");
1187 }
1188}