Skip to content

Commit 48ba50e

Browse files
committed
Update "type parameters from outer function" error messages
1 parent b3164f3 commit 48ba50e

File tree

1 file changed

+122
-6
lines changed

1 file changed

+122
-6
lines changed

src/librustc_resolve/lib.rs

+122-6
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};
44+
use syntax::codemap::{dummy_spanned, respan, 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;
@@ -123,7 +123,7 @@ impl Ord for BindingError {
123123

124124
enum ResolutionError<'a> {
125125
/// error E0401: can't use type parameters from outer function
126-
TypeParametersFromOuterFunction,
126+
TypeParametersFromOuterFunction(Def),
127127
/// error E0403: the name is already used for a type parameter in this type parameter list
128128
NameAlreadyUsedInTypeParameterList(Name, &'a Span),
129129
/// error E0407: method is not a member of trait
@@ -173,13 +173,49 @@ fn resolve_struct_error<'sess, 'a>(resolver: &'sess Resolver,
173173
resolution_error: ResolutionError<'a>)
174174
-> DiagnosticBuilder<'sess> {
175175
match resolution_error {
176-
ResolutionError::TypeParametersFromOuterFunction => {
176+
ResolutionError::TypeParametersFromOuterFunction(outer_def) => {
177177
let mut err = struct_span_err!(resolver.session,
178178
span,
179179
E0401,
180-
"can't use type parameters from outer function; \
181-
try using a local type parameter instead");
180+
"can't use type parameters from outer function");
182181
err.span_label(span, "use of type variable from outer function");
182+
match outer_def {
183+
Def::SelfTy(_, maybe_impl_defid) => {
184+
if let Some(impl_span) = maybe_impl_defid.map_or(None,
185+
|def_id| resolver.definitions.opt_span(def_id)) {
186+
let cm = resolver.session.codemap();
187+
err.span_label(reduce_impl_span_to_impl_keyword(cm, impl_span),
188+
"`Self` type implicitely declared here, on the `impl`");
189+
}
190+
},
191+
Def::TyParam(typaram_defid) => {
192+
if let Some(typaram_span) = resolver.definitions.opt_span(typaram_defid) {
193+
err.span_label(typaram_span, "type variable from outer function");
194+
}
195+
},
196+
Def::Mod(..) | Def::Struct(..) | Def::Union(..) | Def::Enum(..) | Def::Variant(..) |
197+
Def::Trait(..) | Def::TyAlias(..) | Def::TyForeign(..) | Def::TraitAlias(..) |
198+
Def::AssociatedTy(..) | Def::PrimTy(..) | Def::Fn(..) | Def::Const(..) |
199+
Def::Static(..) | Def::StructCtor(..) | Def::VariantCtor(..) | Def::Method(..) |
200+
Def::AssociatedConst(..) | Def::Local(..) | Def::Upvar(..) | Def::Label(..) |
201+
Def::Macro(..) | Def::GlobalAsm(..) | Def::Err =>
202+
bug!("TypeParametersFromOuterFunction should only be used with Def::SelfTy or \
203+
Def::TyParam")
204+
}
205+
206+
// Try to retrieve the span of the function signature and generate a new message with
207+
// a local type parameter
208+
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) {
211+
// Suggest the modification to the user
212+
err.span_suggestion(sugg_span,
213+
sugg_msg,
214+
new_snippet);
215+
} else {
216+
err.help("try using a local type parameter instead");
217+
}
218+
183219
err
184220
}
185221
ResolutionError::NameAlreadyUsedInTypeParameterList(name, first_use_span) => {
@@ -358,6 +394,86 @@ fn resolve_struct_error<'sess, 'a>(resolver: &'sess Resolver,
358394
}
359395
}
360396

397+
/// Adjust the impl span so that just the `impl` keyword is taken by removing
398+
/// everything after `<` (`"impl<T> Iterator for A<T> {}" -> "impl"`) and
399+
/// everything after the first whitespace (`"impl Iterator for A" -> "impl"`)
400+
///
401+
/// Attention: The method used is very fragile since it essentially duplicates the work of the
402+
/// parser. If you need to use this function or something similar, please consider updating the
403+
/// codemap functions and this function to something more robust.
404+
fn reduce_impl_span_to_impl_keyword(cm: &CodeMap, impl_span: Span) -> Span {
405+
let impl_span = cm.span_until_char(impl_span, '<');
406+
let impl_span = cm.span_until_whitespace(impl_span);
407+
impl_span
408+
}
409+
410+
/// Take the span of a type parameter in a function signature and try to generate a span for the
411+
/// function name (with generics) and a new snippet for this span with the pointed type parameter as
412+
/// a new local type parameter.
413+
///
414+
/// For instance:
415+
/// ```
416+
/// // Given span
417+
/// fn my_function(param: T)
418+
/// ^ Original span
419+
///
420+
/// // Result
421+
/// fn my_function(param: T)
422+
/// ^^^^^^^^^^^ Generated span with snippet `my_function<T>`
423+
/// ```
424+
///
425+
/// Attention: The method used is very fragile since it essentially duplicates the work of the
426+
/// parser. If you need to use this function or something similar, please consider updating the
427+
/// codemap functions and this function to something more robust.
428+
fn generate_local_type_param_snippet(cm: &CodeMap, span: Span) -> Option<(Span, String)> {
429+
// Try to extend the span to the previous "fn" keyword to retrieve the function
430+
// signature
431+
let sugg_span = cm.span_extend_to_prev_str(span, "fn");
432+
if sugg_span != span {
433+
if let Ok(snippet) = cm.span_to_snippet(sugg_span) {
434+
use syntax::codemap::BytePos;
435+
436+
// 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+
}
442+
443+
// Consume the generics part of the function signature
444+
let mut bracket_counter = 0;
445+
let mut last_char = None;
446+
for c in snippet[offset..].chars() {
447+
match c {
448+
'<' => bracket_counter += 1,
449+
'>' => bracket_counter -= 1,
450+
'(' => if bracket_counter == 0 { break; }
451+
_ => {}
452+
}
453+
offset += c.len_utf8();
454+
last_char = Some(c);
455+
}
456+
457+
// Adjust the suggestion span to encompass the function name with its generics
458+
let sugg_span = sugg_span.with_hi(BytePos(sugg_span.lo().0 + offset as u32));
459+
460+
// Prepare the new suggested snippet to append the type parameter that triggered
461+
// the error in the generics of the function signature
462+
let mut new_snippet = if last_char == Some('>') {
463+
format!("{}, ", &snippet[..(offset - '>'.len_utf8())])
464+
} else {
465+
format!("{}<", &snippet[..offset])
466+
};
467+
new_snippet.push_str(&cm.span_to_snippet(span).unwrap_or("T".to_string()));
468+
new_snippet.push('>');
469+
470+
return Some((sugg_span, new_snippet));
471+
}
472+
}
473+
474+
None
475+
}
476+
361477
#[derive(Copy, Clone, Debug)]
362478
struct BindingInfo {
363479
span: Span,
@@ -3280,7 +3396,7 @@ impl<'a> Resolver<'a> {
32803396
// its scope.
32813397
if record_used {
32823398
resolve_error(self, span,
3283-
ResolutionError::TypeParametersFromOuterFunction);
3399+
ResolutionError::TypeParametersFromOuterFunction(def));
32843400
}
32853401
return Def::Err;
32863402
}

0 commit comments

Comments
 (0)