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:
parent
7c7bb7dc01
commit
b7fc1a7431
40 changed files with 251 additions and 86 deletions
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue