]>
Commit | Line | Data |
---|---|---|
1 | use super::attr::DEFAULT_INNER_ATTR_FORBIDDEN; | |
2 | use super::diagnostics::{ | |
3 | AttemptLocalParseRecovery, Error, InvalidVariableDeclaration, InvalidVariableDeclarationSub, | |
4 | }; | |
5 | use super::expr::LhsExpr; | |
6 | use super::pat::RecoverComma; | |
7 | use super::path::PathStyle; | |
8 | use super::TrailingToken; | |
9 | use super::{ | |
10 | AttrWrapper, BlockMode, FnParseMode, ForceCollect, Parser, Restrictions, SemiColonMode, | |
11 | }; | |
12 | use crate::maybe_whole; | |
13 | ||
14 | use rustc_ast as ast; | |
15 | use rustc_ast::ptr::P; | |
16 | use rustc_ast::token::{self, Delimiter, TokenKind}; | |
17 | use rustc_ast::util::classify; | |
18 | use rustc_ast::{AttrStyle, AttrVec, Attribute, LocalKind, MacCall, MacCallStmt, MacStmtStyle}; | |
19 | use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, HasAttrs, Local, Stmt}; | |
20 | use rustc_ast::{StmtKind, DUMMY_NODE_ID}; | |
21 | use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, PResult}; | |
22 | use rustc_span::source_map::{BytePos, Span}; | |
23 | use rustc_span::symbol::{kw, sym}; | |
24 | ||
25 | use std::mem; | |
26 | ||
27 | impl<'a> Parser<'a> { | |
28 | /// Parses a statement. This stops just before trailing semicolons on everything but items. | |
29 | /// e.g., a `StmtKind::Semi` parses to a `StmtKind::Expr`, leaving the trailing `;` unconsumed. | |
30 | // Public for rustfmt usage. | |
31 | pub fn parse_stmt(&mut self, force_collect: ForceCollect) -> PResult<'a, Option<Stmt>> { | |
32 | Ok(self.parse_stmt_without_recovery(false, force_collect).unwrap_or_else(|mut e| { | |
33 | e.emit(); | |
34 | self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore); | |
35 | None | |
36 | })) | |
37 | } | |
38 | ||
39 | /// If `force_collect` is [`ForceCollect::Yes`], forces collection of tokens regardless of whether | |
40 | /// or not we have attributes | |
41 | pub(crate) fn parse_stmt_without_recovery( | |
42 | &mut self, | |
43 | capture_semi: bool, | |
44 | force_collect: ForceCollect, | |
45 | ) -> PResult<'a, Option<Stmt>> { | |
46 | let attrs = self.parse_outer_attributes()?; | |
47 | let lo = self.token.span; | |
48 | ||
49 | // Don't use `maybe_whole` so that we have precise control | |
50 | // over when we bump the parser | |
51 | if let token::Interpolated(nt) = &self.token.kind && let token::NtStmt(stmt) = &**nt { | |
52 | let mut stmt = stmt.clone(); | |
53 | self.bump(); | |
54 | stmt.visit_attrs(|stmt_attrs| { | |
55 | attrs.prepend_to_nt_inner(stmt_attrs); | |
56 | }); | |
57 | return Ok(Some(stmt.into_inner())); | |
58 | } | |
59 | ||
60 | if self.token.is_keyword(kw::Mut) && self.is_keyword_ahead(1, &[kw::Let]) { | |
61 | self.bump(); | |
62 | let mut_let_span = lo.to(self.token.span); | |
63 | self.sess.emit_err(InvalidVariableDeclaration { | |
64 | span: mut_let_span, | |
65 | sub: InvalidVariableDeclarationSub::SwitchMutLetOrder(mut_let_span), | |
66 | }); | |
67 | } | |
68 | ||
69 | Ok(Some(if self.token.is_keyword(kw::Let) { | |
70 | self.parse_local_mk(lo, attrs, capture_semi, force_collect)? | |
71 | } else if self.is_kw_followed_by_ident(kw::Mut) { | |
72 | self.recover_stmt_local(lo, attrs, InvalidVariableDeclarationSub::MissingLet)? | |
73 | } else if self.is_kw_followed_by_ident(kw::Auto) { | |
74 | self.bump(); // `auto` | |
75 | self.recover_stmt_local(lo, attrs, InvalidVariableDeclarationSub::UseLetNotAuto)? | |
76 | } else if self.is_kw_followed_by_ident(sym::var) { | |
77 | self.bump(); // `var` | |
78 | self.recover_stmt_local(lo, attrs, InvalidVariableDeclarationSub::UseLetNotVar)? | |
79 | } else if self.check_path() && !self.token.is_qpath_start() && !self.is_path_start_item() { | |
80 | // We have avoided contextual keywords like `union`, items with `crate` visibility, | |
81 | // or `auto trait` items. We aim to parse an arbitrary path `a::b` but not something | |
82 | // that starts like a path (1 token), but it fact not a path. | |
83 | // Also, we avoid stealing syntax from `parse_item_`. | |
84 | if force_collect == ForceCollect::Yes { | |
85 | self.collect_tokens_no_attrs(|this| this.parse_stmt_path_start(lo, attrs)) | |
86 | } else { | |
87 | self.parse_stmt_path_start(lo, attrs) | |
88 | }? | |
89 | } else if let Some(item) = self.parse_item_common( | |
90 | attrs.clone(), | |
91 | false, | |
92 | true, | |
93 | FnParseMode { req_name: |_| true, req_body: true }, | |
94 | force_collect, | |
95 | )? { | |
96 | // FIXME: Bad copy of attrs | |
97 | self.mk_stmt(lo.to(item.span), StmtKind::Item(P(item))) | |
98 | } else if self.eat(&token::Semi) { | |
99 | // Do not attempt to parse an expression if we're done here. | |
100 | self.error_outer_attrs(&attrs.take_for_recovery()); | |
101 | self.mk_stmt(lo, StmtKind::Empty) | |
102 | } else if self.token != token::CloseDelim(Delimiter::Brace) { | |
103 | // Remainder are line-expr stmts. | |
104 | let e = if force_collect == ForceCollect::Yes { | |
105 | self.collect_tokens_no_attrs(|this| { | |
106 | this.parse_expr_res(Restrictions::STMT_EXPR, Some(attrs)) | |
107 | }) | |
108 | } else { | |
109 | self.parse_expr_res(Restrictions::STMT_EXPR, Some(attrs)) | |
110 | }?; | |
111 | if matches!(e.kind, ExprKind::Assign(..)) && self.eat_keyword(kw::Else) { | |
112 | let bl = self.parse_block()?; | |
113 | // Destructuring assignment ... else. | |
114 | // This is not allowed, but point it out in a nice way. | |
115 | let mut err = self.struct_span_err( | |
116 | e.span.to(bl.span), | |
117 | "<assignment> ... else { ... } is not allowed", | |
118 | ); | |
119 | err.emit(); | |
120 | } | |
121 | self.mk_stmt(lo.to(e.span), StmtKind::Expr(e)) | |
122 | } else { | |
123 | self.error_outer_attrs(&attrs.take_for_recovery()); | |
124 | return Ok(None); | |
125 | })) | |
126 | } | |
127 | ||
128 | fn parse_stmt_path_start(&mut self, lo: Span, attrs: AttrWrapper) -> PResult<'a, Stmt> { | |
129 | let stmt = self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { | |
130 | let path = this.parse_path(PathStyle::Expr)?; | |
131 | ||
132 | if this.eat(&token::Not) { | |
133 | let stmt_mac = this.parse_stmt_mac(lo, attrs, path)?; | |
134 | if this.token == token::Semi { | |
135 | return Ok((stmt_mac, TrailingToken::Semi)); | |
136 | } else { | |
137 | return Ok((stmt_mac, TrailingToken::None)); | |
138 | } | |
139 | } | |
140 | ||
141 | let expr = if this.eat(&token::OpenDelim(Delimiter::Brace)) { | |
142 | this.parse_struct_expr(None, path, true)? | |
143 | } else { | |
144 | let hi = this.prev_token.span; | |
145 | this.mk_expr(lo.to(hi), ExprKind::Path(None, path)) | |
146 | }; | |
147 | ||
148 | let expr = this.with_res(Restrictions::STMT_EXPR, |this| { | |
149 | this.parse_dot_or_call_expr_with(expr, lo, attrs) | |
150 | })?; | |
151 | // `DUMMY_SP` will get overwritten later in this function | |
152 | Ok((this.mk_stmt(rustc_span::DUMMY_SP, StmtKind::Expr(expr)), TrailingToken::None)) | |
153 | })?; | |
154 | ||
155 | if let StmtKind::Expr(expr) = stmt.kind { | |
156 | // Perform this outside of the `collect_tokens_trailing_token` closure, | |
157 | // since our outer attributes do not apply to this part of the expression | |
158 | let expr = self.with_res(Restrictions::STMT_EXPR, |this| { | |
159 | this.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(expr)) | |
160 | })?; | |
161 | Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr))) | |
162 | } else { | |
163 | Ok(stmt) | |
164 | } | |
165 | } | |
166 | ||
167 | /// Parses a statement macro `mac!(args)` provided a `path` representing `mac`. | |
168 | /// At this point, the `!` token after the path has already been eaten. | |
169 | fn parse_stmt_mac(&mut self, lo: Span, attrs: AttrVec, path: ast::Path) -> PResult<'a, Stmt> { | |
170 | let args = self.parse_mac_args()?; | |
171 | let delim = args.delim(); | |
172 | let hi = self.prev_token.span; | |
173 | ||
174 | let style = match delim { | |
175 | Some(Delimiter::Brace) => MacStmtStyle::Braces, | |
176 | Some(_) => MacStmtStyle::NoBraces, | |
177 | None => unreachable!(), | |
178 | }; | |
179 | ||
180 | let mac = P(MacCall { path, args, prior_type_ascription: self.last_type_ascription }); | |
181 | ||
182 | let kind = if (style == MacStmtStyle::Braces | |
183 | && self.token != token::Dot | |
184 | && self.token != token::Question) | |
185 | || self.token == token::Semi | |
186 | || self.token == token::Eof | |
187 | { | |
188 | StmtKind::MacCall(P(MacCallStmt { mac, style, attrs, tokens: None })) | |
189 | } else { | |
190 | // Since none of the above applied, this is an expression statement macro. | |
191 | let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac)); | |
192 | let e = self.maybe_recover_from_bad_qpath(e)?; | |
193 | let e = self.parse_dot_or_call_expr_with(e, lo, attrs)?; | |
194 | let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?; | |
195 | StmtKind::Expr(e) | |
196 | }; | |
197 | Ok(self.mk_stmt(lo.to(hi), kind)) | |
198 | } | |
199 | ||
200 | /// Error on outer attributes in this context. | |
201 | /// Also error if the previous token was a doc comment. | |
202 | fn error_outer_attrs(&self, attrs: &[Attribute]) { | |
203 | if let [.., last] = attrs { | |
204 | if last.is_doc_comment() { | |
205 | self.span_err(last.span, Error::UselessDocComment).emit(); | |
206 | } else if attrs.iter().any(|a| a.style == AttrStyle::Outer) { | |
207 | self.struct_span_err(last.span, "expected statement after outer attribute").emit(); | |
208 | } | |
209 | } | |
210 | } | |
211 | ||
212 | fn recover_stmt_local( | |
213 | &mut self, | |
214 | lo: Span, | |
215 | attrs: AttrWrapper, | |
216 | subdiagnostic: fn(Span) -> InvalidVariableDeclarationSub, | |
217 | ) -> PResult<'a, Stmt> { | |
218 | let stmt = self.recover_local_after_let(lo, attrs)?; | |
219 | self.sess.emit_err(InvalidVariableDeclaration { span: lo, sub: subdiagnostic(lo) }); | |
220 | Ok(stmt) | |
221 | } | |
222 | ||
223 | fn parse_local_mk( | |
224 | &mut self, | |
225 | lo: Span, | |
226 | attrs: AttrWrapper, | |
227 | capture_semi: bool, | |
228 | force_collect: ForceCollect, | |
229 | ) -> PResult<'a, Stmt> { | |
230 | self.collect_tokens_trailing_token(attrs, force_collect, |this, attrs| { | |
231 | this.expect_keyword(kw::Let)?; | |
232 | let local = this.parse_local(attrs)?; | |
233 | let trailing = if capture_semi && this.token.kind == token::Semi { | |
234 | TrailingToken::Semi | |
235 | } else { | |
236 | TrailingToken::None | |
237 | }; | |
238 | Ok((this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Local(local)), trailing)) | |
239 | }) | |
240 | } | |
241 | ||
242 | fn recover_local_after_let(&mut self, lo: Span, attrs: AttrWrapper) -> PResult<'a, Stmt> { | |
243 | self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { | |
244 | let local = this.parse_local(attrs)?; | |
245 | // FIXME - maybe capture semicolon in recovery? | |
246 | Ok(( | |
247 | this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Local(local)), | |
248 | TrailingToken::None, | |
249 | )) | |
250 | }) | |
251 | } | |
252 | ||
253 | /// Parses a local variable declaration. | |
254 | fn parse_local(&mut self, attrs: AttrVec) -> PResult<'a, P<Local>> { | |
255 | let lo = self.prev_token.span; | |
256 | ||
257 | if self.token.is_keyword(kw::Const) && self.look_ahead(1, |t| t.is_ident()) { | |
258 | self.struct_span_err( | |
259 | lo.to(self.token.span), | |
260 | "`const` and `let` are mutually exclusive", | |
261 | ) | |
262 | .span_suggestion( | |
263 | lo.to(self.token.span), | |
264 | "remove `let`", | |
265 | "const", | |
266 | Applicability::MaybeIncorrect, | |
267 | ) | |
268 | .emit(); | |
269 | self.bump(); | |
270 | } | |
271 | ||
272 | let (pat, colon) = self.parse_pat_before_ty(None, RecoverComma::Yes, "`let` bindings")?; | |
273 | ||
274 | let (err, ty) = if colon { | |
275 | // Save the state of the parser before parsing type normally, in case there is a `:` | |
276 | // instead of an `=` typo. | |
277 | let parser_snapshot_before_type = self.clone(); | |
278 | let colon_sp = self.prev_token.span; | |
279 | match self.parse_ty() { | |
280 | Ok(ty) => (None, Some(ty)), | |
281 | Err(mut err) => { | |
282 | if let Ok(snip) = self.span_to_snippet(pat.span) { | |
283 | err.span_label(pat.span, format!("while parsing the type for `{}`", snip)); | |
284 | } | |
285 | // we use noexpect here because we don't actually expect Eq to be here | |
286 | // but we are still checking for it in order to be able to handle it if | |
287 | // it is there | |
288 | let err = if self.check_noexpect(&token::Eq) { | |
289 | err.emit(); | |
290 | None | |
291 | } else { | |
292 | // Rewind to before attempting to parse the type and continue parsing. | |
293 | let parser_snapshot_after_type = | |
294 | mem::replace(self, parser_snapshot_before_type); | |
295 | Some((parser_snapshot_after_type, colon_sp, err)) | |
296 | }; | |
297 | (err, None) | |
298 | } | |
299 | } | |
300 | } else { | |
301 | (None, None) | |
302 | }; | |
303 | let init = match (self.parse_initializer(err.is_some()), err) { | |
304 | (Ok(init), None) => { | |
305 | // init parsed, ty parsed | |
306 | init | |
307 | } | |
308 | (Ok(init), Some((_, colon_sp, mut err))) => { | |
309 | // init parsed, ty error | |
310 | // Could parse the type as if it were the initializer, it is likely there was a | |
311 | // typo in the code: `:` instead of `=`. Add suggestion and emit the error. | |
312 | err.span_suggestion_short( | |
313 | colon_sp, | |
314 | "use `=` if you meant to assign", | |
315 | " =", | |
316 | Applicability::MachineApplicable, | |
317 | ); | |
318 | err.emit(); | |
319 | // As this was parsed successfully, continue as if the code has been fixed for the | |
320 | // rest of the file. It will still fail due to the emitted error, but we avoid | |
321 | // extra noise. | |
322 | init | |
323 | } | |
324 | (Err(init_err), Some((snapshot, _, ty_err))) => { | |
325 | // init error, ty error | |
326 | init_err.cancel(); | |
327 | // Couldn't parse the type nor the initializer, only raise the type error and | |
328 | // return to the parser state before parsing the type as the initializer. | |
329 | // let x: <parse_error>; | |
330 | *self = snapshot; | |
331 | return Err(ty_err); | |
332 | } | |
333 | (Err(err), None) => { | |
334 | // init error, ty parsed | |
335 | // Couldn't parse the initializer and we're not attempting to recover a failed | |
336 | // parse of the type, return the error. | |
337 | return Err(err); | |
338 | } | |
339 | }; | |
340 | let kind = match init { | |
341 | None => LocalKind::Decl, | |
342 | Some(init) => { | |
343 | if self.eat_keyword(kw::Else) { | |
344 | if self.token.is_keyword(kw::If) { | |
345 | // `let...else if`. Emit the same error that `parse_block()` would, | |
346 | // but explicitly point out that this pattern is not allowed. | |
347 | let msg = "conditional `else if` is not supported for `let...else`"; | |
348 | return Err(self.error_block_no_opening_brace_msg(msg)); | |
349 | } | |
350 | let els = self.parse_block()?; | |
351 | self.check_let_else_init_bool_expr(&init); | |
352 | self.check_let_else_init_trailing_brace(&init); | |
353 | LocalKind::InitElse(init, els) | |
354 | } else { | |
355 | LocalKind::Init(init) | |
356 | } | |
357 | } | |
358 | }; | |
359 | let hi = if self.token == token::Semi { self.token.span } else { self.prev_token.span }; | |
360 | Ok(P(ast::Local { ty, pat, kind, id: DUMMY_NODE_ID, span: lo.to(hi), attrs, tokens: None })) | |
361 | } | |
362 | ||
363 | fn check_let_else_init_bool_expr(&self, init: &ast::Expr) { | |
364 | if let ast::ExprKind::Binary(op, ..) = init.kind { | |
365 | if op.node.lazy() { | |
366 | let suggs = vec![ | |
367 | (init.span.shrink_to_lo(), "(".to_string()), | |
368 | (init.span.shrink_to_hi(), ")".to_string()), | |
369 | ]; | |
370 | self.struct_span_err( | |
371 | init.span, | |
372 | &format!( | |
373 | "a `{}` expression cannot be directly assigned in `let...else`", | |
374 | op.node.to_string() | |
375 | ), | |
376 | ) | |
377 | .multipart_suggestion( | |
378 | "wrap the expression in parentheses", | |
379 | suggs, | |
380 | Applicability::MachineApplicable, | |
381 | ) | |
382 | .emit(); | |
383 | } | |
384 | } | |
385 | } | |
386 | ||
387 | fn check_let_else_init_trailing_brace(&self, init: &ast::Expr) { | |
388 | if let Some(trailing) = classify::expr_trailing_brace(init) { | |
389 | let err_span = trailing.span.with_lo(trailing.span.hi() - BytePos(1)); | |
390 | let suggs = vec![ | |
391 | (trailing.span.shrink_to_lo(), "(".to_string()), | |
392 | (trailing.span.shrink_to_hi(), ")".to_string()), | |
393 | ]; | |
394 | self.struct_span_err( | |
395 | err_span, | |
396 | "right curly brace `}` before `else` in a `let...else` statement not allowed", | |
397 | ) | |
398 | .multipart_suggestion( | |
399 | "try wrapping the expression in parentheses", | |
400 | suggs, | |
401 | Applicability::MachineApplicable, | |
402 | ) | |
403 | .emit(); | |
404 | } | |
405 | } | |
406 | ||
407 | /// Parses the RHS of a local variable declaration (e.g., `= 14;`). | |
408 | fn parse_initializer(&mut self, eq_optional: bool) -> PResult<'a, Option<P<Expr>>> { | |
409 | let eq_consumed = match self.token.kind { | |
410 | token::BinOpEq(..) => { | |
411 | // Recover `let x <op>= 1` as `let x = 1` | |
412 | self.struct_span_err( | |
413 | self.token.span, | |
414 | "can't reassign to an uninitialized variable", | |
415 | ) | |
416 | .span_suggestion_short( | |
417 | self.token.span, | |
418 | "initialize the variable", | |
419 | "=", | |
420 | Applicability::MaybeIncorrect, | |
421 | ) | |
422 | .help("if you meant to overwrite, remove the `let` binding") | |
423 | .emit(); | |
424 | self.bump(); | |
425 | true | |
426 | } | |
427 | _ => self.eat(&token::Eq), | |
428 | }; | |
429 | ||
430 | Ok(if eq_consumed || eq_optional { Some(self.parse_expr()?) } else { None }) | |
431 | } | |
432 | ||
433 | /// Parses a block. No inner attributes are allowed. | |
434 | pub(super) fn parse_block(&mut self) -> PResult<'a, P<Block>> { | |
435 | let (attrs, block) = self.parse_inner_attrs_and_block()?; | |
436 | if let [.., last] = &*attrs { | |
437 | self.error_on_forbidden_inner_attr(last.span, DEFAULT_INNER_ATTR_FORBIDDEN); | |
438 | } | |
439 | Ok(block) | |
440 | } | |
441 | ||
442 | fn error_block_no_opening_brace_msg( | |
443 | &mut self, | |
444 | msg: &str, | |
445 | ) -> DiagnosticBuilder<'a, ErrorGuaranteed> { | |
446 | let sp = self.token.span; | |
447 | let mut e = self.struct_span_err(sp, msg); | |
448 | let do_not_suggest_help = self.token.is_keyword(kw::In) || self.token == token::Colon; | |
449 | ||
450 | // Check to see if the user has written something like | |
451 | // | |
452 | // if (cond) | |
453 | // bar; | |
454 | // | |
455 | // which is valid in other languages, but not Rust. | |
456 | match self.parse_stmt_without_recovery(false, ForceCollect::No) { | |
457 | // If the next token is an open brace, e.g., we have: | |
458 | // | |
459 | // if expr other_expr { | |
460 | // ^ ^ ^- lookahead(1) is a brace | |
461 | // | |- current token is not "else" | |
462 | // |- (statement we just parsed) | |
463 | // | |
464 | // the place-inside-a-block suggestion would be more likely wrong than right. | |
465 | // | |
466 | // FIXME(compiler-errors): this should probably parse an arbitrary expr and not | |
467 | // just lookahead one token, so we can see if there's a brace after _that_, | |
468 | // since we want to protect against: | |
469 | // `if 1 1 + 1 {` being suggested as `if { 1 } 1 + 1 {` | |
470 | // + + | |
471 | Ok(Some(_)) | |
472 | if (!self.token.is_keyword(kw::Else) | |
473 | && self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Brace))) | |
474 | || do_not_suggest_help => {} | |
475 | // Do not suggest `if foo println!("") {;}` (as would be seen in test for #46836). | |
476 | Ok(Some(Stmt { kind: StmtKind::Empty, .. })) => {} | |
477 | Ok(Some(stmt)) => { | |
478 | let stmt_own_line = self.sess.source_map().is_line_before_span_empty(sp); | |
479 | let stmt_span = if stmt_own_line && self.eat(&token::Semi) { | |
480 | // Expand the span to include the semicolon. | |
481 | stmt.span.with_hi(self.prev_token.span.hi()) | |
482 | } else { | |
483 | stmt.span | |
484 | }; | |
485 | e.multipart_suggestion( | |
486 | "try placing this code inside a block", | |
487 | vec![ | |
488 | (stmt_span.shrink_to_lo(), "{ ".to_string()), | |
489 | (stmt_span.shrink_to_hi(), " }".to_string()), | |
490 | ], | |
491 | // Speculative; has been misleading in the past (#46836). | |
492 | Applicability::MaybeIncorrect, | |
493 | ); | |
494 | } | |
495 | Err(e) => { | |
496 | self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore); | |
497 | e.cancel(); | |
498 | } | |
499 | _ => {} | |
500 | } | |
501 | e.span_label(sp, "expected `{`"); | |
502 | e | |
503 | } | |
504 | ||
505 | fn error_block_no_opening_brace<T>(&mut self) -> PResult<'a, T> { | |
506 | let tok = super::token_descr(&self.token); | |
507 | let msg = format!("expected `{{`, found {}", tok); | |
508 | Err(self.error_block_no_opening_brace_msg(&msg)) | |
509 | } | |
510 | ||
511 | /// Parses a block. Inner attributes are allowed. | |
512 | pub(super) fn parse_inner_attrs_and_block(&mut self) -> PResult<'a, (AttrVec, P<Block>)> { | |
513 | self.parse_block_common(self.token.span, BlockCheckMode::Default) | |
514 | } | |
515 | ||
516 | /// Parses a block. Inner attributes are allowed. | |
517 | pub(super) fn parse_block_common( | |
518 | &mut self, | |
519 | lo: Span, | |
520 | blk_mode: BlockCheckMode, | |
521 | ) -> PResult<'a, (AttrVec, P<Block>)> { | |
522 | maybe_whole!(self, NtBlock, |x| (AttrVec::new(), x)); | |
523 | ||
524 | self.maybe_recover_unexpected_block_label(); | |
525 | if !self.eat(&token::OpenDelim(Delimiter::Brace)) { | |
526 | return self.error_block_no_opening_brace(); | |
527 | } | |
528 | ||
529 | let attrs = self.parse_inner_attributes()?; | |
530 | let tail = match self.maybe_suggest_struct_literal(lo, blk_mode) { | |
531 | Some(tail) => tail?, | |
532 | None => self.parse_block_tail(lo, blk_mode, AttemptLocalParseRecovery::Yes)?, | |
533 | }; | |
534 | Ok((attrs, tail)) | |
535 | } | |
536 | ||
537 | /// Parses the rest of a block expression or function body. | |
538 | /// Precondition: already parsed the '{'. | |
539 | pub(crate) fn parse_block_tail( | |
540 | &mut self, | |
541 | lo: Span, | |
542 | s: BlockCheckMode, | |
543 | recover: AttemptLocalParseRecovery, | |
544 | ) -> PResult<'a, P<Block>> { | |
545 | let mut stmts = vec![]; | |
546 | while !self.eat(&token::CloseDelim(Delimiter::Brace)) { | |
547 | if self.token == token::Eof { | |
548 | break; | |
549 | } | |
550 | let stmt = match self.parse_full_stmt(recover) { | |
551 | Err(mut err) if recover.yes() => { | |
552 | self.maybe_annotate_with_ascription(&mut err, false); | |
553 | err.emit(); | |
554 | self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore); | |
555 | Some(self.mk_stmt_err(self.token.span)) | |
556 | } | |
557 | Ok(stmt) => stmt, | |
558 | Err(err) => return Err(err), | |
559 | }; | |
560 | if let Some(stmt) = stmt { | |
561 | stmts.push(stmt); | |
562 | } else { | |
563 | // Found only `;` or `}`. | |
564 | continue; | |
565 | }; | |
566 | } | |
567 | Ok(self.mk_block(stmts, s, lo.to(self.prev_token.span))) | |
568 | } | |
569 | ||
570 | /// Parses a statement, including the trailing semicolon. | |
571 | pub fn parse_full_stmt( | |
572 | &mut self, | |
573 | recover: AttemptLocalParseRecovery, | |
574 | ) -> PResult<'a, Option<Stmt>> { | |
575 | // Skip looking for a trailing semicolon when we have an interpolated statement. | |
576 | maybe_whole!(self, NtStmt, |x| Some(x.into_inner())); | |
577 | ||
578 | let Some(mut stmt) = self.parse_stmt_without_recovery(true, ForceCollect::No)? else { | |
579 | return Ok(None); | |
580 | }; | |
581 | ||
582 | let mut eat_semi = true; | |
583 | match stmt.kind { | |
584 | // Expression without semicolon. | |
585 | StmtKind::Expr(ref mut expr) | |
586 | if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) => | |
587 | { | |
588 | // Just check for errors and recover; do not eat semicolon yet. | |
589 | if let Err(mut e) = | |
590 | self.expect_one_of(&[], &[token::Semi, token::CloseDelim(Delimiter::Brace)]) | |
591 | { | |
592 | if let TokenKind::DocComment(..) = self.token.kind { | |
593 | if let Ok(snippet) = self.span_to_snippet(self.token.span) { | |
594 | let sp = self.token.span; | |
595 | let marker = &snippet[..3]; | |
596 | let (comment_marker, doc_comment_marker) = marker.split_at(2); | |
597 | ||
598 | e.span_suggestion( | |
599 | sp.with_hi(sp.lo() + BytePos(marker.len() as u32)), | |
600 | &format!( | |
601 | "add a space before `{}` to use a regular comment", | |
602 | doc_comment_marker, | |
603 | ), | |
604 | format!("{} {}", comment_marker, doc_comment_marker), | |
605 | Applicability::MaybeIncorrect, | |
606 | ); | |
607 | } | |
608 | } | |
609 | if let Err(mut e) = | |
610 | self.check_mistyped_turbofish_with_multiple_type_params(e, expr) | |
611 | { | |
612 | if recover.no() { | |
613 | return Err(e); | |
614 | } | |
615 | e.emit(); | |
616 | self.recover_stmt(); | |
617 | } | |
618 | // Don't complain about type errors in body tail after parse error (#57383). | |
619 | let sp = expr.span.to(self.prev_token.span); | |
620 | *expr = self.mk_expr_err(sp); | |
621 | } | |
622 | } | |
623 | StmtKind::Expr(_) | StmtKind::MacCall(_) => {} | |
624 | StmtKind::Local(ref mut local) if let Err(e) = self.expect_semi() => { | |
625 | // We might be at the `,` in `let x = foo<bar, baz>;`. Try to recover. | |
626 | match &mut local.kind { | |
627 | LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => { | |
628 | self.check_mistyped_turbofish_with_multiple_type_params(e, expr)?; | |
629 | // We found `foo<bar, baz>`, have we fully recovered? | |
630 | self.expect_semi()?; | |
631 | } | |
632 | LocalKind::Decl => return Err(e), | |
633 | } | |
634 | eat_semi = false; | |
635 | } | |
636 | StmtKind::Empty | StmtKind::Item(_) | StmtKind::Local(_) | StmtKind::Semi(_) => eat_semi = false, | |
637 | } | |
638 | ||
639 | if eat_semi && self.eat(&token::Semi) { | |
640 | stmt = stmt.add_trailing_semicolon(); | |
641 | } | |
642 | stmt.span = stmt.span.to(self.prev_token.span); | |
643 | Ok(Some(stmt)) | |
644 | } | |
645 | ||
646 | pub(super) fn mk_block(&self, stmts: Vec<Stmt>, rules: BlockCheckMode, span: Span) -> P<Block> { | |
647 | P(Block { | |
648 | stmts, | |
649 | id: DUMMY_NODE_ID, | |
650 | rules, | |
651 | span, | |
652 | tokens: None, | |
653 | could_be_bare_literal: false, | |
654 | }) | |
655 | } | |
656 | ||
657 | pub(super) fn mk_stmt(&self, span: Span, kind: StmtKind) -> Stmt { | |
658 | Stmt { id: DUMMY_NODE_ID, kind, span } | |
659 | } | |
660 | ||
661 | pub(super) fn mk_stmt_err(&self, span: Span) -> Stmt { | |
662 | self.mk_stmt(span, StmtKind::Expr(self.mk_expr_err(span))) | |
663 | } | |
664 | ||
665 | pub(super) fn mk_block_err(&self, span: Span) -> P<Block> { | |
666 | self.mk_block(vec![self.mk_stmt_err(span)], BlockCheckMode::Default, span) | |
667 | } | |
668 | } |