Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve parsing diagnostic for negative supertrait bounds #57364

Merged
merged 1 commit into from
Feb 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 59 additions & 21 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) &&
Expand All @@ -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
Expand Down Expand Up @@ -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))
}
Expand All @@ -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,
Expand Down Expand Up @@ -5492,18 +5492,24 @@ impl<'a> Parser<'a> {
/// TY_BOUND = TY_BOUND_NOPAREN | (TY_BOUND_NOPAREN)
/// TY_BOUND_NOPAREN = [?] [for<LT_PARAM_DEFS>] 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<Span>) -> 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));
if is_bound_start {
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 {
Expand Down Expand Up @@ -5534,28 +5540,60 @@ 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);
hdhoang marked this conversation as resolved.
Show resolved Hide resolved
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
}

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<Span>) -> PResult<'a, GenericBounds> {
self.parse_generic_bounds_common(true, colon_span)
}

/// Parses bounds of a lifetime parameter `BOUND + BOUND + BOUND`, possibly with trailing `+`.
Expand Down Expand Up @@ -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()
};
Expand Down Expand Up @@ -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()
};
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)?;
Expand Down
14 changes: 14 additions & 0 deletions src/test/ui/parser/issue-33418.fixed
Original file line number Diff line number Diff line change
@@ -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() {}
16 changes: 16 additions & 0 deletions src/test/ui/parser/issue-33418.rs
Original file line number Diff line number Diff line change
@@ -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() {}
42 changes: 42 additions & 0 deletions src/test/ui/parser/issue-33418.stderr
Original file line number Diff line number Diff line change
@@ -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