@@ -41,7 +41,7 @@ use rustc::ty;
41
41
use rustc:: hir:: { Freevar , FreevarMap , TraitCandidate , TraitMap , GlobMap } ;
42
42
use rustc:: util:: nodemap:: { NodeMap , NodeSet , FxHashMap , FxHashSet , DefIdMap } ;
43
43
44
- use syntax:: codemap:: { dummy_spanned, respan} ;
44
+ use syntax:: codemap:: { dummy_spanned, respan, CodeMap } ;
45
45
use syntax:: ext:: hygiene:: { Mark , MarkKind , SyntaxContext } ;
46
46
use syntax:: ast:: { self , Name , NodeId , Ident , SpannedIdent , FloatTy , IntTy , UintTy } ;
47
47
use syntax:: ext:: base:: SyntaxExtension ;
@@ -123,7 +123,7 @@ impl Ord for BindingError {
123
123
124
124
enum ResolutionError < ' a > {
125
125
/// error E0401: can't use type parameters from outer function
126
- TypeParametersFromOuterFunction ,
126
+ TypeParametersFromOuterFunction ( Def ) ,
127
127
/// error E0403: the name is already used for a type parameter in this type parameter list
128
128
NameAlreadyUsedInTypeParameterList ( Name , & ' a Span ) ,
129
129
/// error E0407: method is not a member of trait
@@ -173,13 +173,49 @@ fn resolve_struct_error<'sess, 'a>(resolver: &'sess Resolver,
173
173
resolution_error : ResolutionError < ' a > )
174
174
-> DiagnosticBuilder < ' sess > {
175
175
match resolution_error {
176
- ResolutionError :: TypeParametersFromOuterFunction => {
176
+ ResolutionError :: TypeParametersFromOuterFunction ( outer_def ) => {
177
177
let mut err = struct_span_err ! ( resolver. session,
178
178
span,
179
179
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" ) ;
182
181
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
+
183
219
err
184
220
}
185
221
ResolutionError :: NameAlreadyUsedInTypeParameterList ( name, first_use_span) => {
@@ -358,6 +394,86 @@ fn resolve_struct_error<'sess, 'a>(resolver: &'sess Resolver,
358
394
}
359
395
}
360
396
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
+
361
477
#[ derive( Copy , Clone , Debug ) ]
362
478
struct BindingInfo {
363
479
span : Span ,
@@ -3280,7 +3396,7 @@ impl<'a> Resolver<'a> {
3280
3396
// its scope.
3281
3397
if record_used {
3282
3398
resolve_error ( self , span,
3283
- ResolutionError :: TypeParametersFromOuterFunction ) ;
3399
+ ResolutionError :: TypeParametersFromOuterFunction ( def ) ) ;
3284
3400
}
3285
3401
return Def :: Err ;
3286
3402
}
0 commit comments