diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index e6a48912c48b4..5c2b02067d994 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1731,7 +1731,7 @@ impl<'a> Parser<'a> { } } else if self.eat_keyword(keywords::Impl) { // Always parse bounds greedily for better error recovery. - let bounds = self.parse_generic_bounds()?; + let bounds = self.parse_generic_bounds(None)?; impl_dyn_multi = bounds.len() > 1 || self.prev_token_kind == PrevTokenKind::Plus; TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds) } else if self.check_keyword(keywords::Dyn) && @@ -1740,13 +1740,13 @@ impl<'a> Parser<'a> { !can_continue_type_after_non_fn_ident(t))) { self.bump(); // `dyn` // Always parse bounds greedily for better error recovery. - let bounds = self.parse_generic_bounds()?; + let bounds = self.parse_generic_bounds(None)?; impl_dyn_multi = bounds.len() > 1 || self.prev_token_kind == PrevTokenKind::Plus; TyKind::TraitObject(bounds, TraitObjectSyntax::Dyn) } else if self.check(&token::Question) || self.check_lifetime() && self.look_ahead(1, |t| t.is_like_plus()) { // Bound list (trait object type) - TyKind::TraitObject(self.parse_generic_bounds_common(allow_plus)?, + TyKind::TraitObject(self.parse_generic_bounds_common(allow_plus, None)?, TraitObjectSyntax::None) } else if self.eat_lt() { // Qualified path @@ -1792,7 +1792,7 @@ impl<'a> Parser<'a> { let mut bounds = vec![GenericBound::Trait(poly_trait_ref, TraitBoundModifier::None)]; if parse_plus { self.eat_plus(); // `+`, or `+=` gets split and `+` is discarded - bounds.append(&mut self.parse_generic_bounds()?); + bounds.append(&mut self.parse_generic_bounds(None)?); } Ok(TyKind::TraitObject(bounds, TraitObjectSyntax::None)) } @@ -1817,7 +1817,7 @@ impl<'a> Parser<'a> { } self.bump(); // `+` - let bounds = self.parse_generic_bounds()?; + let bounds = self.parse_generic_bounds(None)?; let sum_span = ty.span.to(self.prev_span); let mut err = struct_span_err!(self.sess.span_diagnostic, sum_span, E0178, @@ -5492,11 +5492,16 @@ impl<'a> Parser<'a> { /// TY_BOUND = TY_BOUND_NOPAREN | (TY_BOUND_NOPAREN) /// TY_BOUND_NOPAREN = [?] [for] SIMPLE_PATH (e.g., `?for<'a: 'b> m::Trait<'a>`) /// ``` - fn parse_generic_bounds_common(&mut self, allow_plus: bool) -> PResult<'a, GenericBounds> { + fn parse_generic_bounds_common(&mut self, + allow_plus: bool, + colon_span: Option) -> PResult<'a, GenericBounds> { let mut bounds = Vec::new(); + let mut negative_bounds = Vec::new(); + let mut last_plus_span = None; loop { // This needs to be synchronized with `Token::can_begin_bound`. let is_bound_start = self.check_path() || self.check_lifetime() || + self.check(&token::Not) || // used for error reporting only self.check(&token::Question) || self.check_keyword(keywords::For) || self.check(&token::OpenDelim(token::Paren)); @@ -5504,6 +5509,7 @@ impl<'a> Parser<'a> { let lo = self.span; let has_parens = self.eat(&token::OpenDelim(token::Paren)); let inner_lo = self.span; + let is_negative = self.eat(&token::Not); let question = if self.eat(&token::Question) { Some(self.prev_span) } else { None }; if self.token.is_lifetime() { if let Some(question_span) = question { @@ -5534,13 +5540,20 @@ impl<'a> Parser<'a> { if has_parens { self.expect(&token::CloseDelim(token::Paren))?; } - let poly_trait = PolyTraitRef::new(lifetime_defs, path, lo.to(self.prev_span)); - let modifier = if question.is_some() { - TraitBoundModifier::Maybe + let poly_span = lo.to(self.prev_span); + if is_negative { + negative_bounds.push( + last_plus_span.or(colon_span).unwrap() + .to(poly_span)); } else { - TraitBoundModifier::None - }; - bounds.push(GenericBound::Trait(poly_trait, modifier)); + let poly_trait = PolyTraitRef::new(lifetime_defs, path, poly_span); + let modifier = if question.is_some() { + TraitBoundModifier::Maybe + } else { + TraitBoundModifier::None + }; + bounds.push(GenericBound::Trait(poly_trait, modifier)); + } } } else { break @@ -5548,14 +5561,39 @@ impl<'a> Parser<'a> { if !allow_plus || !self.eat_plus() { break - } + } else { + last_plus_span = Some(self.prev_span); + } + } + + if !negative_bounds.is_empty() { + let plural = negative_bounds.len() > 1; + let mut err = self.struct_span_err(negative_bounds, + "negative trait bounds are not supported"); + let bound_list = colon_span.unwrap().to(self.prev_span); + let mut new_bound_list = String::new(); + if !bounds.is_empty() { + let mut snippets = bounds.iter().map(|bound| bound.span()) + .map(|span| self.sess.source_map().span_to_snippet(span)); + while let Some(Ok(snippet)) = snippets.next() { + new_bound_list.push_str(" + "); + new_bound_list.push_str(&snippet); + } + new_bound_list = new_bound_list.replacen(" +", ":", 1); + } + err.span_suggestion_short(bound_list, + &format!("remove the trait bound{}", + if plural { "s" } else { "" }), + new_bound_list, + Applicability::MachineApplicable); + err.emit(); } return Ok(bounds); } - fn parse_generic_bounds(&mut self) -> PResult<'a, GenericBounds> { - self.parse_generic_bounds_common(true) + fn parse_generic_bounds(&mut self, colon_span: Option) -> PResult<'a, GenericBounds> { + self.parse_generic_bounds_common(true, colon_span) } /// Parses bounds of a lifetime parameter `BOUND + BOUND + BOUND`, possibly with trailing `+`. @@ -5583,7 +5621,7 @@ impl<'a> Parser<'a> { // Parse optional colon and param bounds. let bounds = if self.eat(&token::Colon) { - self.parse_generic_bounds()? + self.parse_generic_bounds(None)? } else { Vec::new() }; @@ -5615,7 +5653,7 @@ impl<'a> Parser<'a> { // Parse optional colon and param bounds. let bounds = if self.eat(&token::Colon) { - self.parse_generic_bounds()? + self.parse_generic_bounds(None)? } else { Vec::new() }; @@ -6028,7 +6066,7 @@ impl<'a> Parser<'a> { // or with mandatory equality sign and the second type. let ty = self.parse_ty()?; if self.eat(&token::Colon) { - let bounds = self.parse_generic_bounds()?; + let bounds = self.parse_generic_bounds(None)?; where_clause.predicates.push(ast::WherePredicate::BoundPredicate( ast::WhereBoundPredicate { span: lo.to(self.prev_span), @@ -6542,14 +6580,14 @@ impl<'a> Parser<'a> { // Parse optional colon and supertrait bounds. let bounds = if self.eat(&token::Colon) { - self.parse_generic_bounds()? + self.parse_generic_bounds(Some(self.prev_span))? } else { Vec::new() }; if self.eat(&token::Eq) { // it's a trait alias - let bounds = self.parse_generic_bounds()?; + let bounds = self.parse_generic_bounds(None)?; tps.where_clause = self.parse_where_clause()?; self.expect(&token::Semi)?; if is_auto == IsAuto::Yes { @@ -7584,7 +7622,7 @@ impl<'a> Parser<'a> { tps.where_clause = self.parse_where_clause()?; let alias = if existential { self.expect(&token::Colon)?; - let bounds = self.parse_generic_bounds()?; + let bounds = self.parse_generic_bounds(None)?; AliasKind::Existential(bounds) } else { self.expect(&token::Eq)?; diff --git a/src/test/ui/parser/issue-33418.fixed b/src/test/ui/parser/issue-33418.fixed new file mode 100644 index 0000000000000..df11f2d855ce0 --- /dev/null +++ b/src/test/ui/parser/issue-33418.fixed @@ -0,0 +1,14 @@ +// run-rustfix + +trait Tr {} //~ ERROR negative trait bounds are not supported +trait Tr2: SuperA {} //~ ERROR negative trait bounds are not supported +trait Tr3: SuperB {} //~ ERROR negative trait bounds are not supported +trait Tr4: SuperB + SuperD {} +trait Tr5 {} + +trait SuperA {} +trait SuperB {} +trait SuperC {} +trait SuperD {} + +fn main() {} diff --git a/src/test/ui/parser/issue-33418.rs b/src/test/ui/parser/issue-33418.rs new file mode 100644 index 0000000000000..5bb5f2afca377 --- /dev/null +++ b/src/test/ui/parser/issue-33418.rs @@ -0,0 +1,16 @@ +// run-rustfix + +trait Tr: !SuperA {} //~ ERROR negative trait bounds are not supported +trait Tr2: SuperA + !SuperB {} //~ ERROR negative trait bounds are not supported +trait Tr3: !SuperA + SuperB {} //~ ERROR negative trait bounds are not supported +trait Tr4: !SuperA + SuperB //~ ERROR negative trait bounds are not supported + + !SuperC + SuperD {} +trait Tr5: !SuperA //~ ERROR negative trait bounds are not supported + + !SuperB {} + +trait SuperA {} +trait SuperB {} +trait SuperC {} +trait SuperD {} + +fn main() {} diff --git a/src/test/ui/parser/issue-33418.stderr b/src/test/ui/parser/issue-33418.stderr new file mode 100644 index 0000000000000..bfe44588a5b0b --- /dev/null +++ b/src/test/ui/parser/issue-33418.stderr @@ -0,0 +1,42 @@ +error: negative trait bounds are not supported + --> $DIR/issue-33418.rs:3:9 + | +LL | trait Tr: !SuperA {} //~ ERROR negative trait bounds are not supported + | ^^^^^^^^^ help: remove the trait bound + +error: negative trait bounds are not supported + --> $DIR/issue-33418.rs:4:19 + | +LL | trait Tr2: SuperA + !SuperB {} //~ ERROR negative trait bounds are not supported + | ---------^^^^^^^^^ + | | + | help: remove the trait bound + +error: negative trait bounds are not supported + --> $DIR/issue-33418.rs:5:10 + | +LL | trait Tr3: !SuperA + SuperB {} //~ ERROR negative trait bounds are not supported + | ^^^^^^^^^--------- + | | + | help: remove the trait bound + +error: negative trait bounds are not supported + --> $DIR/issue-33418.rs:6:10 + | +LL | trait Tr4: !SuperA + SuperB //~ ERROR negative trait bounds are not supported + | __________-^^^^^^^^ +LL | | + !SuperC + SuperD {} + | |_____^^^^^^^^^________- help: remove the trait bounds + +error: negative trait bounds are not supported + --> $DIR/issue-33418.rs:8:10 + | +LL | trait Tr5: !SuperA //~ ERROR negative trait bounds are not supported + | __________-^^^^^^^^ +LL | | + !SuperB {} + | | ^^^^^^^^- + | |_____________| + | help: remove the trait bounds + +error: aborting due to 5 previous errors +