summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2022-12-28 06:35:34 +0000
committerMichael Goulet <michael@errs.io>2022-12-28 18:09:27 +0000
commit01d784131f0a11d19d0ab5398b00ca6bbfd283b1 (patch)
tree6ad2a50282b179732cce8eaf4505b426cabe38c6
parent83a28ef095ba4179a63196f16eadd97f110d6cb3 (diff)
downloadrust-01d784131f0a11d19d0ab5398b00ca6bbfd283b1.tar.gz
rust-01d784131f0a11d19d0ab5398b00ca6bbfd283b1.zip
Make trait/impl where clause mismatch on region error a bit more actionable
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note.rs129
-rw-r--r--src/test/ui/compare-method/region-extra-2.stderr5
-rw-r--r--src/test/ui/compare-method/region-extra.stderr6
-rw-r--r--src/test/ui/generic-associated-types/impl_bounds.stderr5
-rw-r--r--src/test/ui/generic-associated-types/issue-90014.stderr6
-rw-r--r--src/test/ui/generic-associated-types/issue-91883.stderr6
-rw-r--r--src/test/ui/generic-associated-types/issue-92033.stderr6
-rw-r--r--src/test/ui/generic-associated-types/missing-where-clause-on-trait.stderr6
8 files changed, 127 insertions, 42 deletions
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs
index d2dffa4a0b7..891c77d1810 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs
@@ -2,11 +2,14 @@ use crate::errors::RegionOriginNote;
 use crate::infer::error_reporting::{note_and_explain_region, TypeErrCtxt};
 use crate::infer::{self, SubregionOrigin};
 use rustc_errors::{
-    fluent, struct_span_err, AddToDiagnostic, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
+    fluent, struct_span_err, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder,
+    ErrorGuaranteed,
 };
+use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::traits::ObligationCauseCode;
 use rustc_middle::ty::error::TypeError;
-use rustc_middle::ty::{self, Region};
+use rustc_middle::ty::{self, IsSuggestable, Region};
+use rustc_span::symbol::kw;
 
 use super::ObligationCauseAsDiagArg;
 
@@ -313,55 +316,43 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                 );
                 err
             }
-            infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => self
-                .report_extra_impl_obligation(
+            infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => {
+                let mut err = self.report_extra_impl_obligation(
                     span,
                     impl_item_def_id,
                     trait_item_def_id,
                     &format!("`{}: {}`", sup, sub),
-                ),
+                );
+                // We should only suggest rewriting the `where` clause if the predicate is within that `where` clause
+                if self
+                    .tcx
+                    .hir()
+                    .get_generics(impl_item_def_id)
+                    .unwrap()
+                    .where_clause_span
+                    .contains(span)
+                {
+                    self.suggest_copy_trait_method_bounds(
+                        trait_item_def_id,
+                        impl_item_def_id,
+                        &mut err,
+                    );
+                }
+                err
+            }
             infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => {
                 let mut err = self.report_concrete_failure(*parent, sub, sup);
-
                 let trait_item_span = self.tcx.def_span(trait_item_def_id);
                 let item_name = self.tcx.item_name(impl_item_def_id.to_def_id());
                 err.span_label(
                     trait_item_span,
                     format!("definition of `{}` from trait", item_name),
                 );
-
-                let trait_predicates = self.tcx.explicit_predicates_of(trait_item_def_id);
-                let impl_predicates = self.tcx.explicit_predicates_of(impl_item_def_id);
-
-                let impl_predicates: rustc_data_structures::fx::FxHashSet<_> =
-                    impl_predicates.predicates.into_iter().map(|(pred, _)| pred).collect();
-                let clauses: Vec<_> = trait_predicates
-                    .predicates
-                    .into_iter()
-                    .filter(|&(pred, _)| !impl_predicates.contains(pred))
-                    .map(|(pred, _)| format!("{}", pred))
-                    .collect();
-
-                if !clauses.is_empty() {
-                    let generics = self.tcx.hir().get_generics(impl_item_def_id).unwrap();
-                    let where_clause_span = generics.tail_span_for_predicate_suggestion();
-
-                    let suggestion = format!(
-                        "{} {}",
-                        generics.add_where_or_trailing_comma(),
-                        clauses.join(", "),
-                    );
-                    err.span_suggestion(
-                        where_clause_span,
-                        &format!(
-                            "try copying {} from the trait",
-                            if clauses.len() > 1 { "these clauses" } else { "this clause" }
-                        ),
-                        suggestion,
-                        rustc_errors::Applicability::MaybeIncorrect,
-                    );
-                }
-
+                self.suggest_copy_trait_method_bounds(
+                    trait_item_def_id,
+                    impl_item_def_id,
+                    &mut err,
+                );
                 err
             }
             infer::AscribeUserTypeProvePredicate(span) => {
@@ -388,6 +379,66 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         }
     }
 
