Skip to content

Commit

Permalink
Auto merge of rust-lang#81149 - Aaron1011:feature/better-no-method-fo…
Browse files Browse the repository at this point in the history
…und-err, r=estebank

Avoid describing a method as 'not found' when bounds are unsatisfied

Fixes rust-lang#76267

When there is a single applicable method candidate, but its trait bounds
are not satisfied, we avoid saying that the method is "not found".
Insted, we update the error message to directly mention which bounds are
not satisfied, rather than mentioning them in a note.
  • Loading branch information
bors committed Jan 28, 2021
2 parents 0e19020 + dea8a16 commit 643a79a
Show file tree
Hide file tree
Showing 47 changed files with 144 additions and 128 deletions.
8 changes: 4 additions & 4 deletions compiler/rustc_errors/src/diagnostic_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,19 +74,18 @@ macro_rules! forward {
});
};

// Forward pattern for &mut self -> &mut Self, with S: Into<MultiSpan>
// type parameter. No obvious way to make this more generic.
// Forward pattern for &mut self -> &mut Self, with generic parameters.
(
$(#[$attrs:meta])*
pub fn $n:ident<S: Into<MultiSpan>>(
pub fn $n:ident<$($generic:ident: $bound:path),*>(
&mut self,
$($name:ident: $ty:ty),*
$(,)?
) -> &mut Self
) => {
$(#[$attrs])*
forward_inner_docs!(concat!("See [`Diagnostic::", stringify!($n), "()`].") =>
pub fn $n<S: Into<MultiSpan>>(&mut self, $($name: $ty),*) -> &mut Self {
pub fn $n<$($generic: $bound),*>(&mut self, $($name: $ty),*) -> &mut Self {
self.0.diagnostic.$n($($name),*);
self
});
Expand Down Expand Up @@ -398,6 +397,7 @@ impl<'a> DiagnosticBuilder<'a> {
self
}

forward!(pub fn set_primary_message<M: Into<String>>(&mut self, msg: M) -> &mut Self);
forward!(pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self);
forward!(pub fn code(&mut self, s: DiagnosticId) -> &mut Self);

Expand Down
53 changes: 34 additions & 19 deletions compiler/rustc_typeck/src/check/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}

let mut label_span_not_found = || {
if unsatisfied_predicates.is_empty() {
err.span_label(span, format!("{item_kind} not found in `{ty_str}`"));
} else {
err.span_label(span, format!("{item_kind} cannot be called on `{ty_str}` due to unsatisfied trait bounds"));
}
self.tcx.sess.trait_methods_not_found.borrow_mut().insert(orig_span);
};

// If the method name is the name of a field with a function or closure type,
// give a helping note that it has to be called as `(x.f)(...)`.
if let SelfSource::MethodCall(expr) = source {
Expand Down Expand Up @@ -501,12 +510,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let field_kind = if is_accessible { "field" } else { "private field" };
err.span_label(item_name.span, format!("{}, not a method", field_kind));
} else if lev_candidate.is_none() && static_sources.is_empty() {
err.span_label(span, format!("{} not found in `{}`", item_kind, ty_str));
self.tcx.sess.trait_methods_not_found.borrow_mut().insert(orig_span);
label_span_not_found();
}
} else {
err.span_label(span, format!("{} not found in `{}`", item_kind, ty_str));
self.tcx.sess.trait_methods_not_found.borrow_mut().insert(orig_span);
label_span_not_found();
}

if self.is_fn_ty(&rcvr_ty, span) {
Expand Down Expand Up @@ -721,10 +728,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.map(|(_, path)| path)
.collect::<Vec<_>>()
.join("\n");
let actual_prefix = actual.prefix_string();
err.set_primary_message(&format!(
"the {item_kind} `{item_name}` exists for {actual_prefix} `{ty_str}`, but its trait bounds were not satisfied"
));
err.note(&format!(
"the method `{}` exists but the following trait bounds were not \
satisfied:\n{}",
item_name, bound_list
"the following trait bounds were not satisfied:\n{bound_list}"
));
}
}
Expand All @@ -742,7 +751,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
}

if actual.is_enum() {
// Don't emit a suggestion if we found an actual method
// that had unsatisfied trait bounds
if unsatisfied_predicates.is_empty() && actual.is_enum() {
let adt_def = actual.ty_adt_def().expect("enum is not an ADT");
if let Some(suggestion) = lev_distance::find_best_match_for_name(
&adt_def.variants.iter().map(|s| s.ident.name).collect::<Vec<_>>(),
Expand Down Expand Up @@ -778,17 +789,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.span_label(span, msg);
}
} else if let Some(lev_candidate) = lev_candidate {
let def_kind = lev_candidate.kind.as_def_kind();
err.span_suggestion(
span,
&format!(
"there is {} {} with a similar name",
def_kind.article(),
def_kind.descr(lev_candidate.def_id),
),
lev_candidate.ident.to_string(),
Applicability::MaybeIncorrect,
);
// Don't emit a suggestion if we found an actual method
// that had unsatisfied trait bounds
if unsatisfied_predicates.is_empty() {
let def_kind = lev_candidate.kind.as_def_kind();
err.span_suggestion(
span,
&format!(
"there is {} {} with a similar name",
def_kind.article(),
def_kind.descr(lev_candidate.def_id),
),
lev_candidate.ident.to_string(),
Applicability::MaybeIncorrect,
);
}
}

return Some(err);
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_typeck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ This API is completely unstable and subject to change.
#![feature(bool_to_option)]
#![feature(box_syntax)]
#![feature(crate_visibility_modifier)]
#![feature(format_args_capture)]
#![feature(in_band_lifetimes)]
#![feature(is_sorted)]
#![feature(nll)]
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/associated-types/hr-associated-type-bound-2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ where

fn main() {
1u32.f("abc");
//~^ ERROR no method named `f` found for type `u32` in the current scope
//~^ ERROR the method
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
error[E0599]: no method named `f` found for type `u32` in the current scope
error[E0599]: the method `f` exists for type `u32`, but its trait bounds were not satisfied
--> $DIR/hr-associated-type-bound-2.rs:19:10
|
LL | 1u32.f("abc");
| ^ method not found in `u32`
| ^ method cannot be called on `u32` due to unsatisfied trait bounds
|
= note: the method `f` exists but the following trait bounds were not satisfied:
= note: the following trait bounds were not satisfied:
`<u32 as X<'b>>::U: Clone`
which is required by `u32: X`

Expand Down
6 changes: 3 additions & 3 deletions src/test/ui/derives/derive-assoc-type-not-impl.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error[E0599]: no method named `clone` found for struct `Bar<NotClone>` in the current scope
error[E0599]: the method `clone` exists for struct `Bar<NotClone>`, but its trait bounds were not satisfied
--> $DIR/derive-assoc-type-not-impl.rs:18:30
|
LL | struct Bar<T: Foo> {
Expand All @@ -11,7 +11,7 @@ LL | struct NotClone;
| ---------------- doesn't satisfy `NotClone: Clone`
...
LL | Bar::<NotClone> { x: 1 }.clone();
| ^^^^^ method not found in `Bar<NotClone>`
| ^^^^^ method cannot be called on `Bar<NotClone>` due to unsatisfied trait bounds
|
::: $SRC_DIR/core/src/clone.rs:LL:COL
|
Expand All @@ -21,7 +21,7 @@ LL | fn clone(&self) -> Self;
| the method is available for `Arc<Bar<NotClone>>` here
| the method is available for `Rc<Bar<NotClone>>` here
|
= note: the method `clone` exists but the following trait bounds were not satisfied:
= note: the following trait bounds were not satisfied:
`NotClone: Clone`
which is required by `Bar<NotClone>: Clone`
= help: items from traits can only be used if the trait is implemented and in scope
Expand Down
12 changes: 6 additions & 6 deletions src/test/ui/hrtb/issue-30786.migrate.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error[E0599]: no method named `filterx` found for struct `Map<Repeat, [closure@$DIR/issue-30786.rs:127:27: 127:36]>` in the current scope
error[E0599]: the method `filterx` exists for struct `Map<Repeat, [closure@$DIR/issue-30786.rs:127:27: 127:36]>`, but its trait bounds were not satisfied
--> $DIR/issue-30786.rs:128:22
|
LL | pub struct Map<S, F> {
Expand All @@ -8,17 +8,17 @@ LL | pub struct Map<S, F> {
| doesn't satisfy `_: StreamExt`
...
LL | let filter = map.filterx(|x: &_| true);
| ^^^^^^^ method not found in `Map<Repeat, [closure@$DIR/issue-30786.rs:127:27: 127:36]>`
| ^^^^^^^ method cannot be called on `Map<Repeat, [closure@$DIR/issue-30786.rs:127:27: 127:36]>` due to unsatisfied trait bounds
|
= note: the method `filterx` exists but the following trait bounds were not satisfied:
= note: the following trait bounds were not satisfied:
`&'a mut Map<Repeat, [closure@$DIR/issue-30786.rs:127:27: 127:36]>: Stream`
which is required by `Map<Repeat, [closure@$DIR/issue-30786.rs:127:27: 127:36]>: StreamExt`
`&'a mut &Map<Repeat, [closure@$DIR/issue-30786.rs:127:27: 127:36]>: Stream`
which is required by `&Map<Repeat, [closure@$DIR/issue-30786.rs:127:27: 127:36]>: StreamExt`
`&'a mut &mut Map<Repeat, [closure@$DIR/issue-30786.rs:127:27: 127:36]>: Stream`
which is required by `&mut Map<Repeat, [closure@$DIR/issue-30786.rs:127:27: 127:36]>: StreamExt`

error[E0599]: no method named `countx` found for struct `Filter<Map<Repeat, for<'r> fn(&'r u64) -> &'r u64 {identity::<u64>}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>` in the current scope
error[E0599]: the method `countx` exists for struct `Filter<Map<Repeat, for<'r> fn(&'r u64) -> &'r u64 {identity::<u64>}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>`, but its trait bounds were not satisfied
--> $DIR/issue-30786.rs:141:24
|
LL | pub struct Filter<S, F> {
Expand All @@ -28,9 +28,9 @@ LL | pub struct Filter<S, F> {
| doesn't satisfy `_: StreamExt`
...
LL | let count = filter.countx();
| ^^^^^^ method not found in `Filter<Map<Repeat, for<'r> fn(&'r u64) -> &'r u64 {identity::<u64>}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>`
| ^^^^^^ method cannot be called on `Filter<Map<Repeat, for<'r> fn(&'r u64) -> &'r u64 {identity::<u64>}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>` due to unsatisfied trait bounds
|
= note: the method `countx` exists but the following trait bounds were not satisfied:
= note: the following trait bounds were not satisfied:
`&'a mut Filter<Map<Repeat, for<'r> fn(&'r u64) -> &'r u64 {identity::<u64>}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>: Stream`
which is required by `Filter<Map<Repeat, for<'r> fn(&'r u64) -> &'r u64 {identity::<u64>}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>: StreamExt`
`&'a mut &Filter<Map<Repeat, for<'r> fn(&'r u64) -> &'r u64 {identity::<u64>}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>: Stream`
Expand Down
12 changes: 6 additions & 6 deletions src/test/ui/hrtb/issue-30786.nll.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error[E0599]: no method named `filterx` found for struct `Map<Repeat, [closure@$DIR/issue-30786.rs:127:27: 127:36]>` in the current scope
error[E0599]: the method `filterx` exists for struct `Map<Repeat, [closure@$DIR/issue-30786.rs:127:27: 127:36]>`, but its trait bounds were not satisfied
--> $DIR/issue-30786.rs:128:22
|
LL | pub struct Map<S, F> {
Expand All @@ -8,17 +8,17 @@ LL | pub struct Map<S, F> {
| doesn't satisfy `_: StreamExt`
...
LL | let filter = map.filterx(|x: &_| true);
| ^^^^^^^ method not found in `Map<Repeat, [closure@$DIR/issue-30786.rs:127:27: 127:36]>`
| ^^^^^^^ method cannot be called on `Map<Repeat, [closure@$DIR/issue-30786.rs:127:27: 127:36]>` due to unsatisfied trait bounds
|
= note: the method `filterx` exists but the following trait bounds were not satisfied:
= note: the following trait bounds were not satisfied:
`&'a mut Map<Repeat, [closure@$DIR/issue-30786.rs:127:27: 127:36]>: Stream`
which is required by `Map<Repeat, [closure@$DIR/issue-30786.rs:127:27: 127:36]>: StreamExt`
`&'a mut &Map<Repeat, [closure@$DIR/issue-30786.rs:127:27: 127:36]>: Stream`
which is required by `&Map<Repeat, [closure@$DIR/issue-30786.rs:127:27: 127:36]>: StreamExt`
`&'a mut &mut Map<Repeat, [closure@$DIR/issue-30786.rs:127:27: 127:36]>: Stream`
which is required by `&mut Map<Repeat, [closure@$DIR/issue-30786.rs:127:27: 127:36]>: StreamExt`

error[E0599]: no method named `countx` found for struct `Filter<Map<Repeat, for<'r> fn(&'r u64) -> &'r u64 {identity::<u64>}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>` in the current scope
error[E0599]: the method `countx` exists for struct `Filter<Map<Repeat, for<'r> fn(&'r u64) -> &'r u64 {identity::<u64>}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>`, but its trait bounds were not satisfied
--> $DIR/issue-30786.rs:141:24
|
LL | pub struct Filter<S, F> {
Expand All @@ -28,9 +28,9 @@ LL | pub struct Filter<S, F> {
| doesn't satisfy `_: StreamExt`
...
LL | let count = filter.countx();
| ^^^^^^ method not found in `Filter<Map<Repeat, for<'r> fn(&'r u64) -> &'r u64 {identity::<u64>}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>`
| ^^^^^^ method cannot be called on `Filter<Map<Repeat, for<'r> fn(&'r u64) -> &'r u64 {identity::<u64>}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>` due to unsatisfied trait bounds
|
= note: the method `countx` exists but the following trait bounds were not satisfied:
= note: the following trait bounds were not satisfied:
`&'a mut Filter<Map<Repeat, for<'r> fn(&'r u64) -> &'r u64 {identity::<u64>}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>: Stream`
which is required by `Filter<Map<Repeat, for<'r> fn(&'r u64) -> &'r u64 {identity::<u64>}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>: StreamExt`
`&'a mut &Filter<Map<Repeat, for<'r> fn(&'r u64) -> &'r u64 {identity::<u64>}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>: Stream`
Expand Down
8 changes: 4 additions & 4 deletions src/test/ui/hrtb/issue-30786.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ fn variant1() {
// guess.
let map = source.mapx(|x: &_| x);
let filter = map.filterx(|x: &_| true);
//[migrate]~^ ERROR no method named `filterx`
//[nll]~^^ ERROR no method named `filterx`
//[migrate]~^ ERROR the method
//[nll]~^^ ERROR the method
}

fn variant2() {
Expand All @@ -139,8 +139,8 @@ fn variant2() {
let map = source.mapx(identity);
let filter = map.filterx(|x: &_| true);
let count = filter.countx();
//[migrate]~^ ERROR no method named `countx`
//[nll]~^^ ERROR no method named `countx`
//[migrate]~^ ERROR the method
//[nll]~^^ ERROR the method
}

fn main() {}
6 changes: 3 additions & 3 deletions src/test/ui/issues/issue-21596.stderr
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
error[E0599]: no method named `to_string` found for raw pointer `*const u8` in the current scope
error[E0599]: the method `to_string` exists for raw pointer `*const u8`, but its trait bounds were not satisfied
--> $DIR/issue-21596.rs:4:22
|
LL | println!("{}", z.to_string());
| ^^^^^^^^^ method not found in `*const u8`
| ^^^^^^^^^ method cannot be called on `*const u8` due to unsatisfied trait bounds
|
= note: try using `<*const T>::as_ref()` to get a reference to the type behind the pointer: https://doc.rust-lang.org/std/primitive.pointer.html#method.as_ref
= note: using `<*const T>::as_ref()` on a pointer which is unaligned or points to invalid or uninitialized memory is undefined behavior
= note: the method `to_string` exists but the following trait bounds were not satisfied:
= note: the following trait bounds were not satisfied:
`*const u8: std::fmt::Display`
which is required by `*const u8: ToString`

Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/issues/issue-31173.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub fn get_tok(it: &mut IntoIter<u8>) {
//~^ ERROR type mismatch resolving
//~| expected type `u8`
//~| found reference `&_`
.collect(); //~ ERROR no method named `collect`
.collect(); //~ ERROR the method
}

fn main() {}
6 changes: 3 additions & 3 deletions src/test/ui/issues/issue-31173.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ LL | .cloned()
= note: expected type `u8`
found reference `&_`

error[E0599]: no method named `collect` found for struct `Cloned<TakeWhile<&mut std::vec::IntoIter<u8>, [closure@$DIR/issue-31173.rs:6:39: 9:6]>>` in the current scope
error[E0599]: the method `collect` exists for struct `Cloned<TakeWhile<&mut std::vec::IntoIter<u8>, [closure@$DIR/issue-31173.rs:6:39: 9:6]>>`, but its trait bounds were not satisfied
--> $DIR/issue-31173.rs:14:10
|
LL | .collect();
| ^^^^^^^ method not found in `Cloned<TakeWhile<&mut std::vec::IntoIter<u8>, [closure@$DIR/issue-31173.rs:6:39: 9:6]>>`
| ^^^^^^^ method cannot be called on `Cloned<TakeWhile<&mut std::vec::IntoIter<u8>, [closure@$DIR/issue-31173.rs:6:39: 9:6]>>` due to unsatisfied trait bounds
|
::: $SRC_DIR/core/src/iter/adapters/cloned.rs:LL:COL
|
Expand All @@ -23,7 +23,7 @@ LL | pub struct Cloned<I> {
LL | pub struct TakeWhile<I, P> {
| -------------------------- doesn't satisfy `<_ as Iterator>::Item = &_`
|
= note: the method `collect` exists but the following trait bounds were not satisfied:
= note: the following trait bounds were not satisfied:
`<TakeWhile<&mut std::vec::IntoIter<u8>, [closure@$DIR/issue-31173.rs:6:39: 9:6]> as Iterator>::Item = &_`
which is required by `Cloned<TakeWhile<&mut std::vec::IntoIter<u8>, [closure@$DIR/issue-31173.rs:6:39: 9:6]>>: Iterator`
`Cloned<TakeWhile<&mut std::vec::IntoIter<u8>, [closure@$DIR/issue-31173.rs:6:39: 9:6]>>: Iterator`
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/issues/issue-35677.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::collections::HashSet;

fn is_subset<T>(this: &HashSet<T>, other: &HashSet<T>) -> bool {
this.is_subset(other)
//~^ ERROR no method named
//~^ ERROR the method
}

fn main() {}
6 changes: 3 additions & 3 deletions src/test/ui/issues/issue-35677.stderr
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
error[E0599]: no method named `is_subset` found for reference `&HashSet<T>` in the current scope
error[E0599]: the method `is_subset` exists for reference `&HashSet<T>`, but its trait bounds were not satisfied
--> $DIR/issue-35677.rs:4:10
|
LL | this.is_subset(other)
| ^^^^^^^^^ method not found in `&HashSet<T>`
| ^^^^^^^^^ method cannot be called on `&HashSet<T>` due to unsatisfied trait bounds
|
= note: the method `is_subset` exists but the following trait bounds were not satisfied:
= note: the following trait bounds were not satisfied:
`T: Eq`
`T: Hash`

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
fn main() {
let _result = &Some(42).as_deref();
//~^ ERROR no method named `as_deref` found for enum `Option<{integer}>`
//~^ ERROR the method
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
error[E0599]: no method named `as_deref` found for enum `Option<{integer}>` in the current scope
error[E0599]: the method `as_deref` exists for enum `Option<{integer}>`, but its trait bounds were not satisfied
--> $DIR/option-as_deref.rs:2:29
|
LL | let _result = &Some(42).as_deref();
| ^^^^^^^^ help: there is an associated function with a similar name: `as_ref`
| ^^^^^^^^
|
= note: the method `as_deref` exists but the following trait bounds were not satisfied:
= note: the following trait bounds were not satisfied:
`{integer}: Deref`
`<{integer} as Deref>::Target = _`

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
fn main() {
let _result = &mut Some(42).as_deref_mut();
//~^ ERROR no method named `as_deref_mut` found for enum `Option<{integer}>`
//~^ ERROR the method
}
Loading

0 comments on commit 643a79a

Please sign in to comment.