@@ -41,7 +41,7 @@ use rustc::ty;
4141use rustc:: hir:: { Freevar , FreevarMap , TraitCandidate , TraitMap , GlobMap } ;
4242use rustc:: util:: nodemap:: { NodeMap , NodeSet , FxHashMap , FxHashSet , DefIdMap } ;
4343
44- use syntax:: codemap:: { dummy_spanned, respan} ;
44+ use syntax:: codemap:: { dummy_spanned, respan, CodeMap } ;
4545use syntax:: ext:: hygiene:: { Mark , MarkKind , SyntaxContext } ;
4646use syntax:: ast:: { self , Name , NodeId , Ident , SpannedIdent , FloatTy , IntTy , UintTy } ;
4747use syntax:: ext:: base:: SyntaxExtension ;
@@ -123,7 +123,7 @@ impl Ord for BindingError {
123123
124124enum 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 ) ]
362478struct 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