Skip to content

Commit 61d8831

Browse files
committed
Auto merge of #51068 - Crazycolorz5:pluseqsplitting, r=petrochenkov
parser: Split `+=` into `+` and `=` where `+` is explicitly requested (such as generics) Added functions in tokens to check whether a token leads with `+`. Used them when parsing to allow for token splitting of `+=` into `+` and `=`. Fixes #47856
2 parents 0b491a1 + 759a0e0 commit 61d8831

File tree

3 files changed

+71
-10
lines changed

3 files changed

+71
-10
lines changed

src/libsyntax/parse/parser.rs

+44-10
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,40 @@ impl<'a> Parser<'a> {
881881
}
882882
}
883883

884+
/// Expect and consume a `+`. if `+=` is seen, replace it with a `=`
885+
/// and continue. If a `+` is not seen, return false.
886+
///
887+
/// This is using when token splitting += into +.
888+
/// See issue 47856 for an example of when this may occur.
889+
fn eat_plus(&mut self) -> bool {
890+
self.expected_tokens.push(TokenType::Token(token::BinOp(token::Plus)));
891+
match self.token {
892+
token::BinOp(token::Plus) => {
893+
self.bump();
894+
true
895+
}
896+
token::BinOpEq(token::Plus) => {
897+
let span = self.span.with_lo(self.span.lo() + BytePos(1));
898+
self.bump_with(token::Eq, span);
899+
true
900+
}
901+
_ => false,
902+
}
903+
}
904+
905+
906+
/// Checks to see if the next token is either `+` or `+=`.
907+
/// Otherwise returns false.
908+
fn check_plus(&mut self) -> bool {
909+
if self.token.is_like_plus() {
910+
true
911+
}
912+
else {
913+
self.expected_tokens.push(TokenType::Token(token::BinOp(token::Plus)));
914+
false
915+
}
916+
}
917+
884918
/// Expect and consume an `&`. If `&&` is seen, replace it with a single
885919
/// `&` and continue. If an `&` is not seen, signal an error.
886920
fn expect_and(&mut self) -> PResult<'a, ()> {
@@ -1517,7 +1551,7 @@ impl<'a> Parser<'a> {
15171551

15181552
if ts.len() == 1 && !last_comma {
15191553
let ty = ts.into_iter().nth(0).unwrap().into_inner();
1520-
let maybe_bounds = allow_plus && self.token == token::BinOp(token::Plus);
1554+
let maybe_bounds = allow_plus && self.token.is_like_plus();
15211555
match ty.node {
15221556
// `(TY_BOUND_NOPAREN) + BOUND + ...`.
15231557
TyKind::Path(None, ref path) if maybe_bounds => {
@@ -1586,7 +1620,7 @@ impl<'a> Parser<'a> {
15861620
self.parse_ty_bare_fn(lifetime_defs)?
15871621
} else {
15881622
let path = self.parse_path(PathStyle::Type)?;
1589-
let parse_plus = allow_plus && self.check(&token::BinOp(token::Plus));
1623+
let parse_plus = allow_plus && self.check_plus();
15901624
self.parse_remaining_bounds(lifetime_defs, path, lo, parse_plus)?
15911625
}
15921626
} else if self.eat_keyword(keywords::Impl) {
@@ -1603,7 +1637,7 @@ impl<'a> Parser<'a> {
16031637
impl_dyn_multi = bounds.len() > 1 || self.prev_token_kind == PrevTokenKind::Plus;
16041638
TyKind::TraitObject(bounds, TraitObjectSyntax::Dyn)
16051639
} else if self.check(&token::Question) ||
1606-
self.check_lifetime() && self.look_ahead(1, |t| t == &token::BinOp(token::Plus)) {
1640+
self.check_lifetime() && self.look_ahead(1, |t| t.is_like_plus()) {
16071641
// Bound list (trait object type)
16081642
TyKind::TraitObject(self.parse_ty_param_bounds_common(allow_plus)?,
16091643
TraitObjectSyntax::None)
@@ -1623,7 +1657,7 @@ impl<'a> Parser<'a> {
16231657
// Just a type path or bound list (trait object type) starting with a trait.
16241658
// `Type`
16251659
// `Trait1 + Trait2 + 'a`
1626-
if allow_plus && self.check(&token::BinOp(token::Plus)) {
1660+
if allow_plus && self.check_plus() {
16271661
self.parse_remaining_bounds(Vec::new(), path, lo, true)?
16281662
} else {
16291663
TyKind::Path(None, path)
@@ -1650,7 +1684,7 @@ impl<'a> Parser<'a> {
16501684
let poly_trait_ref = PolyTraitRef::new(generic_params, path, lo.to(self.prev_span));
16511685
let mut bounds = vec![TraitTyParamBound(poly_trait_ref, TraitBoundModifier::None)];
16521686
if parse_plus {
1653-
self.bump(); // `+`
1687+
self.eat_plus(); // `+`, or `+=` gets split and `+` is discarded
16541688
bounds.append(&mut self.parse_ty_param_bounds()?);
16551689
}
16561690
Ok(TyKind::TraitObject(bounds, TraitObjectSyntax::None))
@@ -1671,7 +1705,7 @@ impl<'a> Parser<'a> {
16711705

16721706
fn maybe_recover_from_bad_type_plus(&mut self, allow_plus: bool, ty: &Ty) -> PResult<'a, ()> {
16731707
// Do not add `+` to expected tokens.
1674-
if !allow_plus || self.token != token::BinOp(token::Plus) {
1708+
if !allow_plus || !self.token.is_like_plus() {
16751709
return Ok(())
16761710
}
16771711

@@ -4872,7 +4906,7 @@ impl<'a> Parser<'a> {
48724906
break
48734907
}
48744908

4875-
if !allow_plus || !self.eat(&token::BinOp(token::Plus)) {
4909+
if !allow_plus || !self.eat_plus() {
48764910
break
48774911
}
48784912
}
@@ -4891,7 +4925,7 @@ impl<'a> Parser<'a> {
48914925
while self.check_lifetime() {
48924926
lifetimes.push(self.expect_lifetime());
48934927

4894-
if !self.eat(&token::BinOp(token::Plus)) {
4928+
if !self.eat_plus() {
48954929
break
48964930
}
48974931
}
@@ -5037,7 +5071,7 @@ impl<'a> Parser<'a> {
50375071
let mut seen_type = false;
50385072
let mut seen_binding = false;
50395073
loop {
5040-
if self.check_lifetime() && self.look_ahead(1, |t| t != &token::BinOp(token::Plus)) {
5074+
if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) {
50415075
// Parse lifetime argument.
50425076
lifetimes.push(self.expect_lifetime());
50435077
if seen_type || seen_binding {
@@ -5106,7 +5140,7 @@ impl<'a> Parser<'a> {
51065140

51075141
loop {
51085142
let lo = self.span;
5109-
if self.check_lifetime() && self.look_ahead(1, |t| t != &token::BinOp(token::Plus)) {
5143+
if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) {
51105144
let lifetime = self.expect_lifetime();
51115145
// Bounds starting with a colon are mandatory, but possibly empty.
51125146
self.expect(&token::Colon)?;

src/libsyntax/parse/token.rs

+7
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,13 @@ impl Token {
225225
}
226226
}
227227

228+
pub fn is_like_plus(&self) -> bool {
229+
match *self {
230+
BinOp(Plus) | BinOpEq(Plus) => true,
231+
_ => false,
232+
}
233+
}
234+
228235
/// Returns `true` if the token can appear at the start of an expression.
229236
pub fn can_begin_expr(&self) -> bool {
230237
match *self {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// compile-flags: -Z parse-only
12+
// Fixes issue where `+` in generics weren't parsed if they were part of a `+=`.
13+
14+
struct Whitespace<T: Clone + = ()> { t: T }
15+
struct TokenSplit<T: Clone += ()> { t: T }
16+
17+
fn main() {
18+
}
19+
20+
FAIL //~ ERROR

0 commit comments

Comments
 (0)