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