@@ -5330,37 +5330,46 @@ impl<'a> Parser<'a> {
53305330
53315331 /// Parses (possibly empty) list of lifetime and type arguments and associated type bindings,
53325332 /// possibly including trailing comma.
5333- fn parse_generic_args ( & mut self )
5334- -> PResult < ' a , ( Vec < GenericArg > , Vec < TypeBinding > ) > {
5333+ fn parse_generic_args ( & mut self ) -> PResult < ' a , ( Vec < GenericArg > , Vec < TypeBinding > ) > {
53355334 let mut args = Vec :: new ( ) ;
53365335 let mut bindings = Vec :: new ( ) ;
53375336 let mut seen_type = false ;
53385337 let mut seen_binding = false ;
5338+ let mut first_type_or_binding_span: Option < Span > = None ;
5339+ let mut bad_lifetime_pos = vec ! [ ] ;
5340+ let mut last_comma_span = None ;
5341+ let mut suggestions = vec ! [ ] ;
53395342 loop {
53405343 if self . check_lifetime ( ) && self . look_ahead ( 1 , |t| !t. is_like_plus ( ) ) {
53415344 // Parse lifetime argument.
53425345 args. push ( GenericArg :: Lifetime ( self . expect_lifetime ( ) ) ) ;
53435346 if seen_type || seen_binding {
5344- self . struct_span_err (
5345- self . prev_span ,
5346- "lifetime parameters must be declared prior to type parameters"
5347- )
5348- . span_label ( self . prev_span , "must be declared prior to type parameters" )
5349- . emit ( ) ;
5347+ let remove_sp = last_comma_span. unwrap_or ( self . prev_span ) . to ( self . prev_span ) ;
5348+ bad_lifetime_pos. push ( self . prev_span ) ;
5349+ if let Ok ( snippet) = self . sess . source_map ( ) . span_to_snippet ( self . prev_span ) {
5350+ suggestions. push ( ( remove_sp, String :: new ( ) ) ) ;
5351+ suggestions. push ( (
5352+ first_type_or_binding_span. unwrap ( ) . shrink_to_lo ( ) ,
5353+ format ! ( "{}, " , snippet) ) ) ;
5354+ }
53505355 }
53515356 } else if self . check_ident ( ) && self . look_ahead ( 1 , |t| t == & token:: Eq ) {
53525357 // Parse associated type binding.
53535358 let lo = self . span ;
53545359 let ident = self . parse_ident ( ) ?;
53555360 self . bump ( ) ;
53565361 let ty = self . parse_ty ( ) ?;
5362+ let span = lo. to ( self . prev_span ) ;
53575363 bindings. push ( TypeBinding {
53585364 id : ast:: DUMMY_NODE_ID ,
53595365 ident,
53605366 ty,
5361- span : lo . to ( self . prev_span ) ,
5367+ span,
53625368 } ) ;
53635369 seen_binding = true ;
5370+ if first_type_or_binding_span. is_none ( ) {
5371+ first_type_or_binding_span = Some ( span) ;
5372+ }
53645373 } else if self . check_type ( ) {
53655374 // Parse type argument.
53665375 let ty_param = self . parse_ty ( ) ?;
@@ -5375,6 +5384,9 @@ impl<'a> Parser<'a> {
53755384 )
53765385 . emit ( ) ;
53775386 }
5387+ if first_type_or_binding_span. is_none ( ) {
5388+ first_type_or_binding_span = Some ( ty_param. span ) ;
5389+ }
53785390 args. push ( GenericArg :: Type ( ty_param) ) ;
53795391 seen_type = true ;
53805392 } else {
@@ -5383,8 +5395,30 @@ impl<'a> Parser<'a> {
53835395
53845396 if !self . eat ( & token:: Comma ) {
53855397 break
5398+ } else {
5399+ last_comma_span = Some ( self . prev_span ) ;
53865400 }
53875401 }
5402+ if !bad_lifetime_pos. is_empty ( ) {
5403+ let mut err = self . struct_span_err (
5404+ bad_lifetime_pos. clone ( ) ,
5405+ "lifetime parameters must be declared prior to type parameters"
5406+ ) ;
5407+ for sp in & bad_lifetime_pos {
5408+ err. span_label ( * sp, "must be declared prior to type parameters" ) ;
5409+ }
5410+ if !suggestions. is_empty ( ) {
5411+ err. multipart_suggestion_with_applicability (
5412+ & format ! (
5413+ "move the lifetime parameter{} prior to the first type parameter" ,
5414+ if bad_lifetime_pos. len( ) > 1 { "s" } else { "" } ,
5415+ ) ,
5416+ suggestions,
5417+ Applicability :: MachineApplicable ,
5418+ ) ;
5419+ }
5420+ err. emit ( ) ;
5421+ }
53885422 Ok ( ( args, bindings) )
53895423 }
53905424
0 commit comments