Skip to content

Commit 1caaa40

Browse files
committed
parser: gracefully handle fn foo(A | B: type).
1 parent 083963e commit 1caaa40

23 files changed

+125
-97
lines changed

src/libsyntax/parse/parser.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -988,7 +988,7 @@ impl<'a> Parser<'a> {
988988
let (pat, ty) = if is_name_required || self.is_named_argument() {
989989
debug!("parse_arg_general parse_pat (is_name_required:{})", is_name_required);
990990

991-
let pat = self.parse_pat(Some("argument name"))?;
991+
let pat = self.parse_fn_param_pat()?;
992992
if let Err(mut err) = self.expect(&token::Colon) {
993993
if let Some(ident) = self.argument_without_type(
994994
&mut err,

src/libsyntax/parse/parser/expr.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use super::{Parser, PResult, Restrictions, PrevTokenKind, TokenType, PathStyle};
22
use super::{BlockMode, SemiColonMode};
33
use super::{SeqSep, TokenExpectType};
4-
use super::pat::GateOr;
4+
use super::pat::{GateOr, PARAM_EXPECTED};
55

66
use crate::maybe_recover_from_interpolated_ty_qpath;
77
use crate::ptr::P;
@@ -1176,7 +1176,7 @@ impl<'a> Parser<'a> {
11761176
fn parse_fn_block_arg(&mut self) -> PResult<'a, Arg> {
11771177
let lo = self.token.span;
11781178
let attrs = self.parse_arg_attributes()?;
1179-
let pat = self.parse_pat(Some("argument name"))?;
1179+
let pat = self.parse_pat(PARAM_EXPECTED)?;
11801180
let t = if self.eat(&token::Colon) {
11811181
self.parse_ty()?
11821182
} else {

src/libsyntax/parse/parser/pat.rs

+38-8
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ use errors::{Applicability, DiagnosticBuilder};
1414

1515
type Expected = Option<&'static str>;
1616

17+
/// `Expected` for function and lambda parameter patterns.
18+
pub(super) const PARAM_EXPECTED: Expected = Some("parameter name");
19+
1720
/// Whether or not an or-pattern should be gated when occurring in the current context.
1821
#[derive(PartialEq)]
1922
pub enum GateOr { Yes, No }
@@ -49,7 +52,7 @@ impl<'a> Parser<'a> {
4952
let gated_leading_vert = self.eat_or_separator() && gate_or == GateOr::Yes;
5053

5154
// Parse the possibly-or-pattern.
52-
let pat = self.parse_pat_with_or(gate_or, TopLevel::Yes)?;
55+
let pat = self.parse_pat_with_or(None, gate_or, TopLevel::Yes)?;
5356

5457
// If we parsed a leading `|` which should be gated,
5558
// and no other gated or-pattern has been parsed thus far,
@@ -65,11 +68,38 @@ impl<'a> Parser<'a> {
6568
Ok(pat)
6669
}
6770

71+
/// Parse the pattern for a function or function pointer parameter.
72+
/// Special recovery is provided for or-patterns and leading `|`.
73+
pub(super) fn parse_fn_param_pat(&mut self) -> PResult<'a, P<Pat>> {
74+
self.recover_leading_vert("not allowed in a parameter pattern");
75+
let pat = self.parse_pat_with_or(PARAM_EXPECTED, GateOr::No, TopLevel::No)?;
76+
77+
if let PatKind::Or(..) = &pat.node {
78+
self.ban_illegal_fn_param_or_pat(&pat);
79+
}
80+
81+
Ok(pat)
82+
}
83+
84+
/// Ban `A | B` immediately in a parameter pattern and suggest wrapping in parens.
85+
fn ban_illegal_fn_param_or_pat(&self, pat: &Pat) {
86+
let msg = "wrap the pattern in parenthesis";
87+
let fix = format!("({})", pprust::pat_to_string(pat));
88+
self.struct_span_err(pat.span, "an or-pattern parameter must be wrapped in parenthesis")
89+
.span_suggestion(pat.span, msg, fix, Applicability::MachineApplicable)
90+
.emit();
91+
}
92+
6893
/// Parses a pattern, that may be a or-pattern (e.g. `Foo | Bar` in `Some(Foo | Bar)`).
6994
/// Corresponds to `pat<allow_top_alt>` in RFC 2535.
70-
fn parse_pat_with_or(&mut self, gate_or: GateOr, top_level: TopLevel) -> PResult<'a, P<Pat>> {
95+
fn parse_pat_with_or(
96+
&mut self,
97+
expected: Expected,
98+
gate_or: GateOr,
99+
top_level: TopLevel,
100+
) -> PResult<'a, P<Pat>> {
71101
// Parse the first pattern.
72-
let first_pat = self.parse_pat(None)?;
102+
let first_pat = self.parse_pat(expected)?;
73103
self.maybe_recover_unexpected_comma(first_pat.span, top_level)?;
74104

75105
// If the next token is not a `|`,
@@ -81,7 +111,7 @@ impl<'a> Parser<'a> {
81111
let lo = first_pat.span;
82112
let mut pats = vec![first_pat];
83113
while self.eat_or_separator() {
84-
let pat = self.parse_pat(None).map_err(|mut err| {
114+
let pat = self.parse_pat(expected).map_err(|mut err| {
85115
err.span_label(lo, "while parsing this or-pattern staring here");
86116
err
87117
})?;
@@ -176,18 +206,18 @@ impl<'a> Parser<'a> {
176206
/// Recursive possibly-or-pattern parser with recovery for an erroneous leading `|`.
177207
/// See `parse_pat_with_or` for details on parsing or-patterns.
178208
fn parse_pat_with_or_inner(&mut self) -> PResult<'a, P<Pat>> {
179-
self.recover_inner_leading_vert();
180-
self.parse_pat_with_or(GateOr::Yes, TopLevel::No)
209+
self.recover_leading_vert("only allowed in a top-level pattern");
210+
self.parse_pat_with_or(None, GateOr::Yes, TopLevel::No)
181211
}
182212

183213
/// Recover if `|` or `||` is here.
184214
/// The user is thinking that a leading `|` is allowed in this position.
185-
fn recover_inner_leading_vert(&mut self) {
215+
fn recover_leading_vert(&mut self, ctx: &str) {
186216
if let token::BinOp(token::Or) | token::OrOr = self.token.kind {
187217
let span = self.token.span;
188218
let rm_msg = format!("remove the `{}`", pprust::token_to_string(&self.token));
189219

190-
self.struct_span_err(span, "a leading `|` is only allowed in a top-level pattern")
220+
self.struct_span_err(span, &format!("a leading `|` is {}", ctx))
191221
.span_suggestion(span, &rm_msg, String::new(), Applicability::MachineApplicable)
192222
.emit();
193223

src/test/ui/anon-params-denied-2018.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// edition:2018
44

55
trait T {
6-
fn foo(i32); //~ expected one of `:` or `@`, found `)`
6+
fn foo(i32); //~ expected one of `:`, `@`, or `|`, found `)`
77

88
fn bar_with_default_impl(String, String) {}
99
//~^ ERROR expected one of `:`

src/test/ui/anon-params-denied-2018.stderr

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: expected one of `:` or `@`, found `)`
1+
error: expected one of `:`, `@`, or `|`, found `)`
22
--> $DIR/anon-params-denied-2018.rs:6:15
33
|
44
LL | fn foo(i32);
5-
| ^ expected one of `:` or `@` here
5+
| ^ expected one of `:`, `@`, or `|` here
66
|
77
= note: anonymous parameters are removed in the 2018 edition (see RFC 1685)
88
help: if this was a parameter name, give it a type
@@ -14,11 +14,11 @@ help: if this is a type, explicitly ignore the parameter name
1414
LL | fn foo(_: i32);
1515
| ^^^^^^
1616

17-
error: expected one of `:` or `@`, found `,`
17+
error: expected one of `:`, `@`, or `|`, found `,`
1818
--> $DIR/anon-params-denied-2018.rs:8:36
1919
|
2020
LL | fn bar_with_default_impl(String, String) {}
21-
| ^ expected one of `:` or `@` here
21+
| ^ expected one of `:`, `@`, or `|` here
2222
|
2323
= note: anonymous parameters are removed in the 2018 edition (see RFC 1685)
2424
help: if this was a parameter name, give it a type
@@ -30,11 +30,11 @@ help: if this is a type, explicitly ignore the parameter name
3030
LL | fn bar_with_default_impl(_: String, String) {}
3131
| ^^^^^^^^^
3232

33-
error: expected one of `:` or `@`, found `)`
33+
error: expected one of `:`, `@`, or `|`, found `)`
3434
--> $DIR/anon-params-denied-2018.rs:8:44
3535
|
3636
LL | fn bar_with_default_impl(String, String) {}
37-
| ^ expected one of `:` or `@` here
37+
| ^ expected one of `:`, `@`, or `|` here
3838
|
3939
= note: anonymous parameters are removed in the 2018 edition (see RFC 1685)
4040
help: if this was a parameter name, give it a type
@@ -46,11 +46,11 @@ help: if this is a type, explicitly ignore the parameter name
4646
LL | fn bar_with_default_impl(String, _: String) {}
4747
| ^^^^^^^^^
4848

49-
error: expected one of `:` or `@`, found `,`
49+
error: expected one of `:`, `@`, or `|`, found `,`
5050
--> $DIR/anon-params-denied-2018.rs:13:22
5151
|
5252
LL | fn baz(a:usize, b, c: usize) -> usize {
53-
| ^ expected one of `:` or `@` here
53+
| ^ expected one of `:`, `@`, or `|` here
5454
|
5555
= note: anonymous parameters are removed in the 2018 edition (see RFC 1685)
5656
help: if this was a parameter name, give it a type

src/test/ui/or-patterns/feature-gate-or_patterns-leading.stderr

-12
This file was deleted.

src/test/ui/or-patterns/or-patterns-syntactic-fail.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ fn no_top_level_or_patterns() {
2525
// -------- This looks like an or-pattern but is in fact `|A| (B: E | ())`.
2626

2727
// ...and for now neither do we allow or-patterns at the top level of functions.
28-
fn fun(A | B: E) {} //~ ERROR expected one of `:` or `@`, found `|`
28+
fn fun1(A | B: E) {} //~ ERROR an or-pattern parameter must be wrapped in parenthesis
29+
30+
fn fun2(| A | B: E) {}
31+
//~^ ERROR a leading `|` is not allowed in a parameter pattern
32+
//~| ERROR an or-pattern parameter must be wrapped in parenthesis
2933
}
3034

3135
// We also do not allow a leading `|` when not in a top level position:

src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr

+27-15
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,71 @@
1-
error: expected one of `:` or `@`, found `|`
2-
--> $DIR/or-patterns-syntactic-fail.rs:28:14
1+
error: an or-pattern parameter must be wrapped in parenthesis
2+
--> $DIR/or-patterns-syntactic-fail.rs:28:13
33
|
4-
LL | fn fun(A | B: E) {}
5-
| ^ expected one of `:` or `@` here
4+
LL | fn fun1(A | B: E) {}
5+
| ^^^^^ help: wrap the pattern in parenthesis: `(A | B)`
6+
7+
error: a leading `|` is not allowed in a parameter pattern
8+
--> $DIR/or-patterns-syntactic-fail.rs:30:13
9+
|
10+
LL | fn fun2(| A | B: E) {}
11+
| ^ help: remove the `|`
12+
13+
error: an or-pattern parameter must be wrapped in parenthesis
14+
--> $DIR/or-patterns-syntactic-fail.rs:30:15
15+
|
16+
LL | fn fun2(| A | B: E) {}
17+
| ^^^^^ help: wrap the pattern in parenthesis: `(A | B)`
618

719
error: a leading `|` is only allowed in a top-level pattern
8-
--> $DIR/or-patterns-syntactic-fail.rs:37:11
20+
--> $DIR/or-patterns-syntactic-fail.rs:41:11
921
|
1022
LL | let ( | A | B) = E::A;
1123
| ^ help: remove the `|`
1224

1325
error: a leading `|` is only allowed in a top-level pattern
14-
--> $DIR/or-patterns-syntactic-fail.rs:38:11
26+
--> $DIR/or-patterns-syntactic-fail.rs:42:11
1527
|
1628
LL | let ( | A | B,) = (E::B,);
1729
| ^ help: remove the `|`
1830

1931
error: a leading `|` is only allowed in a top-level pattern
20-
--> $DIR/or-patterns-syntactic-fail.rs:39:11
32+
--> $DIR/or-patterns-syntactic-fail.rs:43:11
2133
|
2234
LL | let [ | A | B ] = [E::A];
2335
| ^ help: remove the `|`
2436

2537
error: a leading `|` is only allowed in a top-level pattern
26-
--> $DIR/or-patterns-syntactic-fail.rs:40:13
38+
--> $DIR/or-patterns-syntactic-fail.rs:44:13
2739
|
2840
LL | let TS( | A | B );
2941
| ^ help: remove the `|`
3042

3143
error: a leading `|` is only allowed in a top-level pattern
32-
--> $DIR/or-patterns-syntactic-fail.rs:41:17
44+
--> $DIR/or-patterns-syntactic-fail.rs:45:17
3345
|
3446
LL | let NS { f: | A | B };
3547
| ^ help: remove the `|`
3648

3749
error: a leading `|` is only allowed in a top-level pattern
38-
--> $DIR/or-patterns-syntactic-fail.rs:43:11
50+
--> $DIR/or-patterns-syntactic-fail.rs:47:11
3951
|
4052
LL | let ( || A | B) = E::A;
4153
| ^^ help: remove the `||`
4254

4355
error: a leading `|` is only allowed in a top-level pattern
44-
--> $DIR/or-patterns-syntactic-fail.rs:44:11
56+
--> $DIR/or-patterns-syntactic-fail.rs:48:11
4557
|
4658
LL | let [ || A | B ] = [E::A];
4759
| ^^ help: remove the `||`
4860

4961
error: a leading `|` is only allowed in a top-level pattern
50-
--> $DIR/or-patterns-syntactic-fail.rs:45:13
62+
--> $DIR/or-patterns-syntactic-fail.rs:49:13
5163
|
5264
LL | let TS( || A | B );
5365
| ^^ help: remove the `||`
5466

5567
error: a leading `|` is only allowed in a top-level pattern
56-
--> $DIR/or-patterns-syntactic-fail.rs:46:17
68+
--> $DIR/or-patterns-syntactic-fail.rs:50:17
5769
|
5870
LL | let NS { f: || A | B };
5971
| ^^ help: remove the `||`
@@ -95,7 +107,7 @@ LL | let _ = |A | B: E| ();
95107
= note: an implementation of `std::ops::BitOr` might be missing for `E`
96108

97109
error[E0308]: mismatched types
98-
--> $DIR/or-patterns-syntactic-fail.rs:48:36
110+
--> $DIR/or-patterns-syntactic-fail.rs:52:36
99111
|
100112
LL | let recovery_witness: String = 0;
101113
| ^
@@ -106,7 +118,7 @@ LL | let recovery_witness: String = 0;
106118
= note: expected type `std::string::String`
107119
found type `{integer}`
108120

109-
error: aborting due to 14 previous errors
121+
error: aborting due to 16 previous errors
110122

111123
Some errors have detailed explanations: E0308, E0369.
112124
For more information about an error, try `rustc --explain E0308`.

src/test/ui/parser/inverted-parameters.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,29 @@ struct S;
22

33
impl S {
44
fn foo(&self, &str bar) {}
5-
//~^ ERROR expected one of `:` or `@`
5+
//~^ ERROR expected one of `:`, `@`
66
//~| HELP declare the type after the parameter binding
77
//~| SUGGESTION <identifier>: <type>
88
}
99

1010
fn baz(S quux, xyzzy: i32) {}
11-
//~^ ERROR expected one of `:` or `@`
11+
//~^ ERROR expected one of `:`, `@`
1212
//~| HELP declare the type after the parameter binding
1313
//~| SUGGESTION <identifier>: <type>
1414

1515
fn one(i32 a b) {}
16-
//~^ ERROR expected one of `:` or `@`
16+
//~^ ERROR expected one of `:`, `@`
1717

1818
fn pattern((i32, i32) (a, b)) {}
19-
//~^ ERROR expected `:`
19+
//~^ ERROR expected one of `:`
2020

2121
fn fizz(i32) {}
22-
//~^ ERROR expected one of `:` or `@`
22+
//~^ ERROR expected one of `:`, `@`
2323
//~| HELP if this was a parameter name, give it a type
2424
//~| HELP if this is a type, explicitly ignore the parameter name
2525

2626
fn missing_colon(quux S) {}
27-
//~^ ERROR expected one of `:` or `@`
27+
//~^ ERROR expected one of `:`, `@`
2828
//~| HELP declare the type after the parameter binding
2929
//~| SUGGESTION <identifier>: <type>
3030

0 commit comments

Comments
 (0)