diff options
Diffstat (limited to 'src/libsyntax/parse/parser.rs')
| -rw-r--r-- | src/libsyntax/parse/parser.rs | 67 |
1 files changed, 52 insertions, 15 deletions
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index c7e33a16564..232b8bb5966 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -5530,22 +5530,31 @@ impl<'a> Parser<'a> { fn parse_generic_args(&mut self) -> PResult<'a, (Vec<GenericArg>, Vec<TypeBinding>)> { let mut args = Vec::new(); let mut bindings = Vec::new(); + let mut seen_type = false; let mut seen_binding = false; + + let mut last_comma_span = None; let mut first_type_or_binding_span: Option<Span> = None; + let mut first_binding_span: Option<Span> = None; + let mut bad_lifetime_pos = vec![]; - let mut last_comma_span = None; - let mut suggestions = vec![]; + let mut bad_type_pos = vec![]; + + let mut lifetime_suggestions = vec![]; + let mut type_suggestions = vec![]; loop { if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) { // Parse lifetime argument. args.push(GenericArg::Lifetime(self.expect_lifetime())); + if seen_type || seen_binding { let remove_sp = last_comma_span.unwrap_or(self.prev_span).to(self.prev_span); bad_lifetime_pos.push(self.prev_span); + if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.prev_span) { - suggestions.push((remove_sp, String::new())); - suggestions.push(( + lifetime_suggestions.push((remove_sp, String::new())); + lifetime_suggestions.push(( first_type_or_binding_span.unwrap().shrink_to_lo(), format!("{}, ", snippet))); } @@ -5563,24 +5572,29 @@ impl<'a> Parser<'a> { ty, span, }); + seen_binding = true; if first_type_or_binding_span.is_none() { first_type_or_binding_span = Some(span); } + if first_binding_span.is_none() { + first_binding_span = Some(span); + } } else if self.check_type() { // Parse type argument. let ty_param = self.parse_ty()?; if seen_binding { - self.struct_span_err( - ty_param.span, - "type parameters must be declared prior to associated type bindings" - ) - .span_label( - ty_param.span, - "must be declared prior to associated type bindings", - ) - .emit(); + let remove_sp = last_comma_span.unwrap_or(self.prev_span).to(self.prev_span); + bad_type_pos.push(self.prev_span); + + if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.prev_span) { + type_suggestions.push((remove_sp, String::new())); + type_suggestions.push(( + first_binding_span.unwrap().shrink_to_lo(), + format!("{}, ", snippet))); + } } + if first_type_or_binding_span.is_none() { first_type_or_binding_span = Some(ty_param.span); } @@ -5596,6 +5610,7 @@ impl<'a> Parser<'a> { last_comma_span = Some(self.prev_span); } } + if !bad_lifetime_pos.is_empty() { let mut err = self.struct_span_err( bad_lifetime_pos.clone(), @@ -5604,18 +5619,40 @@ impl<'a> Parser<'a> { for sp in &bad_lifetime_pos { err.span_label(*sp, "must be declared prior to type parameters"); } - if !suggestions.is_empty() { + if !lifetime_suggestions.is_empty() { err.multipart_suggestion_with_applicability( &format!( "move the lifetime parameter{} prior to the first type parameter", if bad_lifetime_pos.len() > 1 { "s" } else { "" }, ), - suggestions, + lifetime_suggestions, Applicability::MachineApplicable, ); } err.emit(); } + + if !bad_type_pos.is_empty() { + let mut err = self.struct_span_err( + bad_type_pos.clone(), + "type parameters must be declared prior to associated type bindings" + ); + for sp in &bad_type_pos { + err.span_label(*sp, "must be declared prior to associated type bindings"); + } + if !type_suggestions.is_empty() { + err.multipart_suggestion_with_applicability( + &format!( + "move the type parameter{} prior to the first associated type binding", + if bad_type_pos.len() > 1 { "s" } else { "" }, + ), + type_suggestions, + Applicability::MachineApplicable, + ); + } + err.emit(); + } + Ok((args, bindings)) } |
