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:
@@ -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(
|
||||
|
||||
@@ -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> {
|
||||
|
||||
Reference in New Issue
Block a user