Skip to content

Commit 903c9df

Browse files
committed
extract should_continue_as_assoc_expr
1 parent be463cb commit 903c9df

File tree

1 file changed

+55
-45
lines changed

1 file changed

+55
-45
lines changed

src/librustc_parse/parser/expr.rs

Lines changed: 55 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -155,53 +155,13 @@ impl<'a> Parser<'a> {
155155
};
156156
let last_type_ascription_set = self.last_type_ascription.is_some();
157157

158-
match (self.expr_is_complete(&lhs), AssocOp::from_token(&self.token)) {
159-
(true, None) => {
160-
self.last_type_ascription = None;
161-
// Semi-statement forms are odd. See https://github.com/rust-lang/rust/issues/29071
162-
return Ok(lhs);
163-
}
164-
(false, _) => {} // continue parsing the expression
165-
// An exhaustive check is done in the following block, but these are checked first
166-
// because they *are* ambiguous but also reasonable looking incorrect syntax, so we
167-
// want to keep their span info to improve diagnostics in these cases in a later stage.
168-
(true, Some(AssocOp::Multiply)) | // `{ 42 } *foo = bar;` or `{ 42 } * 3`
169-
(true, Some(AssocOp::Subtract)) | // `{ 42 } -5`
170-
(true, Some(AssocOp::LAnd)) | // `{ 42 } &&x` (#61475)
171-
(true, Some(AssocOp::Add)) // `{ 42 } + 42
172-
// If the next token is a keyword, then the tokens above *are* unambiguously incorrect:
173-
// `if x { a } else { b } && if y { c } else { d }`
174-
if !self.look_ahead(1, |t| t.is_reserved_ident()) => {
175-
self.last_type_ascription = None;
176-
// These cases are ambiguous and can't be identified in the parser alone
177-
let sp = self.sess.source_map().start_point(self.token.span);
178-
self.sess.ambiguous_block_expr_parse.borrow_mut().insert(sp, lhs.span);
179-
return Ok(lhs);
180-
}
181-
(true, Some(ref op)) if !op.can_continue_expr_unambiguously() => {
182-
self.last_type_ascription = None;
183-
return Ok(lhs);
184-
}
185-
(true, Some(_)) => {
186-
// We've found an expression that would be parsed as a statement, but the next
187-
// token implies this should be parsed as an expression.
188-
// For example: `if let Some(x) = x { x } else { 0 } / 2`
189-
let mut err = self.struct_span_err(self.token.span, &format!(
190-
"expected expression, found `{}`",
191-
pprust::token_to_string(&self.token),
192-
));
193-
err.span_label(self.token.span, "expected expression");
194-
self.sess.expr_parentheses_needed(
195-
&mut err,
196-
lhs.span,
197-
Some(pprust::expr_to_string(&lhs),
198-
));
199-
err.emit();
200-
}
158+
if !self.should_continue_as_assoc_expr(&lhs) {
159+
self.last_type_ascription = None;
160+
return Ok(lhs);
201161
}
202-
self.expected_tokens.push(TokenType::Operator);
203-
while let Some(op) = AssocOp::from_token(&self.token) {
204162

163+
self.expected_tokens.push(TokenType::Operator);
164+
while let Some(op) = self.check_assoc_op() {
205165
// Adjust the span for interpolated LHS to point to the `$lhs` token and not to what
206166
// it refers to. Interpolated identifiers are unwrapped early and never show up here
207167
// as `PrevTokenKind::Interpolated` so if LHS is a single identifier we always process
@@ -338,6 +298,56 @@ impl<'a> Parser<'a> {
338298
Ok(lhs)
339299
}
340300

301+
fn should_continue_as_assoc_expr(&mut self, lhs: &Expr) -> bool {
302+
match (self.expr_is_complete(lhs), self.check_assoc_op()) {
303+
// Semi-statement forms are odd:
304+
// See https://github.com/rust-lang/rust/issues/29071
305+
(true, None) => false,
306+
(false, _) => true, // Continue parsing the expression.
307+
// An exhaustive check is done in the following block, but these are checked first
308+
// because they *are* ambiguous but also reasonable looking incorrect syntax, so we
309+
// want to keep their span info to improve diagnostics in these cases in a later stage.
310+
(true, Some(AssocOp::Multiply)) | // `{ 42 } *foo = bar;` or `{ 42 } * 3`
311+
(true, Some(AssocOp::Subtract)) | // `{ 42 } -5`
312+
(true, Some(AssocOp::LAnd)) | // `{ 42 } &&x` (#61475)
313+
(true, Some(AssocOp::Add)) // `{ 42 } + 42
314+
// If the next token is a keyword, then the tokens above *are* unambiguously incorrect:
315+
// `if x { a } else { b } && if y { c } else { d }`
316+
if !self.look_ahead(1, |t| t.is_reserved_ident()) => {
317+
// These cases are ambiguous and can't be identified in the parser alone.
318+
let sp = self.sess.source_map().start_point(self.token.span);
319+
self.sess.ambiguous_block_expr_parse.borrow_mut().insert(sp, lhs.span);
320+
false
321+
}
322+
(true, Some(ref op)) if !op.can_continue_expr_unambiguously() => false,
323+
(true, Some(_)) => {
324+
self.error_found_expr_would_be_stmt(lhs);
325+
true
326+
}
327+
}
328+
}
329+
330+
/// We've found an expression that would be parsed as a statement,
331+
/// but the next token implies this should be parsed as an expression.
332+
/// For example: `if let Some(x) = x { x } else { 0 } / 2`.
333+
fn error_found_expr_would_be_stmt(&self, lhs: &Expr) {
334+
let mut err = self.struct_span_err(self.token.span, &format!(
335+
"expected expression, found `{}`",
336+
pprust::token_to_string(&self.token),
337+
));
338+
err.span_label(self.token.span, "expected expression");
339+
self.sess.expr_parentheses_needed(&mut err, lhs.span, Some(pprust::expr_to_string(&lhs)));
340+
err.emit();
341+
}
342+
343+
/// Possibly translate the current token to an associative operator.
344+
/// The method does not advance the current token.
345+
///
346+
/// Also performs recovery for `and` / `or` which are mistaken for `&&` and `||` respectively.
347+
fn check_assoc_op(&self) -> Option<AssocOp> {
348+
AssocOp::from_token(&self.token)
349+
}
350+
341351
/// Checks if this expression is a successfully parsed statement.
342352
fn expr_is_complete(&self, e: &Expr) -> bool {
343353
self.restrictions.contains(Restrictions::STMT_EXPR) &&

0 commit comments

Comments
 (0)