Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 56c8e66

Browse files
committedJul 1, 2024
Recover more expressions in patterns
1 parent c0b2ff1 commit 56c8e66

11 files changed

+104
-89
lines changed
 

‎compiler/rustc_parse/messages.ftl

+2-8
Original file line numberDiff line numberDiff line change
@@ -778,15 +778,9 @@ parse_unexpected_expr_in_pat =
778778
expected {$is_bound ->
779779
[true] a pattern range bound
780780
*[false] a pattern
781-
}, found {$is_method_call ->
782-
[true] a method call
783-
*[false] an expression
784-
}
781+
}, found an expression
785782
786-
.label = {$is_method_call ->
787-
[true] method calls
788-
*[false] arbitrary expressions
789-
} are not allowed in patterns
783+
.label = arbitrary expressions are not allowed in patterns
790784
791785
parse_unexpected_if_with_if = unexpected `if` in the condition expression
792786
.suggestion = remove the `if`

‎compiler/rustc_parse/src/errors.rs

-2
Original file line numberDiff line numberDiff line change
@@ -2422,8 +2422,6 @@ pub(crate) struct UnexpectedExpressionInPattern {
24222422
pub span: Span,
24232423
/// Was a `RangePatternBound` expected?
24242424
pub is_bound: bool,
2425-
/// Was the unexpected expression a `MethodCallExpression`?
2426-
pub is_method_call: bool,
24272425
}
24282426

24292427
#[derive(Diagnostic)]

‎compiler/rustc_parse/src/parser/expr.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ pub(super) enum LhsExpr {
4848
}
4949

