From cd8ca262570d856d354d4ea28632197ca8be15b9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Esteban=20K=C3=BCber?= <esteban@kuber.com.ar>
Date: Wed, 27 Jun 2018 15:25:18 -0700
Subject: [PATCH] Point at lifetimes instead of def span for E0195

---
 src/librustc/hir/map/mod.rs                   | 22 +++++++++++-
 src/librustc_typeck/check/compare_method.rs   | 21 ++++++-----
 .../regions-bound-missing-bound-in-impl.rs    |  5 +++
 ...regions-bound-missing-bound-in-impl.stderr | 35 ++++++++++++-------
 src/test/ui/error-codes/E0195.stderr          |  6 ++--
 5 files changed, 63 insertions(+), 26 deletions(-)

diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs
index b7071970a04..33cae96dd07 100644
--- a/src/librustc/hir/map/mod.rs
+++ b/src/librustc/hir/map/mod.rs
@@ -25,7 +25,7 @@ use rustc_target::spec::abi::Abi;
 use syntax::ast::{self, Name, NodeId, CRATE_NODE_ID};
 use syntax::codemap::Spanned;
 use syntax::ext::base::MacroKind;
-use syntax_pos::Span;
+use syntax_pos::{Span, DUMMY_SP};
 
 use hir::*;
 use hir::print::Nested;
@@ -664,6 +664,26 @@ impl<'hir> Map<'hir> {
         self.as_local_node_id(id).map(|id| self.get(id)) // read recorded by `get`
     }
 
+    pub fn get_generics(&self, id: DefId) -> Option<&'hir Generics> {
+        self.get_if_local(id).and_then(|node| {
+            match node {
+                NodeImplItem(ref impl_item) => Some(&impl_item.generics),
+                NodeTraitItem(ref trait_item) => Some(&trait_item.generics),
+                NodeItem(ref item) => {
+                    match item.node {
+                        ItemFn(_, _, ref generics, _) => Some(generics),
+                        _ => None,
+                    }
+                }
+                _ => None,
+            }
+        })
+    }
+
+    pub fn get_generics_span(&self, id: DefId) -> Option<Span> {
+        self.get_generics(id).map(|generics| generics.span).filter(|sp| *sp != DUMMY_SP)
+    }
+
     /// Retrieve the Node corresponding to `id`, returning None if
     /// cannot be found.
     pub fn find(&self, id: NodeId) -> Option<Node<'hir>> {
diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs
index 04c11d30d26..4c903b6fe58 100644
--- a/src/librustc_typeck/check/compare_method.rs
+++ b/src/librustc_typeck/check/compare_method.rs
@@ -356,7 +356,6 @@ fn check_region_bounds_on_impl_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                                 impl_generics: &ty::Generics,
                                                 trait_to_skol_substs: &Substs<'tcx>)
                                                 -> Result<(), ErrorReported> {
-    let span = tcx.sess.codemap().def_span(span);
     let trait_params = trait_generics.own_counts().lifetimes;
     let impl_params = impl_generics.own_counts().lifetimes;
 
@@ -378,16 +377,20 @@ fn check_region_bounds_on_impl_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     // are zero. Since I don't quite know how to phrase things at
     // the moment, give a kind of vague error message.
     if trait_params != impl_params {
-        let mut err = struct_span_err!(tcx.sess,
-                                       span,
-                                       E0195,
-                                       "lifetime parameters or bounds on method `{}` do not match \
-                                        the trait declaration",
-                                       impl_m.ident);
+        let def_span = tcx.sess.codemap().def_span(span);
+        let span = tcx.hir.get_generics_span(impl_m.def_id).unwrap_or(def_span);
+        let mut err = struct_span_err!(
+            tcx.sess,
+            span,
+            E0195,
+            "lifetime parameters or bounds on method `{}` do not match the trait declaration",
+            impl_m.ident,
+        );
         err.span_label(span, "lifetimes do not match method in trait");
         if let Some(sp) = tcx.hir.span_if_local(trait_m.def_id) {
-            err.span_label(tcx.sess.codemap().def_span(sp),
-                           "lifetimes in impl do not match this method in trait");
+            let def_sp = tcx.sess.codemap().def_span(sp);
+            let sp = tcx.hir.get_generics_span(trait_m.def_id).unwrap_or(def_sp);
+            err.span_label(sp, "lifetimes in impl do not match this method in trait");
         }
         err.emit();
         return Err(ErrorReported);
diff --git a/src/test/ui/borrowck/regions-bound-missing-bound-in-impl.rs b/src/test/ui/borrowck/regions-bound-missing-bound-in-impl.rs
index 617de2c5dfe..04f90ea9ad3 100644
--- a/src/test/ui/borrowck/regions-bound-missing-bound-in-impl.rs
+++ b/src/test/ui/borrowck/regions-bound-missing-bound-in-impl.rs
@@ -20,6 +20,7 @@ pub trait Foo<'a, 't> {
     fn no_bound<'b>(self, b: Inv<'b>);
     fn has_bound<'b:'a>(self, b: Inv<'b>);
     fn wrong_bound1<'b,'c,'d:'a+'b>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>);
+    fn wrong_bound2<'b,'c,'d:'a+'b>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>);
     fn okay_bound<'b,'c,'d:'a+'b+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>);
     fn another_bound<'x: 'a>(self, x: Inv<'x>, y: Inv<'t>);
 }
@@ -47,6 +48,10 @@ impl<'a, 't> Foo<'a, 't> for &'a isize {
         // cases.
     }
 
+    fn wrong_bound2(self, b: Inv, c: Inv, d: Inv) {
+        //~^ ERROR lifetime parameters or bounds on method `wrong_bound2` do not match the trait
+    }
+
     fn okay_bound<'b,'c,'e:'b+'c>(self, b: Inv<'b>, c: Inv<'c>, e: Inv<'e>) {
     }
 
