]> git.lizzy.rs Git - rust.git/commitdiff
parser: move into stmt.rs
authorMazdak Farrokhzad <twingoow@gmail.com>
Sun, 11 Aug 2019 18:32:29 +0000 (20:32 +0200)
committerMazdak Farrokhzad <twingoow@gmail.com>
Sun, 11 Aug 2019 18:32:29 +0000 (20:32 +0200)
src/libsyntax/parse/parser.rs
src/libsyntax/parse/parser/expr.rs
src/libsyntax/parse/parser/item.rs
src/libsyntax/parse/parser/path.rs
src/libsyntax/parse/parser/stmt.rs [new file with mode: 0644]

index e2a41a808686ed4304c74c6e4b8c7cc9b0462186..182941406ab8b8995e041cbe3c5e39482f196b6c 100644 (file)
@@ -1,5 +1,4 @@
 mod expr;
-use expr::LhsExpr;
 mod pat;
 mod item;
 pub use item::AliasKind;
@@ -8,23 +7,15 @@
 mod ty;
 mod path;
 pub use path::PathStyle;
+mod stmt;
 
-use crate::ast::{self, AttrStyle};
-use crate::ast::{Arg, Attribute, BindingMode};
-use crate::ast::{Block, BlockCheckMode, Expr, ExprKind, Stmt, StmtKind};
-use crate::ast::{FnDecl};
-use crate::ast::{Ident, IsAsync, Local, Lifetime};
-use crate::ast::{MacStmtStyle, Mac_, MacDelimiter};
-use crate::ast::{Mutability};
-use crate::ast::StrStyle;
-use crate::ast::SelfKind;
-use crate::ast::{GenericParam, GenericParamKind, WhereClause};
-use crate::ast::{TyKind,  GenericBounds};
+use crate::ast::{self, AttrStyle, Attribute, Arg, BindingMode, StrStyle, SelfKind};
+use crate::ast::{FnDecl, Ident, IsAsync, Lifetime, MacDelimiter, Mutability};
+use crate::ast::{GenericParam, GenericParamKind, WhereClause, TyKind,  GenericBounds};
 use crate::ast::{Visibility, VisibilityKind, Unsafety, CrateSugar};
-use crate::ext::base::DummyResult;
 use crate::ext::hygiene::SyntaxContext;
 use crate::source_map::{self, respan};
-use crate::parse::{SeqSep, classify, literal, token};
+use crate::parse::{SeqSep, literal, token};
 use crate::parse::lexer::UnmatchedBrace;
 use crate::parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration};
 use crate::parse::token::{Token, TokenKind, DelimToken};
@@ -1214,450 +1205,6 @@ fn with_res<F, T>(&mut self, r: Restrictions, f: F) -> T
 
     }
 
