Skip to content

Commit

Permalink
Consider methods from traits when suggesting typos
Browse files Browse the repository at this point in the history
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 rust-lang#101013.
  • Loading branch information
estebank committed Feb 10, 2024
1 parent 47d015e commit 75f7b55
Show file tree
Hide file tree
Showing 23 changed files with 223 additions and 68 deletions.
10 changes: 2 additions & 8 deletions compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -553,14 +553,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&& let ty::AssocKind::Fn = assoc.kind
&& assoc.fn_has_self_parameter
{
let fn_sig =
if let ty::Adt(_, args) = callee_ty.peel_refs().kind() {
let args = ty::GenericArgs::identity_for_item(tcx, assoc.def_id)
.rebase_onto(tcx, assoc.container_id(tcx), args);
tcx.fn_sig(assoc.def_id).instantiate(tcx, args)
} else {
tcx.fn_sig(assoc.def_id).instantiate_identity()
};
let args = self.infcx.fresh_args_for_item(call_name.span, assoc.def_id);
let fn_sig = tcx.fn_sig(assoc.def_id).instantiate(tcx, args);
let fn_sig =
self.instantiate_binder_with_fresh_vars(call_name.span, FnCall, fn_sig);
Some((assoc, fn_sig));
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1769,6 +1769,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
);
pcx.allow_similar_names = true;
pcx.assemble_inherent_candidates();
pcx.assemble_extension_candidates_for_all_traits();

