about summary refs log tree commit diff
diff options
context:
space:
mode:
authorGeorg Semmler <github@weiznich.de>2024-05-04 14:26:24 +0200
committerGeorg Semmler <github@weiznich.de>2024-05-19 08:29:27 +0200
commit9b45cfdbdde020682d167ffd9f512fd8401d8cfe (patch)
tree2a40ffc0d41a681900e75d00ebeeff8a554e261f
parentbfa3635df920454cdf03f6d268dfaac769375df3 (diff)
downloadrust-9b45cfdbdde020682d167ffd9f512fd8401d8cfe.tar.gz
rust-9b45cfdbdde020682d167ffd9f512fd8401d8cfe.zip
Actually use the `#[do_not_recommend]` attribute if present
This change tweaks the error message generation to actually use the
`#[do_not_recommend]` attribute if present by just skipping the marked
trait impl in favour of the parent impl. It also adds a compile test for
this behaviour. Without this change the test would output the following
error:

```
error[E0277]: the trait bound `&str: Expression` is not satisfied
  --> /home/weiznich/Documents/rust/rust/tests/ui/diagnostic_namespace/do_not_recommend.rs:53:15
   |
LL |     SelectInt.check("bar");
   |               ^^^^^ the trait `Expression` is not implemented for `&str`, which is required by `&str: AsExpression<Integer>`
   |
   = help: the following other types implement trait `Expression`:
             Bound<T>
             SelectInt
note: required for `&str` to implement `AsExpression<Integer>`
  --> /home/weiznich/Documents/rust/rust/tests/ui/diagnostic_namespace/do_not_recommend.rs:26:13
   |
LL | impl<T, ST> AsExpression<ST> for T
   |             ^^^^^^^^^^^^^^^^     ^
LL | where
LL |     T: Expression<SqlType = ST>,
   |        ------------------------ unsatisfied trait bound introduced here
```

Note how that mentions `&str: Expression` before and now mentions `&str:
AsExpression<Integer>` instead which is much more helpful for users.

Open points for further changes before stabilization:

* We likely want to move the attribute to the `#[diagnostic]` namespace
to relax the guarantees given?
* How does it interact with the new trait solver?
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs26
-rw-r--r--tests/ui/diagnostic_namespace/do_not_recommend/as_expression.current.stderr12
-rw-r--r--tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr21
-rw-r--r--tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs60
-rw-r--r--tests/ui/diagnostic_namespace/do_not_recommend/simple.current.stderr11
-rw-r--r--tests/ui/diagnostic_namespace/do_not_recommend/simple.next.stderr4
-rw-r--r--tests/ui/diagnostic_namespace/do_not_recommend/simple.rs5
-rw-r--r--tests/ui/diagnostic_namespace/do_not_recommend/stacked.current.stderr15
-rw-r--r--tests/ui/diagnostic_namespace/do_not_recommend/stacked.next.stderr15
-rw-r--r--tests/ui/diagnostic_namespace/do_not_recommend/stacked.rs21
-rw-r--r--tests/ui/rfcs/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.stderr12
11 files changed, 177 insertions, 25 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
index d693bac90dc..20dfb5e6642 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
@@ -422,6 +422,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_predicate)) => {
                         let trait_predicate = bound_predicate.rebind(trait_predicate);
                         let trait_predicate = self.resolve_vars_if_possible(trait_predicate);
+                        let trait_predicate = self.apply_do_not_recommend(trait_predicate, &mut obligation);
 
                         // Let's use the root obligation as the main message, when we care about the
                         // most general case ("X doesn't implement Pattern<'_>") over the case that
@@ -1003,6 +1004,31 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         err.emit()
     }
 
