syntax: recover trailing | in or-patterns. · rust-lang/rust@3eba6c1 (original) (raw)

`@@ -18,6 +18,8 @@ type Expected = Option<&'static str>;

`

18

18

`` /// Expected for function and lambda parameter patterns.

``

19

19

`pub(super) const PARAM_EXPECTED: Expected = Some("parameter name");

`

20

20

``

``

21

`+

const WHILE_PARSING_OR_MSG: &str = "while parsing this or-pattern starting here";

`

``

22

+

21

23

`/// Whether or not an or-pattern should be gated when occurring in the current context.

`

22

24

`#[derive(PartialEq)]

`

23

25

`pub enum GateOr { Yes, No }

`

`@@ -40,7 +42,7 @@ impl<'a> Parser<'a> {

`

40

42

`` /// Corresponds to top_pat in RFC 2535 and allows or-pattern at the top level.

``

41

43

`pub(super) fn parse_top_pat(&mut self, gate_or: GateOr) -> PResult<'a, P> {

`

42

44

`// Allow a '|' before the pats (RFCs 1925, 2530, and 2535).

`

43

``

`-

let gated_leading_vert = self.eat_or_separator() && gate_or == GateOr::Yes;

`

``

45

`+

let gated_leading_vert = self.eat_or_separator(None) && gate_or == GateOr::Yes;

`

44

46

`let leading_vert_span = self.prev_span;

`

45

47

``

46

48

`// Parse the possibly-or-pattern.

`

`@@ -63,7 +65,7 @@ impl<'a> Parser<'a> {

`

63

65

`/// Parse the pattern for a function or function pointer parameter.

`

64

66

`` /// Special recovery is provided for or-patterns and leading |.

``

65

67

`pub(super) fn parse_fn_param_pat(&mut self) -> PResult<'a, P> {

`

66

``

`-

self.recover_leading_vert("not allowed in a parameter pattern");

`

``

68

`+

self.recover_leading_vert(None, "not allowed in a parameter pattern");

`

67

69

`let pat = self.parse_pat_with_or(PARAM_EXPECTED, GateOr::No, RecoverComma::No)?;

`

68

70

``

69

71

`if let PatKind::Or(..) = &pat.kind {

`

`@@ -90,7 +92,7 @@ impl<'a> Parser<'a> {

`

90

92

`gate_or: GateOr,

`

91

93

`rc: RecoverComma,

`

92

94

`) -> PResult<'a, P> {

`

93

``

`-

// Parse the first pattern.

`

``

95

`` +

// Parse the first pattern (p_0).

``

94

96

`let first_pat = self.parse_pat(expected)?;

`

95

97

`self.maybe_recover_unexpected_comma(first_pat.span, rc)?;

`

96

98

``

`@@ -100,11 +102,12 @@ impl<'a> Parser<'a> {

`

100

102

`return Ok(first_pat)

`

101

103

`}

`

102

104

``

``

105

`` +

// Parse the patterns p_1 | ... | p_n where n > 0.

``

103

106

`let lo = first_pat.span;

`

104

107

`let mut pats = vec![first_pat];

`

105

``

`-

while self.eat_or_separator() {

`

``

108

`+

while self.eat_or_separator(Some(lo)) {

`

106

109

`let pat = self.parse_pat(expected).map_err(|mut err| {

`

107

``

`-

err.span_label(lo, "while parsing this or-pattern starting here");

`

``

110

`+

err.span_label(lo, WHILE_PARSING_OR_MSG);

`

108

111

` err

`

109

112

`})?;

`

110

113

`self.maybe_recover_unexpected_comma(pat.span, rc)?;

`

`@@ -122,28 +125,65 @@ impl<'a> Parser<'a> {

`

122

125

``

123

126

`` /// Eat the or-pattern | separator.

``

124

127

`` /// If instead a || token is encountered, recover and pretend we parsed |.

``

125

``

