Skip to content

Commit 3aac307

Browse files
committed
Mention implementers of unsatisfied trait
When encountering an unsatisfied trait bound, if there are no other suggestions, mention all the types that *do* implement that trait: ``` error[E0277]: the trait bound `f32: Foo` is not satisfied --> $DIR/impl_wf.rs:22:6 | LL | impl Baz<f32> for f32 { } | ^^^^^^^^ the trait `Foo` is not implemented for `f32` | = help: the following other types implement trait `Foo`: Option<T> i32 str note: required by a bound in `Baz` --> $DIR/impl_wf.rs:18:31 | LL | trait Baz<U: ?Sized> where U: Foo { } | ^^^ required by this bound in `Baz` ``` Mention implementers of traits in `ImplObligation`s. Do not mention other `impl`s for closures, ranges and `?`.
1 parent 6a9080b commit 3aac307

File tree

115 files changed

+690
-183
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

115 files changed

+690
-183
lines changed

compiler/rustc_errors/src/diagnostic.rs

+6
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,12 @@ impl Diagnostic {
378378
self
379379
}
380380

381+
/// Add a help message attached to this diagnostic with a customizable highlighted message.
382+
pub fn highlighted_help(&mut self, msg: Vec<(String, Style)>) -> &mut Self {
383+
self.sub_with_highlights(Level::Help, msg, MultiSpan::new(), None);
384+
self
385+
}
386+
381387
/// Prints the span with some help above it.
382388
/// This is like [`Diagnostic::help()`], but it gets its own span.
383389
pub fn span_help<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {

compiler/rustc_middle/src/ty/mod.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ use rustc_hir::Node;
4141
use rustc_macros::HashStable;
4242
use rustc_query_system::ich::StableHashingContext;
4343
use rustc_session::cstore::CrateStoreDyn;
44-
use rustc_span::symbol::{kw, Ident, Symbol};
44+
use rustc_span::symbol::{kw, sym, Ident, Symbol};
4545
use rustc_span::Span;
4646
use rustc_target::abi::Align;
4747

@@ -2206,7 +2206,7 @@ impl<'tcx> TyCtxt<'tcx> {
22062206
self.impl_trait_ref(def_id).map(|tr| tr.def_id)
22072207
}
22082208

2209-
/// If the given defid describes a method belonging to an impl, returns the
2209+
/// If the given `DefId` describes a method belonging to an impl, returns the
22102210
/// `DefId` of the impl that the method belongs to; otherwise, returns `None`.
22112211
pub fn impl_of_method(self, def_id: DefId) -> Option<DefId> {
22122212
self.opt_associated_item(def_id).and_then(|trait_item| match trait_item.container {
@@ -2215,6 +2215,11 @@ impl<'tcx> TyCtxt<'tcx> {
22152215
})
22162216
}
22172217

2218+
/// If the given `DefId` belongs to a trait that was automatically derived, returns `true`.
2219+
pub fn is_builtin_derive(self, def_id: DefId) -> bool {
2220+
self.has_attr(def_id, sym::automatically_derived)
2221+
}
2222+
22182223
/// Looks up the span of `impl_did` if the impl is local; otherwise returns `Err`
22192224
/// with the name of the crate containing the impl.
22202225
pub fn span_of_impl(self, impl_did: DefId) -> Result<Span, Symbol> {

compiler/rustc_mir_transform/src/check_packed_ref.rs

+6-19
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
use rustc_hir::def_id::{DefId, LocalDefId};
1+
use rustc_hir::def_id::LocalDefId;
22
use rustc_middle::mir::visit::{PlaceContext, Visitor};
33
use rustc_middle::mir::*;
44
use rustc_middle::ty::query::Providers;
55
use rustc_middle::ty::{self, TyCtxt};
66
use rustc_session::lint::builtin::UNALIGNED_REFERENCES;
7-
use rustc_span::symbol::sym;
87

98
use crate::util;
109
use crate::MirLint;
@@ -50,22 +49,6 @@ fn unsafe_derive_on_repr_packed(tcx: TyCtxt<'_>, def_id: LocalDefId) {
5049
});
5150
}
5251

53-
fn builtin_derive_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> {
54-
debug!("builtin_derive_def_id({:?})", def_id);
55-
if let Some(impl_def_id) = tcx.impl_of_method(def_id) {
56-
if tcx.has_attr(impl_def_id, sym::automatically_derived) {
57-
debug!("builtin_derive_def_id({:?}) - is {:?}", def_id, impl_def_id);
58-
Some(impl_def_id)
59-
} else {
60-
debug!("builtin_derive_def_id({:?}) - not automatically derived", def_id);
61-
None
62-
}
63-
} else {
64-
debug!("builtin_derive_def_id({:?}) - not a method", def_id);
65-
None
66-
}
67-
}
68-
6952
impl<'tcx> Visitor<'tcx> for PackedRefChecker<'_, 'tcx> {
7053
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
7154
// Make sure we know where in the MIR we are.
@@ -83,7 +66,11 @@ impl<'tcx> Visitor<'tcx> for PackedRefChecker<'_, 'tcx> {
8366
if context.is_borrow() {
8467
if util::is_disaligned(self.tcx, self.body, self.param_env, *place) {
8568
let def_id = self.body.source.instance.def_id();
86-
if let Some(impl_def_id) = builtin_derive_def_id(self.tcx, def_id) {
69+
if let Some(impl_def_id) = self
70+
.tcx
71+
.impl_of_method(def_id)
72+
.filter(|&def_id| self.tcx.is_builtin_derive(def_id))
73+
{
8774
// If a method is defined in the local crate,
8875
// the impl containing that method should also be.
8976
self.tcx.ensure().unsafe_derive_on_repr_packed(impl_def_id.expect_local());

compiler/rustc_span/src/symbol.rs

+4
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,9 @@ symbols! {
180180
Error,
181181
File,
182182
FileType,
183+
Fn,
184+
FnMut,
185+
FnOnce,
183186
FormatSpec,
184187
Formatter,
185188
From,
@@ -248,6 +251,7 @@ symbols! {
248251
RustcEncodable,
249252
Send,
250253
SeqCst,
254+
SliceIndex,
251255
Some,
252256
String,
253257
StructuralEq,

compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs

+107-19
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use crate::infer::{self, InferCtxt, TyCtxtInferExt};
1414
use rustc_data_structures::fx::FxHashMap;
1515
use rustc_errors::{
1616
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
17+
Style,
1718
};
1819
use rustc_hir as hir;
1920
use rustc_hir::def_id::DefId;
@@ -354,7 +355,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
354355
let have_alt_message = message.is_some() || label.is_some();
355356
let is_try_conversion = self.is_try_conversion(span, trait_ref.def_id());
356357
let is_unsize =
357-
{ Some(trait_ref.def_id()) == self.tcx.lang_items().unsize_trait() };
358+
Some(trait_ref.def_id()) == self.tcx.lang_items().unsize_trait();
358359
let (message, note, append_const_msg) = if is_try_conversion {
359360
(
360361
Some(format!(
@@ -363,7 +364,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
363364
)),
364365
Some(
365366
"the question mark operation (`?`) implicitly performs a \
366-
conversion on the error value using the `From` trait"
367+
conversion on the error value using the `From` trait"
367368
.to_owned(),
368369
),
369370
Some(None),
@@ -519,10 +520,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
519520
}
520521

521522
self.suggest_floating_point_literal(&obligation, &mut err, &trait_ref);
522-
self.suggest_dereferences(&obligation, &mut err, trait_predicate);
523-
self.suggest_fn_call(&obligation, &mut err, trait_predicate);
524-
self.suggest_remove_reference(&obligation, &mut err, trait_predicate);
525-
self.suggest_semicolon_removal(
523+
let mut suggested =
524+
self.suggest_dereferences(&obligation, &mut err, trait_predicate);
525+
suggested |= self.suggest_fn_call(&obligation, &mut err, trait_predicate);
526+
suggested |=
527+
self.suggest_remove_reference(&obligation, &mut err, trait_predicate);
528+
suggested |= self.suggest_semicolon_removal(
526529
&obligation,
527530
&mut err,
528531
span,
@@ -648,10 +651,14 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
648651
trait_predicate,
649652
obligation.cause.body_id,
650653
);
651-
} else if !have_alt_message {
654+
} else if !suggested {
652655
// Can't show anything else useful, try to find similar impls.
653656
let impl_candidates = self.find_similar_impl_candidates(trait_ref);
654-
self.report_similar_impl_candidates(impl_candidates, &mut err);
657+
self.report_similar_impl_candidates(
658+
impl_candidates,
659+
trait_ref,
660+
&mut err,
661+
);
655662
}
656663

657664
// Changing mutability doesn't make a difference to whether we have
@@ -676,7 +683,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
676683
});
677684
let unit_obligation = obligation.with(predicate.to_predicate(tcx));
678685
if self.predicate_may_hold(&unit_obligation) {
679-
err.note("this trait is implemented for `()`");
680686
err.note(
681687
"this error might have been caused by changes to \
682688
Rust's type-inference algorithm (see issue #48950 \
@@ -1301,8 +1307,9 @@ trait InferCtxtPrivExt<'hir, 'tcx> {
13011307
fn report_similar_impl_candidates(
13021308
&self,
13031309
impl_candidates: Vec<ImplCandidate<'tcx>>,
1310+
trait_ref: ty::PolyTraitRef<'tcx>,
13041311
err: &mut Diagnostic,
1305-
);
1312+
) -> bool;
13061313

13071314
/// Gets the parent trait chain start
13081315
fn get_parent_trait_ref(
@@ -1313,7 +1320,11 @@ trait InferCtxtPrivExt<'hir, 'tcx> {
13131320
/// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait
13141321
/// with the same path as `trait_ref`, a help message about
13151322
/// a probable version mismatch is added to `err`
1316-
fn note_version_mismatch(&self, err: &mut Diagnostic, trait_ref: &ty::PolyTraitRef<'tcx>);
1323+
fn note_version_mismatch(
1324+
&self,
1325+
err: &mut Diagnostic,
1326+
trait_ref: &ty::PolyTraitRef<'tcx>,
1327+
) -> bool;
13171328

13181329
/// Creates a `PredicateObligation` with `new_self_ty` replacing the existing type in the
13191330
/// `trait_ref`.
@@ -1675,10 +1686,63 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
16751686
fn report_similar_impl_candidates(
16761687
&self,
16771688
impl_candidates: Vec<ImplCandidate<'tcx>>,
1689+
trait_ref: ty::PolyTraitRef<'tcx>,
16781690
err: &mut Diagnostic,
1679-
) {
1691+
) -> bool {
1692+
let def_id = trait_ref.def_id();
16801693
if impl_candidates.is_empty() {
1681-
return;
1694+
if self.tcx.trait_is_auto(def_id)
1695+
|| self.tcx.lang_items().items().contains(&Some(def_id))
1696+
|| self.tcx.get_diagnostic_name(def_id).is_some()
1697+
{
1698+
// Mentioning implementers of `Copy`, `Debug` and friends is not useful.
1699+
return false;
1700+
}
1701+
let mut normalized_impl_candidates: Vec<_> = self
1702+
.tcx
1703+
.all_impls(def_id)
1704+
// Ignore automatically derived impls and `!Trait` impls.
1705+
.filter(|&def_id| {
1706+
self.tcx.impl_polarity(def_id) != ty::ImplPolarity::Negative
1707+
|| self.tcx.is_builtin_derive(def_id)
1708+
})
1709+
.filter_map(|def_id| self.tcx.impl_trait_ref(def_id))
1710+
// Avoid mentioning type parameters.
1711+
.filter(|trait_ref| !matches!(trait_ref.self_ty().kind(), ty::Param(_)))
1712+
.map(|trait_ref| format!("\n {}", trait_ref.self_ty()))
1713+
.collect();
1714+
normalized_impl_candidates.sort();
1715+
normalized_impl_candidates.dedup();
1716+
let len = normalized_impl_candidates.len();
1717+
if len == 0 {
1718+
return false;
1719+
}
1720+
if len == 1 {
1721+
err.highlighted_help(vec![
1722+
(
1723+
format!(
1724+
"the trait `{}` is implemented for `",
1725+
trait_ref.print_only_trait_path()
1726+
),
1727+
Style::NoStyle,
1728+
),
1729+
(normalized_impl_candidates[0].trim().to_string(), Style::Highlight),
1730+
("`".to_string(), Style::NoStyle),
1731+
]);
1732+
return true;
1733+
}
1734+
let end = if normalized_impl_candidates.len() <= 9 {
1735+
normalized_impl_candidates.len()
1736+
} else {
1737+
8
1738+
};
1739+
err.help(&format!(
1740+
"the following other types implement trait `{}`:{}{}",
1741+
trait_ref.print_only_trait_path(),
1742+
normalized_impl_candidates[..end].join(""),
1743+
if len > 9 { format!("\nand {} others", len - 8) } else { String::new() }
1744+
));
1745+
return true;
16821746
}
16831747

16841748
let len = impl_candidates.len();
@@ -1703,6 +1767,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
17031767
//
17041768
// Prefer more similar candidates first, then sort lexicographically
17051769
// by their normalized string representation.
1770+
let first_candidate = impl_candidates.get(0).map(|candidate| candidate.trait_ref);
17061771
let mut normalized_impl_candidates_and_similarities = impl_candidates
17071772
.into_iter()
17081773
.map(|ImplCandidate { trait_ref, similarity }| {
@@ -1711,17 +1776,33 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
17111776
})
17121777
.collect::<Vec<_>>();
17131778
normalized_impl_candidates_and_similarities.sort();
1779+
normalized_impl_candidates_and_similarities.dedup();
17141780

17151781
let normalized_impl_candidates = normalized_impl_candidates_and_similarities
17161782
.into_iter()
17171783
.map(|(_, normalized)| normalized)
17181784
.collect::<Vec<_>>();
17191785

1720-
err.help(&format!(
1721-
"the following implementations were found:{}{}",
1722-
normalized_impl_candidates[..end].join(""),
1723-
if len > 5 { format!("\nand {} others", len - 4) } else { String::new() }
1724-
));
1786+
if normalized_impl_candidates.len() == 1 {
1787+
err.highlighted_help(vec![
1788+
(
1789+
format!(
1790+
"the trait `{}` is implemented for `",
1791+
first_candidate.unwrap().print_only_trait_path()
1792+
),
1793+
Style::NoStyle,
1794+
),
1795+
(first_candidate.unwrap().self_ty().to_string(), Style::Highlight),
1796+
("`".to_string(), Style::NoStyle),
1797+
]);
1798+
} else {
1799+
err.help(&format!(
1800+
"the following implementations were found:{}{}",
1801+
normalized_impl_candidates[..end].join(""),
1802+
if len > 9 { format!("\nand {} others", len - 8) } else { String::new() }
1803+
));
1804+
}
1805+
true
17251806
}
17261807

17271808
/// Gets the parent trait chain start
@@ -1752,7 +1833,11 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
17521833
/// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait
17531834
/// with the same path as `trait_ref`, a help message about
17541835
/// a probable version mismatch is added to `err`
1755-
fn note_version_mismatch(&self, err: &mut Diagnostic, trait_ref: &ty::PolyTraitRef<'tcx>) {
1836+
fn note_version_mismatch(
1837+
&self,
1838+
err: &mut Diagnostic,
1839+
trait_ref: &ty::PolyTraitRef<'tcx>,
1840+
) -> bool {
17561841
let get_trait_impl = |trait_def_id| {
17571842
self.tcx.find_map_relevant_impl(trait_def_id, trait_ref.skip_binder().self_ty(), Some)
17581843
};
@@ -1763,6 +1848,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
17631848
.filter(|trait_def_id| *trait_def_id != trait_ref.def_id())
17641849
.filter(|trait_def_id| self.tcx.def_path_str(*trait_def_id) == required_trait_path)
17651850
.collect();
1851+
let mut suggested = false;
17661852
for trait_with_same_path in traits_with_same_path {
17671853
if let Some(impl_def_id) = get_trait_impl(trait_with_same_path) {
17681854
let impl_span = self.tcx.def_span(impl_def_id);
@@ -1773,8 +1859,10 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
17731859
trait_crate
17741860
);
17751861
err.note(&crate_msg);
1862+
suggested = true;
17761863
}
17771864
}
1865+
suggested
17781866
}
17791867

17801868
fn mk_trait_obligation_with_new_self_ty(

0 commit comments

Comments
 (0)