Skip to content

Commit 7cfddfb

Browse files
committed
Improve parsing diagnostic for negative supertrait bounds
1 parent 082c861 commit 7cfddfb

File tree

4 files changed

+131
-21
lines changed

4 files changed

+131
-21
lines changed

src/libsyntax/parse/parser.rs

+59-21
Original file line numberDiff line numberDiff line change
@@ -1731,7 +1731,7 @@ impl<'a> Parser<'a> {
17311731
}
17321732
} else if self.eat_keyword(keywords::Impl) {
17331733
// Always parse bounds greedily for better error recovery.
1734-
let bounds = self.parse_generic_bounds()?;
1734+
let bounds = self.parse_generic_bounds(None)?;
17351735
impl_dyn_multi = bounds.len() > 1 || self.prev_token_kind == PrevTokenKind::Plus;
17361736
TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds)
17371737
} else if self.check_keyword(keywords::Dyn) &&
@@ -1740,13 +1740,13 @@ impl<'a> Parser<'a> {
17401740
!can_continue_type_after_non_fn_ident(t))) {
17411741
self.bump(); // `dyn`
17421742
// Always parse bounds greedily for better error recovery.
1743-
let bounds = self.parse_generic_bounds()?;
1743+
let bounds = self.parse_generic_bounds(None)?;
17441744
impl_dyn_multi = bounds.len() > 1 || self.prev_token_kind == PrevTokenKind::Plus;
17451745
TyKind::TraitObject(bounds, TraitObjectSyntax::Dyn)
17461746
} else if self.check(&token::Question) ||
17471747
self.check_lifetime() && self.look_ahead(1, |t| t.is_like_plus()) {
17481748
// Bound list (trait object type)
1749-
TyKind::TraitObject(self.parse_generic_bounds_common(allow_plus)?,
1749+
TyKind::TraitObject(self.parse_generic_bounds_common(allow_plus, None)?,
17501750
TraitObjectSyntax::None)
17511751
} else if self.eat_lt() {
17521752
// Qualified path
@@ -1792,7 +1792,7 @@ impl<'a> Parser<'a> {
17921792
let mut bounds = vec![GenericBound::Trait(poly_trait_ref, TraitBoundModifier::None)];
17931793
if parse_plus {
17941794
self.eat_plus(); // `+`, or `+=` gets split and `+` is discarded
1795-
bounds.append(&mut self.parse_generic_bounds()?);
1795+
bounds.append(&mut self.parse_generic_bounds(None)?);
17961796
}
17971797
Ok(TyKind::TraitObject(bounds, TraitObjectSyntax::None))
17981798
}
@@ -1817,7 +1817,7 @@ impl<'a> Parser<'a> {
18171817
}
18181818

18191819
self.bump(); // `+`
1820-
let bounds = self.parse_generic_bounds()?;
1820+
let bounds = self.parse_generic_bounds(None)?;
18211821
let sum_span = ty.span.to(self.prev_span);
18221822

18231823
let mut err = struct_span_err!(self.sess.span_diagnostic, sum_span, E0178,
@@ -5492,18 +5492,24 @@ impl<'a> Parser<'a> {
54925492
/// TY_BOUND = TY_BOUND_NOPAREN | (TY_BOUND_NOPAREN)
54935493
/// TY_BOUND_NOPAREN = [?] [for<LT_PARAM_DEFS>] SIMPLE_PATH (e.g., `?for<'a: 'b> m::Trait<'a>`)
54945494
/// ```
5495-
fn parse_generic_bounds_common(&mut self, allow_plus: bool) -> PResult<'a, GenericBounds> {
5495+
fn parse_generic_bounds_common(&mut self,
5496+
allow_plus: bool,
5497+
colon_span: Option<Span>) -> PResult<'a, GenericBounds> {
54965498
let mut bounds = Vec::new();
5499+
let mut negative_bounds = Vec::new();
5500+
let mut last_plus_span = None;
54975501
loop {
54985502
// This needs to be synchronized with `Token::can_begin_bound`.
54995503
let is_bound_start = self.check_path() || self.check_lifetime() ||
5504+
self.check(&token::Not) || // used for error reporting only
55005505
self.check(&token::Question) ||
55015506
self.check_keyword(keywords::For) ||
55025507
self.check(&token::OpenDelim(token::Paren));
55035508
if is_bound_start {
55045509
let lo = self.span;
55055510
let has_parens = self.eat(&token::OpenDelim(token::Paren));
55065511
let inner_lo = self.span;
5512+
let is_negative = self.eat(&token::Not);
55075513
let question = if self.eat(&token::Question) { Some(self.prev_span) } else { None };
55085514
if self.token.is_lifetime() {
55095515
if let Some(question_span) = question {
@@ -5534,28 +5540,60 @@ impl<'a> Parser<'a> {
55345540
if has_parens {
55355541
self.expect(&token::CloseDelim(token::Paren))?;
55365542
}
5537-
let poly_trait = PolyTraitRef::new(lifetime_defs, path, lo.to(self.prev_span));
5538-
let modifier = if question.is_some() {
5539-
TraitBoundModifier::Maybe
5543+
let poly_span = lo.to(self.prev_span);
5544+
if is_negative {
5545+
negative_bounds.push(
5546+
last_plus_span.or(colon_span).unwrap()
5547+
.to(poly_span));
55405548
} else {
5541-
TraitBoundModifier::None
5542-
};
5543-
bounds.push(GenericBound::Trait(poly_trait, modifier));
5549+
let poly_trait = PolyTraitRef::new(lifetime_defs, path, poly_span);
5550+
let modifier = if question.is_some() {
5551+
TraitBoundModifier::Maybe
5552+
} else {
5553+
TraitBoundModifier::None
5554+
};
5555+
bounds.push(GenericBound::Trait(poly_trait, modifier));
5556+
}
55445557
}
55455558
} else {
55465559
break
55475560
}
55485561

55495562
if !allow_plus || !self.eat_plus() {
55505563
break
5551-
}
5564+
} else {
5565+
last_plus_span = Some(self.prev_span);
5566+
}
5567+
}
5568+
5569+
if !negative_bounds.is_empty() {
5570+
let plural = negative_bounds.len() > 1;
5571+
let mut err = self.struct_span_err(negative_bounds,
5572+
"negative trait bounds are not supported");
5573+
let bound_list = colon_span.unwrap().to(self.prev_span);
5574+
let mut new_bound_list = String::new();
5575+
if !bounds.is_empty() {
5576+
let mut snippets = bounds.iter().map(|bound| bound.span())
5577+
.map(|span| self.sess.source_map().span_to_snippet(span));
5578+
while let Some(Ok(snippet)) = snippets.next() {
5579+
new_bound_list.push_str(" + ");
5580+
new_bound_list.push_str(&snippet);
5581+
}
5582+
new_bound_list = new_bound_list.replacen(" +", ":", 1);
5583+
}
5584+
err.span_suggestion_short(bound_list,
5585+
&format!("remove the trait bound{}",
5586+
if plural { "s" } else { "" }),
5587+
new_bound_list,
5588+
Applicability::MachineApplicable);
5589+
err.emit();
55525590
}
55535591

55545592
return Ok(bounds);
55555593
}
55565594

5557-
fn parse_generic_bounds(&mut self) -> PResult<'a, GenericBounds> {
5558-
self.parse_generic_bounds_common(true)
5595+
fn parse_generic_bounds(&mut self, colon_span: Option<Span>) -> PResult<'a, GenericBounds> {
5596+
self.parse_generic_bounds_common(true, colon_span)
55595597
}
55605598

55615599
/// Parses bounds of a lifetime parameter `BOUND + BOUND + BOUND`, possibly with trailing `+`.
@@ -5583,7 +5621,7 @@ impl<'a> Parser<'a> {
55835621

55845622
// Parse optional colon and param bounds.
55855623
let bounds = if self.eat(&token::Colon) {
5586-
self.parse_generic_bounds()?
5624+
self.parse_generic_bounds(None)?
55875625
} else {
55885626
Vec::new()
55895627
};
@@ -5615,7 +5653,7 @@ impl<'a> Parser<'a> {
56155653

56165654
// Parse optional colon and param bounds.
56175655
let bounds = if self.eat(&token::Colon) {
5618-
self.parse_generic_bounds()?
5656+
self.parse_generic_bounds(None)?
56195657
} else {
56205658
Vec::new()
56215659
};
@@ -6028,7 +6066,7 @@ impl<'a> Parser<'a> {
60286066
// or with mandatory equality sign and the second type.
60296067
let ty = self.parse_ty()?;
60306068
if self.eat(&token::Colon) {
6031-
let bounds = self.parse_generic_bounds()?;
6069+
let bounds = self.parse_generic_bounds(None)?;
60326070
where_clause.predicates.push(ast::WherePredicate::BoundPredicate(
60336071
ast::WhereBoundPredicate {
60346072
span: lo.to(self.prev_span),
@@ -6542,14 +6580,14 @@ impl<'a> Parser<'a> {
65426580

65436581
// Parse optional colon and supertrait bounds.
65446582
let bounds = if self.eat(&token::Colon) {
6545-
self.parse_generic_bounds()?
6583+
self.parse_generic_bounds(Some(self.prev_span))?
65466584
} else {
65476585
Vec::new()
65486586
};
65496587

65506588
if self.eat(&token::Eq) {
65516589
// it's a trait alias
6552-
let bounds = self.parse_generic_bounds()?;
6590+
let bounds = self.parse_generic_bounds(None)?;
65536591
tps.where_clause = self.parse_where_clause()?;
65546592
self.expect(&token::Semi)?;
65556593
if is_auto == IsAuto::Yes {
@@ -7584,7 +7622,7 @@ impl<'a> Parser<'a> {
75847622
tps.where_clause = self.parse_where_clause()?;
75857623
let alias = if existential {
75867624
self.expect(&token::Colon)?;
7587-
let bounds = self.parse_generic_bounds()?;
7625+
let bounds = self.parse_generic_bounds(None)?;
75887626
AliasKind::Existential(bounds)
75897627
} else {
75907628
self.expect(&token::Eq)?;

src/test/ui/parser/issue-33418.fixed

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// run-rustfix
2+
3+
trait Tr {} //~ ERROR negative trait bounds are not supported
4+
trait Tr2: SuperA {} //~ ERROR negative trait bounds are not supported
5+
trait Tr3: SuperB {} //~ ERROR negative trait bounds are not supported
6+
trait Tr4: SuperB + SuperD {}
7+
trait Tr5 {}
8+
9+
trait SuperA {}
10+
trait SuperB {}
11+
trait SuperC {}
12+
trait SuperD {}
13+
14+
fn main() {}

src/test/ui/parser/issue-33418.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// run-rustfix
2+
3+
trait Tr: !SuperA {} //~ ERROR negative trait bounds are not supported
4+
trait Tr2: SuperA + !SuperB {} //~ ERROR negative trait bounds are not supported
5+
trait Tr3: !SuperA + SuperB {} //~ ERROR negative trait bounds are not supported
6+
trait Tr4: !SuperA + SuperB //~ ERROR negative trait bounds are not supported
7+
+ !SuperC + SuperD {}
8+
trait Tr5: !SuperA //~ ERROR negative trait bounds are not supported
9+
+ !SuperB {}
10+
11+
trait SuperA {}
12+
trait SuperB {}
13+
trait SuperC {}
14+
trait SuperD {}
15+
16+
fn main() {}

src/test/ui/parser/issue-33418.stderr

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
error: negative trait bounds are not supported
2+
--> $DIR/issue-33418.rs:3:9
3+
|
4+
LL | trait Tr: !SuperA {} //~ ERROR negative trait bounds are not supported
5+
| ^^^^^^^^^ help: remove the trait bound
6+
7+
error: negative trait bounds are not supported
8+
--> $DIR/issue-33418.rs:4:19
9+
|
10+
LL | trait Tr2: SuperA + !SuperB {} //~ ERROR negative trait bounds are not supported
11+
| ---------^^^^^^^^^
12+
| |
13+
| help: remove the trait bound
14+
15+
error: negative trait bounds are not supported
16+
--> $DIR/issue-33418.rs:5:10
17+
|
18+
LL | trait Tr3: !SuperA + SuperB {} //~ ERROR negative trait bounds are not supported
19+
| ^^^^^^^^^---------
20+
| |
21+
| help: remove the trait bound
22+
23+
error: negative trait bounds are not supported
24+
--> $DIR/issue-33418.rs:6:10
25+
|
26+
LL | trait Tr4: !SuperA + SuperB //~ ERROR negative trait bounds are not supported
27+
| __________-^^^^^^^^
28+
LL | | + !SuperC + SuperD {}
29+
| |_____^^^^^^^^^________- help: remove the trait bounds
30+
31+
error: negative trait bounds are not supported
32+
--> $DIR/issue-33418.rs:8:10
33+
|
34+
LL | trait Tr5: !SuperA //~ ERROR negative trait bounds are not supported
35+
| __________-^^^^^^^^
36+
LL | | + !SuperB {}
37+
| | ^^^^^^^^-
38+
| |_____________|
39+
| help: remove the trait bounds
40+
41+
error: aborting due to 5 previous errors
42+

0 commit comments

Comments
 (0)