1
Fork 0

Take into account negative impls in "trait item not found" suggestions

This commit is contained in:
LeSeulArtichaut 2020-12-07 12:34:27 +01:00
parent a68864b688
commit cfc38d2d08
3 changed files with 193 additions and 28 deletions

View file

@ -12,6 +12,7 @@ use rustc_hir::lang_items::LangItem;
use rustc_hir::{ExprKind, Node, QPath};
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_middle::hir::map as hir_map;
use rustc_middle::ty::fast_reject::simplify_type;
use rustc_middle::ty::print::with_crate_prefix;
use rustc_middle::ty::{
self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness,
@ -1074,19 +1075,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else {
"items from traits can only be used if the trait is implemented and in scope"
});
let candidates_len = candidates.len();
let message = |action| {
format!(
"the following {traits_define} an item `{name}`, perhaps you need to {action} \
{one_of_them}:",
traits_define =
if candidates.len() == 1 { "trait defines" } else { "traits define" },
if candidates_len == 1 { "trait defines" } else { "traits define" },
action = action,
one_of_them = if candidates.len() == 1 { "it" } else { "one of them" },
one_of_them = if candidates_len == 1 { "it" } else { "one of them" },
name = item_name,
)
};
// Obtain the span for `param` and use it for a structured suggestion.
let mut suggested = false;
if let (Some(ref param), Some(ref table)) =
(param_type, self.in_progress_typeck_results)
{
@ -1147,7 +1148,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Applicability::MaybeIncorrect,
);
}
suggested = true;
return;
}
Node::Item(hir::Item {
kind: hir::ItemKind::Trait(.., bounds, _),
@ -1167,45 +1168,96 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}),
Applicability::MaybeIncorrect,
);
suggested = true;
return;
}
_ => {}
}
}
}
if !suggested {
let action = if let Some(param) = param_type {
format!("restrict type parameter `{}` with", param)
} else {
// FIXME: it might only need to be imported into scope, not implemented.
"implement".to_string()
};
let mut use_note = true;
if let [trait_info] = &candidates[..] {
if let Some(span) = self.tcx.hir().span_if_local(trait_info.def_id) {
err.span_note(
self.tcx.sess.source_map().guess_head_span(span),
&format!(
"`{}` defines an item `{}`, perhaps you need to {} it",
self.tcx.def_path_str(trait_info.def_id),
item_name,
action
),
);
use_note = false
let (potential_candidates, explicitly_negative) = if param_type.is_some() {
// FIXME: Even though negative bounds are not implemented, we could maybe handle
// cases where a positive bound implies a negative impl.
(candidates, Vec::new())
} else if let Some(simp_rcvr_ty) = simplify_type(self.tcx, rcvr_ty, true) {
let mut potential_candidates = Vec::new();
let mut explicitly_negative = Vec::new();
for candidate in candidates {
// Check if there's a negative impl of `candidate` for `rcvr_ty`
if self
.tcx
.all_impls(candidate.def_id)
.filter(|imp_did| {
self.tcx.impl_polarity(*imp_did) == ty::ImplPolarity::Negative
})
.any(|imp_did| {
let imp = self.tcx.impl_trait_ref(imp_did).unwrap();
let imp_simp = simplify_type(self.tcx, imp.self_ty(), true);
imp_simp.map(|s| s == simp_rcvr_ty).unwrap_or(false)
})
{
explicitly_negative.push(candidate);
} else {
potential_candidates.push(candidate);
}
}
if use_note {
(potential_candidates, explicitly_negative)
} else {
// We don't know enough about `recv_ty` to make proper suggestions.
(candidates, Vec::new())
};
let action = if let Some(param) = param_type {
format!("restrict type parameter `{}` with", param)
} else {
// FIXME: it might only need to be imported into scope, not implemented.
"implement".to_string()
};
match &potential_candidates[..] {
[] => {}
[trait_info] if trait_info.def_id.is_local() => {
let span = self.tcx.hir().span_if_local(trait_info.def_id).unwrap();
err.span_note(
self.tcx.sess.source_map().guess_head_span(span),
&format!(
"`{}` defines an item `{}`, perhaps you need to {} it",
self.tcx.def_path_str(trait_info.def_id),
item_name,
action
),
);
}
trait_infos => {
let mut msg = message(action);
for (i, trait_info) in candidates.iter().enumerate() {
for (i, trait_info) in trait_infos.iter().enumerate() {
msg.push_str(&format!(
"\ncandidate #{}: `{}`",
i + 1,
self.tcx.def_path_str(trait_info.def_id),
));
}
err.note(&msg[..]);
err.note(&msg);
}
}
match &explicitly_negative[..] {
[] => {}
[trait_info] => {
let msg = format!(
"the trait `{}` defines an item `{}`, but is explicitely unimplemented",
self.tcx.def_path_str(trait_info.def_id),
item_name
);
err.note(&msg);
}
trait_infos => {
let mut msg = format!(
"the following traits define an item `{}`, but are explicitely unimplemented:",
item_name
);
for trait_info in trait_infos {
msg.push_str(&format!("\n{}", self.tcx.def_path_str(trait_info.def_id)));
}
err.note(&msg);
}
}
}