+    fn apply_do_not_recommend(
+        &self,
+        mut trait_predicate: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>,
+        obligation: &'_ mut PredicateObligation<'tcx>,
+    ) -> ty::Binder<'tcx, ty::TraitPredicate<'tcx>> {
+        let mut base_cause = obligation.cause.code().clone();
+        loop {
+            if let ObligationCauseCode::ImplDerived(ref c) = base_cause {
+                if self.tcx.has_attr(c.impl_or_alias_def_id, sym::do_not_recommend) {
+                    let code = (*c.derived.parent_code).clone();
+                    obligation.cause.map_code(|_| code);
+                    obligation.predicate = c.derived.parent_trait_pred.upcast(self.tcx);
+                    trait_predicate = c.derived.parent_trait_pred.clone();
+                }
+            }
+            if let Some((parent_cause, _parent_pred)) = base_cause.parent() {
+                base_cause = parent_cause.clone();
+            } else {
+                break;
+            }
+        }
+
+        trait_predicate
+    }
+
     fn emit_specialized_closure_kind_error(
         &self,
         obligation: &PredicateObligation<'tcx>,
diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.current.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.current.stderr
new file mode 100644
index 00000000000..41f222e46a7
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.current.stderr
@@ -0,0 +1,12 @@
+error[E0277]: the trait bound `&str: AsExpression<Integer>` is not satisfied
+  --> $DIR/as_expression.rs:57:15
+   |
+LL |     SelectInt.check("bar");
+   |               ^^^^^ the trait `AsExpression<Integer>` is not implemented for `&str`
+   |
+   = help: the trait `AsExpression<Text>` is implemented for `&str`
+   = help: for that trait implementation, expected `Text`, found `Integer`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr
new file mode 100644
index 00000000000..47acf5b968b
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr
@@ -0,0 +1,21 @@
+error[E0277]: the trait bound `&str: AsExpression<<SelectInt as Expression>::SqlType>` is not satisfied
+  --> $DIR/as_expression.rs:57:21
+   |
+LL |     SelectInt.check("bar");
+   |               ----- ^^^^^ the trait `AsExpression<<SelectInt as Expression>::SqlType>` is not implemented for `&str`
+   |               |
+   |               required by a bound introduced by this call
+   |
+   = help: the trait `AsExpression<Text>` is implemented for `&str`
+note: required by a bound in `Foo::check`
+  --> $DIR/as_expression.rs:48:12
+   |
+LL |     fn check<T>(&self, _: T) -> <T as AsExpression<<Self as Expression>::SqlType>>::Expression
+   |        ----- required by a bound in this associated function
+LL |     where
+LL |         T: AsExpression<Self::SqlType>,
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Foo::check`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs
new file mode 100644
index 00000000000..9cdc193d386
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs
@@ -0,0 +1,60 @@
+//@ revisions: current next
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@[next] compile-flags: -Znext-solver
+
+#![feature(do_not_recommend)]
+
+pub trait Expression {
+    type SqlType;
+}
+
+pub trait AsExpression<ST> {
+    type Expression: Expression<SqlType = ST>;
+}
+
+pub struct Text;
+pub struct Integer;
+
+pub struct Bound<T>(T);
+pub struct SelectInt;
+
+impl Expression for SelectInt {
+    type SqlType = Integer;
+}
+
+impl<T> Expression for Bound<T> {
+    type SqlType = T;
+}
+
+#[do_not_recommend]
+impl<T, ST> AsExpression<ST> for T
+where
+    T: Expression<SqlType = ST>,
+{
+    type Expression = T;
+}
+
+impl AsExpression<Integer> for i32 {
+    type Expression = Bound<Integer>;
+}
+
+impl AsExpression<Text> for &'_ str {
+    type Expression = Bound<Text>;
+}
+
+trait Foo: Expression + Sized {
+    fn check<T>(&self, _: T) -> <T as AsExpression<<Self as Expression>::SqlType>>::Expression
+    where
+        T: AsExpression<Self::SqlType>,
+    {
+        todo!()
+    }
+}
+
+impl<T> Foo for T where T: Expression {}
+
+fn main() {
+    SelectInt.check("bar");
+    //[next]~^ ERROR the trait bound `&str: AsExpression<<SelectInt as Expression>::SqlType>` is not satisfied
+    //[current]~^^ ERROR the trait bound `&str: AsExpression<Integer>` is not satisfied
+}
diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/simple.current.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/simple.current.stderr
index a4d4b7b359e..729cb5694e2 100644
--- a/tests/ui/diagnostic_namespace/do_not_recommend/simple.current.stderr
+++ b/tests/ui/diagnostic_namespace/do_not_recommend/simple.current.stderr
@@ -1,16 +1,11 @@
 error[E0277]: the trait bound `*mut (): Foo` is not satisfied
-  --> $DIR/simple.rs:19:17
+  --> $DIR/simple.rs:17:17
    |
 LL |     needs_foo::<*mut ()>();
-   |                 ^^^^^^^ the trait `Send` is not implemented for `*mut ()`, which is required by `*mut (): Foo`
+   |                 ^^^^^^^ the trait `Foo` is not implemented for `*mut ()`
    |
-note: required for `*mut ()` to implement `Foo`
-  --> $DIR/simple.rs:10:9
-   |
-LL | impl<T> Foo for T where T: Send {}
-   |         ^^^     ^          ---- unsatisfied trait bound introduced here
 note: required by a bound in `needs_foo`
-  --> $DIR/simple.rs:14:17
+  --> $DIR/simple.rs:12:17
    |
 LL | fn needs_foo<T: Foo>() {}
    |                 ^^^ required by this bound in `needs_foo`
diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/simple.next.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/simple.next.stderr
index 1341ca8175a..729cb5694e2 100644
--- a/tests/ui/diagnostic_namespace/do_not_recommend/simple.next.stderr
+++ b/tests/ui/diagnostic_namespace/do_not_recommend/simple.next.stderr
@@ -1,11 +1,11 @@
 error[E0277]: the trait bound `*mut (): Foo` is not satisfied
-  --> $DIR/simple.rs:19:17
+  --> $DIR/simple.rs:17:17
    |
 LL |     needs_foo::<*mut ()>();
    |                 ^^^^^^^ the trait `Foo` is not implemented for `*mut ()`
    |
 note: required by a bound in `needs_foo`
-  --> $DIR/simple.rs:14:17
+  --> $DIR/simple.rs:12:17
    |
 LL | fn needs_foo<T: Foo>() {}
    |                 ^^^ required by this bound in `needs_foo`
diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/simple.rs b/tests/ui/diagnostic_namespace/do_not_recommend/simple.rs
index 15ff80ae4d9..6fb15b90138 100644
--- a/tests/ui/diagnostic_namespace/do_not_recommend/simple.rs
+++ b/tests/ui/diagnostic_namespace/do_not_recommend/simple.rs
@@ -8,8 +8,6 @@ trait Foo {}
 
 #[do_not_recommend]
 impl<T> Foo for T where T: Send {}
-//[current]~^ NOTE required for `*mut ()` to implement `Foo`
-//[current]~| NOTE unsatisfied trait bound introduced here
 
 fn needs_foo<T: Foo>() {}
 //~^ NOTE required by a bound in `needs_foo`
@@ -18,6 +16,5 @@ fn needs_foo<T: Foo>() {}
 fn main() {
     needs_foo::<*mut ()>();
     //~^ ERROR the trait bound `*mut (): Foo` is not satisfied
-    //[current]~| NOTE the trait `Send` is not implemented for `*mut ()`
-    //[next]~| NOTE the trait `Foo` is not implemented for `*mut ()`
+    //~| NOTE the trait `Foo` is not implemented for `*mut ()`
 }
diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/stacked.current.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/stacked.current.stderr
new file mode 100644
index 00000000000..41a10a61e1d
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/do_not_recommend/stacked.current.stderr
@@ -0,0 +1,15 @@
+error[E0277]: the trait bound `(): Root` is not satisfied
+  --> $DIR/stacked.rs:19:18
+   |
+LL |     needs_root::<()>();
+   |                  ^^ the trait `Root` is not implemented for `()`
+   |
+note: required by a bound in `needs_root`
+  --> $DIR/stacked.rs:16:18
+   |
+LL | fn needs_root<T: Root>() {}
+   |                  ^^^^ required by this bound in `needs_root`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/stacked.next.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/stacked.next.stderr
new file mode 100644
index 00000000000..41a10a61e1d
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/do_not_recommend/stacked.next.stderr
@@ -0,0 +1,15 @@
+error[E0277]: the trait bound `(): Root` is not satisfied
+  --> $DIR/stacked.rs:19:18
+   |
+LL |     needs_root::<()>();
+   |                  ^^ the trait `Root` is not implemented for `()`
+   |
+note: required by a bound in `needs_root`
+  --> $DIR/stacked.rs:16:18
+   |
+LL | fn needs_root<T: Root>() {}
+   |                  ^^^^ required by this bound in `needs_root`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/stacked.rs b/tests/ui/diagnostic_namespace/do_not_recommend/stacked.rs
new file mode 100644
index 00000000000..695660d3596
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/do_not_recommend/stacked.rs
@@ -0,0 +1,21 @@
+//@ revisions: current next
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@[next] compile-flags: -Znext-solver
+
+#![feature(do_not_recommend)]
+
+trait Root {}
+trait DontRecommend {}
+trait Other {}
+
+#[do_not_recommend]
+impl<T> Root for T where T: DontRecommend {}
+
+impl<T> DontRecommend for T where T: Other {}
+
+fn needs_root<T: Root>() {}
+
+fn main() {
+    needs_root::<()>();
+    //~^ ERROR the trait bound `(): Root` is not satisfied
+}
diff --git a/tests/ui/rfcs/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.stderr b/tests/ui/rfcs/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.stderr
index 284dacf7000..e6f199445bf 100644
--- a/tests/ui/rfcs/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.stderr
+++ b/tests/ui/rfcs/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.stderr
@@ -2,18 +2,8 @@ error[E0277]: the trait bound `u8: Bar` is not satisfied
   --> $DIR/feature-gate-do_not_recommend.rs:19:11
    |
 LL |     stuff(1u8);
-   |     ----- ^^^ the trait `Foo` is not implemented for `u8`, which is required by `u8: Bar`
-   |     |
-   |     required by a bound introduced by this call
+   |           ^^^ the trait `Bar` is not implemented for `u8`
    |
-   = help: the trait `Foo` is implemented for `i32`
-note: required for `u8` to implement `Bar`
-  --> $DIR/feature-gate-do_not_recommend.rs:13:14
-   |
-LL | impl<T: Foo> Bar for T {
-   |         ---  ^^^     ^
-   |         |
-   |         unsatisfied trait bound introduced here
 note: required by a bound in `stuff`
   --> $DIR/feature-gate-do_not_recommend.rs:16:13
    |