about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl2
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_method.rs51
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs4
-rw-r--r--src/test/ui/trait-bounds/impl-missing-where-clause-lifetimes-from-trait.rs30
-rw-r--r--src/test/ui/trait-bounds/impl-missing-where-clause-lifetimes-from-trait.stderr27
5 files changed, 103 insertions, 11 deletions
diff --git a/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl b/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl
index 0894bbcaad4..a5cb8a88819 100644
--- a/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl
@@ -17,6 +17,8 @@ hir_analysis_lifetimes_or_bounds_mismatch_on_trait =
     lifetime parameters or bounds on {$item_kind} `{$ident}` do not match the trait declaration
     .label = lifetimes do not match {$item_kind} in trait
     .generics_label = lifetimes in impl do not match this {$item_kind} in trait
+    .where_label = this `where` clause might not match the one in the trait
+    .bounds_label = this bound might be missing in the impl
 
 hir_analysis_drop_impl_on_wrong_item =
     the `Drop` trait may only be implemented for local structs, enums, and unions
diff --git a/compiler/rustc_hir_analysis/src/check/compare_method.rs b/compiler/rustc_hir_analysis/src/check/compare_method.rs
index 82a77416a19..1d6f9b29176 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_method.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_method.rs
@@ -751,17 +751,45 @@ fn check_region_bounds_on_impl_item<'tcx>(
             .get_generics(impl_m.def_id.expect_local())
             .expect("expected impl item to have generics or else we can't compare them")
             .span;
-        let generics_span = if let Some(local_def_id) = trait_m.def_id.as_local() {
-            Some(
-                tcx.hir()
-                    .get_generics(local_def_id)
-                    .expect("expected trait item to have generics or else we can't compare them")
-                    .span,
-            )
-        } else {
-            None
-        };
 
+        let mut generics_span = None;
+        let mut bounds_span = vec![];
+        let mut where_span = None;
+        if let Some(trait_node) = tcx.hir().get_if_local(trait_m.def_id)
+            && let Some(trait_generics) = trait_node.generics()
+        {
+            generics_span = Some(trait_generics.span);
+            // FIXME: we could potentially look at the impl's bounds to not point at bounds that
+            // *are* present in the impl.
+            for p in trait_generics.predicates {
+                if let hir::WherePredicate::BoundPredicate(pred) = p {
+                    for b in pred.bounds {
+                        if let hir::GenericBound::Outlives(lt) = b {
+                            bounds_span.push(lt.ident.span);
+                        }
+                    }
+                }
+            }
+            if let Some(impl_node) = tcx.hir().get_if_local(impl_m.def_id)
+                && let Some(impl_generics) = impl_node.generics()
+            {
+                let mut impl_bounds = 0;
+                for p in impl_generics.predicates {
+                    if let hir::WherePredicate::BoundPredicate(pred) = p {
+                        for b in pred.bounds {
+                            if let hir::GenericBound::Outlives(_) = b {
+                                impl_bounds += 1;
+                            }
+                        }
+                    }
+                }
+                if impl_bounds == bounds_span.len() {
+                    bounds_span = vec![];
+                } else if impl_generics.has_where_clause_predicates {
+                    where_span = Some(impl_generics.where_clause_span);
+                }
+            }
+        }
         let reported = tcx
             .sess
             .create_err(LifetimesOrBoundsMismatchOnTrait {
@@ -769,9 +797,10 @@ fn check_region_bounds_on_impl_item<'tcx>(
                 item_kind: assoc_item_kind_str(impl_m),
                 ident: impl_m.ident(tcx),
                 generics_span,
+                bounds_span,
+                where_span,
             })
             .emit_unless(delay);
-
         return Err(reported);
     }
 
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index afbb27155a2..5156d432b5b 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -43,6 +43,10 @@ pub struct LifetimesOrBoundsMismatchOnTrait {
     pub span: Span,
     #[label(generics_label)]
     pub generics_span: Option<Span>,
+    #[label(where_label)]
+    pub where_span: Option<Span>,
+    #[label(bounds_label)]
+    pub bounds_span: Vec<Span>,
     pub item_kind: &'static str,
     pub ident: Ident,
 }
diff --git a/src/test/ui/trait-bounds/impl-missing-where-clause-lifetimes-from-trait.rs b/src/test/ui/trait-bounds/impl-missing-where-clause-lifetimes-from-trait.rs
new file mode 100644
index 00000000000..1994120ce14
--- /dev/null
+++ b/src/test/ui/trait-bounds/impl-missing-where-clause-lifetimes-from-trait.rs
@@ -0,0 +1,30 @@
+trait Trait<T> {
+    fn foo<'a, K>(self, _: T, _: K) where T: 'a, K: 'a;
+}
+
+impl Trait<()> for () {
+    fn foo<'a, K>(self, _: (), _: K) where { //~ ERROR E0195
+        todo!();
+    }
+}
+
+struct State;
+
+trait Foo<T> {
+    fn foo<'a>(&self, state: &'a State) -> &'a T
+    where
+        T: 'a;
+}
+
+impl<F, T> Foo<T> for F
+where
+    F: Fn(&State) -> &T,
+{
+    fn foo<'a>(&self, state: &'a State) -> &'a T { //~ ERROR E0195
+        self(state)
+    }
+}
+
+fn main() {
+    ().foo((), ());
+}
diff --git a/src/test/ui/trait-bounds/impl-missing-where-clause-lifetimes-from-trait.stderr b/src/test/ui/trait-bounds/impl-missing-where-clause-lifetimes-from-trait.stderr
new file mode 100644
index 00000000000..3a02369e2ee
--- /dev/null
+++ b/src/test/ui/trait-bounds/impl-missing-where-clause-lifetimes-from-trait.stderr
@@ -0,0 +1,27 @@
+error[E0195]: lifetime parameters or bounds on method `foo` do not match the trait declaration
+  --> $DIR/impl-missing-where-clause-lifetimes-from-trait.rs:6:11
+   |
+LL |     fn foo<'a, K>(self, _: T, _: K) where T: 'a, K: 'a;
+   |           -------                            --     -- this bound might be missing in the impl
+   |           |                                  |
+   |           |                                  this bound might be missing in the impl
+   |           lifetimes in impl do not match this method in trait
+...
+LL |     fn foo<'a, K>(self, _: (), _: K) where {
+   |           ^^^^^^^ lifetimes do not match method in trait
+
+error[E0195]: lifetime parameters or bounds on method `foo` do not match the trait declaration
+  --> $DIR/impl-missing-where-clause-lifetimes-from-trait.rs:23:11
+   |
+LL |     fn foo<'a>(&self, state: &'a State) -> &'a T
+   |           ---- lifetimes in impl do not match this method in trait
+LL |     where
+LL |         T: 'a;
+   |            -- this bound might be missing in the impl
+...
+LL |     fn foo<'a>(&self, state: &'a State) -> &'a T {
+   |           ^^^^ lifetimes do not match method in trait
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0195`.