-    /// Parses the RHS of a local variable declaration (e.g., '= 14;').
-    fn parse_initializer(&mut self, skip_eq: bool) -> PResult<'a, Option<P<Expr>>> {
-        if self.eat(&token::Eq) {
-            Ok(Some(self.parse_expr()?))
-        } else if skip_eq {
-            Ok(Some(self.parse_expr()?))
-        } else {
-            Ok(None)
-        }
-    }
-
-    /// Parses a local variable declaration.
-    fn parse_local(&mut self, attrs: ThinVec<Attribute>) -> PResult<'a, P<Local>> {
-        let lo = self.prev_span;
-        let pat = self.parse_top_level_pat()?;
-
-        let (err, ty) = if self.eat(&token::Colon) {
-            // Save the state of the parser before parsing type normally, in case there is a `:`
-            // instead of an `=` typo.
-            let parser_snapshot_before_type = self.clone();
-            let colon_sp = self.prev_span;
-            match self.parse_ty() {
-                Ok(ty) => (None, Some(ty)),
-                Err(mut err) => {
-                    // Rewind to before attempting to parse the type and continue parsing
-                    let parser_snapshot_after_type = self.clone();
-                    mem::replace(self, parser_snapshot_before_type);
-
-                    let snippet = self.span_to_snippet(pat.span).unwrap();
-                    err.span_label(pat.span, format!("while parsing the type for `{}`", snippet));
-                    (Some((parser_snapshot_after_type, colon_sp, err)), None)
-                }
-            }
-        } else {
-            (None, None)
-        };
-        let init = match (self.parse_initializer(err.is_some()), err) {
-            (Ok(init), None) => {  // init parsed, ty parsed
-                init
-            }
-            (Ok(init), Some((_, colon_sp, mut err))) => {  // init parsed, ty error
-                // Could parse the type as if it were the initializer, it is likely there was a
-                // typo in the code: `:` instead of `=`. Add suggestion and emit the error.
-                err.span_suggestion_short(
-                    colon_sp,
-                    "use `=` if you meant to assign",
-                    "=".to_string(),
-                    Applicability::MachineApplicable
-                );
-                err.emit();
-                // As this was parsed successfully, continue as if the code has been fixed for the
-                // rest of the file. It will still fail due to the emitted error, but we avoid
-                // extra noise.
-                init
-            }
-            (Err(mut init_err), Some((snapshot, _, ty_err))) => {  // init error, ty error
-                init_err.cancel();
-                // Couldn't parse the type nor the initializer, only raise the type error and
-                // return to the parser state before parsing the type as the initializer.
-                // let x: <parse_error>;
-                mem::replace(self, snapshot);
-                return Err(ty_err);
-            }
-            (Err(err), None) => {  // init error, ty parsed
-                // Couldn't parse the initializer and we're not attempting to recover a failed
-                // parse of the type, return the error.
-                return Err(err);
-            }
-        };
-        let hi = if self.token == token::Semi {
-            self.token.span
-        } else {
-            self.prev_span
-        };
-        Ok(P(ast::Local {
-            ty,
-            pat,
-            init,
-            id: ast::DUMMY_NODE_ID,
-            span: lo.to(hi),
-            attrs,
-        }))
-    }
-
-    /// Parse a statement. This stops just before trailing semicolons on everything but items.
-    /// e.g., a `StmtKind::Semi` parses to a `StmtKind::Expr`, leaving the trailing `;` unconsumed.
-    pub fn parse_stmt(&mut self) -> PResult<'a, Option<Stmt>> {
-        Ok(self.parse_stmt_(true))
-    }
-
-    fn parse_stmt_(&mut self, macro_legacy_warnings: bool) -> Option<Stmt> {
-        self.parse_stmt_without_recovery(macro_legacy_warnings).unwrap_or_else(|mut e| {
-            e.emit();
-            self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore);
-            None
-        })
-    }
-
-    fn is_async_fn(&self) -> bool {
-        self.token.is_keyword(kw::Async) &&
-            self.is_keyword_ahead(1, &[kw::Fn])
-    }
-
-    fn is_crate_vis(&self) -> bool {
-        self.token.is_keyword(kw::Crate) && self.look_ahead(1, |t| t != &token::ModSep)
-    }
-
-    fn is_auto_trait_item(&self) -> bool {
-        // auto trait
-        (self.token.is_keyword(kw::Auto) &&
-            self.is_keyword_ahead(1, &[kw::Trait]))
-        || // unsafe auto trait
-        (self.token.is_keyword(kw::Unsafe) &&
-         self.is_keyword_ahead(1, &[kw::Auto]) &&
-         self.is_keyword_ahead(2, &[kw::Trait]))
-    }
-
-    fn parse_stmt_without_recovery(
-        &mut self,
-        macro_legacy_warnings: bool,
-    ) -> PResult<'a, Option<Stmt>> {
-        maybe_whole!(self, NtStmt, |x| Some(x));
-
-        let attrs = self.parse_outer_attributes()?;
-        let lo = self.token.span;
-
-        Ok(Some(if self.eat_keyword(kw::Let) {
-            Stmt {
-                id: ast::DUMMY_NODE_ID,
-                node: StmtKind::Local(self.parse_local(attrs.into())?),
-                span: lo.to(self.prev_span),
-            }
-        } else if let Some(macro_def) = self.eat_macro_def(
-            &attrs,
-            &source_map::respan(lo, VisibilityKind::Inherited),
-            lo,
-        )? {
-            Stmt {
-                id: ast::DUMMY_NODE_ID,
-                node: StmtKind::Item(macro_def),
-                span: lo.to(self.prev_span),
-            }
-        // Starts like a simple path, being careful to avoid contextual keywords
-        // such as a union items, item with `crate` visibility or auto trait items.
-        // Our goal here is to parse an arbitrary path `a::b::c` but not something that starts
-        // like a path (1 token), but it fact not a path.
-        // `union::b::c` - path, `union U { ... }` - not a path.
-        // `crate::b::c` - path, `crate struct S;` - not a path.
-        } else if self.token.is_path_start() &&
-                  !self.token.is_qpath_start() &&
-                  !self.is_union_item() &&
-                  !self.is_crate_vis() &&
-                  !self.is_auto_trait_item() &&
-                  !self.is_async_fn() {
-            let path = self.parse_path(PathStyle::Expr)?;
-
-            if !self.eat(&token::Not) {
-                let expr = if self.check(&token::OpenDelim(token::Brace)) {
-                    self.parse_struct_expr(lo, path, ThinVec::new())?
-                } else {
-                    let hi = self.prev_span;
-                    self.mk_expr(lo.to(hi), ExprKind::Path(None, path), ThinVec::new())
-                };
-
-                let expr = self.with_res(Restrictions::STMT_EXPR, |this| {
-                    let expr = this.parse_dot_or_call_expr_with(expr, lo, attrs.into())?;
-                    this.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(expr))
-                })?;
-
-                return Ok(Some(Stmt {
-                    id: ast::DUMMY_NODE_ID,
-                    node: StmtKind::Expr(expr),
-                    span: lo.to(self.prev_span),
-                }));
-            }
-
-            let (delim, tts) = self.expect_delimited_token_tree()?;
-            let hi = self.prev_span;
-
-            let style = if delim == MacDelimiter::Brace {
-                MacStmtStyle::Braces
-            } else {
-                MacStmtStyle::NoBraces
-            };
-
-            let mac = respan(lo.to(hi), Mac_ {
-                path,
-                tts,
-                delim,
-                prior_type_ascription: self.last_type_ascription,
-            });
-            let node = if delim == MacDelimiter::Brace ||
-                          self.token == token::Semi || self.token == token::Eof {
-                StmtKind::Mac(P((mac, style, attrs.into())))
-            }
-            // We used to incorrectly stop parsing macro-expanded statements here.
-            // If the next token will be an error anyway but could have parsed with the
-            // earlier behavior, stop parsing here and emit a warning to avoid breakage.
-            else if macro_legacy_warnings &&
-                    self.token.can_begin_expr() &&
-                    match self.token.kind {
-                // These can continue an expression, so we can't stop parsing and warn.
-                token::OpenDelim(token::Paren) | token::OpenDelim(token::Bracket) |
-                token::BinOp(token::Minus) | token::BinOp(token::Star) |
-                token::BinOp(token::And) | token::BinOp(token::Or) |
-                token::AndAnd | token::OrOr |
-                token::DotDot | token::DotDotDot | token::DotDotEq => false,
-                _ => true,
-            } {
-                self.warn_missing_semicolon();
-                StmtKind::Mac(P((mac, style, attrs.into())))
-            } else {
-                let e = self.mk_expr(mac.span, ExprKind::Mac(mac), ThinVec::new());
-                let e = self.maybe_recover_from_bad_qpath(e, true)?;
-                let e = self.parse_dot_or_call_expr_with(e, lo, attrs.into())?;
-                let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?;
-                StmtKind::Expr(e)
-            };
-            Stmt {
-                id: ast::DUMMY_NODE_ID,
-                span: lo.to(hi),
-                node,
-            }
-        } else {
-            // FIXME: Bad copy of attrs
-            let old_directory_ownership =
-                mem::replace(&mut self.directory.ownership, DirectoryOwnership::UnownedViaBlock);
-            let item = self.parse_item_(attrs.clone(), false, true)?;
-            self.directory.ownership = old_directory_ownership;
-
-            match item {
-                Some(i) => Stmt {
-                    id: ast::DUMMY_NODE_ID,
-                    span: lo.to(i.span),
-                    node: StmtKind::Item(i),
-                },
-                None => {
-                    let unused_attrs = |attrs: &[Attribute], s: &mut Self| {
-                        if !attrs.is_empty() {
-                            if s.prev_token_kind == PrevTokenKind::DocComment {
-                                s.span_fatal_err(s.prev_span, Error::UselessDocComment).emit();
-                            } else if attrs.iter().any(|a| a.style == AttrStyle::Outer) {
-                                s.span_err(
-                                    s.token.span, "expected statement after outer attribute"
-                                );
-                            }
-                        }
-                    };
-
-                    // Do not attempt to parse an expression if we're done here.
-                    if self.token == token::Semi {
-                        unused_attrs(&attrs, self);
-                        self.bump();
-                        return Ok(None);
-                    }
-
-                    if self.token == token::CloseDelim(token::Brace) {
-                        unused_attrs(&attrs, self);
-                        return Ok(None);
-                    }
-
-                    // Remainder are line-expr stmts.
-                    let e = self.parse_expr_res(
-                        Restrictions::STMT_EXPR, Some(attrs.into()))?;
-                    Stmt {
-                        id: ast::DUMMY_NODE_ID,
-                        span: lo.to(e.span),
-                        node: StmtKind::Expr(e),
-                    }
-                }
-            }
-        }))
-    }
-
-    /// Parses a block. No inner attributes are allowed.
-    pub fn parse_block(&mut self) -> PResult<'a, P<Block>> {
-        maybe_whole!(self, NtBlock, |x| x);
-
-        let lo = self.token.span;
-
-        if !self.eat(&token::OpenDelim(token::Brace)) {
-            let sp = self.token.span;
-            let tok = self.this_token_descr();
-            let mut e = self.span_fatal(sp, &format!("expected `{{`, found {}", tok));
-            let do_not_suggest_help =
-                self.token.is_keyword(kw::In) || self.token == token::Colon;
-
-            if self.token.is_ident_named(sym::and) {
-                e.span_suggestion_short(
-                    self.token.span,
-                    "use `&&` instead of `and` for the boolean operator",
-                    "&&".to_string(),
-                    Applicability::MaybeIncorrect,
-                );
-            }
-            if self.token.is_ident_named(sym::or) {
-                e.span_suggestion_short(
-                    self.token.span,
-                    "use `||` instead of `or` for the boolean operator",
-                    "||".to_string(),
-                    Applicability::MaybeIncorrect,
-                );
-            }
-
-            // Check to see if the user has written something like
-            //
-            //    if (cond)
-            //      bar;
-            //
-            // Which is valid in other languages, but not Rust.
-            match self.parse_stmt_without_recovery(false) {
-                Ok(Some(stmt)) => {
-                    if self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace))
-                        || do_not_suggest_help {
-                        // if the next token is an open brace (e.g., `if a b {`), the place-
-                        // inside-a-block suggestion would be more likely wrong than right
-                        e.span_label(sp, "expected `{`");
-                        return Err(e);
-                    }
-                    let mut stmt_span = stmt.span;
-                    // expand the span to include the semicolon, if it exists
-                    if self.eat(&token::Semi) {
-                        stmt_span = stmt_span.with_hi(self.prev_span.hi());
-                    }
-                    if let Ok(snippet) = self.span_to_snippet(stmt_span) {
-                        e.span_suggestion(
-                            stmt_span,
-                            "try placing this code inside a block",
-                            format!("{{ {} }}", snippet),
-                            // speculative, has been misleading in the past (#46836)
-                            Applicability::MaybeIncorrect,
-                        );
-                    }
-                }
-                Err(mut e) => {
-                    self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore);
-                    self.cancel(&mut e);
-                }
-                _ => ()
-            }
-            e.span_label(sp, "expected `{`");
-            return Err(e);
-        }
-
-        self.parse_block_tail(lo, BlockCheckMode::Default)
-    }
-
-    /// Parses a block. Inner attributes are allowed.
-    crate fn parse_inner_attrs_and_block(&mut self) -> PResult<'a, (Vec<Attribute>, P<Block>)> {
-        maybe_whole!(self, NtBlock, |x| (Vec::new(), x));
-
-        let lo = self.token.span;
-        self.expect(&token::OpenDelim(token::Brace))?;
-        Ok((self.parse_inner_attributes()?,
-            self.parse_block_tail(lo, BlockCheckMode::Default)?))
-    }
-
-    /// Parses the rest of a block expression or function body.
-    /// Precondition: already parsed the '{'.
-    fn parse_block_tail(&mut self, lo: Span, s: BlockCheckMode) -> PResult<'a, P<Block>> {
-        let mut stmts = vec![];
-        while !self.eat(&token::CloseDelim(token::Brace)) {
-            if self.token == token::Eof {
-                break;
-            }
-            let stmt = match self.parse_full_stmt(false) {
-                Err(mut err) => {
-                    err.emit();
-                    self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore);
-                    Some(Stmt {
-                        id: ast::DUMMY_NODE_ID,
-                        node: StmtKind::Expr(DummyResult::raw_expr(self.token.span, true)),
-                        span: self.token.span,
-                    })
-                }
-                Ok(stmt) => stmt,
-            };
-            if let Some(stmt) = stmt {
-                stmts.push(stmt);
-            } else {
-                // Found only `;` or `}`.
-                continue;
-            };
-        }
-        Ok(P(ast::Block {
-            stmts,
-            id: ast::DUMMY_NODE_ID,
-            rules: s,
-            span: lo.to(self.prev_span),
-        }))
-    }
-
-    /// Parses a statement, including the trailing semicolon.
-    crate fn parse_full_stmt(&mut self, macro_legacy_warnings: bool) -> PResult<'a, Option<Stmt>> {
-        // skip looking for a trailing semicolon when we have an interpolated statement
-        maybe_whole!(self, NtStmt, |x| Some(x));
-
-        let mut stmt = match self.parse_stmt_without_recovery(macro_legacy_warnings)? {
-            Some(stmt) => stmt,
-            None => return Ok(None),
-        };
-
-        match stmt.node {
-            StmtKind::Expr(ref expr) if self.token != token::Eof => {
-                // expression without semicolon
-                if classify::expr_requires_semi_to_be_stmt(expr) {
-                    // Just check for errors and recover; do not eat semicolon yet.
-                    if let Err(mut e) =
-                        self.expect_one_of(&[], &[token::Semi, token::CloseDelim(token::Brace)])
-                    {
-                        e.emit();
-                        self.recover_stmt();
-                        // Don't complain about type errors in body tail after parse error (#57383).
-                        let sp = expr.span.to(self.prev_span);
-                        stmt.node = StmtKind::Expr(DummyResult::raw_expr(sp, true));
-                    }
-                }
-            }
-            StmtKind::Local(..) => {
-                // We used to incorrectly allow a macro-expanded let statement to lack a semicolon.
-                if macro_legacy_warnings && self.token != token::Semi {
-                    self.warn_missing_semicolon();
-                } else {
-                    self.expect_one_of(&[], &[token::Semi])?;
-                }
-            }
-            _ => {}
-        }
-
-        if self.eat(&token::Semi) {
-            stmt = stmt.add_trailing_semicolon();
-        }
-        stmt.span = stmt.span.to(self.prev_span);
-        Ok(Some(stmt))
-    }
-
-    fn warn_missing_semicolon(&self) {
-        self.diagnostic().struct_span_warn(self.token.span, {
-            &format!("expected `;`, found {}", self.this_token_descr())
-        }).note({
-            "This was erroneously allowed and will become a hard error in a future release"
-        }).emit();
-    }
-
     /// Parses bounds of a lifetime parameter `BOUND + BOUND + BOUND`, possibly with trailing `+`.
     ///
     /// ```
