@@ -5172,8 +5172,12 @@ impl<'a> Parser<'a> {
51725172 /// Parses (possibly empty) list of lifetime and type parameters, possibly including
51735173 /// trailing comma and erroneous trailing attributes.
51745174 crate fn parse_generic_params ( & mut self ) -> PResult < ' a , Vec < ast:: GenericParam > > {
5175+ let mut lifetimes = Vec :: new ( ) ;
51755176 let mut params = Vec :: new ( ) ;
5176- let mut seen_ty_param = false ;
5177+ let mut seen_ty_param: Option < Span > = None ;
5178+ let mut last_comma_span = None ;
5179+ let mut bad_lifetime_pos = vec ! [ ] ;
5180+ let mut suggestions = vec ! [ ] ;
51775181 loop {
51785182 let attrs = self . parse_outer_attributes ( ) ?;
51795183 if self . check_lifetime ( ) {
@@ -5184,25 +5188,42 @@ impl<'a> Parser<'a> {
51845188 } else {
51855189 Vec :: new ( )
51865190 } ;
5187- params . push ( ast:: GenericParam {
5191+ lifetimes . push ( ast:: GenericParam {
51885192 ident : lifetime. ident ,
51895193 id : lifetime. id ,
51905194 attrs : attrs. into ( ) ,
51915195 bounds,
51925196 kind : ast:: GenericParamKind :: Lifetime ,
51935197 } ) ;
5194- if seen_ty_param {
5195- self . span_err ( self . prev_span ,
5196- "lifetime parameters must be declared prior to type parameters" ) ;
5198+ if let Some ( sp) = seen_ty_param {
5199+ let param_span = self . prev_span ;
5200+ let ate_comma = self . eat ( & token:: Comma ) ;
5201+ let remove_sp = if ate_comma {
5202+ param_span. until ( self . span )
5203+ } else {
5204+ last_comma_span. unwrap_or ( param_span) . to ( param_span)
5205+ } ;
5206+ bad_lifetime_pos. push ( param_span) ;
5207+
5208+ if let Ok ( snippet) = self . sess . source_map ( ) . span_to_snippet ( param_span) {
5209+ suggestions. push ( ( remove_sp, String :: new ( ) ) ) ;
5210+ suggestions. push ( ( sp. shrink_to_lo ( ) , format ! ( "{}, " , snippet) ) ) ;
5211+ }
5212+ if ate_comma {
5213+ last_comma_span = Some ( self . prev_span ) ;
5214+ continue
5215+ }
51975216 }
51985217 } else if self . check_ident ( ) {
51995218 // Parse type parameter.
52005219 params. push ( self . parse_ty_param ( attrs) ?) ;
5201- seen_ty_param = true ;
5220+ if seen_ty_param. is_none ( ) {
5221+ seen_ty_param = Some ( self . prev_span ) ;
5222+ }
52025223 } else {
52035224 // Check for trailing attributes and stop parsing.
52045225 if !attrs. is_empty ( ) {
5205- let param_kind = if seen_ty_param { "type" } else { "lifetime" } ;
5226+ let param_kind = if seen_ty_param. is_some ( ) { "type" } else { "lifetime" } ;
52065227 self . span_err ( attrs[ 0 ] . span ,
52075228 & format ! ( "trailing attribute after {} parameters" , param_kind) ) ;
52085229 }
@@ -5212,8 +5233,24 @@ impl<'a> Parser<'a> {
52125233 if !self . eat ( & token:: Comma ) {
52135234 break
52145235 }
5236+ last_comma_span = Some ( self . prev_span ) ;
5237+ }
5238+ if !bad_lifetime_pos. is_empty ( ) {
5239+ let mut err = self . struct_span_err (
5240+ bad_lifetime_pos,
5241+ "lifetime parameters must be declared prior to type parameters" ,
5242+ ) ;
5243+ if !suggestions. is_empty ( ) {
5244+ err. multipart_suggestion_with_applicability (
5245+ "move the lifetime parameter prior to the first type parameter" ,
5246+ suggestions,
5247+ Applicability :: MachineApplicable ,
5248+ ) ;
5249+ }
5250+ err. emit ( ) ;
52155251 }
5216- Ok ( params)
5252+ lifetimes. extend ( params) ; // ensure the correct order of lifetimes and type params
5253+ Ok ( lifetimes)
52175254 }
52185255
52195256 /// Parse a set of optional generic type parameter declarations. Where
0 commit comments