about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-03-10 10:52:07 +0000
committerbors <bors@rust-lang.org>2018-03-10 10:52:07 +0000
commit87344aa59af2ebb868253228e2b558d701573dff (patch)
treee64ad8739124433604b5a5106a5c8ef617447799
parent948e3a30e6ec8417bcfdb923cd414fdf8fc87795 (diff)
parent0e68bb97285a1ade22cf6e68103dc54fb75db43f (diff)
downloadrust-87344aa59af2ebb868253228e2b558d701573dff.tar.gz
rust-87344aa59af2ebb868253228e2b558d701573dff.zip
Auto merge of #47574 - zilbuz:issue-14844, r=nikomatsakis
Show the used type variable when issuing a "can't use type parameters from outer function" error message

Fix #14844

r? @estebank
-rw-r--r--src/librustc/hir/lowering.rs9
-rw-r--r--src/librustc/hir/map/def_collector.rs44
-rw-r--r--src/librustc/hir/map/definitions.rs31
-rw-r--r--src/librustc_resolve/lib.rs128
-rw-r--r--src/libsyntax/codemap.rs66
-rw-r--r--src/test/compile-fail/inner-static-type-parameter.rs2
-rw-r--r--src/test/compile-fail/issue-3021-c.rs4
-rw-r--r--src/test/compile-fail/issue-3214.rs2
-rw-r--r--src/test/compile-fail/issue-5997-struct.rs2
-rw-r--r--src/test/compile-fail/nested-ty-params.rs2
-rw-r--r--src/test/compile-fail/type-arg-out-of-scope.rs2
-rw-r--r--src/test/ui/error-codes/E0401.rs24
-rw-r--r--src/test/ui/error-codes/E0401.stderr36
13 files changed, 309 insertions, 43 deletions
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 877027a21a2..49611689fc4 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -571,7 +571,8 @@ impl<'a> LoweringContext<'a> {
                         def_node_id,
                         DefPathData::LifetimeDef(name.as_str()),
                         DefIndexAddressSpace::High,
-                        Mark::root()
+                        Mark::root(),
+                        span
                     );
 
                     hir::GenericParam::Lifetime(hir::LifetimeDef {
@@ -1003,7 +1004,8 @@ impl<'a> LoweringContext<'a> {
                             def_node_id,
                             DefPathData::ImplTrait,
                             DefIndexAddressSpace::High,
-                            Mark::root()
+                            Mark::root(),
+                            span
                         );
 
                         let hir_bounds = self.lower_bounds(bounds, itctx);
@@ -1150,7 +1152,8 @@ impl<'a> LoweringContext<'a> {
                         def_node_id,
                         DefPathData::LifetimeDef(name.name().as_str()),
                         DefIndexAddressSpace::High,
-                        Mark::root()
+                        Mark::root(),
+                        lifetime.span
                     );
                     let def_lifetime = hir::Lifetime {
                         id: def_node_id,
diff --git a/src/librustc/hir/map/def_collector.rs b/src/librustc/hir/map/def_collector.rs
index cdd63957478..3619a7fb0c6 100644
--- a/src/librustc/hir/map/def_collector.rs
+++ b/src/librustc/hir/map/def_collector.rs
@@ -18,6 +18,7 @@ use syntax::visit;
 use syntax::symbol::keywords;
 use syntax::symbol::Symbol;
 use syntax::parse::token::{self, Token};
+use syntax_pos::Span;
 
 use hir::map::{ITEM_LIKE_SPACE, REGULAR_SPACE};
 
@@ -57,12 +58,13 @@ impl<'a> DefCollector<'a> {
     fn create_def(&mut self,
                   node_id: NodeId,
                   data: DefPathData,
-                  address_space: DefIndexAddressSpace)
+                  address_space: DefIndexAddressSpace,
+                  span: Span)
                   -> DefIndex {
         let parent_def = self.parent_def.unwrap();
         debug!("create_def(node_id={:?}, data={:?}, parent_def={:?})", node_id, data, parent_def);
         self.definitions
-            .create_def_with_parent(parent_def, node_id, data, address_space, self.expansion)
+            .create_def_with_parent(parent_def, node_id, data, address_space, self.expansion, span)
     }
 
     pub fn with_parent<F: FnOnce(&mut Self)>(&mut self, parent_def: DefIndex, f: F) {
@@ -83,7 +85,7 @@ impl<'a> DefCollector<'a> {
             _ => {}
         }
 
-        self.create_def(expr.id, DefPathData::Initializer, REGULAR_SPACE);
+        self.create_def(expr.id, DefPathData::Initializer, REGULAR_SPACE, expr.span);
     }
 
     fn visit_macro_invoc(&mut self, id: NodeId, const_expr: bool) {
@@ -122,7 +124,7 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
                 return visit::walk_item(self, i);
             }
         };
-        let def = self.create_def(i.id, def_data, ITEM_LIKE_SPACE);
+        let def = self.create_def(i.id, def_data, ITEM_LIKE_SPACE, i.span);
 
         self.with_parent(def, |this| {
             match i.node {
@@ -131,14 +133,16 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
                         let variant_def_index =
                             this.create_def(v.node.data.id(),
                                             DefPathData::EnumVariant(v.node.name.name.as_str()),
-                                            REGULAR_SPACE);
+                                            REGULAR_SPACE,
+                                            v.span);
                         this.with_parent(variant_def_index, |this| {
                             for (index, field) in v.node.data.fields().iter().enumerate() {
                                 let name = field.ident.map(|ident| ident.name)
                                     .unwrap_or_else(|| Symbol::intern(&index.to_string()));
                                 this.create_def(field.id,
                                                 DefPathData::Field(name.as_str()),
-                                                REGULAR_SPACE);
+                                                REGULAR_SPACE,
+                                                field.span);
                             }
 
                             if let Some(ref expr) = v.node.disr_expr {
@@ -152,13 +156,17 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
                     if !struct_def.is_struct() {
                         this.create_def(struct_def.id(),
                                         DefPathData::StructCtor,
-                                        REGULAR_SPACE);
+                                        REGULAR_SPACE,
+                                        i.span);
                     }
 
                     for (index, field) in struct_def.fields().iter().enumerate() {
                         let name = field.ident.map(|ident| ident.name)
                             .unwrap_or_else(|| Symbol::intern(&index.to_string()));
-                        this.create_def(field.id, DefPathData::Field(name.as_str()), REGULAR_SPACE);
+                        this.create_def(field.id,
+                                        DefPathData::Field(name.as_str()),
+                                        REGULAR_SPACE,
+                                        field.span);
                     }
                 }
                 _ => {}
@@ -168,14 +176,15 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
     }
 
     fn visit_use_tree(&mut self, use_tree: &'a UseTree, id: NodeId, _nested: bool) {
-        self.create_def(id, DefPathData::Misc, ITEM_LIKE_SPACE);
+        self.create_def(id, DefPathData::Misc, ITEM_LIKE_SPACE, use_tree.span);
         visit::walk_use_tree(self, use_tree, id);
     }
 
     fn visit_foreign_item(&mut self, foreign_item: &'a ForeignItem) {
         let def = self.create_def(foreign_item.id,
                                   DefPathData::ValueNs(foreign_item.ident.name.as_str()),
-                                  REGULAR_SPACE);
+                                  REGULAR_SPACE,
+                                  foreign_item.span);
 
         self.with_parent(def, |this| {
             visit::walk_foreign_item(this, foreign_item);
@@ -188,14 +197,16 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
                 self.create_def(
                     lifetime_def.lifetime.id,
                     DefPathData::LifetimeDef(lifetime_def.lifetime.ident.name.as_str()),
-                    REGULAR_SPACE
+                    REGULAR_SPACE,
+                    lifetime_def.lifetime.span
                 );
             }
             GenericParam::Type(ref ty_param) => {
                 self.create_def(
                     ty_param.id,
                     DefPathData::TypeParam(ty_param.ident.name.as_str()),
-                    REGULAR_SPACE
+                    REGULAR_SPACE,
+                    ty_param.span
                 );
             }
         }
@@ -211,7 +222,7 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
             TraitItemKind::Macro(..) => return self.visit_macro_invoc(ti.id, false),
         };
 
-        let def = self.create_def(ti.id, def_data, ITEM_LIKE_SPACE);
+        let def = self.create_def(ti.id, def_data, ITEM_LIKE_SPACE, ti.span);
         self.with_parent(def, |this| {
             if let TraitItemKind::Const(_, Some(ref expr)) = ti.node {
                 this.visit_const_expr(expr);
@@ -229,7 +240,7 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
             ImplItemKind::Macro(..) => return self.visit_macro_invoc(ii.id, false),
         };
 
-        let def = self.create_def(ii.id, def_data, ITEM_LIKE_SPACE);
+        let def = self.create_def(ii.id, def_data, ITEM_LIKE_SPACE, ii.span);
         self.with_parent(def, |this| {
             if let ImplItemKind::Const(_, ref expr) = ii.node {
                 this.visit_const_expr(expr);
@@ -255,7 +266,8 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
             ExprKind::Closure(..) => {
                 let def = self.create_def(expr.id,
                                           DefPathData::ClosureExpr,
-                                          REGULAR_SPACE);
+                                          REGULAR_SPACE,
+                                          expr.span);
                 self.parent_def = Some(def);
             }
             _ => {}
@@ -270,7 +282,7 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
             TyKind::Mac(..) => return self.visit_macro_invoc(ty.id, false),
             TyKind::Array(_, ref length) => self.visit_const_expr(length),
             TyKind::ImplTrait(..) => {
-                self.create_def(ty.id, DefPathData::ImplTrait, REGULAR_SPACE);
+                self.create_def(ty.id, DefPathData::ImplTrait, REGULAR_SPACE, ty.span);
             }
             TyKind::Typeof(ref expr) => self.visit_const_expr(expr),
             _ => {}
diff --git a/src/librustc/hir/map/definitions.rs b/src/librustc/hir/map/definitions.rs
index 61a58a60306..1a2840de447 100644
--- a/src/librustc/hir/map/definitions.rs
+++ b/src/librustc/hir/map/definitions.rs
@@ -28,6 +28,7 @@ use std::hash::Hash;
 use syntax::ast;
 use syntax::ext::hygiene::Mark;
 use syntax::symbol::{Symbol, InternedString};
+use syntax_pos::{Span, DUMMY_SP};
 use util::nodemap::NodeMap;
 
 /// The DefPathTable maps DefIndexes to DefKeys and vice versa.
@@ -159,6 +160,7 @@ pub struct Definitions {
     macro_def_scopes: FxHashMap<Mark, DefId>,
     expansions: FxHashMap<DefIndex, Mark>,
     next_disambiguator: FxHashMap<(DefIndex, DefPathData), u32>,
+    def_index_to_span: FxHashMap<DefIndex, Span>,
 }
 
 // Unfortunately we have to provide a manual impl of Clone because of the
@@ -176,6 +178,7 @@ impl Clone for Definitions {
             macro_def_scopes: self.macro_def_scopes.clone(),
             expansions: self.expansions.clone(),
             next_disambiguator: self.next_disambiguator.clone(),
+            def_index_to_span: self.def_index_to_span.clone(),
         }
     }
 }
@@ -410,6 +413,7 @@ impl Definitions {
             macro_def_scopes: FxHashMap(),
             expansions: FxHashMap(),
             next_disambiguator: FxHashMap(),
+            def_index_to_span: FxHashMap(),
         }
     }
 
@@ -493,6 +497,22 @@ impl Definitions {
         self.node_to_hir_id[node_id]
     }
 
+    /// Retrieve the span of the given `DefId` if `DefId` is in the local crate, the span exists and
+    /// it's not DUMMY_SP
+    #[inline]
+    pub fn opt_span(&self, def_id: DefId) -> Option<Span> {
+        if def_id.krate == LOCAL_CRATE {
+            let span = self.def_index_to_span.get(&def_id.index).cloned().unwrap_or(DUMMY_SP);
+            if span != DUMMY_SP {
+                Some(span)
+            } else {
+                None
+            }
+        } else {
+            None
+        }
+    }
+
     /// Add a definition with a parent definition.
     pub fn create_root_def(&mut self,
                            crate_name: &str,
@@ -530,7 +550,8 @@ impl Definitions {
                                   node_id: ast::NodeId,
                                   data: DefPathData,
                                   address_space: DefIndexAddressSpace,
-                                  expansion: Mark)
+                                  expansion: Mark,
+                                  span: Span)
                                   -> DefIndex {
         debug!("create_def_with_parent(parent={:?}, node_id={:?}, data={:?})",
                parent, node_id, data);
@@ -583,6 +604,11 @@ impl Definitions {
             self.expansions.insert(index, expansion);
         }
 
+        // The span is added if it isn't DUMMY_SP
+        if span != DUMMY_SP {
+            self.def_index_to_span.insert(index, span);
+        }
+
         index
     }
 
@@ -692,7 +718,8 @@ macro_rules! define_global_metadata_kind {
                         ast::DUMMY_NODE_ID,
                         DefPathData::GlobalMetaData(instance.name().as_str()),
                         GLOBAL_MD_ADDRESS_SPACE,
-                        Mark::root()
+                        Mark::root(),
+                        DUMMY_SP
                     );
 
                     // Make sure calling def_index does not crash.
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index fc1ff248184..a6b776125ae 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -41,7 +41,7 @@ use rustc::ty;
 use rustc::hir::{Freevar, FreevarMap, TraitCandidate, TraitMap, GlobMap};
 use rustc::util::nodemap::{NodeMap, NodeSet, FxHashMap, FxHashSet, DefIdMap};
 
-use syntax::codemap::{dummy_spanned, respan};
+use syntax::codemap::{dummy_spanned, respan, CodeMap};
 use syntax::ext::hygiene::{Mark, MarkKind, SyntaxContext};
 use syntax::ast::{self, Name, NodeId, Ident, SpannedIdent, FloatTy, IntTy, UintTy};
 use syntax::ext::base::SyntaxExtension;
@@ -123,7 +123,7 @@ impl Ord for BindingError {
 
 enum ResolutionError<'a> {
     /// error E0401: can't use type parameters from outer function
-    TypeParametersFromOuterFunction,
+    TypeParametersFromOuterFunction(Def),
     /// error E0403: the name is already used for a type parameter in this type parameter list
     NameAlreadyUsedInTypeParameterList(Name, &'a Span),
     /// error E0407: method is not a member of trait
@@ -173,13 +173,49 @@ fn resolve_struct_error<'sess, 'a>(resolver: &'sess Resolver,
                                    resolution_error: ResolutionError<'a>)
                                    -> DiagnosticBuilder<'sess> {
     match resolution_error {
-        ResolutionError::TypeParametersFromOuterFunction => {
+        ResolutionError::TypeParametersFromOuterFunction(outer_def) => {
             let mut err = struct_span_err!(resolver.session,
                                            span,
                                            E0401,
-                                           "can't use type parameters from outer function; \
-                                           try using a local type parameter instead");
+                                           "can't use type parameters from outer function");
             err.span_label(span, "use of type variable from outer function");
+            match outer_def {
+                Def::SelfTy(_, maybe_impl_defid) => {
+                    if let Some(impl_span) = maybe_impl_defid.map_or(None,
+                            |def_id| resolver.definitions.opt_span(def_id)) {
+                        let cm = resolver.session.codemap();
+                        err.span_label(reduce_impl_span_to_impl_keyword(cm, impl_span),
+                                    "`Self` type implicitely declared here, on the `impl`");
+                    }
+                },
+                Def::TyParam(typaram_defid) => {
+                    if let Some(typaram_span) = resolver.definitions.opt_span(typaram_defid) {
+                        err.span_label(typaram_span, "type variable from outer function");
+                    }
+                },
+                Def::Mod(..) | Def::Struct(..) | Def::Union(..) | Def::Enum(..) | Def::Variant(..) |
+                Def::Trait(..) | Def::TyAlias(..) | Def::TyForeign(..) | Def::TraitAlias(..) |
+                Def::AssociatedTy(..) | Def::PrimTy(..) | Def::Fn(..) | Def::Const(..) |
+                Def::Static(..) | Def::StructCtor(..) | Def::VariantCtor(..) | Def::Method(..) |
+                Def::AssociatedConst(..) | Def::Local(..) | Def::Upvar(..) | Def::Label(..) |
+                Def::Macro(..) | Def::GlobalAsm(..) | Def::Err =>
+                    bug!("TypeParametersFromOuterFunction should only be used with Def::SelfTy or \
+                         Def::TyParam")
+            }
+
+            // Try to retrieve the span of the function signature and generate a new message with
+            // a local type parameter
+            let sugg_msg = "try using a local type parameter instead";
+            if let Some((sugg_span, new_snippet)) = generate_local_type_param_snippet(
+                                                        resolver.session.codemap(), span) {
+                // Suggest the modification to the user
+                err.span_suggestion(sugg_span,
+                                    sugg_msg,
+                                    new_snippet);
+            } else {
+                err.help("try using a local type parameter instead");
+            }
+
             err
         }
         ResolutionError::NameAlreadyUsedInTypeParameterList(name, first_use_span) => {
@@ -358,6 +394,86 @@ fn resolve_struct_error<'sess, 'a>(resolver: &'sess Resolver,
     }
 }
 
+/// Adjust the impl span so that just the `impl` keyword is taken by removing
+/// everything after `<` (`"impl<T> Iterator for A<T> {}" -> "impl"`) and
+/// everything after the first whitespace (`"impl Iterator for A" -> "impl"`)
+///
+/// Attention: The method used is very fragile since it essentially duplicates the work of the
+/// parser. If you need to use this function or something similar, please consider updating the
+/// codemap functions and this function to something more robust.
+fn reduce_impl_span_to_impl_keyword(cm: &CodeMap, impl_span: Span) -> Span {
+    let impl_span = cm.span_until_char(impl_span, '<');
+    let impl_span = cm.span_until_whitespace(impl_span);
+    impl_span
+}
+
+/// Take the span of a type parameter in a function signature and try to generate a span for the
+/// function name (with generics) and a new snippet for this span with the pointed type parameter as
+/// a new local type parameter.
+///
+/// For instance:
+/// ```
+/// // Given span
+/// fn my_function(param: T)
+///                       ^ Original span
+///
+/// // Result
+/// fn my_function(param: T)
+///    ^^^^^^^^^^^ Generated span with snippet `my_function<T>`
+/// ```
+///
+/// Attention: The method used is very fragile since it essentially duplicates the work of the
+/// parser. If you need to use this function or something similar, please consider updating the
+/// codemap functions and this function to something more robust.
+fn generate_local_type_param_snippet(cm: &CodeMap, span: Span) -> Option<(Span, String)> {
+    // Try to extend the span to the previous "fn" keyword to retrieve the function
+    // signature
+    let sugg_span = cm.span_extend_to_prev_str(span, "fn");
+    if sugg_span != span {
+        if let Ok(snippet) = cm.span_to_snippet(sugg_span) {
+            use syntax::codemap::BytePos;
+
+            // Consume the function name
+            let mut offset = 0;
+            for c in snippet.chars().take_while(|c| c.is_ascii_alphanumeric() ||
+                                                    *c == '_') {
+                offset += c.len_utf8();
+            }
+
+            // Consume the generics part of the function signature
+            let mut bracket_counter = 0;
+            let mut last_char = None;
+            for c in snippet[offset..].chars() {
+                match c {
+                    '<' => bracket_counter += 1,
+                    '>' => bracket_counter -= 1,
+                    '(' => if bracket_counter == 0 { break; }
+                    _ => {}
+                }
+                offset += c.len_utf8();
+                last_char = Some(c);
+            }
+
+            // Adjust the suggestion span to encompass the function name with its generics
+            let sugg_span = sugg_span.with_hi(BytePos(sugg_span.lo().0 + offset as u32));
+
+            // Prepare the new suggested snippet to append the type parameter that triggered
+            // the error in the generics of the function signature
+            let mut new_snippet = if last_char == Some('>') {
+                format!("{}, ", &snippet[..(offset - '>'.len_utf8())])
+            } else {
+                format!("{}<", &snippet[..offset])
+            };
+            new_snippet.push_str(&cm.span_to_snippet(span).unwrap_or("T".to_string()));
+            new_snippet.push('>');
+
+            return Some((sugg_span, new_snippet));
+        }
+    }
+
+    None
+}
+
 #[derive(Copy, Clone, Debug)]
 struct BindingInfo {
     span: Span,
@@ -3280,7 +3396,7 @@ impl<'a> Resolver<'a> {
                             // its scope.
                             if record_used {
                                 resolve_error(self, span,
-                                              ResolutionError::TypeParametersFromOuterFunction);
+                                    ResolutionError::TypeParametersFromOuterFunction(def));
                             }
                             return Def::Err;
                         }
diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs
index 53ddbfbfd4a..c340f1b8c8a 100644
--- a/src/libsyntax/codemap.rs
+++ b/src/libsyntax/codemap.rs
@@ -533,7 +533,12 @@ impl CodeMap {
         Ok(FileLines {file: lo.file, lines: lines})
     }
 
-    pub fn span_to_snippet(&self, sp: Span) -> Result<String, SpanSnippetError> {
+    /// Extract the source surrounding the given `Span` using the `extract_source` function. The
+    /// extract function takes three arguments: a string slice containing the source, an index in
+    /// the slice for the beginning of the span and an index in the slice for the end of the span.
+    fn span_to_source<F>(&self, sp: Span, extract_source: F) -> Result<String, SpanSnippetError>
+        where F: Fn(&str, usize, usize) -> String
+    {
         if sp.lo() > sp.hi() {
             return Err(SpanSnippetError::IllFormedSpan(sp));
         }
@@ -567,9 +572,9 @@ impl CodeMap {
             }
 
             if let Some(ref src) = local_begin.fm.src {
-                return Ok((&src[start_index..end_index]).to_string());
+                return Ok(extract_source(src, start_index, end_index));
             } else if let Some(src) = local_begin.fm.external_src.borrow().get_source() {
-                return Ok((&src[start_index..end_index]).to_string());
+                return Ok(extract_source(src, start_index, end_index));
             } else {
                 return Err(SpanSnippetError::SourceNotAvailable {
                     filename: local_begin.fm.name.clone()
@@ -578,6 +583,17 @@ impl CodeMap {
         }
     }
 
+    /// Return the source snippet as `String` corresponding to the given `Span`
+    pub fn span_to_snippet(&self, sp: Span) -> Result<String, SpanSnippetError> {
+        self.span_to_source(sp, |src, start_index, end_index| src[start_index..end_index]
+                                                                .to_string())
+    }
+
+    /// Return the source snippet as `String` before the given `Span`
+    pub fn span_to_prev_source(&self, sp: Span) -> Result<String, SpanSnippetError> {
+        self.span_to_source(sp, |src, start_index, _| src[..start_index].to_string())
+    }
+
     /// Given a `Span`, try to get a shorter span ending before the first occurrence of `c` `char`
     pub fn span_until_char(&self, sp: Span, c: char) -> Span {
         match self.span_to_snippet(sp) {
@@ -593,6 +609,32 @@ impl CodeMap {
         }
     }
 
+    /// Extend the given `Span` to just after the previous occurrence of `c`. Return the same span
+    /// if no character could be found or if an error occurred while retrieving the code snippet.
+    pub fn span_extend_to_prev_char(&self, sp: Span, c: char) -> Span {
+        if let Ok(prev_source) = self.span_to_prev_source(sp) {
+            let prev_source = prev_source.rsplit(c).nth(0).unwrap_or("").trim_left();
+            if !prev_source.is_empty() && !prev_source.contains('\n') {
+                return sp.with_lo(BytePos(sp.lo().0 - prev_source.len() as u32));
+            }
+        }
+
+        sp
+    }
+
+    /// Extend the given `Span` to just after the previous occurrence of `pat`. Return the same span
+    /// if no character could be found or if an error occurred while retrieving the code snippet.
+    pub fn span_extend_to_prev_str(&self, sp: Span, pat: &str) -> Span {
+        if let Ok(prev_source) = self.span_to_prev_source(sp) {
+            let prev_source = prev_source.rsplit(pat).nth(0).unwrap_or("").trim_left();
+            if !prev_source.is_empty() && !prev_source.contains('\n') {
+                return sp.with_lo(BytePos(sp.lo().0 - prev_source.len() as u32));
+            }
+        }
+
+        sp
+    }
+
     /// Given a `Span`, get a new `Span` covering the first token and all its trailing whitespace or
     /// the original `Span`.
     ///
@@ -615,6 +657,24 @@ impl CodeMap {
         sp
     }
 
+    /// Given a `Span`, get a new `Span` covering the first token without its trailing whitespace or
+    /// the original `Span` in case of error.
+    ///
+    /// If `sp` points to `"let mut x"`, then a span pointing at `"let"` will be returned.
+    pub fn span_until_whitespace(&self, sp: Span) -> Span {
+        if let Ok(snippet) = self.span_to_snippet(sp) {
+            let mut offset = 0;
+            // Get the bytes width of all the non-whitespace characters
+            for c in snippet.chars().take_while(|c| !c.is_whitespace()) {
+                offset += c.len_utf8();
+            }
+            if offset > 1 {
+                return sp.with_hi(BytePos(sp.lo().0 + offset as u32));
+            }
+        }
+        sp
+    }
+
     /// Given a `Span`, try to get a shorter span ending just after the first occurrence of `char`
     /// `c`.
     pub fn span_through_char(&self, sp: Span, c: char) -> Span {
diff --git a/src/test/compile-fail/inner-static-type-parameter.rs b/src/test/compile-fail/inner-static-type-parameter.rs
index 6fb497092d2..4d763017c0f 100644
--- a/src/test/compile-fail/inner-static-type-parameter.rs
+++ b/src/test/compile-fail/inner-static-type-parameter.rs
@@ -14,7 +14,7 @@ enum Bar<T> { What } //~ ERROR parameter `T` is never used
 
 fn foo<T>() {
     static a: Bar<T> = Bar::What;
-//~^ ERROR can't use type parameters from outer function; try using a local type parameter instead
+//~^ ERROR can't use type parameters from outer function
 }
 
 fn main() {
diff --git a/src/test/compile-fail/issue-3021-c.rs b/src/test/compile-fail/issue-3021-c.rs
index 635006a3b4d..55975cc8e86 100644
--- a/src/test/compile-fail/issue-3021-c.rs
+++ b/src/test/compile-fail/issue-3021-c.rs
@@ -11,8 +11,8 @@
 fn siphash<T>() {
 
     trait t {
-        fn g(&self, x: T) -> T;  //~ ERROR can't use type parameters from outer function; try using
-        //~^ ERROR can't use type parameters from outer function; try using
+        fn g(&self, x: T) -> T;  //~ ERROR can't use type parameters from outer function
+        //~^ ERROR can't use type parameters from outer function
     }
 }
 
diff --git a/src/test/compile-fail/issue-3214.rs b/src/test/compile-fail/issue-3214.rs
index 010cfb54c1a..9a769c39eca 100644
--- a/src/test/compile-fail/issue-3214.rs
+++ b/src/test/compile-fail/issue-3214.rs
@@ -10,7 +10,7 @@
 
 fn foo<T>() {
     struct foo {
-        x: T, //~ ERROR can't use type parameters from outer function;
+        x: T, //~ ERROR can't use type parameters from outer function
     }
 
     impl<T> Drop for foo<T> {
diff --git a/src/test/compile-fail/issue-5997-struct.rs b/src/test/compile-fail/issue-5997-struct.rs
index e9cfafc98df..af9e66b770b 100644
--- a/src/test/compile-fail/issue-5997-struct.rs
+++ b/src/test/compile-fail/issue-5997-struct.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 fn f<T>() -> bool {
-    struct S(T); //~ ERROR can't use type parameters from outer function; try using
+    struct S(T); //~ ERROR can't use type parameters from outer function
 
     true
 }
diff --git a/src/test/compile-fail/nested-ty-params.rs b/src/test/compile-fail/nested-ty-params.rs
index 0ee2a3add87..aac37289bb7 100644
--- a/src/test/compile-fail/nested-ty-params.rs
+++ b/src/test/compile-fail/nested-ty-params.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern:can't use type parameters from outer function; try using
+// error-pattern:can't use type parameters from outer function
 fn hd<U>(v: Vec<U> ) -> U {
     fn hd1(w: [U]) -> U { return w[0]; }
 
diff --git a/src/test/compile-fail/type-arg-out-of-scope.rs b/src/test/compile-fail/type-arg-out-of-scope.rs
index 3249794e5c8..04cd961e97f 100644
--- a/src/test/compile-fail/type-arg-out-of-scope.rs
+++ b/src/test/compile-fail/type-arg-out-of-scope.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern:can't use type parameters from outer function; try using
+// error-pattern:can't use type parameters from outer function
 fn foo<T>(x: T) {
     fn bar(f: Box<FnMut(T) -> T>) { }
 }
diff --git a/src/test/ui/error-codes/E0401.rs b/src/test/ui/error-codes/E0401.rs
index 09bc950efd2..15b94662577 100644
--- a/src/test/ui/error-codes/E0401.rs
+++ b/src/test/ui/error-codes/E0401.rs
@@ -8,11 +8,33 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+trait Baz<T> {}
+
 fn foo<T>(x: T) {
-    fn bar(y: T) { //~ ERROR E0401
+    fn bar<U, V: Baz<U>, W: Fn()>(y: T) { //~ ERROR E0401
+    }
+    fn baz<U,
+           V: Baz<U>,
+           W: Fn()>
+           (y: T) { //~ ERROR E0401
     }
     bar(x);
 }
 
+
+struct A<T> {
+    inner: T,
+}
+
+impl<T> Iterator for A<T> {
+    type Item = u8;
+    fn next(&mut self) -> Option<u8> {
+        fn helper(sel: &Self) -> u8 { //~ ERROR E0401
+            unimplemented!();
+        }
+        Some(helper(self))
+    }
+}
+
 fn main() {
 }
diff --git a/src/test/ui/error-codes/E0401.stderr b/src/test/ui/error-codes/E0401.stderr
index 15e1eda7722..c306ff4a04f 100644
--- a/src/test/ui/error-codes/E0401.stderr
+++ b/src/test/ui/error-codes/E0401.stderr
@@ -1,9 +1,35 @@
-error[E0401]: can't use type parameters from outer function; try using a local type parameter instead
-  --> $DIR/E0401.rs:12:15
+error[E0401]: can't use type parameters from outer function
+  --> $DIR/E0401.rs:14:38
    |
-LL |     fn bar(y: T) { //~ ERROR E0401
-   |               ^ use of type variable from outer function
+LL | fn foo<T>(x: T) {
+   |        - type variable from outer function
+LL |     fn bar<U, V: Baz<U>, W: Fn()>(y: T) { //~ ERROR E0401
+   |        --------------------------    ^ use of type variable from outer function
+   |        |
+   |        help: try using a local type parameter instead: `bar<U, V: Baz<U>, W: Fn(), T>`
 
-error: aborting due to previous error
+error[E0401]: can't use type parameters from outer function
+  --> $DIR/E0401.rs:19:16
+   |
+LL | fn foo<T>(x: T) {
+   |        - type variable from outer function
+...
+LL |            (y: T) { //~ ERROR E0401
+   |                ^ use of type variable from outer function
+   |
+   = help: try using a local type parameter instead
+
+error[E0401]: can't use type parameters from outer function
+  --> $DIR/E0401.rs:32:25
+   |
+LL | impl<T> Iterator for A<T> {
+   | ---- `Self` type implicitely declared here, on the `impl`
+...
+LL |         fn helper(sel: &Self) -> u8 { //~ ERROR E0401
+   |            ------       ^^^^ use of type variable from outer function
+   |            |
+   |            help: try using a local type parameter instead: `helper<Self>`
+
+error: aborting due to 3 previous errors
 
 If you want more information on this error, try using "rustc --explain E0401"