Skip to content

Commit b7fc1a7

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 7c7bb7d commit b7fc1a7

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
@@ -766,6 +766,67 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
766766
values
767767
}
768768

769+
pub fn cmp_traits(
770+
&self,
771+
def_id1: DefId,
772+
args1: &[ty::GenericArg<'tcx>],
773+
def_id2: DefId,
774+
args2: &[ty::GenericArg<'tcx>],
775+
) -> (DiagStyledString, DiagStyledString) {
776+
let mut values = (DiagStyledString::new(), DiagStyledString::new());
777+
778+
if def_id1 != def_id2 {
779+
values.0.push_highlighted(self.tcx.def_path_str(def_id1).as_str());
780+
values.1.push_highlighted(self.tcx.def_path_str(def_id2).as_str());
781+
} else {
782+
values.0.push_normal(self.tcx.item_name(def_id1).as_str());
783+
values.1.push_normal(self.tcx.item_name(def_id2).as_str());
784+
}
785+
786+
if args1.len() != args2.len() {
787+
let (pre, post) = if args1.len() > 0 { ("<", ">") } else { ("", "") };
788+
values.0.push_normal(format!(
789+
"{pre}{}{post}",
790+
args1.iter().map(|a| a.to_string()).collect::<Vec<_>>().join(", ")
791+
));
792+
let (pre, post) = if args2.len() > 0 { ("<", ">") } else { ("", "") };
793+
values.1.push_normal(format!(
794+
"{pre}{}{post}",
795+
args2.iter().map(|a| a.to_string()).collect::<Vec<_>>().join(", ")
796+
));
797+
return values;
798+
}
799+
800+
if args1.len() > 0 {
801+
values.0.push_normal("<");
802+
values.1.push_normal("<");
803+
}
804+
for (i, (a, b)) in std::iter::zip(args1, args2).enumerate() {
805+
let a_str = a.to_string();
806+
let b_str = b.to_string();
807+
if let (Some(a), Some(b)) = (a.as_type(), b.as_type()) {
808+
let (a, b) = self.cmp(a, b);
809+
values.0.0.extend(a.0);
810+
values.1.0.extend(b.0);
811+
} else if a_str != b_str {
812+
values.0.push_highlighted(a_str);
813+
values.1.push_highlighted(b_str);
814+
} else {
815+
values.0.push_normal(a_str);
816+
values.1.push_normal(b_str);
817+
}
818+
if i + 1 < args1.len() {
819+
values.0.push_normal(", ");
820+
values.1.push_normal(", ");
821+
}
822+
}
823+
if args1.len() > 0 {
824+
values.0.push_normal(">");
825+
values.1.push_normal(">");
826+
}
827+
values
828+
}
829+
769830
/// Compares two given types, eliding parts that are the same between them and highlighting
770831
/// relevant differences, and return two representation of those types for highlighted printing.
771832
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
@@ -1832,21 +1832,63 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
18321832
if impl_trait_ref.references_error() {
18331833
return false;
18341834
}
1835-
let self_ty = impl_trait_ref.self_ty().to_string();
1836-
err.highlighted_help(vec![
1837-
StringPart::normal(format!(
1838-
"the trait `{}` ",
1839-
impl_trait_ref.print_trait_sugared()
1840-
)),
1841-
StringPart::highlighted("is"),
1835+
1836+
let traits = self.cmp_traits(
1837+
obligation_trait_ref.def_id,
1838+
&obligation_trait_ref.args[1..],
1839+
impl_trait_ref.def_id,
1840+
&impl_trait_ref.args[1..],
1841+
);
1842+
let traits_content = (traits.0.content(), traits.1.content());
1843+
let types = self.cmp(obligation_trait_ref.self_ty(), impl_trait_ref.self_ty());
1844+
let types_content = (types.0.content(), types.1.content());
1845+
let mut msg = vec![StringPart::normal("the trait `")];
1846+
if traits_content.0 == traits_content.1 {
1847+
msg.push(StringPart::normal(
1848+
impl_trait_ref.print_trait_sugared().to_string(),
1849+
));
1850+
} else {
1851+
msg.extend(traits.0.0);
1852+
}
1853+
msg.extend([
1854+
StringPart::normal("` "),
1855+
StringPart::highlighted("is not"),
18421856
StringPart::normal(" implemented for `"),
1843-
if let [TypeError::Sorts(_)] = &terrs[..] {
1844-
StringPart::normal(self_ty)
1845-
} else {
1846-
StringPart::highlighted(self_ty)
1847-
},
1848-
StringPart::normal("`"),
18491857
]);
1858+
if types_content.0 == types_content.1 {
1859+
msg.push(StringPart::normal(obligation_trait_ref.self_ty().to_string()));
1860+
} else {
1861+
msg.extend(types.0.0);
1862+
}
1863+
msg.push(StringPart::normal("`"));
1864+
if types_content.0 == types_content.1 {
1865+
msg.push(StringPart::normal("\nbut trait `"));
1866+
msg.extend(traits.1.0);
1867+
msg.extend([
1868+
StringPart::normal("` "),
1869+
StringPart::highlighted("is"),
1870+
StringPart::normal(" implemented for it"),
1871+
]);
1872+
} else if traits_content.0 == traits_content.1 {
1873+
msg.extend([
1874+
StringPart::normal("\nbut it "),
1875+
StringPart::highlighted("is"),
1876+
StringPart::normal(" implemented for `"),
1877+
]);
1878+
msg.extend(types.1.0);
1879+
msg.push(StringPart::normal("`"));
1880+
} else {
1881+
msg.push(StringPart::normal("\nbut trait `"));
1882+
msg.extend(traits.1.0);
1883+
msg.extend([
1884+
StringPart::normal("` "),
1885+
StringPart::highlighted("is"),
1886+
StringPart::normal(" implemented for `"),
1887+
]);
1888+
msg.extend(types.1.0);
1889+
msg.push(StringPart::normal("`"));
1890+
}
1891+
err.highlighted_help(msg);
18501892

18511893
if let [TypeError::Sorts(exp_found)] = &terrs[..] {
18521894
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

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

Lines changed: 15 additions & 13 deletions
Loading

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

Lines changed: 2 additions & 1 deletion
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

Lines changed: 4 additions & 2 deletions
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

Lines changed: 4 additions & 2 deletions
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

Lines changed: 2 additions & 1 deletion
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)