let method_names = pcx.candidate_method_names(|_| true);
pcx.allow_similar_names = false;
Expand All @@ -1778,6 +1779,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
pcx.reset();
pcx.method_name = Some(method_name);
pcx.assemble_inherent_candidates();
pcx.assemble_extension_candidates_for_all_traits();
pcx.pick_core().and_then(|pick| pick.ok()).map(|pick| pick.item)
})
.collect();
Expand Down
88 changes: 75 additions & 13 deletions compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1264,27 +1264,89 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&& Some(similar_candidate.name) != confusable_suggested
{
let def_kind = similar_candidate.kind.as_def_kind();
// Methods are defined within the context of a struct and their first parameter is always self,
// which represents the instance of the struct the method is being called on
// Associated functions don’t take self as a parameter and
// they are not methods because they don’t have an instance of the struct to work with.
if def_kind == DefKind::AssocFn && similar_candidate.fn_has_self_parameter {
let an = self.tcx.def_kind_descr_article(def_kind, similar_candidate.def_id);
// Methods are defined within the context of a struct and their first parameter
// is always `self`, which represents the instance of the struct the method is
// being called on Associated functions don’t take self as a parameter and they are
// not methods because they don’t have an instance of the struct to work with.
if def_kind == DefKind::AssocFn {
let ty_args = self.infcx.fresh_args_for_item(span, similar_candidate.def_id);
let fn_sig = tcx.fn_sig(similar_candidate.def_id).instantiate(tcx, ty_args);
let fn_sig =
self.instantiate_binder_with_fresh_vars(span, infer::FnCall, fn_sig);
if similar_candidate.fn_has_self_parameter {
if let Some(args) = args
&& fn_sig.inputs()[1..].len() == args.len()
{
// We found a method with the same number of arguments as the method
// call expression the user wrote.
err.span_suggestion(
span,
format!("there is {an} method with a similar name"),
similar_candidate.name,
Applicability::MaybeIncorrect,
);
} else {
// We found a method but either the expression is not a method call or
// the argument count didn't match.
err.span_help(
tcx.def_span(similar_candidate.def_id),
format!(
"there is {an} method `{}` with a similar name{}",
similar_candidate.name,
if let None = args {
""
} else {
", but with different arguments"
},
),
);
}
} else if let Some(args) = args
&& fn_sig.inputs().len() == args.len()
{
// We have fn call expression and the argument count match the associated
// function we found.
err.span_suggestion(
span,
format!(
"there is {an} {} with a similar name",
self.tcx.def_kind_descr(def_kind, similar_candidate.def_id)
),
similar_candidate.name,
Applicability::MaybeIncorrect,
);
} else {
err.span_help(
tcx.def_span(similar_candidate.def_id),
format!(
"there is {an} {} `{}` with a similar name",
self.tcx.def_kind_descr(def_kind, similar_candidate.def_id),
similar_candidate.name,
),
);
}
} else if let Mode::Path = mode {
// We have an associated item syntax and we found something that isn't an fn.
err.span_suggestion(
span,
"there is a method with a similar name",
format!(
"there is {an} {} with a similar name",
self.tcx.def_kind_descr(def_kind, similar_candidate.def_id)
),
similar_candidate.name,
Applicability::MaybeIncorrect,
);
} else {
err.span_suggestion(
span,
// The expression is a function or method call, but the item we found is an
// associated const or type.
err.span_help(
tcx.def_span(similar_candidate.def_id),
format!(
"there is {} {} with a similar name",
self.tcx.def_kind_descr_article(def_kind, similar_candidate.def_id),
self.tcx.def_kind_descr(def_kind, similar_candidate.def_id)
"there is {an} {} `{}` with a similar name",
self.tcx.def_kind_descr(def_kind, similar_candidate.def_id),
similar_candidate.name,
),
similar_candidate.name,
Applicability::MaybeIncorrect,
);
}
}
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/associated-item/associated-item-enum.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ LL | Enum::mispellable_trait();
| ^^^^^^^^^^^^^^^^^
| |
| variant or associated item not found in `Enum`
| help: there is an associated function with a similar name: `misspellable`
| help: there is an associated function with a similar name: `misspellable_trait`

error[E0599]: no variant or associated item named `MISPELLABLE` found for enum `Enum` in the current scope
--> $DIR/associated-item-enum.rs:19:11
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/attributes/rustc_confusables_std_cases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ fn main() {
//~^ HELP you might have meant to use `len`
x.size(); //~ ERROR E0599
//~^ HELP you might have meant to use `len`
//~| HELP there is a method with a similar name
//~| HELP there is a method `resize` with a similar name
x.append(42); //~ ERROR E0308
//~^ HELP you might have meant to use `push`
String::new().push(""); //~ ERROR E0308
Expand Down
6 changes: 2 additions & 4 deletions tests/ui/attributes/rustc_confusables_std_cases.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,12 @@ error[E0599]: no method named `size` found for struct `Vec<{integer}>` in the cu
LL | x.size();
| ^^^^
|
help: there is a method `resize` with a similar name, but with different arguments
--> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
help: you might have meant to use `len`
|
LL | x.len();
| ~~~
help: there is a method with a similar name
|
LL | x.resize();
| ~~~~~~

error[E0308]: mismatched types
--> $DIR/rustc_confusables_std_cases.rs:20:14
Expand Down
4 changes: 3 additions & 1 deletion tests/ui/auto-ref-slice-plus-ref.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ error[E0599]: no method named `test_mut` found for struct `Vec<{integer}>` in th
--> $DIR/auto-ref-slice-plus-ref.rs:7:7
|
LL | a.test_mut();
| ^^^^^^^^ help: there is a method with a similar name: `get_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

error[E0599]: no method named `test` found for struct `Vec<{integer}>` in the current scope
--> $DIR/auto-ref-slice-plus-ref.rs:8:7
Expand Down
4 changes: 4 additions & 0 deletions tests/ui/confuse-field-and-method/issue-33784.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ help: to call the function stored in `closure`, surround the field access with p
|
LL | (p.closure)();
| + +
help: there is a method with a similar name
|
LL | p.clone();
| ~~~~~

error[E0599]: no method named `fn_ptr` found for reference `&&Obj<{closure@$DIR/issue-33784.rs:25:43: 25:45}>` in the current scope
--> $DIR/issue-33784.rs:29:7
Expand Down
4 changes: 3 additions & 1 deletion tests/ui/hygiene/no_implicit_prelude.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ LL | fn f() { ::bar::m!(); }
| ----------- in this macro invocation
...
LL | ().clone()
| ^^^^^ method not found in `()`
| ^^^^^
|
= help: items from traits can only be used if the trait is in scope
help: there is a method `clone_from` with a similar name, but with different arguments
--> $SRC_DIR/core/src/clone.rs:LL:COL
= note: this error originates in the macro `::bar::m` (in Nightly builds, run with -Z macro-backtrace for more info)
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
|
Expand Down
36 changes: 30 additions & 6 deletions tests/ui/impl-trait/no-method-suggested-traits.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ error[E0599]: no method named `method` found for type `u32` in the current scope
--> $DIR/no-method-suggested-traits.rs:23:10
|
LL | 1u32.method();
| ^^^^^^ method not found in `u32`
| ^^^^^^
|
= help: items from traits can only be used if the trait is in scope
help: the following traits are implemented but not in scope; perhaps add a `use` for one of them:
Expand All @@ -15,12 +15,16 @@ LL + use no_method_suggested_traits::foo::PubPub;
|
LL + use no_method_suggested_traits::qux::PrivPub;
|
help: there is a method with a similar name
|
LL | 1u32.method2();
| ~~~~~~~

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

error[E0599]: no method named `method` found for type `char` in the current scope
--> $DIR/no-method-suggested-traits.rs:30:9
Expand All @@ -41,31 +49,39 @@ LL | fn method(&self) {}
| ------ the method is available for `char` here
...
LL | 'a'.method();
| ^^^^^^ method not found in `char`
| ^^^^^^
|
= help: items from traits can only be used if the trait is in scope
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
|
LL + use foo::Bar;
|
help: there is a method with a similar name
|
LL | 'a'.method2();
| ~~~~~~~

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

error[E0599]: no method named `method` found for type `i32` in the current scope
--> $DIR/no-method-suggested-traits.rs:35:10
|
LL | 1i32.method();
| ^^^^^^ method not found in `i32`
| ^^^^^^
|
::: $DIR/auxiliary/no_method_suggested_traits.rs:8:12
|
Expand All @@ -77,18 +93,26 @@ help: the following trait is implemented but not in scope; perhaps add a `use` f
|
LL + use no_method_suggested_traits::foo::PubPub;
|
help: there is a method with a similar name
|
LL | 1i32.method3();
| ~~~~~~~

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

error[E0599]: no method named `method` found for struct `Foo` in the current scope
--> $DIR/no-method-suggested-traits.rs:40:9
Expand Down
16 changes: 8 additions & 8 deletions tests/ui/issues/issue-28344.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ error[E0599]: no function or associated item named `bitor` found for trait objec
--> $DIR/issue-28344.rs:4:25
|
LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8);
| ^^^^^
| |
| function or associated item not found in `dyn BitXor<_>`
| help: there is a method with a similar name: `bitxor`
| ^^^^^ function or associated item not found in `dyn BitXor<_>`
|
help: there is a method `bitxor` with a similar name, but with different arguments
--> $SRC_DIR/core/src/ops/bit.rs:LL:COL

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

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

Expand Down
4 changes: 3 additions & 1 deletion tests/ui/issues/issue-39175.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ error[E0599]: no method named `exec` found for mutable reference `&mut Command`
--> $DIR/issue-39175.rs:14:39
|
LL | Command::new("echo").arg("hello").exec();
| ^^^^ method not found in `&mut Command`
| ^^^^
|
= help: items from traits can only be used if the trait is in scope
help: there is a method `pre_exec` with a similar name, but with different arguments
--> $SRC_DIR/std/src/os/unix/process.rs:LL:COL
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
|
LL + use std::os::unix::process::CommandExt;
Expand Down
Loading

0 comments on commit 75f7b55

Please sign in to comment.