Add trait diff highlighting logic and use it in E0277

When a trait is not implemented for a type, but there *is* an `impl`
for another type or different trait params, we format the output to
use highlighting in the same way that E0308 does for types.

The logic accounts for 3 cases:
- When both the type and trait in the expected predicate and the candidate are different
- When only the types are different
- When only the trait generic params are different

For each case, we use slightly different formatting and wording.
This commit is contained in:
Esteban Küber 2024-10-24 22:16:43 +00:00
parent 7c7bb7dc01
commit b7fc1a7431
40 changed files with 251 additions and 86 deletions

View file

@ -766,6 +766,67 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
values
}
pub fn cmp_traits(
&self,
def_id1: DefId,
args1: &[ty::GenericArg<'tcx>],
def_id2: DefId,
args2: &[ty::GenericArg<'tcx>],
) -> (DiagStyledString, DiagStyledString) {
let mut values = (DiagStyledString::new(), DiagStyledString::new());
if def_id1 != def_id2 {
values.0.push_highlighted(self.tcx.def_path_str(def_id1).as_str());
values.1.push_highlighted(self.tcx.def_path_str(def_id2).as_str());
} else {
values.0.push_normal(self.tcx.item_name(def_id1).as_str());
values.1.push_normal(self.tcx.item_name(def_id2).as_str());
}
if args1.len() != args2.len() {
let (pre, post) = if args1.len() > 0 { ("<", ">") } else { ("", "") };
values.0.push_normal(format!(
"{pre}{}{post}",
args1.iter().map(|a| a.to_string()).collect::<Vec<_>>().join(", ")
));
let (pre, post) = if args2.len() > 0 { ("<", ">") } else { ("", "") };
values.1.push_normal(format!(
"{pre}{}{post}",
args2.iter().map(|a| a.to_string()).collect::<Vec<_>>().join(", ")
));
return values;
}
if args1.len() > 0 {
values.0.push_normal("<");
values.1.push_normal("<");
}
for (i, (a, b)) in std::iter::zip(args1, args2).enumerate() {
let a_str = a.to_string();
let b_str = b.to_string();
if let (Some(a), Some(b)) = (a.as_type(), b.as_type()) {
let (a, b) = self.cmp(a, b);
values.0.0.extend(a.0);
values.1.0.extend(b.0);
} else if a_str != b_str {
values.0.push_highlighted(a_str);
values.1.push_highlighted(b_str);
} else {
values.0.push_normal(a_str);
values.1.push_normal(b_str);
}
if i + 1 < args1.len() {
values.0.push_normal(", ");
values.1.push_normal(", ");
}
}
if args1.len() > 0 {
values.0.push_normal(">");
values.1.push_normal(">");
}
values
}
/// Compares two given types, eliding parts that are the same between them and highlighting
/// relevant differences, and return two representation of those types for highlighted printing.
pub fn cmp(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) -> (DiagStyledString, DiagStyledString) {

View file

@ -1832,21 +1832,63 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
if impl_trait_ref.references_error() {
return false;
}
let self_ty = impl_trait_ref.self_ty().to_string();
err.highlighted_help(vec![
StringPart::normal(format!(
"the trait `{}` ",
impl_trait_ref.print_trait_sugared()
)),
StringPart::highlighted("is"),
let traits = self.cmp_traits(
obligation_trait_ref.def_id,
&obligation_trait_ref.args[1..],
impl_trait_ref.def_id,
&impl_trait_ref.args[1..],
);
let traits_content = (traits.0.content(), traits.1.content());
let types = self.cmp(obligation_trait_ref.self_ty(), impl_trait_ref.self_ty());
let types_content = (types.0.content(), types.1.content());
let mut msg = vec![StringPart::normal("the trait `")];
if traits_content.0 == traits_content.1 {
msg.push(StringPart::normal(
impl_trait_ref.print_trait_sugared().to_string(),
));
} else {
msg.extend(traits.0.0);
}
msg.extend([
StringPart::normal("` "),
StringPart::highlighted("is not"),
StringPart::normal(" implemented for `"),
if let [TypeError::Sorts(_)] = &terrs[..] {
StringPart::normal(self_ty)
} else {
StringPart::highlighted(self_ty)
},
StringPart::normal("`"),
]);
if types_content.0 == types_content.1 {
msg.push(StringPart::normal(obligation_trait_ref.self_ty().to_string()));
} else {
msg.extend(types.0.0);
}
msg.push(StringPart::normal("`"));
if types_content.0 == types_content.1 {
msg.push(StringPart::normal("\nbut trait `"));
msg.extend(traits.1.0);
msg.extend([
StringPart::normal("` "),
StringPart::highlighted("is"),
StringPart::normal(" implemented for it"),
]);
} else if traits_content.0 == traits_content.1 {
msg.extend([
StringPart::normal("\nbut it "),
StringPart::highlighted("is"),
StringPart::normal(" implemented for `"),
]);
msg.extend(types.1.0);
msg.push(StringPart::normal("`"));
} else {
msg.push(StringPart::normal("\nbut trait `"));
msg.extend(traits.1.0);
msg.extend([
StringPart::normal("` "),
StringPart::highlighted("is"),
StringPart::normal(" implemented for `"),
]);
msg.extend(types.1.0);
msg.push(StringPart::normal("`"));
}
err.highlighted_help(msg);
if let [TypeError::Sorts(exp_found)] = &terrs[..] {
let exp_found = self.resolve_vars_if_possible(*exp_found);