1
Fork 0

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 `?`.
This commit is contained in:
Esteban Kuber 2021-12-13 20:56:40 +00:00
parent 6a9080b25e
commit 3aac307ca6
115 changed files with 690 additions and 183 deletions

View file

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

View file

@ -41,7 +41,7 @@ use rustc_hir::Node;
use rustc_macros::HashStable;
use rustc_query_system::ich::StableHashingContext;
use rustc_session::cstore::CrateStoreDyn;
use rustc_span::symbol::{kw, Ident, Symbol};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::Span;
use rustc_target::abi::Align;
@ -2206,7 +2206,7 @@ impl<'tcx> TyCtxt<'tcx> {
self.impl_trait_ref(def_id).map(|tr| tr.def_id)
}
/// If the given defid describes a method belonging to an impl, returns the
/// If the given `DefId` describes a method belonging to an impl, returns the
/// `DefId` of the impl that the method belongs to; otherwise, returns `None`.
pub fn impl_of_method(self, def_id: DefId) -> Option<DefId> {
self.opt_associated_item(def_id).and_then(|trait_item| match trait_item.container {
@ -2215,6 +2215,11 @@ impl<'tcx> TyCtxt<'tcx> {
})
}
/// If the given `DefId` belongs to a trait that was automatically derived, returns `true`.
pub fn is_builtin_derive(self, def_id: DefId) -> bool {
self.has_attr(def_id, sym::automatically_derived)
}
/// Looks up the span of `impl_did` if the impl is local; otherwise returns `Err`
/// with the name of the crate containing the impl.
pub fn span_of_impl(self, impl_did: DefId) -> Result<Span, Symbol> {

View file

@ -1,10 +1,9 @@
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::def_id::LocalDefId;
use rustc_middle::mir::visit::{PlaceContext, Visitor};
use rustc_middle::mir::*;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, TyCtxt};
use rustc_session::lint::builtin::UNALIGNED_REFERENCES;
use rustc_span::symbol::sym;
use crate::util;
use crate::MirLint;
@ -50,22 +49,6 @@ fn unsafe_derive_on_repr_packed(tcx: TyCtxt<'_>, def_id: LocalDefId) {
});
}
fn builtin_derive_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> {
debug!("builtin_derive_def_id({:?})", def_id);
if let Some(impl_def_id) = tcx.impl_of_method(def_id) {
if tcx.has_attr(impl_def_id, sym::automatically_derived) {
debug!("builtin_derive_def_id({:?}) - is {:?}", def_id, impl_def_id);
Some(impl_def_id)
} else {
debug!("builtin_derive_def_id({:?}) - not automatically derived", def_id);
None
}
} else {
debug!("builtin_derive_def_id({:?}) - not a method", def_id);
None
}
}
impl<'tcx> Visitor<'tcx> for PackedRefChecker<'_, 'tcx> {
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
// Make sure we know where in the MIR we are.
@ -83,7 +66,11 @@ impl<'tcx> Visitor<'tcx> for PackedRefChecker<'_, 'tcx> {
if context.is_borrow() {
if util::is_disaligned(self.tcx, self.body, self.param_env, *place) {
let def_id = self.body.source.instance.def_id();
if let Some(impl_def_id) = builtin_derive_def_id(self.tcx, def_id) {
if let Some(impl_def_id) = self
.tcx
.impl_of_method(def_id)
.filter(|&def_id| self.tcx.is_builtin_derive(def_id))
{
// If a method is defined in the local crate,
// the impl containing that method should also be.
self.tcx.ensure().unsafe_derive_on_repr_packed(impl_def_id.expect_local());

View file

@ -180,6 +180,9 @@ symbols! {
Error,
File,
FileType,
Fn,
FnMut,
FnOnce,
FormatSpec,
Formatter,
From,
@ -248,6 +251,7 @@ symbols! {
RustcEncodable,
Send,
SeqCst,
SliceIndex,
Some,
String,
StructuralEq,

View file

@ -14,6 +14,7 @@ use crate::infer::{self, InferCtxt, TyCtxtInferExt};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
Style,
};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
@ -354,7 +355,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
let have_alt_message = message.is_some() || label.is_some();
let is_try_conversion = self.is_try_conversion(span, trait_ref.def_id());
let is_unsize =
{ Some(trait_ref.def_id()) == self.tcx.lang_items().unsize_trait() };
Some(trait_ref.def_id()) == self.tcx.lang_items().unsize_trait();
let (message, note, append_const_msg) = if is_try_conversion {
(
Some(format!(
@ -363,7 +364,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
)),
Some(
"the question mark operation (`?`) implicitly performs a \
conversion on the error value using the `From` trait"
conversion on the error value using the `From` trait"
.to_owned(),
),
Some(None),
@ -519,10 +520,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
}
self.suggest_floating_point_literal(&obligation, &mut err, &trait_ref);
self.suggest_dereferences(&obligation, &mut err, trait_predicate);
self.suggest_fn_call(&obligation, &mut err, trait_predicate);
self.suggest_remove_reference(&obligation, &mut err, trait_predicate);
self.suggest_semicolon_removal(
let mut suggested =
self.suggest_dereferences(&obligation, &mut err, trait_predicate);
suggested |= self.suggest_fn_call(&obligation, &mut err, trait_predicate);
suggested |=
self.suggest_remove_reference(&obligation, &mut err, trait_predicate);
suggested |= self.suggest_semicolon_removal(
&obligation,
&mut err,
span,
@ -648,10 +651,14 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
trait_predicate,
obligation.cause.body_id,
);
} else if !have_alt_message {
} else if !suggested {
// Can't show anything else useful, try to find similar impls.
let impl_candidates = self.find_similar_impl_candidates(trait_ref);
self.report_similar_impl_candidates(impl_candidates, &mut err);
self.report_similar_impl_candidates(
impl_candidates,
trait_ref,
&mut err,
);
}
// Changing mutability doesn't make a difference to whether we have
@ -676,7 +683,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
});
let unit_obligation = obligation.with(predicate.to_predicate(tcx));
if self.predicate_may_hold(&unit_obligation) {
err.note("this trait is implemented for `()`");
err.note(
"this error might have been caused by changes to \
Rust's type-inference algorithm (see issue #48950 \
@ -1301,8 +1307,9 @@ trait InferCtxtPrivExt<'hir, 'tcx> {
fn report_similar_impl_candidates(
&self,
impl_candidates: Vec<ImplCandidate<'tcx>>,
trait_ref: ty::PolyTraitRef<'tcx>,
err: &mut Diagnostic,
);
) -> bool;
/// Gets the parent trait chain start
fn get_parent_trait_ref(
@ -1313,7 +1320,11 @@ trait InferCtxtPrivExt<'hir, 'tcx> {
/// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait
/// with the same path as `trait_ref`, a help message about
/// a probable version mismatch is added to `err`
fn note_version_mismatch(&self, err: &mut Diagnostic, trait_ref: &ty::PolyTraitRef<'tcx>);
fn note_version_mismatch(
&self,
err: &mut Diagnostic,
trait_ref: &ty::PolyTraitRef<'tcx>,
) -> bool;
/// Creates a `PredicateObligation` with `new_self_ty` replacing the existing type in the
/// `trait_ref`.
@ -1675,10 +1686,63 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
fn report_similar_impl_candidates(
&self,
impl_candidates: Vec<ImplCandidate<'tcx>>,
trait_ref: ty::PolyTraitRef<'tcx>,
err: &mut Diagnostic,
) {
) -> bool {
let def_id = trait_ref.def_id();
if impl_candidates.is_empty() {
return;
if self.tcx.trait_is_auto(def_id)
|| self.tcx.lang_items().items().contains(&Some(def_id))
|| self.tcx.get_diagnostic_name(def_id).is_some()
{
// Mentioning implementers of `Copy`, `Debug` and friends is not useful.
return false;
}
let mut normalized_impl_candidates: Vec<_> = self
.tcx
.all_impls(def_id)
// Ignore automatically derived impls and `!Trait` impls.
.filter(|&def_id| {
self.tcx.impl_polarity(def_id) != ty::ImplPolarity::Negative
|| self.tcx.is_builtin_derive(def_id)
})
.filter_map(|def_id| self.tcx.impl_trait_ref(def_id))
// Avoid mentioning type parameters.
.filter(|trait_ref| !matches!(trait_ref.self_ty().kind(), ty::Param(_)))
.map(|trait_ref| format!("\n {}", trait_ref.self_ty()))
.collect();
normalized_impl_candidates.sort();
normalized_impl_candidates.dedup();
let len = normalized_impl_candidates.len();
if len == 0 {
return false;
}
if len == 1 {
err.highlighted_help(vec![
(
format!(
"the trait `{}` is implemented for `",
trait_ref.print_only_trait_path()
),
Style::NoStyle,
),
(normalized_impl_candidates[0].trim().to_string(), Style::Highlight),
("`".to_string(), Style::NoStyle),
]);
return true;
}
let end = if normalized_impl_candidates.len() <= 9 {
normalized_impl_candidates.len()
} else {
8
};
err.help(&format!(
"the following other types implement trait `{}`:{}{}",
trait_ref.print_only_trait_path(),
normalized_impl_candidates[..end].join(""),
if len > 9 { format!("\nand {} others", len - 8) } else { String::new() }
));
return true;
}
let len = impl_candidates.len();
@ -1703,6 +1767,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
//
// Prefer more similar candidates first, then sort lexicographically
// by their normalized string representation.
let first_candidate = impl_candidates.get(0).map(|candidate| candidate.trait_ref);
let mut normalized_impl_candidates_and_similarities = impl_candidates
.into_iter()
.map(|ImplCandidate { trait_ref, similarity }| {
@ -1711,17 +1776,33 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
})
.collect::<Vec<_>>();
normalized_impl_candidates_and_similarities.sort();
normalized_impl_candidates_and_similarities.dedup();
let normalized_impl_candidates = normalized_impl_candidates_and_similarities
.into_iter()
.map(|(_, normalized)| normalized)
.collect::<Vec<_>>();
err.help(&format!(
"the following implementations were found:{}{}",
normalized_impl_candidates[..end].join(""),
if len > 5 { format!("\nand {} others", len - 4) } else { String::new() }
));
if normalized_impl_candidates.len() == 1 {
err.highlighted_help(vec![
(
format!(
"the trait `{}` is implemented for `",
first_candidate.unwrap().print_only_trait_path()
),
Style::NoStyle,
),
(first_candidate.unwrap().self_ty().to_string(), Style::Highlight),
("`".to_string(), Style::NoStyle),
]);
} else {
err.help(&format!(
"the following implementations were found:{}{}",
normalized_impl_candidates[..end].join(""),
if len > 9 { format!("\nand {} others", len - 8) } else { String::new() }
));
}
true
}
/// Gets the parent trait chain start
@ -1752,7 +1833,11 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
/// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait
/// with the same path as `trait_ref`, a help message about
/// a probable version mismatch is added to `err`
fn note_version_mismatch(&self, err: &mut Diagnostic, trait_ref: &ty::PolyTraitRef<'tcx>) {
fn note_version_mismatch(
&self,
err: &mut Diagnostic,
trait_ref: &ty::PolyTraitRef<'tcx>,
) -> bool {
let get_trait_impl = |trait_def_id| {
self.tcx.find_map_relevant_impl(trait_def_id, trait_ref.skip_binder().self_ty(), Some)
};
@ -1763,6 +1848,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
.filter(|trait_def_id| *trait_def_id != trait_ref.def_id())
.filter(|trait_def_id| self.tcx.def_path_str(*trait_def_id) == required_trait_path)
.collect();
let mut suggested = false;
for trait_with_same_path in traits_with_same_path {
if let Some(impl_def_id) = get_trait_impl(trait_with_same_path) {
let impl_span = self.tcx.def_span(impl_def_id);
@ -1773,8 +1859,10 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
trait_crate
);
err.note(&crate_msg);
suggested = true;
}
}
suggested
}
fn mk_trait_obligation_with_new_self_ty(

View file

@ -58,7 +58,7 @@ pub trait InferCtxtExt<'tcx> {
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
);
) -> bool;
fn get_closure_name(&self, def_id: DefId, err: &mut Diagnostic, msg: &str) -> Option<String>;
@ -67,7 +67,7 @@ pub trait InferCtxtExt<'tcx> {
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
);
) -> bool;
fn suggest_add_reference_to_arg(
&self,
@ -82,7 +82,7 @@ pub trait InferCtxtExt<'tcx> {
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
);
) -> bool;
fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic);
@ -99,7 +99,7 @@ pub trait InferCtxtExt<'tcx> {
err: &mut Diagnostic,
span: Span,
trait_pred: ty::PolyTraitPredicate<'tcx>,
);
) -> bool;
fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span>;
@ -494,14 +494,14 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) {
) -> bool {
// It only make sense when suggesting dereferences for arguments
let code = if let ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } =
obligation.cause.code()
{
parent_code.clone()
} else {
return;
return false;
};
let param_env = obligation.param_env;
let body_id = obligation.cause.body_id;
@ -513,7 +513,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
_ => trait_pred,
};
let Some(real_ty) = real_trait_pred.self_ty().no_bound_vars() else {
return;
return false;
};
if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() {
@ -537,11 +537,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
format!("&{}{}", derefs, &src[1..]),
Applicability::MachineApplicable,
);
return true;
}
}
}
}
}
false
}
/// Given a closure's `DefId`, return the given name of the closure.
@ -584,22 +586,22 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) {
) -> bool {
let Some(self_ty) = trait_pred.self_ty().no_bound_vars() else {
return;
return false;
};
let (def_id, output_ty, callable) = match *self_ty.kind() {
ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig().output(), "closure"),
ty::FnDef(def_id, _) => (def_id, self_ty.fn_sig(self.tcx).output(), "function"),
_ => return,
_ => return false,
};
let msg = format!("use parentheses to call the {}", callable);
// `mk_trait_obligation_with_new_self_ty` only works for types with no escaping bound
// variables, so bail out if we have any.
let Some(output_ty) = output_ty.no_bound_vars() else {
return;
return false;
};
let new_obligation =
@ -611,7 +613,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
| EvaluationResult::EvaluatedToOkModuloRegions
| EvaluationResult::EvaluatedToAmbig,
) => {}
_ => return,
_ => return false,
}
let hir = self.tcx.hir();
// Get the name of the callable and the arguments to be used in the suggestion.
@ -622,7 +624,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
})) => {
err.span_label(*span, "consider calling this closure");
let Some(name) = self.get_closure_name(def_id, err, &msg) else {
return;
return false;
};
let args = decl.inputs.iter().map(|_| "_").collect::<Vec<_>>().join(", ");
let sugg = format!("({})", args);
@ -650,7 +652,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
let sugg = format!("({})", args);
(format!("{}{}", ident, sugg), sugg)
}
_ => return,
_ => return false,
};
if matches!(obligation.cause.code(), ObligationCauseCode::FunctionArgumentObligation { .. })
{
@ -667,6 +669,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
} else {
err.help(&format!("{}: `{}`", msg, snippet));
}
true
}
fn suggest_add_reference_to_arg(
@ -808,19 +811,20 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) {
) -> bool {
let span = obligation.cause.span;
let mut suggested = false;
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
let refs_number =
snippet.chars().filter(|c| !c.is_whitespace()).take_while(|c| *c == '&').count();
if let Some('\'') = snippet.chars().filter(|c| !c.is_whitespace()).nth(refs_number) {
// Do not suggest removal of borrow from type arguments.
return;
return false;
}
let Some(mut suggested_ty) = trait_pred.self_ty().no_bound_vars() else {
return;
return false;
};
for refs_remaining in 0..refs_number {
@ -856,10 +860,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
String::new(),
Applicability::MachineApplicable,
);
suggested = true;
break;
}
}
}
suggested
}
fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic) {
@ -996,7 +1002,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
err: &mut Diagnostic,
span: Span,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) {
) -> bool {
let hir = self.tcx.hir();
let parent_node = hir.get_parent_node(obligation.cause.body_id);
let node = hir.find(parent_node);
@ -1015,7 +1021,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
{
let sp = self.tcx.sess.source_map().end_point(stmt.span);
err.span_label(sp, "consider removing this semicolon");
return true;
}
false
}
fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span> {