@@ -2149,6 +1696,10 @@ fn choose_generics_over_qpath(&self) -> bool {
             self.is_keyword_ahead(1, &[kw::Const]))
     }
 
+    fn is_crate_vis(&self) -> bool {
+        self.token.is_keyword(kw::Crate) && self.look_ahead(1, |t| t != &token::ModSep)
+    }
+
     /// Parses `pub`, `pub(crate)` and `pub(in path)` plus shortcuts `crate` for `pub(crate)`,
     /// `pub(self)` for `pub(in self)` and `pub(super)` for `pub(in super)`.
     /// If the following element can't be a tuple (i.e., it's a function definition), then
index 19d5bcd3ee9e27784f4ff9db4e9e1a04ca592cf2..4432c1329cbfeb9aa4bdc8d1309a2b53f22bed54 100644 (file)
@@ -1,11 +1,10 @@
 use super::{Parser, PResult, Restrictions, PrevTokenKind, TokenType, PathStyle};
-use super::{BlockCheckMode, BlockMode, SemiColonMode};
+use super::{BlockMode, SemiColonMode};
 use super::{SeqSep, TokenExpectType};
 
 use crate::maybe_recover_from_interpolated_ty_qpath;
 use crate::ptr::P;
-use crate::ast::{self, Attribute, AttrStyle};
-use crate::ast::{Ident, CaptureBy};
+use crate::ast::{self, Attribute, AttrStyle, Ident, CaptureBy, BlockCheckMode};
 use crate::ast::{Expr, ExprKind, RangeLimits, Label, Movability, IsAsync, Arm};
 use crate::ast::{Ty, TyKind, FunctionRetTy, Arg, FnDecl};
 use crate::ast::{BinOpKind, BinOp, UnOp};
