Skip to content

When a trait isn't implemented, but another similar impl is found, point at it #145640

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions compiler/rustc_errors/src/emitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1526,16 +1526,17 @@ impl HumanEmitter {
label_width += 2;
}
let mut line = 0;
let mut pad = false;
for (text, style) in msgs.iter() {
let text =
self.translator.translate_message(text, args).map_err(Report::new).unwrap();
// Account for newlines to align output to its label.
for text in normalize_whitespace(&text).lines() {
for text in normalize_whitespace(&text).split('\n') {
buffer.append(
line,
&format!(
"{}{}",
if line == 0 { String::new() } else { " ".repeat(label_width) },
if pad { " ".repeat(label_width) } else { String::new() },
text
),
match style {
Expand All @@ -1544,7 +1545,9 @@ impl HumanEmitter {
},
);
line += 1;
pad = true;
}
pad = false;
// We add lines above, but if the last line has no explicit newline (which would
// yield an empty line), then we revert one line up to continue with the next
// styled text chunk on the same line as the last one from the prior one. Otherwise
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1862,13 +1862,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
// ignore `do_not_recommend` items
.filter(|def_id| !self.tcx.do_not_recommend_impl(*def_id))
// Ignore automatically derived impls and `!Trait` impls.
.filter_map(|def_id| self.tcx.impl_trait_header(def_id))
.filter_map(|header| {
.filter_map(|def_id| self.tcx.impl_trait_header(def_id).map(|h| (h, def_id)))
.filter_map(|(header, def_id)| {
(header.polarity != ty::ImplPolarity::Negative
|| self.tcx.is_automatically_derived(def_id))
.then(|| header.trait_ref.instantiate_identity())
.then(|| (header.trait_ref.instantiate_identity(), def_id))
})
.filter(|trait_ref| {
.filter(|(trait_ref, _)| {
let self_ty = trait_ref.self_ty();
// Avoid mentioning type parameters.
if let ty::Param(_) = self_ty.kind() {
Expand All @@ -1886,7 +1886,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
})
.collect();

impl_candidates.sort_by_key(|tr| tr.to_string());
impl_candidates.sort_by_key(|(tr, _)| tr.to_string());
impl_candidates.dedup();
impl_candidates
};
Expand Down Expand Up @@ -1914,7 +1914,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
let candidates = if impl_candidates.is_empty() {
alternative_candidates(trait_def_id)
} else {
impl_candidates.into_iter().map(|cand| cand.trait_ref).collect()
impl_candidates.into_iter().map(|cand| (cand.trait_ref, cand.impl_def_id)).collect()
};
let mut span: MultiSpan = self.tcx.def_span(trait_def_id).into();
span.push_span_label(self.tcx.def_span(trait_def_id), "this is the required trait");
Expand Down Expand Up @@ -1946,7 +1946,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
self.tcx.def_span(found_type),
"this type doesn't implement the required trait",
);
for trait_ref in candidates {
for (trait_ref, _) in candidates {
if let ty::Adt(def, _) = trait_ref.self_ty().peel_refs().kind()
&& let candidate_def_id = def.did()
&& let Some(name) = self.tcx.opt_item_name(candidate_def_id)
Expand Down Expand Up @@ -2133,7 +2133,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
msg.extend(types.1.0);
msg.push(StringPart::normal("`"));
}
err.highlighted_help(msg);
err.highlighted_span_help(self.tcx.def_span(single.impl_def_id), msg);

if let [TypeError::Sorts(exp_found)] = &terrs[..] {
let exp_found = self.resolve_vars_if_possible(*exp_found);
Expand All @@ -2159,12 +2159,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}

let other = if other { "other " } else { "" };
let report = |mut candidates: Vec<TraitRef<'tcx>>, err: &mut Diag<'_>| {
candidates.retain(|tr| !tr.references_error());
let report = |mut candidates: Vec<(TraitRef<'tcx>, DefId)>, err: &mut Diag<'_>| {
candidates.retain(|(tr, _)| !tr.references_error());
if candidates.is_empty() {
return false;
}
if let &[cand] = &candidates[..] {
if let &[(cand, def_id)] = &candidates[..] {
if self.tcx.is_diagnostic_item(sym::FromResidual, cand.def_id)
&& !self.tcx.features().enabled(sym::try_trait_v2)
{
Expand All @@ -2180,56 +2180,87 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
};
let trait_ = self.tcx.short_string(cand.print_trait_sugared(), err.long_ty_path());
let self_ty = self.tcx.short_string(cand.self_ty(), err.long_ty_path());
err.highlighted_help(vec![
StringPart::normal(format!("the trait `{trait_}` ",)),
StringPart::highlighted("is"),
StringPart::normal(desc),
StringPart::highlighted(self_ty),
StringPart::normal("`"),
StringPart::normal(mention_castable),
]);
err.highlighted_span_help(
self.tcx.def_span(def_id),
vec![
StringPart::normal(format!("the trait `{trait_}` ",)),
StringPart::highlighted("is"),
StringPart::normal(desc),
StringPart::highlighted(self_ty),
StringPart::normal("`"),
StringPart::normal(mention_castable),
],
);
return true;
}
let trait_ref = TraitRef::identity(self.tcx, candidates[0].def_id);
let trait_ref = TraitRef::identity(self.tcx, candidates[0].0.def_id);
// Check if the trait is the same in all cases. If so, we'll only show the type.
let mut traits: Vec<_> =
candidates.iter().map(|c| c.print_only_trait_path().to_string()).collect();
candidates.iter().map(|(c, _)| c.print_only_trait_path().to_string()).collect();
traits.sort();
traits.dedup();
// FIXME: this could use a better heuristic, like just checking
// that args[1..] is the same.
let all_traits_equal = traits.len() == 1;

let candidates: Vec<String> = candidates
.into_iter()
.map(|c| {
if all_traits_equal {
format!("\n {}", self.tcx.short_string(c.self_ty(), err.long_ty_path()))
} else {
format!(
"\n `{}` implements `{}`",
self.tcx.short_string(c.self_ty(), err.long_ty_path()),
self.tcx.short_string(c.print_only_trait_path(), err.long_ty_path()),
)
}
})
.collect();

let end = if candidates.len() <= 9 || self.tcx.sess.opts.verbose {
candidates.len()
} else {
8
};
err.help(format!(
"the following {other}types implement trait `{}`:{}{}",
trait_ref.print_trait_sugared(),
candidates[..end].join(""),
if candidates.len() > 9 && !self.tcx.sess.opts.verbose {
format!("\nand {} others", candidates.len() - 8)
} else {
String::new()
if candidates.len() < 5 {
let spans: Vec<_> =
candidates.iter().map(|(_, def_id)| self.tcx.def_span(def_id)).collect();
let mut span: MultiSpan = spans.into();
for (c, def_id) in &candidates {
let msg = if all_traits_equal {
format!("`{}`", self.tcx.short_string(c.self_ty(), err.long_ty_path()))
} else {
format!(
"`{}` implements `{}`",
self.tcx.short_string(c.self_ty(), err.long_ty_path()),
self.tcx.short_string(c.print_only_trait_path(), err.long_ty_path()),
)
};
span.push_span_label(self.tcx.def_span(def_id), msg);
}
));
err.span_help(
span,
format!(
"the following {other}types implement trait `{}`",
trait_ref.print_trait_sugared(),
),
);
} else {
let candidate_names: Vec<String> = candidates
.iter()
.map(|(c, _)| {
if all_traits_equal {
format!(
"\n {}",
self.tcx.short_string(c.self_ty(), err.long_ty_path())
)
} else {
format!(
"\n `{}` implements `{}`",
self.tcx.short_string(c.self_ty(), err.long_ty_path()),
self.tcx
.short_string(c.print_only_trait_path(), err.long_ty_path()),
)
}
})
.collect();
err.help(format!(
"the following {other}types implement trait `{}`:{}{}",
trait_ref.print_trait_sugared(),
candidate_names[..end].join(""),
if candidates.len() > 9 && !self.tcx.sess.opts.verbose {
format!("\nand {} others", candidates.len() - 8)
} else {
String::new()
}
));
}
true
};

Expand Down Expand Up @@ -2279,7 +2310,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
.collect();
impl_candidates.sort_by_key(|cand| (cand.similarity, cand.trait_ref.to_string()));
let mut impl_candidates: Vec<_> =
impl_candidates.into_iter().map(|cand| cand.trait_ref).collect();
impl_candidates.into_iter().map(|cand| (cand.trait_ref, cand.impl_def_id)).collect();
impl_candidates.dedup();

report(impl_candidates, err)
Expand Down
10 changes: 7 additions & 3 deletions tests/ui-fulldeps/rustc-dev-remap.only-remap.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@ help: the trait `VisitorResult` is not implemented for `NotAValidResultType`
|
LL | struct NotAValidResultType;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: the following other types implement trait `VisitorResult`:
()
ControlFlow<T>
help: the following other types implement trait `VisitorResult`
--> /rustc-dev/xyz/compiler/rustc_ast_ir/src/visit.rs:LL:COL
|
= note: `()`
::: /rustc-dev/xyz/compiler/rustc_ast_ir/src/visit.rs:LL:COL
|
= note: `ControlFlow<T>`
note: required by a bound in `rustc_ast::visit::Visitor::Result`
--> /rustc-dev/xyz/compiler/rustc_ast/src/visit.rs:LL:COL
= note: this error originates in the macro `common_visitor_and_walkers` (in Nightly builds, run with -Z macro-backtrace for more info)
Expand Down
11 changes: 8 additions & 3 deletions tests/ui-fulldeps/rustc-dev-remap.remap-unremap.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,14 @@ help: the trait `VisitorResult` is not implemented for `NotAValidResultType`
|
LL | struct NotAValidResultType;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: the following other types implement trait `VisitorResult`:
()
ControlFlow<T>
help: the following other types implement trait `VisitorResult`
--> $COMPILER_DIR_REAL/rustc_ast_ir/src/visit.rs:LL:COL
|
LL | impl VisitorResult for () {
| ^^^^^^^^^^^^^^^^^^^^^^^^^ `()`
...
LL | impl<T> VisitorResult for ControlFlow<T> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ControlFlow<T>`
note: required by a bound in `rustc_ast::visit::Visitor::Result`
--> $COMPILER_DIR_REAL/rustc_ast/src/visit.rs:LL:COL
|
Expand Down
12 changes: 8 additions & 4 deletions tests/ui/allocator/not-an-allocator.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ LL | #[global_allocator]
LL | static A: usize = 0;
| ^^^^^ the trait `GlobalAlloc` is not implemented for `usize`
|
= help: the trait `GlobalAlloc` is implemented for `System`
help: the trait `GlobalAlloc` is implemented for `System`
--> $SRC_DIR/std/src/sys/alloc/unix.rs:LL:COL

error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied
--> $DIR/not-an-allocator.rs:2:11
Expand All @@ -16,7 +17,8 @@ LL | #[global_allocator]
LL | static A: usize = 0;
| ^^^^^ the trait `GlobalAlloc` is not implemented for `usize`
|
= help: the trait `GlobalAlloc` is implemented for `System`
help: the trait `GlobalAlloc` is implemented for `System`
--> $SRC_DIR/std/src/sys/alloc/unix.rs:LL:COL
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied
Expand All @@ -27,7 +29,8 @@ LL | #[global_allocator]
LL | static A: usize = 0;
| ^^^^^ the trait `GlobalAlloc` is not implemented for `usize`
|
= help: the trait `GlobalAlloc` is implemented for `System`
help: the trait `GlobalAlloc` is implemented for `System`
--> $SRC_DIR/std/src/sys/alloc/unix.rs:LL:COL
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied
Expand All @@ -38,7 +41,8 @@ LL | #[global_allocator]
LL | static A: usize = 0;
| ^^^^^ the trait `GlobalAlloc` is not implemented for `usize`
|
= help: the trait `GlobalAlloc` is implemented for `System`
help: the trait `GlobalAlloc` is implemented for `System`
--> $SRC_DIR/std/src/sys/alloc/unix.rs:LL:COL
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error: aborting due to 4 previous errors
Expand Down
24 changes: 20 additions & 4 deletions tests/ui/associated-types/associated-types-path-2.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ LL | f1(2u32, 4u32);
| |
| required by a bound introduced by this call
|
= help: the trait `Foo` is implemented for `i32`
help: the trait `Foo` is implemented for `i32`
--> $DIR/associated-types-path-2.rs:11:1
|
LL | impl Foo for i32 {
| ^^^^^^^^^^^^^^^^
note: required by a bound in `f1`
--> $DIR/associated-types-path-2.rs:15:14
|
Expand All @@ -38,7 +42,11 @@ error[E0277]: the trait bound `u32: Foo` is not satisfied
LL | f1(2u32, 4u32);
| ^^^^ the trait `Foo` is not implemented for `u32`
|
= help: the trait `Foo` is implemented for `i32`
help: the trait `Foo` is implemented for `i32`
--> $DIR/associated-types-path-2.rs:11:1
|
LL | impl Foo for i32 {
| ^^^^^^^^^^^^^^^^

error[E0277]: the trait bound `u32: Foo` is not satisfied
--> $DIR/associated-types-path-2.rs:37:8
Expand All @@ -48,7 +56,11 @@ LL | f1(2u32, 4i32);
| |
| required by a bound introduced by this call
|
= help: the trait `Foo` is implemented for `i32`
help: the trait `Foo` is implemented for `i32`
--> $DIR/associated-types-path-2.rs:11:1
|
LL | impl Foo for i32 {
| ^^^^^^^^^^^^^^^^
note: required by a bound in `f1`
--> $DIR/associated-types-path-2.rs:15:14
|
Expand All @@ -61,7 +73,11 @@ error[E0277]: the trait bound `u32: Foo` is not satisfied
LL | f1(2u32, 4i32);
| ^^^^ the trait `Foo` is not implemented for `u32`
|
= help: the trait `Foo` is implemented for `i32`
help: the trait `Foo` is implemented for `i32`
--> $DIR/associated-types-path-2.rs:11:1
|
LL | impl Foo for i32 {
| ^^^^^^^^^^^^^^^^

error[E0308]: mismatched types
--> $DIR/associated-types-path-2.rs:43:18
Expand Down
6 changes: 4 additions & 2 deletions tests/ui/associated-types/hr-associated-type-bound-1.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ error[E0277]: the trait bound `str: Clone` is not satisfied
LL | type U = str;
| ^^^ the trait `Clone` is not implemented for `str`
|
= help: the trait `Clone` is implemented for `String`
help: the trait `Clone` is implemented for `String`
--> $SRC_DIR/alloc/src/string.rs:LL:COL
note: required by a bound in `X`
--> $DIR/hr-associated-type-bound-1.rs:3:33
|
Expand All @@ -20,7 +21,8 @@ error[E0277]: the trait bound `str: Clone` is not satisfied
LL | 1i32.f("abc");
| ^ the trait `Clone` is not implemented for `str`
|
= help: the trait `Clone` is implemented for `String`
help: the trait `Clone` is implemented for `String`
--> $SRC_DIR/alloc/src/string.rs:LL:COL
note: required by a bound in `X::f`
--> $DIR/hr-associated-type-bound-1.rs:3:33
|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ error[E0277]: the trait bound `str: Clone` is not satisfied
LL | type V = str;
| ^^^ the trait `Clone` is not implemented for `str`
|
= help: the trait `Clone` is implemented for `String`
help: the trait `Clone` is implemented for `String`
--> $SRC_DIR/alloc/src/string.rs:LL:COL
note: required by a bound in `Y`
--> $DIR/hr-associated-type-bound-param-1.rs:4:36
|
Expand All @@ -20,7 +21,8 @@ error[E0277]: the trait bound `str: Clone` is not satisfied
LL | 1u8.g("abc");
| ^ the trait `Clone` is not implemented for `str`
|
= help: the trait `Clone` is implemented for `String`
help: the trait `Clone` is implemented for `String`
--> $SRC_DIR/alloc/src/string.rs:LL:COL
note: required by a bound in `Y::g`
--> $DIR/hr-associated-type-bound-param-1.rs:4:36
|
Expand Down
Loading
Loading