5050
#[derive(Debug)]
51-
enum DestructuredFloat {
51+
pub(crate) enum DestructuredFloat {
5252
/// 1e2
5353
Single(Symbol, Span),
5454
/// 1.
@@ -1044,7 +1044,7 @@ impl<'a> Parser<'a> {
10441044
// we should break everything including floats into more basic proc-macro style
10451045
// tokens in the lexer (probably preferable).
10461046
// See also `TokenKind::break_two_token_op` which does similar splitting of `>>` into `>`.
1047-
fn break_up_float(&self, float: Symbol, span: Span) -> DestructuredFloat {
1047+
pub(crate) fn break_up_float(&self, float: Symbol, span: Span) -> DestructuredFloat {
10481048
#[derive(Debug)]
10491049
enum FloatComponent {
10501050
IdentLike(String),

‎compiler/rustc_parse/src/parser/pat.rs

+50-35
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::errors::{
1010
UnexpectedParenInRangePatSugg, UnexpectedVertVertBeforeFunctionParam,
1111
UnexpectedVertVertInPattern,
1212
};
13-
use crate::parser::expr::{could_be_unclosed_char_literal, LhsExpr};
13+
use crate::parser::expr::{could_be_unclosed_char_literal, DestructuredFloat, LhsExpr};
1414
use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
1515
use rustc_ast::mut_visit::{noop_visit_pat, MutVisitor};
1616
use rustc_ast::ptr::P;
@@ -346,37 +346,51 @@ impl<'a> Parser<'a> {
346346
/// ^^^^^
347347
/// ```
348348
/// Only the end bound is spanned, and this function have no idea if there were a `..=` before `pat_span`, hence the parameter.
349+
/// This function returns `Some` if a trailing expression was recovered, and said expression's span.
349350
#[must_use = "the pattern must be discarded as `PatKind::Err` if this function returns Some"]
350351
fn maybe_recover_trailing_expr(
351352
&mut self,
352353
pat_span: Span,
353354
is_end_bound: bool,
354-
) -> Option<ErrorGuaranteed> {
355+
) -> Option<(ErrorGuaranteed, Span)> {
355356
if self.prev_token.is_keyword(kw::Underscore) || !self.may_recover() {
356357
// Don't recover anything after an `_` or if recovery is disabled.
357358
return None;
358359
}
359360

360-
// Check for `.hello()`, but allow `.Hello()` to be recovered as `, Hello()` in `parse_seq_to_before_tokens()`.
361-
let has_trailing_method = self.check_noexpect(&token::Dot)
361+
// Returns `true` iff `token` is a `x.y` float
362+
let is_float_literal = |that: &Self, token: &Token| -> bool {
363+
use token::{Lit, LitKind};
364+
365+
let token::Literal(Lit { kind: LitKind::Float, symbol, suffix: None }) = token.kind
366+
else {
367+
return false;
368+
};
369+
370+
matches!(that.break_up_float(symbol, token.span), DestructuredFloat::MiddleDot(..))
371+
};
372+
373+
// Check for `.hello` or `.0`.
374+
let has_dot_expr = self.check_noexpect(&token::Dot) // `.`
362375
&& self.look_ahead(1, |tok| {
363-
tok.ident()
364-
.and_then(|(ident, _)| ident.name.as_str().chars().next())
365-
.is_some_and(char::is_lowercase)
366-
})
367-
&& self.look_ahead(2, |tok| tok.kind == token::OpenDelim(Delimiter::Parenthesis));
376+
tok.is_ident() // `hello`
377+
|| tok.is_integer_lit() // `0`
378+
|| is_float_literal(&self, &tok) // `0.0`
379+
});
368380

369381
// Check for operators.
370382
// `|` is excluded as it is used in pattern alternatives and lambdas,
371383
// `?` is included for error propagation,
372384
// `[` is included for indexing operations,
373-
// `[]` is excluded as `a[]` isn't an expression and should be recovered as `a, []` (cf. `tests/ui/parser/pat-lt-bracket-7.rs`)
385+
// `[]` is excluded as `a[]` isn't an expression and should be recovered as `a, []` (cf. `tests/ui/parser/pat-lt-bracket-7.rs`),
386+
// `as` is included for type casts
374387
let has_trailing_operator = matches!(self.token.kind, token::BinOp(op) if op != BinOpToken::Or)
375388
|| self.token.kind == token::Question
376389
|| (self.token.kind == token::OpenDelim(Delimiter::Bracket)
377-
&& self.look_ahead(1, |tok| tok.kind != token::CloseDelim(Delimiter::Bracket)));
390+
&& self.look_ahead(1, |tok| tok.kind != token::CloseDelim(Delimiter::Bracket))) // excludes `[]`
391+
|| self.token.is_keyword(kw::As);
378392

379-
if !has_trailing_method && !has_trailing_operator {
393+
if !has_dot_expr && !has_trailing_operator {
380394
// Nothing to recover here.
381395
return None;
382396
}
@@ -394,8 +408,6 @@ impl<'a> Parser<'a> {
394408
)
395409
.map_err(|err| err.cancel())
396410
{
397-
let non_assoc_span = expr.span;
398-
399411
// Parse an associative expression such as `+ expr`, `% expr`, ...
400412
// Assignements, ranges and `|` are disabled by [`Restrictions::IS_PAT`].
401413
let lhs = LhsExpr::Parsed { expr, starts_statement: false };
@@ -410,14 +422,11 @@ impl<'a> Parser<'a> {
410422
|| self.token.kind == token::CloseDelim(Delimiter::Parenthesis)
411423
&& self.look_ahead(1, Token::is_range_separator);
412424

413-
// Check that `parse_expr_assoc_with` didn't eat a rhs.
414-
let is_method_call = has_trailing_method && non_assoc_span == expr.span;
415-
416-
return Some(self.dcx().emit_err(UnexpectedExpressionInPattern {
417-
span: expr.span,
418-
is_bound,
419-
is_method_call,
420-
}));
425+
return Some((
426+
self.dcx()
427+
.emit_err(UnexpectedExpressionInPattern { span: expr.span, is_bound }),
428+
expr.span,
429+
));
421430
}
422431
}
423432

@@ -534,7 +543,7 @@ impl<'a> Parser<'a> {
534543
self.parse_pat_tuple_struct(qself, path)?
535544
} else {
536545
match self.maybe_recover_trailing_expr(span, false) {
537-
Some(guar) => PatKind::Err(guar),
546+
Some((guar, _)) => PatKind::Err(guar),
538547
None => PatKind::Path(qself, path),
539548
}
540549
}
@@ -567,10 +576,10 @@ impl<'a> Parser<'a> {
567576
// Try to parse everything else as literal with optional minus
568577
match self.parse_literal_maybe_minus() {
569578
Ok(begin) => {
570-
let begin = match self.maybe_recover_trailing_expr(begin.span, false) {
571-
Some(guar) => self.mk_expr_err(begin.span, guar),
572-
None => begin,
573-
};
579+
let begin = self
580+
.maybe_recover_trailing_expr(begin.span, false)
581+
.map(|(guar, sp)| self.mk_expr_err(sp, guar))
582+
.unwrap_or(begin);
574583

575584
match self.parse_range_end() {
576585
Some(form) => self.parse_pat_range_begin_with(begin, form)?,
@@ -700,7 +709,8 @@ impl<'a> Parser<'a> {
700709
// For backward compatibility, `(..)` is a tuple pattern as well.
701710
let paren_pattern =
702711
fields.len() == 1 && !(matches!(trailing_comma, Trailing::Yes) || fields[0].is_rest());
703-
if paren_pattern {
712+
713+
let pat = if paren_pattern {
704714
let pat = fields.into_iter().next().unwrap();
705715
let close_paren = self.prev_token.span;
706716

@@ -718,7 +728,7 @@ impl<'a> Parser<'a> {
718728
},
719729
});
720730

721-
self.parse_pat_range_begin_with(begin.clone(), form)
731+
self.parse_pat_range_begin_with(begin.clone(), form)?
722732
}
723733
// recover ranges with parentheses around the `(start)..`
724734
PatKind::Err(guar)
@@ -733,15 +743,20 @@ impl<'a> Parser<'a> {
733743
},
734744
});
735745

736-
self.parse_pat_range_begin_with(self.mk_expr_err(pat.span, *guar), form)
746+
self.parse_pat_range_begin_with(self.mk_expr_err(pat.span, *guar), form)?
737747
}
738748

739749
// (pat) with optional parentheses
740-
_ => Ok(PatKind::Paren(pat)),
750+
_ => PatKind::Paren(pat),
741751
}
742752
} else {
743-
Ok(PatKind::Tuple(fields))
744-
}
753+
PatKind::Tuple(fields)
754+
};
755+
756+
Ok(match self.maybe_recover_trailing_expr(open_paren.to(self.prev_token.span), false) {
757+
None => pat,
758+
Some((guar, _)) => PatKind::Err(guar),
759+
})
745760
}
746761

747762
/// Parse a mutable binding with the `mut` token already eaten.
@@ -991,7 +1006,7 @@ impl<'a> Parser<'a> {
9911006
}
9921007

9931008
Ok(match recovered {
994-
Some(guar) => self.mk_expr_err(bound.span, guar),
1009+
Some((guar, sp)) => self.mk_expr_err(sp, guar),
9951010
None => bound,
9961011
})
9971012
}
@@ -1060,7 +1075,7 @@ impl<'a> Parser<'a> {
10601075
// but not `ident @ subpat` as `subpat` was already checked and `ident` continues with `@`.
10611076

10621077
let pat = if sub.is_none()
1063-
&& let Some(guar) = self.maybe_recover_trailing_expr(ident.span, false)
1078+
&& let Some((guar, _)) = self.maybe_recover_trailing_expr(ident.span, false)
10641079
{
10651080
PatKind::Err(guar)
10661081
} else {

‎tests/ui/parser/bad-name.stderr

+16-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,20 @@
1-
error: expected one of `:`, `;`, `=`, `@`, or `|`, found `.`
2-
--> $DIR/bad-name.rs:4:8
1+
error: field expressions cannot have generic arguments
2+
--> $DIR/bad-name.rs:4:12
33
|
44
LL | let x.y::<isize>.z foo;
5-
| ^ expected one of `:`, `;`, `=`, `@`, or `|`
5+
| ^^^^^^^
66

7-
error: aborting due to 1 previous error
7+
error: expected a pattern, found an expression
8+
--> $DIR/bad-name.rs:4:7
9+
|
10+
LL | let x.y::<isize>.z foo;
11+
| ^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
12+
13+
error: expected one of `(`, `.`, `::`, `:`, `;`, `=`, `?`, `|`, or an operator, found `foo`
14+
--> $DIR/bad-name.rs:4:22
15+
|
16+
LL | let x.y::<isize>.z foo;
17+
| ^^^ expected one of 9 possible tokens
18+
19+
error: aborting due to 3 previous errors
820

‎tests/ui/parser/pat-recover-exprs.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
fn main() {
22
match u8::MAX {
33
u8::MAX.abs() => (),
4-
//~^ error: expected a pattern, found a method call
4+
//~^ error: expected a pattern, found an expression
55
x.sqrt() @ .. => (),
6-
//~^ error: expected a pattern, found a method call
6+
//~^ error: expected a pattern, found an expression
77
//~| error: left-hand side of `@` must be a binding
88
z @ w @ v.u() => (),
9-
//~^ error: expected a pattern, found a method call
9+
//~^ error: expected a pattern, found an expression
1010
y.ilog(3) => (),
11-
//~^ error: expected a pattern, found a method call
11+
//~^ error: expected a pattern, found an expression
1212
n + 1 => (),
1313
//~^ error: expected a pattern, found an expression
1414
("".f() + 14 * 8) => (),

‎tests/ui/parser/pat-recover-exprs.stderr

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
error: expected a pattern, found a method call
1+
error: expected a pattern, found an expression
22
--> $DIR/pat-recover-exprs.rs:3:9
33
|
44
LL | u8::MAX.abs() => (),
5-
| ^^^^^^^^^^^^^ method calls are not allowed in patterns
5+
| ^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
66

7-
error: expected a pattern, found a method call
7+
error: expected a pattern, found an expression
88
--> $DIR/pat-recover-exprs.rs:5:9
99
|
1010
LL | x.sqrt() @ .. => (),
11-
| ^^^^^^^^ method calls are not allowed in patterns
11+
| ^^^^^^^^ arbitrary expressions are not allowed in patterns
1212

1313
error: left-hand side of `@` must be a binding
1414
--> $DIR/pat-recover-exprs.rs:5:9
@@ -21,17 +21,17 @@ LL | x.sqrt() @ .. => (),
2121
|
2222
= note: bindings are `x`, `mut x`, `ref x`, and `ref mut x`
2323

24-
error: expected a pattern, found a method call
24+
error: expected a pattern, found an expression
2525
--> $DIR/pat-recover-exprs.rs:8:17
2626
|
2727
LL | z @ w @ v.u() => (),
28-
| ^^^^^ method calls are not allowed in patterns
28+
| ^^^^^ arbitrary expressions are not allowed in patterns
2929

30-
error: expected a pattern, found a method call
30+
error: expected a pattern, found an expression
3131
--> $DIR/pat-recover-exprs.rs:10:9
3232
|
3333
LL | y.ilog(3) => (),
34-
| ^^^^^^^^^ method calls are not allowed in patterns
34+
| ^^^^^^^^^ arbitrary expressions are not allowed in patterns
3535

3636
error: expected a pattern, found an expression
3737
--> $DIR/pat-recover-exprs.rs:12:9

‎tests/ui/parser/pat-recover-methodcalls.rs

+5-6
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ struct Bar { baz: String }
44
fn foo(foo: Foo) -> bool {
55
match foo {
66
Foo("hi".to_owned()) => true,
7-
//~^ error: expected a pattern, found a method call
7+
//~^ error: expected a pattern, found an expression
88
_ => false
99
}
1010
}
1111

1212
fn bar(bar: Bar) -> bool {
1313
match bar {
1414
Bar { baz: "hi".to_owned() } => true,
15-
//~^ error: expected a pattern, found a method call
15+
//~^ error: expected a pattern, found an expression
1616
_ => false
1717
}
1818
}
@@ -22,16 +22,15 @@ fn baz() { // issue #90121
2222

2323
match foo.as_slice() {
2424
&["foo".to_string()] => {}
25-
//~^ error: expected a pattern, found a method call
25+
//~^ error: expected a pattern, found an expression
2626
_ => {}
2727
};
2828
}
2929

3030
fn main() {
3131
if let (-1.some(4)) = (0, Some(4)) {}
32-
//~^ error: expected a pattern, found a method call
32+
//~^ error: expected a pattern, found an expression
3333

3434
if let (-1.Some(4)) = (0, Some(4)) {}
35-
//~^ error: expected one of `)`, `,`, `...`, `..=`, `..`, or `|`, found `.`
36-
//~| help: missing `,`
35+
//~^ error: expected a pattern, found an expression
3736
}
+11-14
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,32 @@
1-
error: expected a pattern, found a method call
1+
error: expected a pattern, found an expression
22
--> $DIR/pat-recover-methodcalls.rs:6:13
33
|
44
LL | Foo("hi".to_owned()) => true,
5-
| ^^^^^^^^^^^^^^^ method calls are not allowed in patterns
5+
| ^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
66

7-
error: expected a pattern, found a method call
7+
error: expected a pattern, found an expression
88
--> $DIR/pat-recover-methodcalls.rs:14:20
99
|
1010
LL | Bar { baz: "hi".to_owned() } => true,
11-
| ^^^^^^^^^^^^^^^ method calls are not allowed in patterns
11+
| ^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
1212

13-
error: expected a pattern, found a method call
13+
error: expected a pattern, found an expression
1414
--> $DIR/pat-recover-methodcalls.rs:24:11
1515
|
1616
LL | &["foo".to_string()] => {}
17-
| ^^^^^^^^^^^^^^^^^ method calls are not allowed in patterns
17+
| ^^^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
1818

19-
error: expected a pattern, found a method call
19+
error: expected a pattern, found an expression
2020
--> $DIR/pat-recover-methodcalls.rs:31:13
2121
|
2222
LL | if let (-1.some(4)) = (0, Some(4)) {}
23-
| ^^^^^^^^^^ method calls are not allowed in patterns
23+
| ^^^^^^^^^^ arbitrary expressions are not allowed in patterns
2424

25-
error: expected one of `)`, `,`, `...`, `..=`, `..`, or `|`, found `.`
26-
--> $DIR/pat-recover-methodcalls.rs:34:15
25+
error: expected a pattern, found an expression
26+
--> $DIR/pat-recover-methodcalls.rs:34:13
2727
|
2828
LL | if let (-1.Some(4)) = (0, Some(4)) {}
29-
| ^
30-
| |
31-
| expected one of `)`, `,`, `...`, `..=`, `..`, or `|`
32-
| help: missing `,`
29+
| ^^^^^^^^^^ arbitrary expressions are not allowed in patterns
3330

3431
error: aborting due to 5 previous errors
3532

‎tests/ui/parser/pat-recover-ranges.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ fn main() {
2222
//~| warning: `...` range patterns are deprecated
2323
//~| warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021!
2424
0.x()..="y".z() => (),
25-
//~^ error: expected a pattern range bound, found a method call
26-
//~| error: expected a pattern range bound, found a method call
25+
//~^ error: expected a pattern range bound, found an expression
26+
//~| error: expected a pattern range bound, found an expression
2727
};
2828
}
2929

‎tests/ui/parser/pat-recover-ranges.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -106,17 +106,17 @@ error: expected a pattern range bound, found an expression
106106
LL | (1 + 4)...1 * 2 => (),
107107
| ^^^^^ arbitrary expressions are not allowed in patterns
108108

109-
error: expected a pattern range bound, found a method call
109+
error: expected a pattern range bound, found an expression
110110
--> $DIR/pat-recover-ranges.rs:24:9
111111
|
112112
LL | 0.x()..="y".z() => (),
113-
| ^^^^^ method calls are not allowed in patterns
113+
| ^^^^^ arbitrary expressions are not allowed in patterns
114114

115-
error: expected a pattern range bound, found a method call
115+
error: expected a pattern range bound, found an expression
116116
--> $DIR/pat-recover-ranges.rs:24:17
117117
|
118118
LL | 0.x()..="y".z() => (),
119-
| ^^^^^^^ method calls are not allowed in patterns
119+
| ^^^^^^^ arbitrary expressions are not allowed in patterns
120120

121121
warning: `...` range patterns are deprecated
122122
--> $DIR/pat-recover-ranges.rs:18:16

0 commit comments

Comments
 (0)
Please sign in to comment.