index c5608a160cf60b46c2414a75f6e144f737590e77..e85ef9cc974194f0a1576f4f01f3038442a9dd24 100644 (file)
@@ -569,6 +569,11 @@ fn expected_item_err(&mut self, attrs: &[Attribute]) -> PResult<'a,  ()> {
         Err(err)
     }
 
+    pub(super) fn is_async_fn(&self) -> bool {
+        self.token.is_keyword(kw::Async) &&
+            self.is_keyword_ahead(1, &[kw::Fn])
+    }
+
     /// Parses a macro invocation inside a `trait`, `impl` or `extern` block.
     fn parse_assoc_macro_invoc(&mut self, item_kind: &str, vis: Option<&Visibility>,
                                at_end: &mut bool) -> PResult<'a, Option<Mac>>
index 2760cd38cabc6b53c44614092b4c9ae6ced6a73d..3eb4d45045a9e00b32338d7f8050410d909b1236 100644 (file)
@@ -1,11 +1,11 @@
-use super::{Parser, PResult, TokenType, BlockCheckMode};
+use super::{Parser, PResult, TokenType};
 
 use crate::{maybe_whole, ThinVec};
 use crate::ast::{self, QSelf, Path, PathSegment, Ident, ParenthesizedArgs, AngleBracketedArgs};
