1 use super::attr
::DEFAULT_INNER_ATTR_FORBIDDEN
;
2 use super::diagnostics
::{AttemptLocalParseRecovery, Error}
;
3 use super::expr
::LhsExpr
;
4 use super::pat
::{GateOr, RecoverComma}
;
5 use super::path
::PathStyle
;
6 use super::TrailingToken
;
7 use super::{AttrWrapper, BlockMode, ForceCollect, Parser, Restrictions, SemiColonMode}
;
8 use crate::maybe_whole
;
11 use rustc_ast
::ptr
::P
;
12 use rustc_ast
::token
::{self, TokenKind}
;
13 use rustc_ast
::util
::classify
;
14 use rustc_ast
::AstLike
;
15 use rustc_ast
::{AttrStyle, AttrVec, Attribute, MacCall, MacCallStmt, MacStmtStyle}
;
16 use rustc_ast
::{Block, BlockCheckMode, Expr, ExprKind, Local, Stmt}
;
17 use rustc_ast
::{StmtKind, DUMMY_NODE_ID}
;
18 use rustc_errors
::{Applicability, PResult}
;
19 use rustc_span
::source_map
::{BytePos, Span}
;
20 use rustc_span
::symbol
::{kw, sym}
;
25 /// Parses a statement. This stops just before trailing semicolons on everything but items.
26 /// e.g., a `StmtKind::Semi` parses to a `StmtKind::Expr`, leaving the trailing `;` unconsumed.
27 // Public for rustfmt usage.
28 pub fn parse_stmt(&mut self, force_collect
: ForceCollect
) -> PResult
<'a
, Option
<Stmt
>> {
29 Ok(self.parse_stmt_without_recovery(false, force_collect
).unwrap_or_else(|mut e
| {
31 self.recover_stmt_(SemiColonMode
::Break
, BlockMode
::Ignore
);
36 /// If `force_capture` is true, forces collection of tokens regardless of whether
37 /// or not we have attributes
38 crate fn parse_stmt_without_recovery(
41 force_collect
: ForceCollect
,
42 ) -> PResult
<'a
, Option
<Stmt
>> {
43 let attrs
= self.parse_outer_attributes()?
;
44 let lo
= self.token
.span
;
46 // Don't use `maybe_whole` so that we have precise control
47 // over when we bump the parser
48 if let token
::Interpolated(nt
) = &self.token
.kind
{
49 if let token
::NtStmt(stmt
) = &**nt
{
50 let mut stmt
= stmt
.clone();
51 return self.collect_tokens_trailing_token(
55 stmt
.visit_attrs(|stmt_attrs
| {
56 mem
::swap(stmt_attrs
, &mut attrs
);
57 stmt_attrs
.extend(attrs
);
59 // Make sure we capture the token::Interpolated
61 Ok((Some(stmt
), TrailingToken
::None
))
67 Ok(Some(if self.token
.is_keyword(kw
::Let
) {
68 self.parse_local_mk(lo
, attrs
, capture_semi
, force_collect
)?
69 } else if self.is_kw_followed_by_ident(kw
::Mut
) {
70 self.recover_stmt_local(
72 attrs
.take_for_recovery().into(),
76 } else if self.is_kw_followed_by_ident(kw
::Auto
) {
77 self.bump(); // `auto`
78 let msg
= "write `let` instead of `auto` to introduce a new variable";
79 self.recover_stmt_local(lo
, attrs
.take_for_recovery().into(), msg
, "let")?
80 } else if self.is_kw_followed_by_ident(sym
::var
) {
82 let msg
= "write `let` instead of `var` to introduce a new variable";
83 self.recover_stmt_local(lo
, attrs
.take_for_recovery().into(), msg
, "let")?
84 } else if self.check_path() && !self.token
.is_qpath_start() && !self.is_path_start_item() {
85 // We have avoided contextual keywords like `union`, items with `crate` visibility,
86 // or `auto trait` items. We aim to parse an arbitrary path `a::b` but not something
87 // that starts like a path (1 token), but it fact not a path.
88 // Also, we avoid stealing syntax from `parse_item_`.
89 self.parse_stmt_path_start(lo
, attrs
, force_collect
)?
90 } else if let Some(item
) =
91 self.parse_item_common(attrs
.clone(), false, true, |_
| true, force_collect
)?
93 // FIXME: Bad copy of attrs
94 self.mk_stmt(lo
.to(item
.span
), StmtKind
::Item(P(item
)))
95 } else if self.eat(&token
::Semi
) {
96 // Do not attempt to parse an expression if we're done here.
97 self.error_outer_attrs(&attrs
.take_for_recovery());
98 self.mk_stmt(lo
, StmtKind
::Empty
)
99 } else if self.token
!= token
::CloseDelim(token
::Brace
) {
100 // Remainder are line-expr stmts.
101 let e
= self.parse_expr_res(Restrictions
::STMT_EXPR
, Some(attrs
))?
;
102 self.mk_stmt(lo
.to(e
.span
), StmtKind
::Expr(e
))
104 self.error_outer_attrs(&attrs
.take_for_recovery());
109 fn parse_stmt_path_start(
113 force_collect
: ForceCollect
,
114 ) -> PResult
<'a
, Stmt
> {
115 self.collect_tokens_trailing_token(attrs
, force_collect
, |this
, attrs
| {
116 let path
= this
.parse_path(PathStyle
::Expr
)?
;
118 if this
.eat(&token
::Not
) {
119 let stmt_mac
= this
.parse_stmt_mac(lo
, attrs
.into(), path
)?
;
120 if this
.token
== token
::Semi
{
121 return Ok((stmt_mac
, TrailingToken
::Semi
));
123 return Ok((stmt_mac
, TrailingToken
::None
));
127 let expr
= if this
.eat(&token
::OpenDelim(token
::Brace
)) {
128 this
.parse_struct_expr(path
, AttrVec
::new(), true)?
130 let hi
= this
.prev_token
.span
;
131 this
.mk_expr(lo
.to(hi
), ExprKind
::Path(None
, path
), AttrVec
::new())
134 let expr
= this
.with_res(Restrictions
::STMT_EXPR
, |this
| {
135 let expr
= this
.parse_dot_or_call_expr_with(expr
, lo
, attrs
)?
;
136 this
.parse_assoc_expr_with(0, LhsExpr
::AlreadyParsed(expr
))
139 this
.mk_stmt(lo
.to(this
.prev_token
.span
), StmtKind
::Expr(expr
)),
145 /// Parses a statement macro `mac!(args)` provided a `path` representing `mac`.
146 /// At this point, the `!` token after the path has already been eaten.
147 fn parse_stmt_mac(&mut self, lo
: Span
, attrs
: AttrVec
, path
: ast
::Path
) -> PResult
<'a
, Stmt
> {
148 let args
= self.parse_mac_args()?
;
149 let delim
= args
.delim();
150 let hi
= self.prev_token
.span
;
153 if delim
== token
::Brace { MacStmtStyle::Braces }
else { MacStmtStyle::NoBraces }
;
155 let mac
= MacCall { path, args, prior_type_ascription: self.last_type_ascription }
;
157 let kind
= if delim
== token
::Brace
|| self.token
== token
::Semi
|| self.token
== token
::Eof
159 StmtKind
::MacCall(P(MacCallStmt { mac, style, attrs, tokens: None }
))
161 // Since none of the above applied, this is an expression statement macro.
162 let e
= self.mk_expr(lo
.to(hi
), ExprKind
::MacCall(mac
), AttrVec
::new());
163 let e
= self.maybe_recover_from_bad_qpath(e
, true)?
;
164 let e
= self.parse_dot_or_call_expr_with(e
, lo
, attrs
.into())?
;
165 let e
= self.parse_assoc_expr_with(0, LhsExpr
::AlreadyParsed(e
))?
;
168 Ok(self.mk_stmt(lo
.to(hi
), kind
))
171 /// Error on outer attributes in this context.
172 /// Also error if the previous token was a doc comment.
173 fn error_outer_attrs(&self, attrs
: &[Attribute
]) {
174 if let [.., last
] = attrs
{
175 if last
.is_doc_comment() {
176 self.span_fatal_err(last
.span
, Error
::UselessDocComment
).emit();
177 } else if attrs
.iter().any(|a
| a
.style
== AttrStyle
::Outer
) {
178 self.struct_span_err(last
.span
, "expected statement after outer attribute").emit();
183 fn recover_stmt_local(
189 ) -> PResult
<'a
, Stmt
> {
190 let stmt
= self.recover_local_after_let(lo
, attrs
)?
;
191 self.struct_span_err(lo
, "invalid variable declaration")
192 .span_suggestion(lo
, msg
, sugg
.to_string(), Applicability
::MachineApplicable
)
202 force_collect
: ForceCollect
,
203 ) -> PResult
<'a
, Stmt
> {
204 self.collect_tokens_trailing_token(attrs
, force_collect
, |this
, attrs
| {
205 this
.expect_keyword(kw
::Let
)?
;
206 let local
= this
.parse_local(attrs
.into())?
;
207 let trailing
= if capture_semi
&& this
.token
.kind
== token
::Semi
{
212 Ok((this
.mk_stmt(lo
.to(this
.prev_token
.span
), StmtKind
::Local(local
)), trailing
))
216 fn recover_local_after_let(&mut self, lo
: Span
, attrs
: AttrVec
) -> PResult
<'a
, Stmt
> {
217 let local
= self.parse_local(attrs
)?
;
218 Ok(self.mk_stmt(lo
.to(self.prev_token
.span
), StmtKind
::Local(local
)))
221 /// Parses a local variable declaration.
222 fn parse_local(&mut self, attrs
: AttrVec
) -> PResult
<'a
, P
<Local
>> {
223 let lo
= self.prev_token
.span
;
225 self.parse_pat_before_ty(None
, GateOr
::Yes
, RecoverComma
::Yes
, "`let` bindings")?
;
227 let (err
, ty
) = if colon
{
228 // Save the state of the parser before parsing type normally, in case there is a `:`
229 // instead of an `=` typo.
230 let parser_snapshot_before_type
= self.clone();
231 let colon_sp
= self.prev_token
.span
;
232 match self.parse_ty() {
233 Ok(ty
) => (None
, Some(ty
)),
235 if let Ok(snip
) = self.span_to_snippet(pat
.span
) {
236 err
.span_label(pat
.span
, format
!("while parsing the type for `{}`", snip
));
238 let err
= if self.check(&token
::Eq
) {
242 // Rewind to before attempting to parse the type and continue parsing.
243 let parser_snapshot_after_type
=
244 mem
::replace(self, parser_snapshot_before_type
);
245 Some((parser_snapshot_after_type
, colon_sp
, err
))
253 let init
= match (self.parse_initializer(err
.is_some()), err
) {
254 (Ok(init
), None
) => {
255 // init parsed, ty parsed
258 (Ok(init
), Some((_
, colon_sp
, mut err
))) => {
259 // init parsed, ty error
260 // Could parse the type as if it were the initializer, it is likely there was a
261 // typo in the code: `:` instead of `=`. Add suggestion and emit the error.
262 err
.span_suggestion_short(
264 "use `=` if you meant to assign",
266 Applicability
::MachineApplicable
,
269 // As this was parsed successfully, continue as if the code has been fixed for the
270 // rest of the file. It will still fail due to the emitted error, but we avoid
274 (Err(mut init_err
), Some((snapshot
, _
, ty_err
))) => {
275 // init error, ty error
277 // Couldn't parse the type nor the initializer, only raise the type error and
278 // return to the parser state before parsing the type as the initializer.
279 // let x: <parse_error>;
283 (Err(err
), None
) => {
284 // init error, ty parsed
285 // Couldn't parse the initializer and we're not attempting to recover a failed
286 // parse of the type, return the error.
290 let hi
= if self.token
== token
::Semi { self.token.span }
else { self.prev_token.span }
;
291 Ok(P(ast
::Local { ty, pat, init, id: DUMMY_NODE_ID, span: lo.to(hi), attrs, tokens: None }
))
294 /// Parses the RHS of a local variable declaration (e.g., `= 14;`).
295 fn parse_initializer(&mut self, eq_optional
: bool
) -> PResult
<'a
, Option
<P
<Expr
>>> {
296 let eq_consumed
= match self.token
.kind
{
297 token
::BinOpEq(..) => {
298 // Recover `let x <op>= 1` as `let x = 1`
299 self.struct_span_err(
301 "can't reassign to an uninitialized variable",
303 .span_suggestion_short(
305 "initialize the variable",
307 Applicability
::MaybeIncorrect
,
309 .help("if you meant to overwrite, remove the `let` binding")
314 _
=> self.eat(&token
::Eq
),
317 Ok(if eq_consumed
|| eq_optional { Some(self.parse_expr()?) }
else { None }
)
320 /// Parses a block. No inner attributes are allowed.
321 pub(super) fn parse_block(&mut self) -> PResult
<'a
, P
<Block
>> {
322 let (attrs
, block
) = self.parse_inner_attrs_and_block()?
;
323 if let [.., last
] = &*attrs
{
324 self.error_on_forbidden_inner_attr(last
.span
, DEFAULT_INNER_ATTR_FORBIDDEN
);
329 fn error_block_no_opening_brace
<T
>(&mut self) -> PResult
<'a
, T
> {
330 let sp
= self.token
.span
;
331 let tok
= super::token_descr(&self.token
);
332 let mut e
= self.struct_span_err(sp
, &format
!("expected `{{`, found {}", tok
));
333 let do_not_suggest_help
= self.token
.is_keyword(kw
::In
) || self.token
== token
::Colon
;
335 // Check to see if the user has written something like
340 // which is valid in other languages, but not Rust.
341 match self.parse_stmt_without_recovery(false, ForceCollect
::No
) {
342 // If the next token is an open brace (e.g., `if a b {`), the place-
343 // inside-a-block suggestion would be more likely wrong than right.
345 if self.look_ahead(1, |t
| t
== &token
::OpenDelim(token
::Brace
))
346 || do_not_suggest_help
=> {}
348 let stmt_own_line
= self.sess
.source_map().is_line_before_span_empty(sp
);
349 let stmt_span
= if stmt_own_line
&& self.eat(&token
::Semi
) {
350 // Expand the span to include the semicolon.
351 stmt
.span
.with_hi(self.prev_token
.span
.hi())
355 if let Ok(snippet
) = self.span_to_snippet(stmt_span
) {
358 "try placing this code inside a block",
359 format
!("{{ {} }}", snippet
),
360 // Speculative; has been misleading in the past (#46836).
361 Applicability
::MaybeIncorrect
,
366 self.recover_stmt_(SemiColonMode
::Break
, BlockMode
::Ignore
);
371 e
.span_label(sp
, "expected `{`");
375 /// Parses a block. Inner attributes are allowed.
376 pub(super) fn parse_inner_attrs_and_block(
378 ) -> PResult
<'a
, (Vec
<Attribute
>, P
<Block
>)> {
379 self.parse_block_common(self.token
.span
, BlockCheckMode
::Default
)
382 /// Parses a block. Inner attributes are allowed.
383 pub(super) fn parse_block_common(
386 blk_mode
: BlockCheckMode
,
387 ) -> PResult
<'a
, (Vec
<Attribute
>, P
<Block
>)> {
388 maybe_whole
!(self, NtBlock
, |x
| (Vec
::new(), x
));
390 if !self.eat(&token
::OpenDelim(token
::Brace
)) {
391 return self.error_block_no_opening_brace();
394 let attrs
= self.parse_inner_attributes()?
;
395 let tail
= if let Some(tail
) = self.maybe_suggest_struct_literal(lo
, blk_mode
) {
398 self.parse_block_tail(lo
, blk_mode
, AttemptLocalParseRecovery
::Yes
)?
403 /// Parses the rest of a block expression or function body.
404 /// Precondition: already parsed the '{'.
405 crate fn parse_block_tail(
409 recover
: AttemptLocalParseRecovery
,
410 ) -> PResult
<'a
, P
<Block
>> {
411 let mut stmts
= vec
![];
412 while !self.eat(&token
::CloseDelim(token
::Brace
)) {
413 if self.token
== token
::Eof
{
416 let stmt
= match self.parse_full_stmt(recover
) {
417 Err(mut err
) if recover
.yes() => {
418 self.maybe_annotate_with_ascription(&mut err
, false);
420 self.recover_stmt_(SemiColonMode
::Ignore
, BlockMode
::Ignore
);
421 Some(self.mk_stmt_err(self.token
.span
))
424 Err(err
) => return Err(err
),
426 if let Some(stmt
) = stmt
{
429 // Found only `;` or `}`.
433 Ok(self.mk_block(stmts
, s
, lo
.to(self.prev_token
.span
)))
436 /// Parses a statement, including the trailing semicolon.
437 pub fn parse_full_stmt(
439 recover
: AttemptLocalParseRecovery
,
440 ) -> PResult
<'a
, Option
<Stmt
>> {
441 // Skip looking for a trailing semicolon when we have an interpolated statement.
442 maybe_whole
!(self, NtStmt
, |x
| Some(x
));
444 let mut stmt
= match self.parse_stmt_without_recovery(true, ForceCollect
::No
)?
{
446 None
=> return Ok(None
),
449 let mut eat_semi
= true;
451 // Expression without semicolon.
452 StmtKind
::Expr(ref mut expr
)
453 if self.token
!= token
::Eof
&& classify
::expr_requires_semi_to_be_stmt(expr
) =>
455 // Just check for errors and recover; do not eat semicolon yet.
457 self.expect_one_of(&[], &[token
::Semi
, token
::CloseDelim(token
::Brace
)])
459 if let TokenKind
::DocComment(..) = self.token
.kind
{
460 if let Ok(snippet
) = self.span_to_snippet(self.token
.span
) {
461 let sp
= self.token
.span
;
462 let marker
= &snippet
[..3];
463 let (comment_marker
, doc_comment_marker
) = marker
.split_at(2);
466 sp
.with_hi(sp
.lo() + BytePos(marker
.len() as u32)),
468 "add a space before `{}` to use a regular comment",
471 format
!("{} {}", comment_marker
, doc_comment_marker
),
472 Applicability
::MaybeIncorrect
,
477 self.check_mistyped_turbofish_with_multiple_type_params(e
, expr
)
485 // Don't complain about type errors in body tail after parse error (#57383).
486 let sp
= expr
.span
.to(self.prev_token
.span
);
487 *expr
= self.mk_expr_err(sp
);
490 StmtKind
::Expr(_
) | StmtKind
::MacCall(_
) => {}
491 StmtKind
::Local(ref mut local
) => {
492 if let Err(e
) = self.expect_semi() {
493 // We might be at the `,` in `let x = foo<bar, baz>;`. Try to recover.
494 match &mut local
.init
{
495 Some(ref mut expr
) => {
496 self.check_mistyped_turbofish_with_multiple_type_params(e
, expr
)?
;
497 // We found `foo<bar, baz>`, have we fully recovered?
500 None
=> return Err(e
),
505 StmtKind
::Empty
| StmtKind
::Item(_
) | StmtKind
::Semi(_
) => eat_semi
= false,
508 if eat_semi
&& self.eat(&token
::Semi
) {
509 stmt
= stmt
.add_trailing_semicolon();
511 stmt
.span
= stmt
.span
.to(self.prev_token
.span
);
515 pub(super) fn mk_block(&self, stmts
: Vec
<Stmt
>, rules
: BlockCheckMode
, span
: Span
) -> P
<Block
> {
516 P(Block { stmts, id: DUMMY_NODE_ID, rules, span, tokens: None }
)
519 pub(super) fn mk_stmt(&self, span
: Span
, kind
: StmtKind
) -> Stmt
{
520 Stmt { id: DUMMY_NODE_ID, kind, span }
523 pub(super) fn mk_stmt_err(&self, span
: Span
) -> Stmt
{
524 self.mk_stmt(span
, StmtKind
::Expr(self.mk_expr_err(span
)))
527 pub(super) fn mk_block_err(&self, span
: Span
) -> P
<Block
> {
528 self.mk_block(vec
![self.mk_stmt_err(span
)], BlockCheckMode
::Default
, span
)