+    pub fn suggest_copy_trait_method_bounds(
+        &self,
+        trait_item_def_id: DefId,
+        impl_item_def_id: LocalDefId,
+        err: &mut Diagnostic,
+    ) {
+        // FIXME(compiler-errors): Right now this is only being used for region
+        // predicate mismatches. Ideally, we'd use it for *all* predicate mismatches,
+        // but right now it's not really very smart when it comes to implicit `Sized`
+        // predicates and bounds on the trait itself.
+
+        let impl_def_id =
+            self.tcx.associated_item(impl_item_def_id).impl_container(self.tcx).unwrap();
+        let trait_substs = self
+            .tcx
+            .impl_trait_ref(impl_def_id)
+            .unwrap()
+            // Replace the explicit self type with `Self` for better suggestion rendering
+            .with_self_ty(self.tcx, self.tcx.mk_ty_param(0, kw::SelfUpper))
+            .substs;
+        let trait_item_substs =
+            ty::InternalSubsts::identity_for_item(self.tcx, impl_item_def_id.to_def_id())
+                .rebase_onto(self.tcx, impl_def_id, trait_substs);
+
+        let mut is_suggestable = true;
+        let trait_predicates = self
+            .tcx
+            .bound_explicit_predicates_of(trait_item_def_id)
+            .map_bound(|p| p.predicates)
+            .subst_iter_copied(self.tcx, trait_item_substs)
+            .map(|(pred, _)| {
+                if !pred.is_suggestable(self.tcx, false) {
+                    is_suggestable = false;
+                }
+                pred.to_string()
+            })
+            .collect::<Vec<_>>();
+
+        let generics = self.tcx.hir().get_generics(impl_item_def_id).unwrap();
+
+        if is_suggestable {
+            if trait_predicates.is_empty() {
+                err.span_suggestion_verbose(
+                    generics.where_clause_span,
+                    "remove the `where` clause",
+                    String::new(),
+                    Applicability::MachineApplicable,
+                );
+            } else {
+                let space = if generics.where_clause_span.is_empty() { " " } else { "" };
+                err.span_suggestion_verbose(
+                    generics.where_clause_span,
+                    "copy the `where` clause predicates from the trait",
+                    format!("{space}where {}", trait_predicates.join(", ")),
+                    Applicability::MachineApplicable,
+                );
+            }
+        }
+    }
+
     pub(super) fn report_placeholder_failure(
         &self,
         placeholder_origin: SubregionOrigin<'tcx>,
diff --git a/src/test/ui/compare-method/region-extra-2.stderr b/src/test/ui/compare-method/region-extra-2.stderr
index f01d7f4710c..eb19d57ab05 100644
--- a/src/test/ui/compare-method/region-extra-2.stderr
+++ b/src/test/ui/compare-method/region-extra-2.stderr
@@ -6,6 +6,11 @@ LL |     fn renew<'b: 'a>(self) -> &'b mut [T];
 ...
 LL |     fn renew<'b: 'a>(self) -> &'b mut [T] where 'a: 'b {
    |                                                     ^^ impl has extra requirement `'a: 'b`
+   |
+help: copy the `where` clause predicates from the trait
+   |
+LL |     fn renew<'b: 'a>(self) -> &'b mut [T] where 'b: 'a {
+   |                                           ~~~~~~~~~~~~
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/compare-method/region-extra.stderr b/src/test/ui/compare-method/region-extra.stderr
index 4a3af65e904..1a471e18d9d 100644
--- a/src/test/ui/compare-method/region-extra.stderr
+++ b/src/test/ui/compare-method/region-extra.stderr
@@ -6,6 +6,12 @@ LL |     fn foo();
 ...
 LL |     fn foo() where 'a: 'b { }
    |                        ^^ impl has extra requirement `'a: 'b`
+   |
+help: remove the `where` clause
+   |
+LL -     fn foo() where 'a: 'b { }
+LL +     fn foo()  { }
+   |
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/generic-associated-types/impl_bounds.stderr b/src/test/ui/generic-associated-types/impl_bounds.stderr
index 3acd85c8ac6..3456b345cc2 100644
--- a/src/test/ui/generic-associated-types/impl_bounds.stderr
+++ b/src/test/ui/generic-associated-types/impl_bounds.stderr
@@ -15,6 +15,11 @@ LL |     type B<'a, 'b> where 'a: 'b;
 ...
 LL |     type B<'a, 'b> = (&'a(), &'b ()) where 'b: 'a;
    |                                                ^^ impl has extra requirement `'b: 'a`
+   |
+help: copy the `where` clause predicates from the trait
+   |
+LL |     type B<'a, 'b> = (&'a(), &'b ()) where 'a: 'b;
+   |                                      ~~~~~~~~~~~~
 
 error[E0277]: the trait bound `T: Copy` is not satisfied
   --> $DIR/impl_bounds.rs:18:33
diff --git a/src/test/ui/generic-associated-types/issue-90014.stderr b/src/test/ui/generic-associated-types/issue-90014.stderr
index 2d3f4a6af7e..b4b1bc7da7f 100644
--- a/src/test/ui/generic-associated-types/issue-90014.stderr
+++ b/src/test/ui/generic-associated-types/issue-90014.stderr
@@ -5,13 +5,17 @@ LL |     type Fut<'a> where Self: 'a;
    |     ------------ definition of `Fut` from trait
 ...
 LL |     type Fut<'a> = impl Future<Output = ()>;
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^- help: try copying this clause from the trait: `where Self: 'a`
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: type must outlive the lifetime `'a` as defined here
   --> $DIR/issue-90014.rs:13:14
    |
 LL |     type Fut<'a> = impl Future<Output = ()>;
    |              ^^
+help: copy the `where` clause predicates from the trait
+   |
+LL |     type Fut<'a> = impl Future<Output = ()> where Self: 'a;
+   |                                             ++++++++++++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/generic-associated-types/issue-91883.stderr b/src/test/ui/generic-associated-types/issue-91883.stderr
index 1cfc2aaf161..d5db962094c 100644
--- a/src/test/ui/generic-associated-types/issue-91883.stderr
+++ b/src/test/ui/generic-associated-types/issue-91883.stderr
@@ -5,7 +5,7 @@ LL |     type Cursor<'tx>: Cursor<'tx>
    |     ----------------------------- definition of `Cursor` from trait
 ...
 LL |     type Cursor<'tx> = CursorImpl<'tx>;
-   |                        ^^^^^^^^^^^^^^^- help: try copying these clauses from the trait: `where 'db: 'tx, Self: 'tx`
+   |                        ^^^^^^^^^^^^^^^
    |
 note: lifetime parameter instantiated with the lifetime `'db` as defined here
   --> $DIR/issue-91883.rs:29:6
@@ -17,6 +17,10 @@ note: but lifetime parameter must outlive the lifetime `'tx` as defined here
    |
 LL |     type Cursor<'tx> = CursorImpl<'tx>;
    |                 ^^^
+help: copy the `where` clause predicates from the trait
+   |
+LL |     type Cursor<'tx> = CursorImpl<'tx> where 'db: 'tx, Self: 'tx;
+   |                                        +++++++++++++++++++++++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/generic-associated-types/issue-92033.stderr b/src/test/ui/generic-associated-types/issue-92033.stderr
index cd7eed25421..ddc420a7b4e 100644
--- a/src/test/ui/generic-associated-types/issue-92033.stderr
+++ b/src/test/ui/generic-associated-types/issue-92033.stderr
@@ -5,13 +5,17 @@ LL |     type TextureIter<'a>: Iterator<Item = &'a Texture>
    |     -------------------------------------------------- definition of `TextureIter` from trait
 ...
 LL |     type TextureIter<'a> = std::option::IntoIter<&'a Texture>;
-   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- help: try copying this clause from the trait: `where Self: 'a`
+   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: type must outlive the lifetime `'a` as defined here
   --> $DIR/issue-92033.rs:20:22
    |
 LL |     type TextureIter<'a> = std::option::IntoIter<&'a Texture>;
    |                      ^^
+help: copy the `where` clause predicates from the trait
+   |
+LL |     type TextureIter<'a> = std::option::IntoIter<&'a Texture> where Self: 'a;
+   |                                                               ++++++++++++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/generic-associated-types/missing-where-clause-on-trait.stderr b/src/test/ui/generic-associated-types/missing-where-clause-on-trait.stderr
index ffdba6676bf..8a71fc73a9d 100644
--- a/src/test/ui/generic-associated-types/missing-where-clause-on-trait.stderr
+++ b/src/test/ui/generic-associated-types/missing-where-clause-on-trait.stderr
@@ -6,6 +6,12 @@ LL |     type Assoc<'a, 'b>;
 ...
 LL |     type Assoc<'a, 'b> = () where 'a: 'b;
    |                                       ^^ impl has extra requirement `'a: 'b`
+   |
+help: remove the `where` clause
+   |
+LL -     type Assoc<'a, 'b> = () where 'a: 'b;
+LL +     type Assoc<'a, 'b> = () ;
+   |
 
 error: aborting due to previous error