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

0 commit comments

Comments
 (0)