Skip to content

Commit 16d424f

Browse files
committed
Some tweaks to "type parameters from outer function" diagnostic
Follow up to #47574.
1 parent 883e746 commit 16d424f

File tree

4 files changed

+42
-27
lines changed

4 files changed

+42
-27
lines changed

src/librustc_resolve/lib.rs

+18-12
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ use rustc::ty;
4141
use rustc::hir::{Freevar, FreevarMap, TraitCandidate, TraitMap, GlobMap};
4242
use rustc::util::nodemap::{NodeMap, NodeSet, FxHashMap, FxHashSet, DefIdMap};
4343

44-
use syntax::codemap::{dummy_spanned, respan, CodeMap};
44+
use syntax::codemap::{dummy_spanned, respan, BytePos, CodeMap};
4545
use syntax::ext::hygiene::{Mark, MarkKind, SyntaxContext};
4646
use syntax::ast::{self, Name, NodeId, Ident, SpannedIdent, FloatTy, IntTy, UintTy};
4747
use syntax::ext::base::SyntaxExtension;
@@ -179,11 +179,12 @@ fn resolve_struct_error<'sess, 'a>(resolver: &'sess Resolver,
179179
E0401,
180180
"can't use type parameters from outer function");
181181
err.span_label(span, "use of type variable from outer function");
182+
183+
let cm = resolver.session.codemap();
182184
match outer_def {
183185
Def::SelfTy(_, maybe_impl_defid) => {
184186
if let Some(impl_span) = maybe_impl_defid.map_or(None,
185187
|def_id| resolver.definitions.opt_span(def_id)) {
186-
let cm = resolver.session.codemap();
187188
err.span_label(reduce_impl_span_to_impl_keyword(cm, impl_span),
188189
"`Self` type implicitely declared here, on the `impl`");
189190
}
@@ -206,12 +207,13 @@ fn resolve_struct_error<'sess, 'a>(resolver: &'sess Resolver,
206207
// Try to retrieve the span of the function signature and generate a new message with
207208
// a local type parameter
208209
let sugg_msg = "try using a local type parameter instead";
209-
if let Some((sugg_span, new_snippet)) = generate_local_type_param_snippet(
210-
resolver.session.codemap(), span) {
210+
if let Some((sugg_span, new_snippet)) = generate_local_type_param_snippet(cm, span) {
211211
// Suggest the modification to the user
212212
err.span_suggestion(sugg_span,
213213
sugg_msg,
214214
new_snippet);
215+
} else if let Some(sp) = generate_fn_name_span(cm, span) {
216+
err.span_label(sp, "try adding a local type parameter in this method instead");
215217
} else {
216218
err.help("try using a local type parameter instead");
217219
}
@@ -407,6 +409,15 @@ fn reduce_impl_span_to_impl_keyword(cm: &CodeMap, impl_span: Span) -> Span {
407409
impl_span
408410
}
409411

412+
fn generate_fn_name_span(cm: &CodeMap, span: Span) -> Option<Span> {
413+
let prev_span = cm.span_extend_to_prev_str(span, "fn", true);
414+
cm.span_to_snippet(prev_span).map(|snippet| {
415+
let len = snippet.find(|c: char| !c.is_alphanumeric() && c != '_')
416+
.expect("no label after fn");
417+
prev_span.with_hi(BytePos(prev_span.lo().0 + len as u32))
418+
}).ok()
419+
}
420+
410421
/// Take the span of a type parameter in a function signature and try to generate a span for the
411422
/// function name (with generics) and a new snippet for this span with the pointed type parameter as
412423
/// a new local type parameter.
@@ -428,17 +439,12 @@ fn reduce_impl_span_to_impl_keyword(cm: &CodeMap, impl_span: Span) -> Span {
428439
fn generate_local_type_param_snippet(cm: &CodeMap, span: Span) -> Option<(Span, String)> {
429440
// Try to extend the span to the previous "fn" keyword to retrieve the function
430441
// signature
431-
let sugg_span = cm.span_extend_to_prev_str(span, "fn");
442+
let sugg_span = cm.span_extend_to_prev_str(span, "fn", false);
432443
if sugg_span != span {
433444
if let Ok(snippet) = cm.span_to_snippet(sugg_span) {
434-
use syntax::codemap::BytePos;
435-
436445
// Consume the function name
437-
let mut offset = 0;
438-
for c in snippet.chars().take_while(|c| c.is_ascii_alphanumeric() ||
439-
*c == '_') {
440-
offset += c.len_utf8();
441-
}
446+
let mut offset = snippet.find(|c: char| !c.is_alphanumeric() && c != '_')
447+
.expect("no label after fn");
442448

443449
// Consume the generics part of the function signature
444450
let mut bracket_counter = 0;

src/libsyntax/codemap.rs

+15-7
Original file line numberDiff line numberDiff line change
@@ -622,13 +622,21 @@ impl CodeMap {
622622
sp
623623
}
624624

625-
/// Extend the given `Span` to just after the previous occurrence of `pat`. Return the same span
626-
/// if no character could be found or if an error occurred while retrieving the code snippet.
627-
pub fn span_extend_to_prev_str(&self, sp: Span, pat: &str) -> Span {
628-
if let Ok(prev_source) = self.span_to_prev_source(sp) {
629-
let prev_source = prev_source.rsplit(pat).nth(0).unwrap_or("").trim_left();
630-
if !prev_source.is_empty() && !prev_source.contains('\n') {
631-
return sp.with_lo(BytePos(sp.lo().0 - prev_source.len() as u32));
625+
/// Extend the given `Span` to just after the previous occurrence of `pat` when surrounded by
626+
/// whitespace. Return the same span if no character could be found or if an error occurred
627+
/// while retrieving the code snippet.
628+
pub fn span_extend_to_prev_str(&self, sp: Span, pat: &str, accept_newlines: bool) -> Span {
629+
// assure that the pattern is delimited, to avoid the following
630+
// fn my_fn()
631+
// ^^^^ returned span without the check
632+
// ---------- correct span
633+
for ws in &[" ", "\t", "\n"] {
634+
let pat = pat.to_owned() + ws;
635+
if let Ok(prev_source) = self.span_to_prev_source(sp) {
636+
let prev_source = prev_source.rsplit(&pat).nth(0).unwrap_or("").trim_left();
637+
if !prev_source.is_empty() && (!prev_source.contains('\n') || accept_newlines) {
638+
return sp.with_lo(BytePos(sp.lo().0 - prev_source.len() as u32));
639+
}
632640
}
633641
}
634642

src/test/ui/error-codes/E0401.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@
1111
trait Baz<T> {}
1212

1313
fn foo<T>(x: T) {
14-
fn bar<U, V: Baz<U>, W: Fn()>(y: T) { //~ ERROR E0401
14+
fn bfnr<U, V: Baz<U>, W: Fn()>(y: T) { //~ ERROR E0401
1515
}
1616
fn baz<U,
1717
V: Baz<U>,
1818
W: Fn()>
1919
(y: T) { //~ ERROR E0401
2020
}
21-
bar(x);
21+
bfnr(x);
2222
}
2323

2424

src/test/ui/error-codes/E0401.stderr

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
11
error[E0401]: can't use type parameters from outer function
2-
--> $DIR/E0401.rs:14:38
2+
--> $DIR/E0401.rs:14:39
33
|
44
LL | fn foo<T>(x: T) {
55
| - type variable from outer function
6-
LL | fn bar<U, V: Baz<U>, W: Fn()>(y: T) { //~ ERROR E0401
7-
| -------------------------- ^ use of type variable from outer function
6+
LL | fn bfnr<U, V: Baz<U>, W: Fn()>(y: T) { //~ ERROR E0401
7+
| --------------------------- ^ use of type variable from outer function
88
| |
9-
| help: try using a local type parameter instead: `bar<U, V: Baz<U>, W: Fn(), T>`
9+
| help: try using a local type parameter instead: `bfnr<U, V: Baz<U>, W: Fn(), T>`
1010

1111
error[E0401]: can't use type parameters from outer function
1212
--> $DIR/E0401.rs:19:16
1313
|
1414
LL | fn foo<T>(x: T) {
1515
| - type variable from outer function
1616
...
17+
LL | fn baz<U,
18+
| --- try adding a local type parameter in this method instead
19+
...
1720
LL | (y: T) { //~ ERROR E0401
1821
| ^ use of type variable from outer function
19-
|
20-
= help: try using a local type parameter instead
2122

2223
error[E0401]: can't use type parameters from outer function
2324
--> $DIR/E0401.rs:32:25

0 commit comments

Comments
 (0)