-use crate::ast::{AnonConst, GenericArg, AssocTyConstraint, AssocTyConstraintKind};
+use crate::ast::{AnonConst, GenericArg, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode};
 use crate::parse::token::{self, Token};
 use crate::source_map::{Span, BytePos};
-use crate::symbol::{kw};
+use crate::symbol::kw;
 
 use std::mem;
 use log::debug;
diff --git a/src/libsyntax/parse/parser/stmt.rs b/src/libsyntax/parse/parser/stmt.rs
new file mode 100644 (file)
index 0000000..f182edc
--- /dev/null
@@ -0,0 +1,458 @@
+use super::{Parser, PResult, Restrictions, PrevTokenKind, SemiColonMode, BlockMode};
+use super::expr::LhsExpr;
+use super::path::PathStyle;
+
+use crate::ptr::P;
+use crate::{maybe_whole, ThinVec};
+use crate::ast::{self, Stmt, StmtKind, Local, Block, BlockCheckMode, Expr, ExprKind};
+use crate::ast::{Attribute, AttrStyle, VisibilityKind, MacStmtStyle, Mac_, MacDelimiter};
+use crate::ext::base::DummyResult;
+use crate::parse::{classify, DirectoryOwnership};
+use crate::parse::diagnostics::Error;
+use crate::parse::token::{self};
+use crate::source_map::{respan, Span};
+use crate::symbol::{kw, sym};
+
+use std::mem;
+use errors::Applicability;
+
+impl<'a> Parser<'a> {
+    /// Parse a statement. This stops just before trailing semicolons on everything but items.
+    /// e.g., a `StmtKind::Semi` parses to a `StmtKind::Expr`, leaving the trailing `;` unconsumed.
+    pub fn parse_stmt(&mut self) -> PResult<'a, Option<Stmt>> {
+        Ok(self.parse_stmt_(true))
+    }
+
+    fn parse_stmt_(&mut self, macro_legacy_warnings: bool) -> Option<Stmt> {
+        self.parse_stmt_without_recovery(macro_legacy_warnings).unwrap_or_else(|mut e| {
+            e.emit();
+            self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore);
+            None
+        })
+    }
+
+    fn parse_stmt_without_recovery(
+        &mut self,
+        macro_legacy_warnings: bool,
+    ) -> PResult<'a, Option<Stmt>> {
+        maybe_whole!(self, NtStmt, |x| Some(x));
+
+        let attrs = self.parse_outer_attributes()?;
+        let lo = self.token.span;
+
+        Ok(Some(if self.eat_keyword(kw::Let) {
+            Stmt {
+                id: ast::DUMMY_NODE_ID,
+                node: StmtKind::Local(self.parse_local(attrs.into())?),
+                span: lo.to(self.prev_span),
+            }
+        } else if let Some(macro_def) = self.eat_macro_def(
+            &attrs,
+            &respan(lo, VisibilityKind::Inherited),
+            lo,
+        )? {
+            Stmt {
+                id: ast::DUMMY_NODE_ID,
+                node: StmtKind::Item(macro_def),
+                span: lo.to(self.prev_span),
+            }
+        // Starts like a simple path, being careful to avoid contextual keywords
+        // such as a union items, item with `crate` visibility or auto trait items.
+        // Our goal here is to parse an arbitrary path `a::b::c` but not something that starts
+        // like a path (1 token), but it fact not a path.
+        // `union::b::c` - path, `union U { ... }` - not a path.
+        // `crate::b::c` - path, `crate struct S;` - not a path.
+        } else if self.token.is_path_start() &&
+                  !self.token.is_qpath_start() &&
+                  !self.is_union_item() &&
+                  !self.is_crate_vis() &&
+                  !self.is_auto_trait_item() &&
+                  !self.is_async_fn() {
+            let path = self.parse_path(PathStyle::Expr)?;
+
+            if !self.eat(&token::Not) {
+                let expr = if self.check(&token::OpenDelim(token::Brace)) {
+                    self.parse_struct_expr(lo, path, ThinVec::new())?
+                } else {
+                    let hi = self.prev_span;
+                    self.mk_expr(lo.to(hi), ExprKind::Path(None, path), ThinVec::new())
+                };
+
+                let expr = self.with_res(Restrictions::STMT_EXPR, |this| {
+                    let expr = this.parse_dot_or_call_expr_with(expr, lo, attrs.into())?;
+                    this.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(expr))
+                })?;
+
+                return Ok(Some(Stmt {
+                    id: ast::DUMMY_NODE_ID,
+                    node: StmtKind::Expr(expr),
+                    span: lo.to(self.prev_span),
+                }));
+            }
+
+            let (delim, tts) = self.expect_delimited_token_tree()?;
+            let hi = self.prev_span;
+
+            let style = if delim == MacDelimiter::Brace {
+                MacStmtStyle::Braces
+            } else {
+                MacStmtStyle::NoBraces
+            };
+
+            let mac = respan(lo.to(hi), Mac_ {
+                path,
+                tts,
+                delim,
+                prior_type_ascription: self.last_type_ascription,
+            });
+            let node = if delim == MacDelimiter::Brace ||
+                          self.token == token::Semi || self.token == token::Eof {
+                StmtKind::Mac(P((mac, style, attrs.into())))
+            }
+            // We used to incorrectly stop parsing macro-expanded statements here.
+            // If the next token will be an error anyway but could have parsed with the
+            // earlier behavior, stop parsing here and emit a warning to avoid breakage.
+            else if macro_legacy_warnings &&
+                    self.token.can_begin_expr() &&
+                    match self.token.kind {
+                // These can continue an expression, so we can't stop parsing and warn.
+                token::OpenDelim(token::Paren) | token::OpenDelim(token::Bracket) |
+                token::BinOp(token::Minus) | token::BinOp(token::Star) |
+                token::BinOp(token::And) | token::BinOp(token::Or) |
+                token::AndAnd | token::OrOr |
+                token::DotDot | token::DotDotDot | token::DotDotEq => false,
+                _ => true,
+            } {
+                self.warn_missing_semicolon();
+                StmtKind::Mac(P((mac, style, attrs.into())))
+            } else {
+                let e = self.mk_expr(mac.span, ExprKind::Mac(mac), ThinVec::new());
+                let e = self.maybe_recover_from_bad_qpath(e, true)?;
+                let e = self.parse_dot_or_call_expr_with(e, lo, attrs.into())?;
+                let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?;
+                StmtKind::Expr(e)
+            };
+            Stmt {
+                id: ast::DUMMY_NODE_ID,
+                span: lo.to(hi),
+                node,
+            }
+        } else {
+            // FIXME: Bad copy of attrs
+            let old_directory_ownership =
+                mem::replace(&mut self.directory.ownership, DirectoryOwnership::UnownedViaBlock);
+            let item = self.parse_item_(attrs.clone(), false, true)?;
+            self.directory.ownership = old_directory_ownership;
+
+            match item {
+                Some(i) => Stmt {
+                    id: ast::DUMMY_NODE_ID,
+                    span: lo.to(i.span),
+                    node: StmtKind::Item(i),
+                },
+                None => {
+                    let unused_attrs = |attrs: &[Attribute], s: &mut Self| {
+                        if !attrs.is_empty() {
+                            if s.prev_token_kind == PrevTokenKind::DocComment {
+                                s.span_fatal_err(s.prev_span, Error::UselessDocComment).emit();
+                            } else if attrs.iter().any(|a| a.style == AttrStyle::Outer) {
+                                s.span_err(
+                                    s.token.span, "expected statement after outer attribute"
+                                );
+                            }
+                        }
+                    };
+
+                    // Do not attempt to parse an expression if we're done here.
+                    if self.token == token::Semi {
+                        unused_attrs(&attrs, self);
+                        self.bump();
+                        return Ok(None);
+                    }
+
+                    if self.token == token::CloseDelim(token::Brace) {
+                        unused_attrs(&attrs, self);
+                        return Ok(None);
+                    }
+
+                    // Remainder are line-expr stmts.
+                    let e = self.parse_expr_res(
+                        Restrictions::STMT_EXPR, Some(attrs.into()))?;
+                    Stmt {
+                        id: ast::DUMMY_NODE_ID,
+                        span: lo.to(e.span),
+                        node: StmtKind::Expr(e),
+                    }
+                }
+            }
+        }))
+    }
+
+    /// Parses a local variable declaration.
+    fn parse_local(&mut self, attrs: ThinVec<Attribute>) -> PResult<'a, P<Local>> {
+        let lo = self.prev_span;
+        let pat = self.parse_top_level_pat()?;
+
+        let (err, ty) = if self.eat(&token::Colon) {
+            // Save the state of the parser before parsing type normally, in case there is a `:`
+            // instead of an `=` typo.
+            let parser_snapshot_before_type = self.clone();
+            let colon_sp = self.prev_span;
+            match self.parse_ty() {
+                Ok(ty) => (None, Some(ty)),
+                Err(mut err) => {
+                    // Rewind to before attempting to parse the type and continue parsing
+                    let parser_snapshot_after_type = self.clone();
+                    mem::replace(self, parser_snapshot_before_type);
+
+                    let snippet = self.span_to_snippet(pat.span).unwrap();
+                    err.span_label(pat.span, format!("while parsing the type for `{}`", snippet));
+                    (Some((parser_snapshot_after_type, colon_sp, err)), None)
+                }
+            }
+        } else {
+            (None, None)
+        };
+        let init = match (self.parse_initializer(err.is_some()), err) {
+            (Ok(init), None) => {  // init parsed, ty parsed
+                init
+            }
+            (Ok(init), Some((_, colon_sp, mut err))) => {  // init parsed, ty error
+                // Could parse the type as if it were the initializer, it is likely there was a
+                // typo in the code: `:` instead of `=`. Add suggestion and emit the error.
+                err.span_suggestion_short(
+                    colon_sp,
+                    "use `=` if you meant to assign",
+                    "=".to_string(),
+                    Applicability::MachineApplicable
+                );
+                err.emit();
+                // As this was parsed successfully, continue as if the code has been fixed for the
+                // rest of the file. It will still fail due to the emitted error, but we avoid
+                // extra noise.
+                init
+            }
+            (Err(mut init_err), Some((snapshot, _, ty_err))) => {  // init error, ty error
+                init_err.cancel();
+                // Couldn't parse the type nor the initializer, only raise the type error and
+                // return to the parser state before parsing the type as the initializer.
+                // let x: <parse_error>;
+                mem::replace(self, snapshot);
+                return Err(ty_err);
+            }
+            (Err(err), None) => {  // init error, ty parsed
+                // Couldn't parse the initializer and we're not attempting to recover a failed
+                // parse of the type, return the error.
+                return Err(err);
+            }
+        };
+        let hi = if self.token == token::Semi {
+            self.token.span
+        } else {
+            self.prev_span
+        };
+        Ok(P(ast::Local {
+            ty,
+            pat,
+            init,
+            id: ast::DUMMY_NODE_ID,
+            span: lo.to(hi),
+            attrs,
+        }))
+    }
+
+    /// Parses the RHS of a local variable declaration (e.g., '= 14;').
+    fn parse_initializer(&mut self, skip_eq: bool) -> PResult<'a, Option<P<Expr>>> {
+        if self.eat(&token::Eq) {
+            Ok(Some(self.parse_expr()?))
+        } else if skip_eq {
+            Ok(Some(self.parse_expr()?))
+        } else {
+            Ok(None)
+        }
+    }
+
+    fn is_auto_trait_item(&self) -> bool {
+        // auto trait
+        (self.token.is_keyword(kw::Auto) &&
+            self.is_keyword_ahead(1, &[kw::Trait]))
+        || // unsafe auto trait
+        (self.token.is_keyword(kw::Unsafe) &&
+         self.is_keyword_ahead(1, &[kw::Auto]) &&
+         self.is_keyword_ahead(2, &[kw::Trait]))
+    }
+
+    /// Parses a block. No inner attributes are allowed.
+    pub fn parse_block(&mut self) -> PResult<'a, P<Block>> {
+        maybe_whole!(self, NtBlock, |x| x);
+
+        let lo = self.token.span;
+
+        if !self.eat(&token::OpenDelim(token::Brace)) {
+            let sp = self.token.span;
+            let tok = self.this_token_descr();
+            let mut e = self.span_fatal(sp, &format!("expected `{{`, found {}", tok));
+            let do_not_suggest_help =
+                self.token.is_keyword(kw::In) || self.token == token::Colon;
+
+            if self.token.is_ident_named(sym::and) {
+                e.span_suggestion_short(
+                    self.token.span,
+                    "use `&&` instead of `and` for the boolean operator",
+                    "&&".to_string(),
+                    Applicability::MaybeIncorrect,
+                );
+            }
+            if self.token.is_ident_named(sym::or) {
+                e.span_suggestion_short(
+                    self.token.span,
+                    "use `||` instead of `or` for the boolean operator",
+                    "||".to_string(),
+                    Applicability::MaybeIncorrect,
+                );
+            }
+
+            // Check to see if the user has written something like
+            //
+            //    if (cond)
+            //      bar;
+            //
+            // Which is valid in other languages, but not Rust.
+            match self.parse_stmt_without_recovery(false) {
+                Ok(Some(stmt)) => {
+                    if self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace))
+                        || do_not_suggest_help {
+                        // if the next token is an open brace (e.g., `if a b {`), the place-
+                        // inside-a-block suggestion would be more likely wrong than right
+                        e.span_label(sp, "expected `{`");
+                        return Err(e);
+                    }
+                    let mut stmt_span = stmt.span;
+                    // expand the span to include the semicolon, if it exists
+                    if self.eat(&token::Semi) {
+                        stmt_span = stmt_span.with_hi(self.prev_span.hi());
+                    }
+                    if let Ok(snippet) = self.span_to_snippet(stmt_span) {
+                        e.span_suggestion(
+                            stmt_span,
+                            "try placing this code inside a block",
+                            format!("{{ {} }}", snippet),
+                            // speculative, has been misleading in the past (#46836)
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                }
+                Err(mut e) => {
+                    self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore);
+                    self.cancel(&mut e);
+                }
+                _ => ()
+            }
+            e.span_label(sp, "expected `{`");
+            return Err(e);
+        }
+
+        self.parse_block_tail(lo, BlockCheckMode::Default)
+    }
+
+    /// Parses a block. Inner attributes are allowed.
+    crate fn parse_inner_attrs_and_block(&mut self) -> PResult<'a, (Vec<Attribute>, P<Block>)> {
+        maybe_whole!(self, NtBlock, |x| (Vec::new(), x));
+
+        let lo = self.token.span;
+        self.expect(&token::OpenDelim(token::Brace))?;
+        Ok((self.parse_inner_attributes()?,
+            self.parse_block_tail(lo, BlockCheckMode::Default)?))
+    }
+
+    /// Parses the rest of a block expression or function body.
+    /// Precondition: already parsed the '{'.
+    pub(super) fn parse_block_tail(
+        &mut self,
+        lo: Span,
+        s: BlockCheckMode
+    ) -> PResult<'a, P<Block>> {
+        let mut stmts = vec![];
+        while !self.eat(&token::CloseDelim(token::Brace)) {
+            if self.token == token::Eof {
+                break;
+            }
+            let stmt = match self.parse_full_stmt(false) {
+                Err(mut err) => {
+                    err.emit();
+                    self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore);
+                    Some(Stmt {
+                        id: ast::DUMMY_NODE_ID,
+                        node: StmtKind::Expr(DummyResult::raw_expr(self.token.span, true)),
+                        span: self.token.span,
+                    })
+                }
+                Ok(stmt) => stmt,
+            };
+            if let Some(stmt) = stmt {
+                stmts.push(stmt);
+            } else {
+                // Found only `;` or `}`.
+                continue;
+            };
+        }
+        Ok(P(ast::Block {
+            stmts,
+            id: ast::DUMMY_NODE_ID,
+            rules: s,
+            span: lo.to(self.prev_span),
+        }))
+    }
+
+    /// Parses a statement, including the trailing semicolon.
+    crate fn parse_full_stmt(&mut self, macro_legacy_warnings: bool) -> PResult<'a, Option<Stmt>> {
+        // skip looking for a trailing semicolon when we have an interpolated statement
+        maybe_whole!(self, NtStmt, |x| Some(x));
+
+        let mut stmt = match self.parse_stmt_without_recovery(macro_legacy_warnings)? {
+            Some(stmt) => stmt,
+            None => return Ok(None),
+        };
+
+        match stmt.node {
+            StmtKind::Expr(ref expr) if self.token != token::Eof => {
+                // expression without semicolon
+                if classify::expr_requires_semi_to_be_stmt(expr) {
+                    // Just check for errors and recover; do not eat semicolon yet.
+                    if let Err(mut e) =
+                        self.expect_one_of(&[], &[token::Semi, token::CloseDelim(token::Brace)])
+                    {
+                        e.emit();
+                        self.recover_stmt();
+                        // Don't complain about type errors in body tail after parse error (#57383).
+                        let sp = expr.span.to(self.prev_span);
+                        stmt.node = StmtKind::Expr(DummyResult::raw_expr(sp, true));
+                    }
+                }
+            }
+            StmtKind::Local(..) => {
+                // We used to incorrectly allow a macro-expanded let statement to lack a semicolon.
+                if macro_legacy_warnings && self.token != token::Semi {
+                    self.warn_missing_semicolon();
+                } else {
+                    self.expect_one_of(&[], &[token::Semi])?;
+                }
+            }
+            _ => {}
+        }
+
+        if self.eat(&token::Semi) {
+            stmt = stmt.add_trailing_semicolon();
+        }
+        stmt.span = stmt.span.to(self.prev_span);
+        Ok(Some(stmt))
+    }
+
+    fn warn_missing_semicolon(&self) {
+        self.diagnostic().struct_span_warn(self.token.span, {
+            &format!("expected `;`, found {}", self.this_token_descr())
+        }).note({
+            "This was erroneously allowed and will become a hard error in a future release"
+        }).emit();
+    }
+}