Skip to content

Commit 98e7b2d

Browse files
committed
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.
1 parent 5980a32 commit 98e7b2d

File tree

40 files changed

+251
-86
lines changed

40 files changed

+251
-86
lines changed

compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,67 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
765765
values
766766
}
767767

768+
pub fn cmp_traits(
769+
&self,
770+
def_id1: DefId,
771+
args1: &[ty::GenericArg<'tcx>],
772+
def_id2: DefId,
773+
args2: &[ty::GenericArg<'tcx>],
774+
) -> (DiagStyledString, DiagStyledString) {
775+
let mut values = (DiagStyledString::new(), DiagStyledString::new());
776+
777+
if def_id1 != def_id2 {
778+
values.0.push_highlighted(self.tcx.def_path_str(def_id1).as_str());
779+
values.1.push_highlighted(self.tcx.def_path_str(def_id2).as_str());
780+
} else {
781+
values.0.push_normal(self.tcx.item_name(def_id1).as_str());
782+
values.1.push_normal(self.tcx.item_name(def_id2).as_str());
783+
}
784+
785+
if args1.len() != args2.len() {
786+
let (pre, post) = if args1.len() > 0 { ("<", ">") } else { ("", "") };
787+
values.0.push_normal(format!(
788+
"{pre}{}{post}",
789+
args1.iter().map(|a| a.to_string()).collect::<Vec<_>>().join(", ")
790+
));
791+
let (pre, post) = if args2.len() > 0 { ("<", ">") } else { ("", "") };
792+
values.1.push_normal(format!(
793+
"{pre}{}{post}",
794+
args2.iter().map(|a| a.to_string()).collect::<Vec<_>>().join(", ")
795+
));
796+
return values;
797+
}
798+
799+
if args1.len() > 0 {
800+
values.0.push_normal("<");
801+
values.1.push_normal("<");
802+
}
803+
for (i, (a, b)) in std::iter::zip(args1, args2).enumerate() {
804+
let a_str = a.to_string();
805+
let b_str = b.to_string();
806+
if let (Some(a), Some(b)) = (a.as_type(), b.as_type()) {
807+
let (a, b) = self.cmp(a, b);
808+
values.0.0.extend(a.0);
809+
values.1.0.extend(b.0);
810+
} else if a_str != b_str {
811+
values.0.push_highlighted(a_str);
812+
values.1.push_highlighted(b_str);
813+
} else {
814+
values.0.push_normal(a_str);
815+
values.1.push_normal(b_str);
816+
}
817+
if i + 1 < args1.len() {
818+
values.0.push_normal(", ");
819+
values.1.push_normal(", ");
820+
}
821+
}
822+
if args1.len() > 0 {
823+
values.0.push_normal(">");
824+
values.1.push_normal(">");
825+
}
826+
values
827+
}
828+
768829
/// Compares two given types, eliding parts that are the same between them and highlighting
769830
/// relevant differences, and return two representation of those types for highlighted printing.
770831
pub fn cmp(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) -> (DiagStyledString, DiagStyledString) {

compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1835,21 +1835,63 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
18351835
if impl_trait_ref.references_error() {
18361836
return false;
18371837
}
1838-
let self_ty = impl_trait_ref.self_ty().to_string();
1839-
err.highlighted_help(vec![
1840-
StringPart::normal(format!(
1841-
"the trait `{}` ",
1842-
impl_trait_ref.print_trait_sugared()
1843-
)),
1844-
StringPart::highlighted("is"),
1838+
1839+
let traits = self.cmp_traits(
1840+
obligation_trait_ref.def_id,
1841+
&obligation_trait_ref.args[1..],
1842+
impl_trait_ref.def_id,
1843+
&impl_trait_ref.args[1..],
1844+
);
1845+
let traits_content = (traits.0.content(), traits.1.content());
1846+
let types = self.cmp(obligation_trait_ref.self_ty(), impl_trait_ref.self_ty());
1847+
let types_content = (types.0.content(), types.1.content());
1848+
let mut msg = vec![StringPart::normal("the trait `")];
1849+
if traits_content.0 == traits_content.1 {
1850+
msg.push(StringPart::normal(
1851+
impl_trait_ref.print_trait_sugared().to_string(),
1852+
));
1853+
} else {
1854+
msg.extend(traits.0.0);
1855+
}
1856+
msg.extend([
1857+
StringPart::normal("` "),
1858+
StringPart::highlighted("is not"),
18451859
StringPart::normal(" implemented for `"),
1846-
if let [TypeError::Sorts(_)] = &terrs[..] {
1847-
StringPart::normal(self_ty)
1848-
} else {
1849-
StringPart::highlighted(self_ty)
1850-
},
1851-
StringPart::normal("`"),
18521860
]);
1861+
if types_content.0 == types_content.1 {
1862+
msg.push(StringPart::normal(obligation_trait_ref.self_ty().to_string()));
1863+
} else {
1864+
msg.extend(types.0.0);
1865+
}
1866+
msg.push(StringPart::normal("`"));
1867+
if types_content.0 == types_content.1 {
1868+
msg.push(StringPart::normal("\nbut trait `"));
1869+
msg.extend(traits.1.0);
1870+
msg.extend([
1871+
StringPart::normal("` "),
1872+
StringPart::highlighted("is"),
1873+
StringPart::normal(" implemented for it"),
1874+
]);
1875+
} else if traits_content.0 == traits_content.1 {
1876+
msg.extend([
1877+
StringPart::normal("\nbut it "),
1878+
StringPart::highlighted("is"),
1879+
StringPart::normal(" implemented for `"),
1880+
]);
1881+
msg.extend(types.1.0);
1882+
msg.push(StringPart::normal("`"));
1883+
} else {
1884+
msg.push(StringPart::normal("\nbut trait `"));
1885+
msg.extend(traits.1.0);
1886+
msg.extend([
1887+
StringPart::normal("` "),
1888+
StringPart::highlighted("is"),
1889+
StringPart::normal(" implemented for `"),
1890+
]);
1891+
msg.extend(types.1.0);
1892+
msg.push(StringPart::normal("`"));
1893+
}
1894+
err.highlighted_help(msg);
18531895

18541896
if let [TypeError::Sorts(exp_found)] = &terrs[..] {
18551897
let exp_found = self.resolve_vars_if_possible(*exp_found);

tests/ui/const-generics/associated-type-bound-fail.stderr

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ error[E0277]: the trait bound `u16: Bar<N>` is not satisfied
44
LL | type Assoc = u16;
55
| ^^^ the trait `Bar<N>` is not implemented for `u16`
66
|
7-
= help: the trait `Bar<3>` is implemented for `u16`
7+
= help: the trait `Bar<N>` is not implemented for `u16`
8+
but trait `Bar<3>` is implemented for it
89
note: required by a bound in `Foo::Assoc`
910
--> $DIR/associated-type-bound-fail.rs:4:17
1011
|

tests/ui/const-generics/defaults/rp_impl_trait_fail.stderr

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ LL |
1818
LL | 1_u32
1919
| ----- return type was inferred to be `u32` here
2020
|
21-
= help: the trait `Traitor<N, 2>` is implemented for `u32`
21+
= help: the trait `Traitor<N, N>` is not implemented for `u32`
22+
but trait `Traitor<N, 2>` is implemented for it
2223

2324
error[E0277]: the trait bound `u64: Traitor` is not satisfied
2425
--> $DIR/rp_impl_trait_fail.rs:21:13
@@ -29,7 +30,8 @@ LL |
2930
LL | 1_u64
3031
| ----- return type was inferred to be `u64` here
3132
|
32-
= help: the trait `Traitor<1, 2>` is implemented for `u64`
33+
= help: the trait `Traitor<1, 1>` is not implemented for `u64`
34+
but trait `Traitor<1, 2>` is implemented for it
3335

3436
error[E0284]: type annotations needed
3537
--> $DIR/rp_impl_trait_fail.rs:28:5

tests/ui/const-generics/defaults/trait_objects_fail.stderr

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ error[E0277]: the trait bound `u32: Trait` is not satisfied
44
LL | foo(&10_u32);
55
| ^^^^^^^ the trait `Trait` is not implemented for `u32`
66
|
7-
= help: the trait `Trait<2>` is implemented for `u32`
7+
= help: the trait `Trait<12>` is not implemented for `u32`
8+
but trait `Trait<2>` is implemented for it
89
= note: required for the cast from `&u32` to `&dyn Trait`
910

1011
error[E0277]: the trait bound `bool: Traitor<_>` is not satisfied
@@ -13,7 +14,8 @@ error[E0277]: the trait bound `bool: Traitor<_>` is not satisfied
1314
LL | bar(&true);
1415
| ^^^^^ the trait `Traitor<_>` is not implemented for `bool`
1516
|
16-
= help: the trait `Traitor<2, 3>` is implemented for `bool`
17+
= help: the trait `Traitor<_, _>` is not implemented for `bool`
18+
but trait `Traitor<2, 3>` is implemented for it
1719
= note: required for the cast from `&bool` to `&dyn Traitor<_>`
1820

1921
error: aborting due to 2 previous errors

tests/ui/const-generics/defaults/wfness.stderr

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,17 @@ error[E0277]: the trait bound `(): Trait<2>` is not satisfied
1010
LL | (): Trait<N>;
1111
| ^^^^^^^^ the trait `Trait<2>` is not implemented for `()`
1212
|
13-
= help: the trait `Trait<3>` is implemented for `()`
13+
= help: the trait `Trait<2>` is not implemented for `()`
14+
but trait `Trait<3>` is implemented for it
1415

1516
error[E0277]: the trait bound `(): Trait<1>` is not satisfied
1617
--> $DIR/wfness.rs:18:13
1718
|
1819
LL | fn foo() -> DependentDefaultWfness {
1920
| ^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait<1>` is not implemented for `()`
2021
|
21-
= help: the trait `Trait<3>` is implemented for `()`
22+
= help: the trait `Trait<1>` is not implemented for `()`
23+
but trait `Trait<3>` is implemented for it
2224
note: required by a bound in `WhereClause`
2325
--> $DIR/wfness.rs:8:9
2426
|

tests/ui/const-generics/occurs-check/unused-substs-1.stderr

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ error[E0277]: the trait bound `A<_>: Bar<_>` is not satisfied
44
LL | let _ = A;
55
| ^ the trait `Bar<_>` is not implemented for `A<_>`
66
|
7-
= help: the trait `Bar<_>` is implemented for `A<{ 6 + 1 }>`
7+
= help: the trait `Bar<_>` is not implemented for `A<_>`
8+
but it is implemented for `A<{ 6 + 1 }>`
89
note: required by a bound in `A`
910
--> $DIR/unused-substs-1.rs:9:11
1011
|

tests/ui/diagnostic_namespace/do_not_recommend/as_expression.current.stderr

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ error[E0277]: the trait bound `&str: AsExpression<Integer>` is not satisfied
44
LL | SelectInt.check("bar");
55
| ^^^^^ the trait `AsExpression<Integer>` is not implemented for `&str`
66
|
7-
= help: the trait `AsExpression<Text>` is implemented for `&str`
7+
= help: the trait `AsExpression<Integer>` is not implemented for `&str`
8+
but trait `AsExpression<Text>` is implemented for it
89
= help: for that trait implementation, expected `Text`, found `Integer`
910

1011
error: aborting due to 1 previous error

tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ error[E0277]: the trait bound `&str: AsExpression<Integer>` is not satisfied
2222
LL | SelectInt.check("bar");
2323
| ^^^^^ the trait `AsExpression<Integer>` is not implemented for `&str`
2424
|
25-
= help: the trait `AsExpression<Text>` is implemented for `&str`
25+
= help: the trait `AsExpression<Integer>` is not implemented for `&str`
26+
but trait `AsExpression<Text>` is implemented for it
2627
= help: for that trait implementation, expected `Text`, found `Integer`
2728

2829
error[E0271]: type mismatch resolving `<SelectInt as Expression>::SqlType == Text`

tests/ui/generic-const-items/unsatisfied-bounds.stderr

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ error[E0277]: the trait bound `Infallible: From<()>` is not satisfied
1616
LL | let () = K::<()>;
1717
| ^^ the trait `From<()>` is not implemented for `Infallible`
1818
|
19-
= help: the trait `From<!>` is implemented for `Infallible`
19+
= help: the trait `From<()>` is not implemented for `Infallible`
20+
but trait `From<!>` is implemented for it
2021
= help: for that trait implementation, expected `!`, found `()`
2122
note: required by a bound in `K`
2223
--> $DIR/unsatisfied-bounds.rs:12:17
@@ -48,7 +49,8 @@ error[E0277]: the trait bound `Infallible: From<()>` is not satisfied
4849
LL | let _ = <() as Trait<&'static str>>::B::<()>;
4950
| ^^ the trait `From<()>` is not implemented for `Infallible`
5051
|
51-
= help: the trait `From<!>` is implemented for `Infallible`
52+
= help: the trait `From<()>` is not implemented for `Infallible`
53+
but trait `From<!>` is implemented for it
5254
= help: for that trait implementation, expected `!`, found `()`
5355
note: required by a bound in `Trait::B`
5456
--> $DIR/unsatisfied-bounds.rs:21:21

0 commit comments

Comments
 (0)