Skip to content

Commit 385eea1

Browse files
committed
Consider methods from traits when suggesting typos
Do not provide a structured suggestion when the arguments don't match. ``` error[E0599]: no method named `test_mut` found for struct `Vec<{integer}>` in the current scope --> $DIR/auto-ref-slice-plus-ref.rs:7:7 | LL | a.test_mut(); | ^^^^^^^^ | = help: items from traits can only be used if the trait is implemented and in scope note: `MyIter` defines an item `test_mut`, perhaps you need to implement it --> $DIR/auto-ref-slice-plus-ref.rs:14:1 | LL | trait MyIter { | ^^^^^^^^^^^^ help: there is a method `get_mut` with a similar name, but with different arguments --> $SRC_DIR/core/src/slice/mod.rs:LL:COL ``` Consider methods beyond inherent ones when suggesting typos. ``` error[E0599]: no method named `owned` found for reference `&dyn Foo` in the current scope --> $DIR/object-pointer-types.rs:11:7 | LL | fn owned(self: Box<Self>); | --------- the method might not be found because of this arbitrary self type ... LL | x.owned(); | ^^^^^ help: there is a method with a similar name: `to_owned` ``` Fix #101013.
1 parent d30dfb0 commit 385eea1

23 files changed

+223
-68
lines changed

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

+2-8
Original file line numberDiff line numberDiff line change
@@ -553,14 +553,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
553553
&& let ty::AssocKind::Fn = assoc.kind
554554
&& assoc.fn_has_self_parameter
555555
{
556-
let fn_sig =
557-
if let ty::Adt(_, args) = callee_ty.peel_refs().kind() {
558-
let args = ty::GenericArgs::identity_for_item(tcx, assoc.def_id)
559-
.rebase_onto(tcx, assoc.container_id(tcx), args);
560-
tcx.fn_sig(assoc.def_id).instantiate(tcx, args)
561-
} else {
562-
tcx.fn_sig(assoc.def_id).instantiate_identity()
563-
};
556+
let args = self.infcx.fresh_args_for_item(call_name.span, assoc.def_id);
557+
let fn_sig = tcx.fn_sig(assoc.def_id).instantiate(tcx, args);
564558
let fn_sig =
565559
self.instantiate_binder_with_fresh_vars(call_name.span, FnCall, fn_sig);
566560
Some((assoc, fn_sig));

compiler/rustc_hir_typeck/src/method/probe.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1768,6 +1768,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
17681768
);
17691769
pcx.allow_similar_names = true;
17701770
pcx.assemble_inherent_candidates();
1771+
pcx.assemble_extension_candidates_for_all_traits();
17711772

17721773
let method_names = pcx.candidate_method_names(|_| true);
17731774
pcx.allow_similar_names = false;
@@ -1777,6 +1778,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
17771778
pcx.reset();
17781779
pcx.method_name = Some(method_name);
17791780
pcx.assemble_inherent_candidates();
1781+
pcx.assemble_extension_candidates_for_all_traits();
17801782
pcx.pick_core().and_then(|pick| pick.ok()).map(|pick| pick.item)
17811783
})
17821784
.collect();

compiler/rustc_hir_typeck/src/method/suggest.rs

+75-13
Original file line numberDiff line numberDiff line change
@@ -1359,27 +1359,89 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13591359
&& Some(similar_candidate.name) != confusable_suggested
13601360
{
13611361
let def_kind = similar_candidate.kind.as_def_kind();
1362-
// Methods are defined within the context of a struct and their first parameter is always self,
1363-
// which represents the instance of the struct the method is being called on
1364-
// Associated functions don’t take self as a parameter and
1365-
// they are not methods because they don’t have an instance of the struct to work with.
1366-
if def_kind == DefKind::AssocFn && similar_candidate.fn_has_self_parameter {
1362+
let an = self.tcx.def_kind_descr_article(def_kind, similar_candidate.def_id);
1363+
// Methods are defined within the context of a struct and their first parameter
1364+
// is always `self`, which represents the instance of the struct the method is
1365+
// being called on Associated functions don’t take self as a parameter and they are
1366+
// not methods because they don’t have an instance of the struct to work with.
1367+
if def_kind == DefKind::AssocFn {
1368+
let ty_args = self.infcx.fresh_args_for_item(span, similar_candidate.def_id);
1369+
let fn_sig = tcx.fn_sig(similar_candidate.def_id).instantiate(tcx, ty_args);
1370+
let fn_sig =
1371+
self.instantiate_binder_with_fresh_vars(span, infer::FnCall, fn_sig);
1372+
if similar_candidate.fn_has_self_parameter {
1373+
if let Some(args) = args
1374+
&& fn_sig.inputs()[1..].len() == args.len()
1375+
{
1376+
// We found a method with the same number of arguments as the method
1377+
// call expression the user wrote.
1378+
err.span_suggestion(
1379+
span,
1380+
format!("there is {an} method with a similar name"),
1381+
similar_candidate.name,
1382+
Applicability::MaybeIncorrect,
1383+
);
1384+
} else {
1385+
// We found a method but either the expression is not a method call or
1386+
// the argument count didn't match.
1387+
err.span_help(
1388+
tcx.def_span(similar_candidate.def_id),
1389+
format!(
1390+
"there is {an} method `{}` with a similar name{}",
1391+
similar_candidate.name,
1392+
if let None = args {
1393+
""
1394+
} else {
1395+
", but with different arguments"
1396+
},
1397+
),
1398+
);
1399+
}
1400+
} else if let Some(args) = args
1401+
&& fn_sig.inputs().len() == args.len()
1402+
{
1403+
// We have fn call expression and the argument count match the associated
1404+
// function we found.
1405+
err.span_suggestion(
1406+
span,
1407+
format!(
1408+
"there is {an} {} with a similar name",
1409+
self.tcx.def_kind_descr(def_kind, similar_candidate.def_id)
1410+
),
1411+
similar_candidate.name,
1412+
Applicability::MaybeIncorrect,
1413+
);
1414+
} else {
1415+
err.span_help(
1416+
tcx.def_span(similar_candidate.def_id),
1417+
format!(
1418+
"there is {an} {} `{}` with a similar name",
1419+
self.tcx.def_kind_descr(def_kind, similar_candidate.def_id),
1420+
similar_candidate.name,
1421+
),
1422+
);
1423+
}
1424+
} else if let Mode::Path = mode {
1425+
// We have an associated item syntax and we found something that isn't an fn.
13671426
err.span_suggestion(
13681427
span,
1369-
"there is a method with a similar name",
1428+
format!(
1429+
"there is {an} {} with a similar name",
1430+
self.tcx.def_kind_descr(def_kind, similar_candidate.def_id)
1431+
),
13701432
similar_candidate.name,
13711433
Applicability::MaybeIncorrect,
13721434
);
13731435
} else {
1374-
err.span_suggestion(
1375-
span,
1436+
// The expression is a function or method call, but the item we found is an
1437+
// associated const or type.
1438+
err.span_help(
1439+
tcx.def_span(similar_candidate.def_id),
13761440
format!(
1377-
"there is {} {} with a similar name",
1378-
self.tcx.def_kind_descr_article(def_kind, similar_candidate.def_id),
1379-
self.tcx.def_kind_descr(def_kind, similar_candidate.def_id)
1441+
"there is {an} {} `{}` with a similar name",
1442+
self.tcx.def_kind_descr(def_kind, similar_candidate.def_id),
1443+
similar_candidate.name,
13801444
),
1381-
similar_candidate.name,
1382-
Applicability::MaybeIncorrect,
13831445
);
13841446
}
13851447
}

tests/ui/associated-item/associated-item-enum.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ LL | Enum::mispellable_trait();
2020
| ^^^^^^^^^^^^^^^^^
2121
| |
2222
| variant or associated item not found in `Enum`
23-
| help: there is an associated function with a similar name: `misspellable`
23+
| help: there is an associated function with a similar name: `misspellable_trait`
2424

2525
error[E0599]: no variant or associated item named `MISPELLABLE` found for enum `Enum` in the current scope
2626
--> $DIR/associated-item-enum.rs:19:11

tests/ui/attributes/rustc_confusables_std_cases.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ fn main() {
1616
//~^ HELP you might have meant to use `len`
1717
x.size(); //~ ERROR E0599
1818
//~^ HELP you might have meant to use `len`
19-
//~| HELP there is a method with a similar name
19+
//~| HELP there is a method `resize` with a similar name
2020
x.append(42); //~ ERROR E0308
2121
//~^ HELP you might have meant to use `push`
2222
String::new().push(""); //~ ERROR E0308

tests/ui/attributes/rustc_confusables_std_cases.stderr

+2-4
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,12 @@ error[E0599]: no method named `size` found for struct `Vec<{integer}>` in the cu
4848
LL | x.size();
4949
| ^^^^
5050
|
51+
help: there is a method `resize` with a similar name, but with different arguments
52+
--> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
5153
help: you might have meant to use `len`
5254
|
5355
LL | x.len();
5456
| ~~~
55-
help: there is a method with a similar name
56-
|
57-
LL | x.resize();
58-
| ~~~~~~
5957

6058
error[E0308]: mismatched types
6159
--> $DIR/rustc_confusables_std_cases.rs:20:14

tests/ui/auto-ref-slice-plus-ref.stderr

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@ error[E0599]: no method named `test_mut` found for struct `Vec<{integer}>` in th
22
--> $DIR/auto-ref-slice-plus-ref.rs:7:7
33
|
44
LL | a.test_mut();
5-
| ^^^^^^^^ help: there is a method with a similar name: `get_mut`
5+
| ^^^^^^^^
66
|
77
= help: items from traits can only be used if the trait is implemented and in scope
88
note: `MyIter` defines an item `test_mut`, perhaps you need to implement it
99
--> $DIR/auto-ref-slice-plus-ref.rs:14:1
1010
|
1111
LL | trait MyIter {
1212
| ^^^^^^^^^^^^
13+
help: there is a method `get_mut` with a similar name, but with different arguments
14+
--> $SRC_DIR/core/src/slice/mod.rs:LL:COL
1315

1416
error[E0599]: no method named `test` found for struct `Vec<{integer}>` in the current scope
1517
--> $DIR/auto-ref-slice-plus-ref.rs:8:7

tests/ui/confuse-field-and-method/issue-33784.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ help: to call the function stored in `closure`, surround the field access with p
88
|
99
LL | (p.closure)();
1010
| + +
11+
help: there is a method with a similar name
12+
|
13+
LL | p.clone();
14+
| ~~~~~
1115

1216
error[E0599]: no method named `fn_ptr` found for reference `&&Obj<{closure@$DIR/issue-33784.rs:25:43: 25:45}>` in the current scope
1317
--> $DIR/issue-33784.rs:29:7

tests/ui/hygiene/no_implicit_prelude.stderr

+3-1
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ LL | fn f() { ::bar::m!(); }
2020
| ----------- in this macro invocation
2121
...
2222
LL | ().clone()
23-
| ^^^^^ method not found in `()`
23+
| ^^^^^
2424
|
2525
= help: items from traits can only be used if the trait is in scope
26+
help: there is a method `clone_from` with a similar name, but with different arguments
27+
--> $SRC_DIR/core/src/clone.rs:LL:COL
2628
= note: this error originates in the macro `::bar::m` (in Nightly builds, run with -Z macro-backtrace for more info)
2729
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
2830
|

tests/ui/impl-trait/no-method-suggested-traits.stderr

+30-6
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error[E0599]: no method named `method` found for type `u32` in the current scope
22
--> $DIR/no-method-suggested-traits.rs:23:10
33
|
44
LL | 1u32.method();
5-
| ^^^^^^ method not found in `u32`
5+
| ^^^^^^
66
|
77
= help: items from traits can only be used if the trait is in scope
88
help: the following traits are implemented but not in scope; perhaps add a `use` for one of them:
@@ -15,12 +15,16 @@ LL + use no_method_suggested_traits::foo::PubPub;
1515
|
1616
LL + use no_method_suggested_traits::qux::PrivPub;
1717
|
18+
help: there is a method with a similar name
19+
|
20+
LL | 1u32.method2();
21+
| ~~~~~~~
1822

1923
error[E0599]: no method named `method` found for struct `Rc<&mut Box<&u32>>` in the current scope
2024
--> $DIR/no-method-suggested-traits.rs:26:44
2125
|
2226
LL | std::rc::Rc::new(&mut Box::new(&1u32)).method();
23-
| ^^^^^^ method not found in `Rc<&mut Box<&u32>>`
27+
| ^^^^^^
2428
|
2529
= help: items from traits can only be used if the trait is in scope
2630
help: the following traits are implemented but not in scope; perhaps add a `use` for one of them:
@@ -33,6 +37,10 @@ LL + use no_method_suggested_traits::foo::PubPub;
3337
|
3438
LL + use no_method_suggested_traits::qux::PrivPub;
3539
|
40+
help: there is a method with a similar name
41+
|
42+
LL | std::rc::Rc::new(&mut Box::new(&1u32)).method2();
43+
| ~~~~~~~
3644

3745
error[E0599]: no method named `method` found for type `char` in the current scope
3846
--> $DIR/no-method-suggested-traits.rs:30:9
@@ -41,31 +49,39 @@ LL | fn method(&self) {}
4149
| ------ the method is available for `char` here
4250
...
4351
LL | 'a'.method();
44-
| ^^^^^^ method not found in `char`
52+
| ^^^^^^
4553
|
4654
= help: items from traits can only be used if the trait is in scope
4755
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
4856
|
4957
LL + use foo::Bar;
5058
|
59+
help: there is a method with a similar name
60+
|
61+
LL | 'a'.method2();
62+
| ~~~~~~~
5163

5264
error[E0599]: no method named `method` found for struct `Rc<&mut Box<&char>>` in the current scope
5365
--> $DIR/no-method-suggested-traits.rs:32:43
5466
|
5567
LL | std::rc::Rc::new(&mut Box::new(&'a')).method();
56-
| ^^^^^^ method not found in `Rc<&mut Box<&char>>`
68+
| ^^^^^^
5769
|
5870
= help: items from traits can only be used if the trait is in scope
5971
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
6072
|
6173
LL + use foo::Bar;
6274
|
75+
help: there is a method with a similar name
76+
|
77+
LL | std::rc::Rc::new(&mut Box::new(&'a')).method2();
78+
| ~~~~~~~
6379

6480
error[E0599]: no method named `method` found for type `i32` in the current scope
6581
--> $DIR/no-method-suggested-traits.rs:35:10
6682
|
6783
LL | 1i32.method();
68-
| ^^^^^^ method not found in `i32`
84+
| ^^^^^^
6985
|
7086
::: $DIR/auxiliary/no_method_suggested_traits.rs:8:12
7187
|
@@ -77,18 +93,26 @@ help: the following trait is implemented but not in scope; perhaps add a `use` f
7793
|
7894
LL + use no_method_suggested_traits::foo::PubPub;
7995
|
96+
help: there is a method with a similar name
97+
|
98+
LL | 1i32.method3();
99+
| ~~~~~~~
80100

81101
error[E0599]: no method named `method` found for struct `Rc<&mut Box<&i32>>` in the current scope
82102
--> $DIR/no-method-suggested-traits.rs:37:44
83103
|
84104
LL | std::rc::Rc::new(&mut Box::new(&1i32)).method();
85-
| ^^^^^^ method not found in `Rc<&mut Box<&i32>>`
105+
| ^^^^^^
86106
|
87107
= help: items from traits can only be used if the trait is in scope
88108
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
89109
|
90110
LL + use no_method_suggested_traits::foo::PubPub;
91111
|
112+
help: there is a method with a similar name
113+
|
114+
LL | std::rc::Rc::new(&mut Box::new(&1i32)).method3();
115+
| ~~~~~~~
92116

93117
error[E0599]: no method named `method` found for struct `Foo` in the current scope
94118
--> $DIR/no-method-suggested-traits.rs:40:9

tests/ui/issues/issue-28344.stderr

+8-8
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ error[E0599]: no function or associated item named `bitor` found for trait objec
2222
--> $DIR/issue-28344.rs:4:25
2323
|
2424
LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8);
25-
| ^^^^^
26-
| |
27-
| function or associated item not found in `dyn BitXor<_>`
28-
| help: there is a method with a similar name: `bitxor`
25+
| ^^^^^ function or associated item not found in `dyn BitXor<_>`
26+
|
27+
help: there is a method `bitxor` with a similar name, but with different arguments
28+
--> $SRC_DIR/core/src/ops/bit.rs:LL:COL
2929

3030
warning: trait objects without an explicit `dyn` are deprecated
3131
--> $DIR/issue-28344.rs:10:13
@@ -50,10 +50,10 @@ error[E0599]: no function or associated item named `bitor` found for trait objec
5050
--> $DIR/issue-28344.rs:10:21
5151
|
5252
LL | let g = BitXor::bitor;
53-
| ^^^^^
54-
| |
55-
| function or associated item not found in `dyn BitXor<_>`
56-
| help: there is a method with a similar name: `bitxor`
53+
| ^^^^^ function or associated item not found in `dyn BitXor<_>`
54+
|
55+
help: there is a method `bitxor` with a similar name, but with different arguments
56+
--> $SRC_DIR/core/src/ops/bit.rs:LL:COL
5757

5858
error: aborting due to 4 previous errors; 2 warnings emitted
5959

tests/ui/issues/issue-39175.stderr

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ error[E0599]: no method named `exec` found for mutable reference `&mut Command`
22
--> $DIR/issue-39175.rs:14:39
33
|
44
LL | Command::new("echo").arg("hello").exec();
5-
| ^^^^ method not found in `&mut Command`
5+
| ^^^^
66
|
77
= help: items from traits can only be used if the trait is in scope
8+
help: there is a method `pre_exec` with a similar name, but with different arguments
9+
--> $SRC_DIR/std/src/os/unix/process.rs:LL:COL
810
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
911
|
1012
LL + use std::os::unix::process::CommandExt;

0 commit comments

Comments
 (0)