From 16d424f1471988ed4f7601b8eca42c72a23ca38e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 13 Mar 2018 22:58:45 -0700 Subject: [PATCH] Some tweaks to "type parameters from outer function" diagnostic Follow up to #47574. --- src/librustc_resolve/lib.rs | 30 +++++++++++++++++----------- src/libsyntax/codemap.rs | 22 +++++++++++++------- src/test/ui/error-codes/E0401.rs | 4 ++-- src/test/ui/error-codes/E0401.stderr | 13 ++++++------ 4 files changed, 42 insertions(+), 27 deletions(-) diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index a6b776125ae97..c84caee13e82a 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, CodeMap}; +use syntax::codemap::{dummy_spanned, respan, BytePos, CodeMap}; use syntax::ext::hygiene::{Mark, MarkKind, SyntaxContext}; use syntax::ast::{self, Name, NodeId, Ident, SpannedIdent, FloatTy, IntTy, UintTy}; use syntax::ext::base::SyntaxExtension; @@ -179,11 +179,12 @@ fn resolve_struct_error<'sess, 'a>(resolver: &'sess Resolver, E0401, "can't use type parameters from outer function"); err.span_label(span, "use of type variable from outer function"); + + let cm = resolver.session.codemap(); 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`"); } @@ -206,12 +207,13 @@ fn resolve_struct_error<'sess, 'a>(resolver: &'sess Resolver, // 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) { + if let Some((sugg_span, new_snippet)) = generate_local_type_param_snippet(cm, span) { // Suggest the modification to the user err.span_suggestion(sugg_span, sugg_msg, new_snippet); + } else if let Some(sp) = generate_fn_name_span(cm, span) { + err.span_label(sp, "try adding a local type parameter in this method instead"); } else { err.help("try using a local type parameter instead"); } @@ -407,6 +409,15 @@ fn reduce_impl_span_to_impl_keyword(cm: &CodeMap, impl_span: Span) -> Span { impl_span } +fn generate_fn_name_span(cm: &CodeMap, span: Span) -> Option { + let prev_span = cm.span_extend_to_prev_str(span, "fn", true); + cm.span_to_snippet(prev_span).map(|snippet| { + let len = snippet.find(|c: char| !c.is_alphanumeric() && c != '_') + .expect("no label after fn"); + prev_span.with_hi(BytePos(prev_span.lo().0 + len as u32)) + }).ok() +} + /// 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. @@ -428,17 +439,12 @@ fn reduce_impl_span_to_impl_keyword(cm: &CodeMap, impl_span: Span) -> Span { 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"); + let sugg_span = cm.span_extend_to_prev_str(span, "fn", false); 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(); - } + let mut offset = snippet.find(|c: char| !c.is_alphanumeric() && c != '_') + .expect("no label after fn"); // Consume the generics part of the function signature let mut bracket_counter = 0; diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index c340f1b8c8ab3..951f8a871ca66 100644 --- a/src/libsyntax/codemap.rs +++ b/src/libsyntax/codemap.rs @@ -622,13 +622,21 @@ impl CodeMap { 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)); + /// Extend the given `Span` to just after the previous occurrence of `pat` when surrounded by + /// whitespace. 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, accept_newlines: bool) -> Span { + // assure that the pattern is delimited, to avoid the following + // fn my_fn() + // ^^^^ returned span without the check + // ---------- correct span + for ws in &[" ", "\t", "\n"] { + let pat = pat.to_owned() + ws; + 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') || accept_newlines) { + return sp.with_lo(BytePos(sp.lo().0 - prev_source.len() as u32)); + } } } diff --git a/src/test/ui/error-codes/E0401.rs b/src/test/ui/error-codes/E0401.rs index 15b946625778c..4fc74f5ef2219 100644 --- a/src/test/ui/error-codes/E0401.rs +++ b/src/test/ui/error-codes/E0401.rs @@ -11,14 +11,14 @@ trait Baz {} fn foo(x: T) { - fn bar, W: Fn()>(y: T) { //~ ERROR E0401 + fn bfnr, W: Fn()>(y: T) { //~ ERROR E0401 } fn baz, W: Fn()> (y: T) { //~ ERROR E0401 } - bar(x); + bfnr(x); } diff --git a/src/test/ui/error-codes/E0401.stderr b/src/test/ui/error-codes/E0401.stderr index c306ff4a04f6a..e5a2be4dfb026 100644 --- a/src/test/ui/error-codes/E0401.stderr +++ b/src/test/ui/error-codes/E0401.stderr @@ -1,12 +1,12 @@ error[E0401]: can't use type parameters from outer function - --> $DIR/E0401.rs:14:38 + --> $DIR/E0401.rs:14:39 | LL | fn foo(x: T) { | - type variable from outer function -LL | fn bar, W: Fn()>(y: T) { //~ ERROR E0401 - | -------------------------- ^ use of type variable from outer function +LL | fn bfnr, W: Fn()>(y: T) { //~ ERROR E0401 + | --------------------------- ^ use of type variable from outer function | | - | help: try using a local type parameter instead: `bar, W: Fn(), T>` + | help: try using a local type parameter instead: `bfnr, W: Fn(), T>` error[E0401]: can't use type parameters from outer function --> $DIR/E0401.rs:19:16 @@ -14,10 +14,11 @@ error[E0401]: can't use type parameters from outer function LL | fn foo(x: T) { | - type variable from outer function ... +LL | fn baz $DIR/E0401.rs:32:25