`-

fn eat_or_separator(&mut self) -> bool {

`

``

128

`+

fn eat_or_separator(&mut self, lo: Option) -> bool {

`

``

129

`+

if self.recover_trailing_vert(lo) {

`

``

130

`+

return false;

`

``

131

`+

}

`

``

132

+

126

133

`match self.token.kind {

`

127

134

` token::OrOr => {

`

128

135

`` // Found ||; Recover and pretend we parsed |.

``

129

``

`-

self.ban_unexpected_or_or();

`

``

136

`+

self.ban_unexpected_or_or(lo);

`

130

137

`self.bump();

`

131

138

`true

`

132

139

`}

`

133

140

` _ => self.eat(&token::BinOp(token::Or)),

`

134

141

`}

`

135

142

`}

`

136

143

``

``

144

`` +

/// Recover if | or || is the current token and we have one of the

``

``

145

`` +

/// tokens =>, if, =, :, ;, ,, ], ), or } ahead of us.

``

``

146

`+

///

`

``

147

`+

/// These tokens all indicate that we reached the end of the or-pattern

`

``

148

`` +

/// list and can now reliably say that the | was an illegal trailing vert.

``

``

149

`` +

/// Note that there are more tokens such as @ for which we know that the |

``

``

150

`+

/// is an illegal parse. However, the user's intent is less clear in that case.

`

``

151

`+

fn recover_trailing_vert(&mut self, lo: Option) -> bool {

`

``

152

`+

let is_end_ahead = self.look_ahead(1, |token| match &token.kind {

`

``

153

`` +

token::FatArrow // e.g. a | => 0,.

``

``

154

`` +

| token::Ident(kw::If, false) // e.g. a | if expr.

``

``

155

`` +

| token::Eq // e.g. let a | = 0.

``

``

156

`` +

| token::Semi // e.g. let a |;.

``

``

157

`` +

| token::Colon // e.g. let a | :.

``

``

158

`` +

| token::Comma // e.g. let (a |,).

``

``

159

`` +

| token::CloseDelim(token::Bracket) // e.g. let [a | ].

``

``

160

`` +

| token::CloseDelim(token::Paren) // e.g. let (a | ).

``

``

161

`` +

| token::CloseDelim(token::Brace) => true, // e.g. let A { f: a | }.

``

``

162

`+

_ => false,

`

``

163

`+

});

`

``

164

`+

match (is_end_ahead, &self.token.kind) {

`

``

165

`+

(true, token::BinOp(token::Or)) | (true, token::OrOr) => {

`

``

166

`+

self.ban_illegal_vert(lo, "trailing", "not allowed in an or-pattern");

`

``

167

`+

self.bump();

`

``

168

`+

true

`

``

169

`+

}

`

``

170

`+

_ => false,

`

``

171

`+

}

`

``

172

`+

}

`

``

173

+

137

174

`` /// We have parsed || instead of |. Error and suggest | instead.

``

138

``

`-

fn ban_unexpected_or_or(&mut self) {

`

139

``

`` -

self.struct_span_err(self.token.span, "unexpected token || after pattern")

``

140

``

`-

.span_suggestion(

`

141

``

`-

self.token.span,

`

142

``

`` -

"use a single | to separate multiple alternative patterns",

``

143

``

`-

"|".to_owned(),

`

144

``

`-

Applicability::MachineApplicable

`

145

``

`-

)

`

146

``

`-

.emit();

`

``

175

`+

fn ban_unexpected_or_or(&mut self, lo: Option) {

`

``

176

`` +

let mut err = self.struct_span_err(self.token.span, "unexpected token || after pattern");

``

``

177

`+

err.span_suggestion(

`

``

178

`+

self.token.span,

`

``

179

`` +

"use a single | to separate multiple alternative patterns",

``

``

180

`+

"|".to_owned(),

`

``

181

`+

Applicability::MachineApplicable

`

``

182

`+

);

`

``

