Dedup logic and improve output for other types that impl trait

This commit is contained in:
Esteban Kuber 2022-03-27 01:49:01 +00:00
parent e2bba0708a
commit ef91519b45
47 changed files with 363 additions and 357 deletions

View file

@ -30,7 +30,7 @@ use rustc_middle::traits::select::OverflowError;
use rustc_middle::ty::error::ExpectedFound;
use rustc_middle::ty::fold::TypeFolder;
use rustc_middle::ty::{
self, SubtypePredicate, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable,
self, SubtypePredicate, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeFoldable,
};
use rustc_span::symbol::{kw, sym};
use rustc_span::{ExpnKind, MultiSpan, Span, DUMMY_SP};
@ -1756,6 +1756,60 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
trait_ref: ty::PolyTraitRef<'tcx>,
err: &mut Diagnostic,
) -> bool {
let report = |mut candidates: Vec<TraitRef<'_>>, err: &mut Diagnostic| {
candidates.sort();
candidates.dedup();
let len = candidates.len();
if candidates.len() == 0 {
return false;
}
let trait_ref = candidates[0];
if candidates.len() == 1 {
err.highlighted_help(vec![
(
format!(
"the trait `{}` is implemented for `",
trait_ref.print_only_trait_path()
),
Style::NoStyle,
),
(candidates[0].self_ty().to_string(), Style::Highlight),
("`".to_string(), Style::NoStyle),
]);
return true;
}
// Check if the trait is the same in all cases. If so, we'll only show the type.
// FIXME: there *has* to be a better way!
let mut traits: Vec<_> = candidates
.iter()
.map(|c| format!("{}", c).split(" as ").last().unwrap().to_string())
.collect();
traits.sort();
traits.dedup();
let mut candidates: Vec<String> = candidates
.into_iter()
.map(|c| {
if traits.len() == 1 {
format!("\n {}", c.self_ty())
} else {
format!("\n {}", c)
}
})
.collect();
candidates.sort();
candidates.dedup();
let end = if candidates.len() <= 9 { candidates.len() } else { 8 };
err.help(&format!(
"the following other types implement trait `{}`:{}{}",
trait_ref.print_only_trait_path(),
candidates[..end].join(""),
if len > 9 { format!("\nand {} others", len - 8) } else { String::new() }
));
true
};
let def_id = trait_ref.def_id();
if impl_candidates.is_empty() {
if self.tcx.trait_is_auto(def_id)
@ -1765,7 +1819,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
// Mentioning implementers of `Copy`, `Debug` and friends is not useful.
return false;
}
let mut normalized_impl_candidates: Vec<_> = self
let normalized_impl_candidates: Vec<_> = self
.tcx
.all_impls(def_id)
// Ignore automatically derived impls and `!Trait` impls.
@ -1776,45 +1830,10 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
.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;
return report(normalized_impl_candidates, err);
}
let len = impl_candidates.len();
let end = if impl_candidates.len() <= 9 { impl_candidates.len() } else { 8 };
let normalize = |candidate| {
self.tcx.infer_ctxt().enter(|ref infcx| {
let normalized = infcx
@ -1822,8 +1841,8 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
.normalize(candidate)
.ok();
match normalized {
Some(normalized) => format!("\n {}", normalized.value),
None => format!("\n {}", candidate),
Some(normalized) => normalized.value,
None => candidate,
}
})
};
@ -1834,7 +1853,6 @@ 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 }| {
@ -1850,26 +1868,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
.map(|(_, normalized)| normalized)
.collect::<Vec<_>>();
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
report(normalized_impl_candidates, err)
}
/// Gets the parent trait chain start

View file

@ -557,8 +557,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
}
} else if real_trait_pred != trait_pred {
// This branch addresses #87437.
let obligation =
self.mk_trait_obligation_with_new_self_ty(param_env, real_trait_pred, base_ty);
let obligation = self.mk_trait_obligation_with_new_self_ty(
param_env,
real_trait_pred,
base_ty,
);
if self.predicate_may_hold(&obligation) {
err.span_suggestion_verbose(
span.shrink_to_lo(),