Skip to content

Commit df81ccb

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 9562fd1 commit df81ccb

40 files changed

+251
-86
lines changed

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

+61
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

+55-13
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

+2-1
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

+4-2
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

+4-2
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

+4-2
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

+2-1
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

+2-1
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

+2-1
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

+4-2
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

tests/ui/impl-trait/diagnostics/highlight-difference-between-expected-trait-and-found-trait.svg

+15-13
Loading

tests/ui/impl-trait/in-trait/return-dont-satisfy-bounds.stderr

+2-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ LL | fn foo<F2: Foo<u8>>(self) -> impl Foo<u8> {
3232
LL | self
3333
| ---- return type was inferred to be `Bar` here
3434
|
35-
= help: the trait `Foo<char>` is implemented for `Bar`
35+
= help: the trait `Foo<u8>` is not implemented for `Bar`
36+
but trait `Foo<char>` is implemented for it
3637
= help: for that trait implementation, expected `char`, found `u8`
3738

3839
error: aborting due to 3 previous errors

tests/ui/impl-trait/issues/issue-62742.stderr

+4-2
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ error[E0277]: the trait bound `RawImpl<_>: Raw<_>` is not satisfied
2929
LL | WrongImpl::foo(0i32);
3030
| ^^^^^^^^^ the trait `Raw<_>` is not implemented for `RawImpl<_>`
3131
|
32-
= help: the trait `Raw<[_]>` is implemented for `RawImpl<_>`
32+
= help: the trait `Raw<_>` is not implemented for `RawImpl<_>`
33+
but trait `Raw<[_]>` is implemented for it
3334
note: required by a bound in `SafeImpl`
3435
--> $DIR/issue-62742.rs:33:35
3536
|
@@ -67,7 +68,8 @@ error[E0277]: the trait bound `RawImpl<()>: Raw<()>` is not satisfied
6768
LL | WrongImpl::<()>::foo(0i32);
6869
| ^^^^^^^^^^^^^^^ the trait `Raw<()>` is not implemented for `RawImpl<()>`
6970
|
70-
= help: the trait `Raw<[()]>` is implemented for `RawImpl<()>`
71+
= help: the trait `Raw<()>` is not implemented for `RawImpl<()>`
72+
but trait `Raw<[()]>` is implemented for it
7173
= help: for that trait implementation, expected `[()]`, found `()`
7274
note: required by a bound in `SafeImpl`
7375
--> $DIR/issue-62742.rs:33:35

tests/ui/impl-trait/nested-rpit-hrtb.stderr

+4-2
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ error[E0277]: the trait bound `for<'a> &'a (): Qux<'b>` is not satisfied
8383
LL | fn one_hrtb_mention_fn_trait_param_uses<'b>() -> impl for<'a> Bar<'a, Assoc = impl Qux<'b>> {}
8484
| ^^^^^^^^^^^^ the trait `for<'a> Qux<'b>` is not implemented for `&'a ()`
8585
|
86-
= help: the trait `Qux<'_>` is implemented for `()`
86+
= help: the trait `Qux<'b>` is not implemented for `&'a ()`
87+
but trait `Qux<'_>` is implemented for `()`
8788
= help: for that trait implementation, expected `()`, found `&'a ()`
8889

8990
error: implementation of `Bar` is not general enough
@@ -101,7 +102,8 @@ error[E0277]: the trait bound `for<'a, 'b> &'a (): Qux<'b>` is not satisfied
101102
LL | fn two_htrb_trait_param_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b> Qux<'b>> {}
102103
| ^^^^^^^^^^^^^^^^^^^^ the trait `for<'a, 'b> Qux<'b>` is not implemented for `&'a ()`
103104
|
104-
= help: the trait `Qux<'_>` is implemented for `()`
105+
= help: the trait `Qux<'b>` is not implemented for `&'a ()`
106+
but trait `Qux<'_>` is implemented for `()`
105107
= help: for that trait implementation, expected `()`, found `&'a ()`
106108

107109
error: aborting due to 9 previous errors

tests/ui/indexing/index-help.stderr

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ LL | x[0i32];
55
| ^^^^ slice indices are of type `usize` or ranges of `usize`
66
|
77
= help: the trait `SliceIndex<[{integer}]>` is not implemented for `i32`
8-
= help: the trait `SliceIndex<[{integer}]>` is implemented for `usize`
8+
= help: the trait `SliceIndex<[{integer}]>` is not implemented for `i32`
9+
but it is implemented for `usize`
910
= help: for that trait implementation, expected `usize`, found `i32`
1011
= note: required for `Vec<{integer}>` to implement `Index<i32>`
1112

0 commit comments

Comments
 (0)