183

`+

if let Some(lo) = lo {

`

``

184

`+

err.span_label(lo, WHILE_PARSING_OR_MSG);

`

``

185

`+

}

`

``

186

`+

err.emit();

`

147

187

`}

`

148

188

``

149

189

`/// Some special error handling for the "top-level" patterns in a match arm,

`

`@@ -198,25 +238,38 @@ impl<'a> Parser<'a> {

`

198

238

`` /// Recursive possibly-or-pattern parser with recovery for an erroneous leading |.

``

199

239

`` /// See parse_pat_with_or for details on parsing or-patterns.

``

200

240

`fn parse_pat_with_or_inner(&mut self) -> PResult<'a, P> {

`

201

``

`-

self.recover_leading_vert("only allowed in a top-level pattern");

`

``

241

`+

self.recover_leading_vert(None, "only allowed in a top-level pattern");

`

202

242

`self.parse_pat_with_or(None, GateOr::Yes, RecoverComma::No)

`

203

243

`}

`

204

244

``

205

245

`` /// Recover if | or || is here.

``

206

246

`` /// The user is thinking that a leading | is allowed in this position.

``

207

``

`-

fn recover_leading_vert(&mut self, ctx: &str) {

`

``

247

`+

fn recover_leading_vert(&mut self, lo: Option, ctx: &str) {

`

208

248

`if let token::BinOp(token::Or) | token::OrOr = self.token.kind {

`

209

``

`-

let span = self.token.span;

`

210

``

`` -

let rm_msg = format!("remove the {}", pprust::token_to_string(&self.token));

``

211

``

-

212

``

`` -

self.struct_span_err(span, &format!("a leading | is {}", ctx))

``

213

``

`-

.span_suggestion(span, &rm_msg, String::new(), Applicability::MachineApplicable)

`

214

``

`-

.emit();

`

215

``

-

``

249

`+

self.ban_illegal_vert(lo, "leading", ctx);

`

216

250

`self.bump();

`

217

251

`}

`

218

252

`}

`

219

253

``

``

254

`` +

/// A | or possibly || token shouldn't be here. Ban it.

``

``

255

`+

fn ban_illegal_vert(&mut self, lo: Option, pos: &str, ctx: &str) {

`

``

256

`+

let span = self.token.span;

`

``

257

`` +

let mut err = self.struct_span_err(span, &format!("a {} | is {}", pos, ctx));

``

``

258

`+

err.span_suggestion(

`

``

259

`+

span,

`

``

260

`` +

&format!("remove the {}", pprust::token_to_string(&self.token)),

``

``

261

`+

String::new(),

`

``

262

`+

Applicability::MachineApplicable,

`

``

263

`+

);

`

``

264

`+

if let Some(lo) = lo {

`

``

265

`+

err.span_label(lo, WHILE_PARSING_OR_MSG);

`

``

266

`+

}

`

``

267

`+

if let token::OrOr = self.token.kind {

`

``

268

`` +

err.note("alternatives in or-patterns are separated with |, not ||");

``

``

269

`+

}

`

``

270

`+

err.emit();

`

``

271

`+

}

`

``

272

+

220

273

`` /// Parses a pattern, with a setting whether modern range patterns (e.g., a..=b, a..b are

``

221

274

`/// allowed).

`

222

275

`fn parse_pat_with_range_pat(

`

`@@ -259,7 +312,7 @@ impl<'a> Parser<'a> {

`

259

312

`self.bump();

`

260

313

`self.parse_pat_range_to(RangeEnd::Included(RangeSyntax::DotDotDot), "...")?

`

261

314

`}

`

262

``

`-

// At this point, token != &, &&, (, [

`

``

315

`` +

// At this point, token != &, &&, (, [, .., ..=, or ....

``

263

316

` _ => if self.eat_keyword(kw::Underscore) {

`

264

317

`// Parse _

`

265

318

`PatKind::Wild

`