1 use super::{Parser, PResult, Restrictions, PrevTokenKind, SemiColonMode, BlockMode}
;
2 use super::expr
::LhsExpr
;
3 use super::path
::PathStyle
;
6 use crate::{maybe_whole, ThinVec}
;
7 use crate::ast
::{self, Stmt, StmtKind, Local, Block, BlockCheckMode, Expr, ExprKind}
;
8 use crate::ast
::{Attribute, AttrStyle, VisibilityKind, MacStmtStyle, Mac_, MacDelimiter}
;
9 use crate::ext
::base
::DummyResult
;
10 use crate::parse
::{classify, DirectoryOwnership}
;
11 use crate::parse
::diagnostics
::Error
;
12 use crate::parse
::token
::{self}
;
13 use crate::source_map
::{respan, Span}
;
14 use crate::symbol
::{kw, sym}
;
17 use errors
::Applicability
;
20 /// Parse a statement. This stops just before trailing semicolons on everything but items.
21 /// e.g., a `StmtKind::Semi` parses to a `StmtKind::Expr`, leaving the trailing `;` unconsumed.
22 pub fn parse_stmt(&mut self) -> PResult
<'a
, Option
<Stmt
>> {
23 Ok(self.parse_stmt_(true))
26 fn parse_stmt_(&mut self, macro_legacy_warnings
: bool
) -> Option
<Stmt
> {
27 self.parse_stmt_without_recovery(macro_legacy_warnings
).unwrap_or_else(|mut e
| {
29 self.recover_stmt_(SemiColonMode
::Break
, BlockMode
::Ignore
);
34 fn parse_stmt_without_recovery(
36 macro_legacy_warnings
: bool
,
37 ) -> PResult
<'a
, Option
<Stmt
>> {
38 maybe_whole
!(self, NtStmt
, |x
| Some(x
));
40 let attrs
= self.parse_outer_attributes()?
;
41 let lo
= self.token
.span
;
43 Ok(Some(if self.eat_keyword(kw
::Let
) {
45 id
: ast
::DUMMY_NODE_ID
,
46 node
: StmtKind
::Local(self.parse_local(attrs
.into())?
),
47 span
: lo
.to(self.prev_span
),
49 } else if let Some(macro_def
) = self.eat_macro_def(
51 &respan(lo
, VisibilityKind
::Inherited
),
55 id
: ast
::DUMMY_NODE_ID
,
56 node
: StmtKind
::Item(macro_def
),
57 span
: lo
.to(self.prev_span
),
59 // Starts like a simple path, being careful to avoid contextual keywords
60 // such as a union items, item with `crate` visibility or auto trait items.
61 // Our goal here is to parse an arbitrary path `a::b::c` but not something that starts
62 // like a path (1 token), but it fact not a path.
63 // `union::b::c` - path, `union U { ... }` - not a path.
64 // `crate::b::c` - path, `crate struct S;` - not a path.
65 } else if self.token
.is_path_start() &&
66 !self.token
.is_qpath_start() &&
67 !self.is_union_item() &&
68 !self.is_crate_vis() &&
69 !self.is_auto_trait_item() &&
71 let path
= self.parse_path(PathStyle
::Expr
)?
;
73 if !self.eat(&token
::Not
) {
74 let expr
= if self.check(&token
::OpenDelim(token
::Brace
)) {
75 self.parse_struct_expr(lo
, path
, ThinVec
::new())?
77 let hi
= self.prev_span
;
78 self.mk_expr(lo
.to(hi
), ExprKind
::Path(None
, path
), ThinVec
::new())
81 let expr
= self.with_res(Restrictions
::STMT_EXPR
, |this
| {
82 let expr
= this
.parse_dot_or_call_expr_with(expr
, lo
, attrs
.into())?
;
83 this
.parse_assoc_expr_with(0, LhsExpr
::AlreadyParsed(expr
))
87 id
: ast
::DUMMY_NODE_ID
,
88 node
: StmtKind
::Expr(expr
),
89 span
: lo
.to(self.prev_span
),
93 let (delim
, tts
) = self.expect_delimited_token_tree()?
;
94 let hi
= self.prev_span
;
96 let style
= if delim
== MacDelimiter
::Brace
{
99 MacStmtStyle
::NoBraces
102 let mac
= respan(lo
.to(hi
), Mac_
{
106 prior_type_ascription
: self.last_type_ascription
,
108 let node
= if delim
== MacDelimiter
::Brace
||
109 self.token
== token
::Semi
|| self.token
== token
::Eof
{
110 StmtKind
::Mac(P((mac
, style
, attrs
.into())))
112 // We used to incorrectly stop parsing macro-expanded statements here.
113 // If the next token will be an error anyway but could have parsed with the
114 // earlier behavior, stop parsing here and emit a warning to avoid breakage.
115 else if macro_legacy_warnings
&&
116 self.token
.can_begin_expr() &&
117 match self.token
.kind
{
118 // These can continue an expression, so we can't stop parsing and warn.
119 token
::OpenDelim(token
::Paren
) | token
::OpenDelim(token
::Bracket
) |
120 token
::BinOp(token
::Minus
) | token
::BinOp(token
::Star
) |
121 token
::BinOp(token
::And
) | token
::BinOp(token
::Or
) |
122 token
::AndAnd
| token
::OrOr
|
123 token
::DotDot
| token
::DotDotDot
| token
::DotDotEq
=> false,
126 self.warn_missing_semicolon();
127 StmtKind
::Mac(P((mac
, style
, attrs
.into())))
129 let e
= self.mk_expr(mac
.span
, ExprKind
::Mac(mac
), ThinVec
::new());
130 let e
= self.maybe_recover_from_bad_qpath(e
, true)?
;
131 let e
= self.parse_dot_or_call_expr_with(e
, lo
, attrs
.into())?
;
132 let e
= self.parse_assoc_expr_with(0, LhsExpr
::AlreadyParsed(e
))?
;
136 id
: ast
::DUMMY_NODE_ID
,
141 // FIXME: Bad copy of attrs
142 let old_directory_ownership
=
143 mem
::replace(&mut self.directory
.ownership
, DirectoryOwnership
::UnownedViaBlock
);
144 let item
= self.parse_item_(attrs
.clone(), false, true)?
;
145 self.directory
.ownership
= old_directory_ownership
;
149 id
: ast
::DUMMY_NODE_ID
,
151 node
: StmtKind
::Item(i
),
154 let unused_attrs
= |attrs
: &[Attribute
], s
: &mut Self| {
155 if !attrs
.is_empty() {
156 if s
.prev_token_kind
== PrevTokenKind
::DocComment
{
157 s
.span_fatal_err(s
.prev_span
, Error
::UselessDocComment
).emit();
158 } else if attrs
.iter().any(|a
| a
.style
== AttrStyle
::Outer
) {
160 s
.token
.span
, "expected statement after outer attribute"
166 // Do not attempt to parse an expression if we're done here.
167 if self.token
== token
::Semi
{
168 unused_attrs(&attrs
, self);
173 if self.token
== token
::CloseDelim(token
::Brace
) {
174 unused_attrs(&attrs
, self);
178 // Remainder are line-expr stmts.
179 let e
= self.parse_expr_res(
180 Restrictions
::STMT_EXPR
, Some(attrs
.into()))?
;
182 id
: ast
::DUMMY_NODE_ID
,
184 node
: StmtKind
::Expr(e
),
191 /// Parses a local variable declaration.
192 fn parse_local(&mut self, attrs
: ThinVec
<Attribute
>) -> PResult
<'a
, P
<Local
>> {
193 let lo
= self.prev_span
;
194 let pat
= self.parse_top_level_pat()?
;
196 let (err
, ty
) = if self.eat(&token
::Colon
) {
197 // Save the state of the parser before parsing type normally, in case there is a `:`
198 // instead of an `=` typo.
199 let parser_snapshot_before_type
= self.clone();
200 let colon_sp
= self.prev_span
;
201 match self.parse_ty() {
202 Ok(ty
) => (None
, Some(ty
)),
204 // Rewind to before attempting to parse the type and continue parsing
205 let parser_snapshot_after_type
= self.clone();
206 mem
::replace(self, parser_snapshot_before_type
);
208 let snippet
= self.span_to_snippet(pat
.span
).unwrap();
209 err
.span_label(pat
.span
, format
!("while parsing the type for `{}`", snippet
));
210 (Some((parser_snapshot_after_type
, colon_sp
, err
)), None
)
216 let init
= match (self.parse_initializer(err
.is_some()), err
) {
217 (Ok(init
), None
) => { // init parsed, ty parsed
220 (Ok(init
), Some((_
, colon_sp
, mut err
))) => { // init parsed, ty error
221 // Could parse the type as if it were the initializer, it is likely there was a
222 // typo in the code: `:` instead of `=`. Add suggestion and emit the error.
223 err
.span_suggestion_short(
225 "use `=` if you meant to assign",
227 Applicability
::MachineApplicable
230 // As this was parsed successfully, continue as if the code has been fixed for the
231 // rest of the file. It will still fail due to the emitted error, but we avoid
235 (Err(mut init_err
), Some((snapshot
, _
, ty_err
))) => { // init error, ty error
237 // Couldn't parse the type nor the initializer, only raise the type error and
238 // return to the parser state before parsing the type as the initializer.
239 // let x: <parse_error>;
240 mem
::replace(self, snapshot
);
243 (Err(err
), None
) => { // init error, ty parsed
244 // Couldn't parse the initializer and we're not attempting to recover a failed
245 // parse of the type, return the error.
249 let hi
= if self.token
== token
::Semi
{
258 id
: ast
::DUMMY_NODE_ID
,
264 /// Parses the RHS of a local variable declaration (e.g., '= 14;').
265 fn parse_initializer(&mut self, skip_eq
: bool
) -> PResult
<'a
, Option
<P
<Expr
>>> {
266 if self.eat(&token
::Eq
) {
267 Ok(Some(self.parse_expr()?
))
269 Ok(Some(self.parse_expr()?
))
275 fn is_auto_trait_item(&self) -> bool
{
277 (self.token
.is_keyword(kw
::Auto
) &&
278 self.is_keyword_ahead(1, &[kw
::Trait
]))
279 || // unsafe auto trait
280 (self.token
.is_keyword(kw
::Unsafe
) &&
281 self.is_keyword_ahead(1, &[kw
::Auto
]) &&
282 self.is_keyword_ahead(2, &[kw
::Trait
]))
285 /// Parses a block. No inner attributes are allowed.
286 pub fn parse_block(&mut self) -> PResult
<'a
, P
<Block
>> {
287 maybe_whole
!(self, NtBlock
, |x
| x
);
289 let lo
= self.token
.span
;
291 if !self.eat(&token
::OpenDelim(token
::Brace
)) {
292 let sp
= self.token
.span
;
293 let tok
= self.this_token_descr();
294 let mut e
= self.span_fatal(sp
, &format
!("expected `{{`, found {}", tok
));
295 let do_not_suggest_help
=
296 self.token
.is_keyword(kw
::In
) || self.token
== token
::Colon
;
298 if self.token
.is_ident_named(sym
::and
) {
299 e
.span_suggestion_short(
301 "use `&&` instead of `and` for the boolean operator",
303 Applicability
::MaybeIncorrect
,
306 if self.token
.is_ident_named(sym
::or
) {
307 e
.span_suggestion_short(
309 "use `||` instead of `or` for the boolean operator",
311 Applicability
::MaybeIncorrect
,
315 // Check to see if the user has written something like
320 // Which is valid in other languages, but not Rust.
321 match self.parse_stmt_without_recovery(false) {
323 if self.look_ahead(1, |t
| t
== &token
::OpenDelim(token
::Brace
))
324 || do_not_suggest_help
{
325 // if the next token is an open brace (e.g., `if a b {`), the place-
326 // inside-a-block suggestion would be more likely wrong than right
327 e
.span_label(sp
, "expected `{`");
330 let mut stmt_span
= stmt
.span
;
331 // expand the span to include the semicolon, if it exists
332 if self.eat(&token
::Semi
) {
333 stmt_span
= stmt_span
.with_hi(self.prev_span
.hi());
335 if let Ok(snippet
) = self.span_to_snippet(stmt_span
) {
338 "try placing this code inside a block",
339 format
!("{{ {} }}", snippet
),
340 // speculative, has been misleading in the past (#46836)
341 Applicability
::MaybeIncorrect
,
346 self.recover_stmt_(SemiColonMode
::Break
, BlockMode
::Ignore
);
351 e
.span_label(sp
, "expected `{`");
355 self.parse_block_tail(lo
, BlockCheckMode
::Default
)
358 /// Parses a block. Inner attributes are allowed.
359 crate fn parse_inner_attrs_and_block(&mut self) -> PResult
<'a
, (Vec
<Attribute
>, P
<Block
>)> {
360 maybe_whole
!(self, NtBlock
, |x
| (Vec
::new(), x
));
362 let lo
= self.token
.span
;
363 self.expect(&token
::OpenDelim(token
::Brace
))?
;
364 Ok((self.parse_inner_attributes()?
,
365 self.parse_block_tail(lo
, BlockCheckMode
::Default
)?
))
368 /// Parses the rest of a block expression or function body.
369 /// Precondition: already parsed the '{'.
370 pub(super) fn parse_block_tail(
374 ) -> PResult
<'a
, P
<Block
>> {
375 let mut stmts
= vec
![];
376 while !self.eat(&token
::CloseDelim(token
::Brace
)) {
377 if self.token
== token
::Eof
{
380 let stmt
= match self.parse_full_stmt(false) {
383 self.recover_stmt_(SemiColonMode
::Ignore
, BlockMode
::Ignore
);
385 id
: ast
::DUMMY_NODE_ID
,
386 node
: StmtKind
::Expr(DummyResult
::raw_expr(self.token
.span
, true)),
387 span
: self.token
.span
,
392 if let Some(stmt
) = stmt
{
395 // Found only `;` or `}`.
401 id
: ast
::DUMMY_NODE_ID
,
403 span
: lo
.to(self.prev_span
),
407 /// Parses a statement, including the trailing semicolon.
408 crate fn parse_full_stmt(&mut self, macro_legacy_warnings
: bool
) -> PResult
<'a
, Option
<Stmt
>> {
409 // skip looking for a trailing semicolon when we have an interpolated statement
410 maybe_whole
!(self, NtStmt
, |x
| Some(x
));
412 let mut stmt
= match self.parse_stmt_without_recovery(macro_legacy_warnings
)?
{
414 None
=> return Ok(None
),
418 StmtKind
::Expr(ref expr
) if self.token
!= token
::Eof
=> {
419 // expression without semicolon
420 if classify
::expr_requires_semi_to_be_stmt(expr
) {
421 // Just check for errors and recover; do not eat semicolon yet.
423 self.expect_one_of(&[], &[token
::Semi
, token
::CloseDelim(token
::Brace
)])
427 // Don't complain about type errors in body tail after parse error (#57383).
428 let sp
= expr
.span
.to(self.prev_span
);
429 stmt
.node
= StmtKind
::Expr(DummyResult
::raw_expr(sp
, true));
433 StmtKind
::Local(..) => {
434 // We used to incorrectly allow a macro-expanded let statement to lack a semicolon.
435 if macro_legacy_warnings
&& self.token
!= token
::Semi
{
436 self.warn_missing_semicolon();
438 self.expect_one_of(&[], &[token
::Semi
])?
;
444 if self.eat(&token
::Semi
) {
445 stmt
= stmt
.add_trailing_semicolon();
447 stmt
.span
= stmt
.span
.to(self.prev_span
);
451 fn warn_missing_semicolon(&self) {
452 self.diagnostic().struct_span_warn(self.token
.span
, {
453 &format
!("expected `;`, found {}", self.this_token_descr())
455 "This was erroneously allowed and will become a hard error in a future release"