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