diff --git a/src/test/ui/borrowck/regions-bound-missing-bound-in-impl.stderr b/src/test/ui/borrowck/regions-bound-missing-bound-in-impl.stderr
index b58dbd1e4d1..b530fea1e59 100644
--- a/src/test/ui/borrowck/regions-bound-missing-bound-in-impl.stderr
+++ b/src/test/ui/borrowck/regions-bound-missing-bound-in-impl.stderr
@@ -1,42 +1,51 @@
 error[E0195]: lifetime parameters or bounds on method `no_bound` do not match the trait declaration
-  --> $DIR/regions-bound-missing-bound-in-impl.rs:28:5
+  --> $DIR/regions-bound-missing-bound-in-impl.rs:29:16
    |
 LL |     fn no_bound<'b>(self, b: Inv<'b>);
-   |     ---------------------------------- lifetimes in impl do not match this method in trait
+   |                ---- lifetimes in impl do not match this method in trait
 ...
 LL |     fn no_bound<'b:'a>(self, b: Inv<'b>) {
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetimes do not match method in trait
+   |                ^^^^^^^ lifetimes do not match method in trait
 
 error[E0195]: lifetime parameters or bounds on method `has_bound` do not match the trait declaration
-  --> $DIR/regions-bound-missing-bound-in-impl.rs:32:5
+  --> $DIR/regions-bound-missing-bound-in-impl.rs:33:17
    |
 LL |     fn has_bound<'b:'a>(self, b: Inv<'b>);
-   |     -------------------------------------- lifetimes in impl do not match this method in trait
+   |                 ------- lifetimes in impl do not match this method in trait
 ...
 LL |     fn has_bound<'b>(self, b: Inv<'b>) {
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetimes do not match method in trait
+   |                 ^^^^ lifetimes do not match method in trait
 
 error[E0308]: method not compatible with trait
-  --> $DIR/regions-bound-missing-bound-in-impl.rs:36:5
+  --> $DIR/regions-bound-missing-bound-in-impl.rs:37:5
    |
 LL |     fn wrong_bound1<'b,'c,'d:'a+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>) {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
    |
    = note: expected type `fn(&'a isize, Inv<'c>, Inv<'c>, Inv<'d>)`
               found type `fn(&'a isize, Inv<'_>, Inv<'c>, Inv<'d>)`
-note: the lifetime 'c as defined on the method body at 36:5...
-  --> $DIR/regions-bound-missing-bound-in-impl.rs:36:5
+note: the lifetime 'c as defined on the method body at 37:5...
+  --> $DIR/regions-bound-missing-bound-in-impl.rs:37:5
    |
 LL |     fn wrong_bound1<'b,'c,'d:'a+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>) {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-note: ...does not necessarily outlive the lifetime 'c as defined on the method body at 36:5
-  --> $DIR/regions-bound-missing-bound-in-impl.rs:36:5
+note: ...does not necessarily outlive the lifetime 'c as defined on the method body at 37:5
+  --> $DIR/regions-bound-missing-bound-in-impl.rs:37:5
    |
 LL |     fn wrong_bound1<'b,'c,'d:'a+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>) {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+error[E0195]: lifetime parameters or bounds on method `wrong_bound2` do not match the trait declaration
+  --> $DIR/regions-bound-missing-bound-in-impl.rs:51:5
+   |
+LL |     fn wrong_bound2<'b,'c,'d:'a+'b>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>);
+   |                    ---------------- lifetimes in impl do not match this method in trait
+...
+LL |     fn wrong_bound2(self, b: Inv, c: Inv, d: Inv) {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetimes do not match method in trait
+
 error[E0276]: impl has stricter requirements than trait
-  --> $DIR/regions-bound-missing-bound-in-impl.rs:53:5
+  --> $DIR/regions-bound-missing-bound-in-impl.rs:58:5
    |
 LL |     fn another_bound<'x: 'a>(self, x: Inv<'x>, y: Inv<'t>);
    |     ------------------------------------------------------- definition of `another_bound` from trait
@@ -44,7 +53,7 @@ LL |     fn another_bound<'x: 'a>(self, x: Inv<'x>, y: Inv<'t>);
 LL |     fn another_bound<'x: 't>(self, x: Inv<'x>, y: Inv<'t>) {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `'x: 't`
 
-error: aborting due to 4 previous errors
+error: aborting due to 5 previous errors
 
 Some errors occurred: E0195, E0276, E0308.
 For more information about an error, try `rustc --explain E0195`.
diff --git a/src/test/ui/error-codes/E0195.stderr b/src/test/ui/error-codes/E0195.stderr
index f2cf661830d..3860c93a45f 100644
--- a/src/test/ui/error-codes/E0195.stderr
+++ b/src/test/ui/error-codes/E0195.stderr
@@ -1,11 +1,11 @@
 error[E0195]: lifetime parameters or bounds on method `bar` do not match the trait declaration
-  --> $DIR/E0195.rs:19:5
+  --> $DIR/E0195.rs:19:11
    |
 LL |     fn bar<'a,'b:'a>(x: &'a str, y: &'b str);
-   |     ----------------------------------------- lifetimes in impl do not match this method in trait
+   |           ---------- lifetimes in impl do not match this method in trait
 ...
 LL |     fn bar<'a,'b>(x: &'a str, y: &'b str) { //~ ERROR E0195
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetimes do not match method in trait
+   |           ^^^^^^^ lifetimes do not match method in trait
 
 error: aborting due to previous error