Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some tweaks to "type parameters from outer function" diagnostic #49007

Merged
merged 1 commit into from
Mar 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 18 additions & 12 deletions src/librustc_resolve/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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`");
}
Expand All @@ -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");
}
Expand Down Expand Up @@ -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<Span> {
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.
Expand All @@ -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;
Expand Down
22 changes: 15 additions & 7 deletions src/libsyntax/codemap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/error-codes/E0401.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@
trait Baz<T> {}

fn foo<T>(x: T) {
fn bar<U, V: Baz<U>, W: Fn()>(y: T) { //~ ERROR E0401
fn bfnr<U, V: Baz<U>, W: Fn()>(y: T) { //~ ERROR E0401
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is called below, so you need to rename the call, too. Or just undo the rename

}
fn baz<U,
V: Baz<U>,
W: Fn()>
(y: T) { //~ ERROR E0401
}
bar(x);
bfnr(x);
}


Expand Down
13 changes: 7 additions & 6 deletions src/test/ui/error-codes/E0401.stderr
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
error[E0401]: can't use type parameters from outer function
--> $DIR/E0401.rs:14:38
--> $DIR/E0401.rs:14:39
|
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
LL | fn bfnr<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>`
| help: try using a local type parameter instead: `bfnr<U, V: Baz<U>, W: Fn(), T>`

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 | fn baz<U,
| --- try adding a local type parameter in this method instead
...
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
Expand Down