diff options
| author | bors <bors@rust-lang.org> | 2019-12-21 11:05:03 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2019-12-21 11:05:03 +0000 |
| commit | c64eecf4d0907095928fb36fd3a1dd5fb2d9ff06 (patch) | |
| tree | 073a8038727414aed13f54622fce43ca63990ca6 | |
| parent | 9ff30a7810c586819a78188c173a7b74adbb9730 (diff) | |
| parent | 621661f8a63f2118f3add5c3d686d9a2b6f62e5e (diff) | |
| download | rust-c64eecf4d0907095928fb36fd3a1dd5fb2d9ff06.tar.gz rust-c64eecf4d0907095928fb36fd3a1dd5fb2d9ff06.zip | |
Auto merge of #66994 - Centril:stmt-polish, r=estebank
refactor expr & stmt parsing + improve recovery
Summary of important changes (best read commit-by-commit, ignoring whitespace changes):
- `AttrVec` is introduces as an alias for `ThinVec<Attribute>`
- `parse_expr_bottom` and `parse_stmt` are thoroughly refactored.
- Extract diagnostics logic for `vec![...]` in a pattern context.
- Recovery is added for `do catch { ... }`
- Recovery is added for `'label: non_block_expr`
- Recovery is added for `var $local`, `auto $local`, and `mut $local`. Fixes #65257.
- Recovery is added for `e1 and e2` and `e1 or e2`.
- ~~`macro_legacy_warnings` is turned into an error (has been a warning for 3 years!)~~
- Fixes #63396 by forward-porting #64105 which now works thanks to added recovery.
- `ui-fulldeps/ast_stmt_expr_attr.rs` is turned into UI and pretty tests.
- Recovery is fixed for `#[attr] if expr {}`
r? @estebank
47 files changed, 1764 insertions, 1187 deletions
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 3f8085f2344..6b83788298e 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -53,7 +53,6 @@ use crate::util::nodemap::{DefIdMap, NodeMap}; use errors::Applicability; use rustc_data_structures::fx::FxHashSet; use rustc_index::vec::IndexVec; -use rustc_data_structures::thin_vec::ThinVec; use rustc_data_structures::sync::Lrc; use std::collections::BTreeMap; @@ -1205,7 +1204,7 @@ impl<'a> LoweringContext<'a> { id: ty.id, kind: ExprKind::Path(qself.clone(), path.clone()), span: ty.span, - attrs: ThinVec::new(), + attrs: AttrVec::new(), }; let ct = self.with_new_scopes(|this| { @@ -2751,7 +2750,7 @@ impl<'a> LoweringContext<'a> { /// has no attributes and is not targeted by a `break`. fn lower_block_expr(&mut self, b: &Block) -> hir::Expr { let block = self.lower_block(b, false); - self.expr_block(block, ThinVec::new()) + self.expr_block(block, AttrVec::new()) } fn lower_pat(&mut self, p: &Pat) -> P<hir::Pat> { @@ -3102,7 +3101,7 @@ impl<'a> LoweringContext<'a> { fn stmt_let_pat( &mut self, - attrs: ThinVec<Attribute>, + attrs: AttrVec, span: Span, init: Option<P<hir::Expr>>, pat: P<hir::Pat>, diff --git a/src/librustc/hir/lowering/expr.rs b/src/librustc/hir/lowering/expr.rs index f8465baeb13..04031710dc5 100644 --- a/src/librustc/hir/lowering/expr.rs +++ b/src/librustc/hir/lowering/expr.rs @@ -1318,8 +1318,7 @@ impl LoweringContext<'_> { &mut self, span: Span, expr: P<hir::Expr>, - attrs: ThinVec<Attribute> - ) -> hir::Expr { + attrs: AttrVec) -> hir::Expr { self.expr(span, hir::ExprKind::DropTemps(expr), attrs) } @@ -1333,7 +1332,7 @@ impl LoweringContext<'_> { self.expr(span, hir::ExprKind::Match(arg, arms, source), ThinVec::new()) } - fn expr_break(&mut self, span: Span, attrs: ThinVec<Attribute>) -> P<hir::Expr> { + fn expr_break(&mut self, span: Span, attrs: AttrVec) -> P<hir::Expr> { let expr_break = hir::ExprKind::Break(self.lower_loop_destination(None), None); P(self.expr(span, expr_break, attrs)) } @@ -1404,7 +1403,7 @@ impl LoweringContext<'_> { span: Span, components: &[Symbol], params: Option<P<hir::GenericArgs>>, - attrs: ThinVec<Attribute>, + attrs: AttrVec, ) -> hir::Expr { let path = self.std_path(span, components, params, true); self.expr( @@ -1423,7 +1422,7 @@ impl LoweringContext<'_> { span: Span, ident: Ident, binding: hir::HirId, - attrs: ThinVec<Attribute>, + attrs: AttrVec, ) -> hir::Expr { let expr_path = hir::ExprKind::Path(hir::QPath::Resolved( None, @@ -1459,16 +1458,11 @@ impl LoweringContext<'_> { self.expr_block(P(blk), ThinVec::new()) } - pub(super) fn expr_block(&mut self, b: P<hir::Block>, attrs: ThinVec<Attribute>) -> hir::Expr { + pub(super) fn expr_block(&mut self, b: P<hir::Block>, attrs: AttrVec) -> hir::Expr { self.expr(b.span, hir::ExprKind::Block(b, None), attrs) } - pub(super) fn expr( - &mut self, - span: Span, - kind: hir::ExprKind, - attrs: ThinVec<Attribute> - ) -> hir::Expr { + pub(super) fn expr(&mut self, span: Span, kind: hir::ExprKind, attrs: AttrVec) -> hir::Expr { hir::Expr { hir_id: self.next_id(), kind, span, attrs } } diff --git a/src/librustc/hir/lowering/item.rs b/src/librustc/hir/lowering/item.rs index 46c944fa678..6cae8e2cc04 100644 --- a/src/librustc/hir/lowering/item.rs +++ b/src/librustc/hir/lowering/item.rs @@ -11,7 +11,6 @@ use crate::hir::def_id::DefId; use crate::hir::def::{Res, DefKind}; use crate::util::nodemap::NodeMap; -use rustc_data_structures::thin_vec::ThinVec; use rustc_target::spec::abi; use std::collections::BTreeSet; @@ -899,7 +898,7 @@ impl LoweringContext<'_> { /// Construct `ExprKind::Err` for the given `span`. fn expr_err(&mut self, span: Span) -> hir::Expr { - self.expr(span, hir::ExprKind::Err, ThinVec::new()) + self.expr(span, hir::ExprKind::Err, AttrVec::new()) } fn lower_impl_item(&mut self, i: &AssocItem) -> hir::ImplItem { @@ -1182,7 +1181,7 @@ impl LoweringContext<'_> { // // If this is the simple case, this parameter will end up being the same as the // original parameter, but with a different pattern id. - let mut stmt_attrs = ThinVec::new(); + let mut stmt_attrs = AttrVec::new(); stmt_attrs.extend(parameter.attrs.iter().cloned()); let (new_parameter_pat, new_parameter_id) = this.pat_ident(desugared_span, ident); let new_parameter = hir::Param { @@ -1226,7 +1225,7 @@ impl LoweringContext<'_> { desugared_span, ident, hir::BindingAnnotation::Mutable); let move_expr = this.expr_ident(desugared_span, ident, new_parameter_id); let move_stmt = this.stmt_let_pat( - ThinVec::new(), + AttrVec::new(), desugared_span, Some(P(move_expr)), move_pat, @@ -1271,7 +1270,7 @@ impl LoweringContext<'_> { let user_body = this.expr_drop_temps( desugared_span, P(user_body), - ThinVec::new(), + AttrVec::new(), ); // As noted above, create the final block like @@ -1288,9 +1287,9 @@ impl LoweringContext<'_> { statements.into(), Some(P(user_body)), ); - this.expr_block(P(body), ThinVec::new()) + this.expr_block(P(body), AttrVec::new()) }); - (HirVec::from(parameters), this.expr(body_span, async_expr, ThinVec::new())) + (HirVec::from(parameters), this.expr(body_span, async_expr, AttrVec::new())) }) } diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 2cffcc5bfad..368269ff200 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -20,7 +20,7 @@ use errors::FatalError; use syntax_pos::{Span, DUMMY_SP, MultiSpan}; use syntax::source_map::Spanned; use syntax::ast::{self, CrateSugar, Ident, Name, NodeId, AsmDialect}; -use syntax::ast::{Attribute, Label, LitKind, StrStyle, FloatTy, IntTy, UintTy}; +use syntax::ast::{AttrVec, Attribute, Label, LitKind, StrStyle, FloatTy, IntTy, UintTy}; pub use syntax::ast::{Mutability, Constness, Unsafety, Movability, CaptureBy}; pub use syntax::ast::{IsAuto, ImplPolarity, BorrowKind}; use syntax::attr::{InlineAttr, OptimizeAttr}; @@ -29,7 +29,6 @@ use syntax::tokenstream::TokenStream; use syntax::util::parser::ExprPrecedence; use rustc_target::spec::abi::Abi; use rustc_data_structures::sync::{par_for_each_in, Send, Sync}; -use rustc_data_structures::thin_vec::ThinVec; use rustc_macros::HashStable; use rustc_serialize::{self, Encoder, Encodable, Decoder, Decodable}; use std::collections::{BTreeSet, BTreeMap}; @@ -1274,7 +1273,7 @@ pub struct Local { pub init: Option<P<Expr>>, pub hir_id: HirId, pub span: Span, - pub attrs: ThinVec<Attribute>, + pub attrs: AttrVec, /// Can be `ForLoopDesugar` if the `let` statement is part of a `for` loop /// desugaring. Otherwise will be `Normal`. pub source: LocalSource, @@ -1459,7 +1458,7 @@ pub struct AnonConst { pub struct Expr { pub hir_id: HirId, pub kind: ExprKind, - pub attrs: ThinVec<Attribute>, + pub attrs: AttrVec, pub span: Span, } diff --git a/src/librustc_interface/util.rs b/src/librustc_interface/util.rs index 4d686fc310f..a8800082c9a 100644 --- a/src/librustc_interface/util.rs +++ b/src/librustc_interface/util.rs @@ -10,7 +10,6 @@ use rustc_data_structures::jobserver; use rustc_data_structures::sync::{Lock, Lrc}; use rustc_data_structures::stable_hasher::StableHasher; use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::thin_vec::ThinVec; use rustc_data_structures::fx::{FxHashSet, FxHashMap}; use rustc_errors::registry::Registry; use rustc_metadata::dynamic_lib::DynamicLibrary; @@ -24,7 +23,7 @@ use std::ops::DerefMut; use smallvec::SmallVec; use syntax::ptr::P; use syntax::mut_visit::{*, MutVisitor, visit_clobber}; -use syntax::ast::BlockCheckMode; +use syntax::ast::{AttrVec, BlockCheckMode}; use syntax::util::lev_distance::find_best_match_for_name; use syntax::source_map::{FileLoader, RealFileLoader, SourceMap}; use syntax::symbol::{Symbol, sym}; @@ -741,7 +740,7 @@ impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> { id: resolver.next_node_id(), kind: ast::ExprKind::Block(P(b), None), span: syntax_pos::DUMMY_SP, - attrs: ThinVec::new(), + attrs: AttrVec::new(), }); ast::Stmt { @@ -756,7 +755,7 @@ impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> { kind: ast::ExprKind::Loop(P(empty_block), None), id: self.resolver.next_node_id(), span: syntax_pos::DUMMY_SP, - attrs: ThinVec::new(), + attrs: AttrVec::new(), }); let loop_stmt = ast::Stmt { diff --git a/src/librustc_parse/lib.rs b/src/librustc_parse/lib.rs index 58c36524380..3de7f888724 100644 --- a/src/librustc_parse/lib.rs +++ b/src/librustc_parse/lib.rs @@ -2,6 +2,7 @@ #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] +#![feature(slice_patterns)] use syntax::ast; use syntax::print::pprust; diff --git a/src/librustc_parse/parser/attr.rs b/src/librustc_parse/parser/attr.rs index 00fd6b8a25b..51310fb88f6 100644 --- a/src/librustc_parse/parser/attr.rs +++ b/src/librustc_parse/parser/attr.rs @@ -1,4 +1,4 @@ -use super::{SeqSep, Parser, TokenType, PathStyle}; +use super::{Parser, TokenType, PathStyle}; use rustc_errors::PResult; use syntax::attr; use syntax::ast; @@ -301,8 +301,10 @@ impl<'a> Parser<'a> { crate fn parse_meta_item_kind(&mut self) -> PResult<'a, ast::MetaItemKind> { Ok(if self.eat(&token::Eq) { ast::MetaItemKind::NameValue(self.parse_unsuffixed_lit()?) - } else if self.eat(&token::OpenDelim(token::Paren)) { - ast::MetaItemKind::List(self.parse_meta_seq()?) + } else if self.check(&token::OpenDelim(token::Paren)) { + // Matches `meta_seq = ( COMMASEP(meta_item_inner) )`. + let (list, _) = self.parse_paren_comma_seq(|p| p.parse_meta_item_inner())?; + ast::MetaItemKind::List(list) } else { ast::MetaItemKind::Word }) @@ -311,16 +313,12 @@ impl<'a> Parser<'a> { /// Matches `meta_item_inner : (meta_item | UNSUFFIXED_LIT) ;`. fn parse_meta_item_inner(&mut self) -> PResult<'a, ast::NestedMetaItem> { match self.parse_unsuffixed_lit() { - Ok(lit) => { - return Ok(ast::NestedMetaItem::Literal(lit)) - } + Ok(lit) => return Ok(ast::NestedMetaItem::Literal(lit)), Err(ref mut err) => err.cancel(), } match self.parse_meta_item() { - Ok(mi) => { - return Ok(ast::NestedMetaItem::MetaItem(mi)) - } + Ok(mi) => return Ok(ast::NestedMetaItem::MetaItem(mi)), Err(ref mut err) => err.cancel(), } @@ -328,11 +326,4 @@ impl<'a> Parser<'a> { let msg = format!("expected unsuffixed literal or identifier, found `{}`", found); Err(self.diagnostic().struct_span_err(self.token.span, &msg)) } - - /// Matches `meta_seq = ( COMMASEP(meta_item_inner) )`. - fn parse_meta_seq(&mut self) -> PResult<'a, Vec<ast::NestedMetaItem>> { - self.parse_seq_to_end(&token::CloseDelim(token::Paren), - SeqSep::trailing_allowed(token::Comma), - |p: &mut Parser<'a>| p.parse_meta_item_inner()) - } } diff --git a/src/librustc_parse/parser/diagnostics.rs b/src/librustc_parse/parser/diagnostics.rs index ba125cacab4..16daefd1450 100644 --- a/src/librustc_parse/parser/diagnostics.rs +++ b/src/librustc_parse/parser/diagnostics.rs @@ -4,14 +4,13 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::{self, PResult, Applicability, DiagnosticBuilder, Handler, pluralize}; use rustc_error_codes::*; use syntax::ast::{self, Param, BinOpKind, BindingMode, BlockCheckMode, Expr, ExprKind, Ident, Item}; -use syntax::ast::{ItemKind, Mutability, Pat, PatKind, PathSegment, QSelf, Ty, TyKind}; +use syntax::ast::{ItemKind, Mutability, Pat, PatKind, PathSegment, QSelf, Ty, TyKind, AttrVec}; use syntax::token::{self, TokenKind, token_can_begin_expr}; use syntax::print::pprust; use syntax::ptr::P; -use syntax::ThinVec; use syntax::util::parser::AssocOp; use syntax::struct_span_err; -use syntax_pos::symbol::{kw, sym}; +use syntax_pos::symbol::kw; use syntax_pos::{Span, DUMMY_SP, MultiSpan, SpanSnippetError}; use log::{debug, trace}; @@ -32,7 +31,7 @@ pub(super) fn dummy_arg(ident: Ident) -> Param { id: ast::DUMMY_NODE_ID }; Param { - attrs: ThinVec::default(), + attrs: AttrVec::default(), id: ast::DUMMY_NODE_ID, pat, span: ident.span, @@ -164,7 +163,7 @@ impl RecoverQPath for Expr { Self { span: path.span, kind: ExprKind::Path(qself, path), - attrs: ThinVec::new(), + attrs: AttrVec::new(), id: ast::DUMMY_NODE_ID, } } @@ -312,22 +311,6 @@ impl<'a> Parser<'a> { }; self.last_unexpected_token_span = Some(self.token.span); let mut err = self.fatal(&msg_exp); - if self.token.is_ident_named(sym::and) { - err.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) { - err.span_suggestion_short( - self.token.span, - "use `||` instead of `or` for the boolean operator", - "||".to_string(), - Applicability::MaybeIncorrect, - ); - } let sp = if self.token == token::Eof { // This is EOF; don't want to point at the following char, but rather the last token. self.prev_span @@ -567,7 +550,7 @@ impl<'a> Parser<'a> { ); let mk_err_expr = |this: &Self, span| { - Ok(Some(this.mk_expr(span, ExprKind::Err, ThinVec::new()))) + Ok(Some(this.mk_expr(span, ExprKind::Err, AttrVec::new()))) }; match lhs.kind { @@ -986,21 +969,32 @@ impl<'a> Parser<'a> { /// Consumes alternative await syntaxes like `await!(<expr>)`, `await <expr>`, /// `await? <expr>`, `await(<expr>)`, and `await { <expr> }`. - pub(super) fn parse_incorrect_await_syntax( + pub(super) fn recover_incorrect_await_syntax( &mut self, lo: Span, await_sp: Span, - ) -> PResult<'a, (Span, ExprKind)> { - if self.token == token::Not { + attrs: AttrVec, + ) -> PResult<'a, P<Expr>> { + let (hi, expr, is_question) = if self.token == token::Not { // Handle `await!(<expr>)`. - self.expect(&token::Not)?; - self.expect(&token::OpenDelim(token::Paren))?; - let expr = self.parse_expr()?; - self.expect(&token::CloseDelim(token::Paren))?; - let sp = self.error_on_incorrect_await(lo, self.prev_span, &expr, false); - return Ok((sp, ExprKind::Await(expr))) - } + self.recover_await_macro()? + } else { + self.recover_await_prefix(await_sp)? + }; + let sp = self.error_on_incorrect_await(lo, hi, &expr, is_question); + let expr = self.mk_expr(lo.to(sp), ExprKind::Await(expr), attrs); + self.maybe_recover_from_bad_qpath(expr, true) + } + + fn recover_await_macro(&mut self) -> PResult<'a, (Span, P<Expr>, bool)> { + self.expect(&token::Not)?; + self.expect(&token::OpenDelim(token::Paren))?; + let expr = self.parse_expr()?; + self.expect(&token::CloseDelim(token::Paren))?; + Ok((self.prev_span, expr, false)) + } + fn recover_await_prefix(&mut self, await_sp: Span) -> PResult<'a, (Span, P<Expr>, bool)> { let is_question = self.eat(&token::Question); // Handle `await? <expr>`. let expr = if self.token == token::OpenDelim(token::Brace) { // Handle `await { <expr> }`. @@ -1010,7 +1004,7 @@ impl<'a> Parser<'a> { None, self.token.span, BlockCheckMode::Default, - ThinVec::new(), + AttrVec::new(), ) } else { self.parse_expr() @@ -1018,8 +1012,7 @@ impl<'a> Parser<'a> { err.span_label(await_sp, "while parsing this incorrect await expression"); err })?; - let sp = self.error_on_incorrect_await(lo, expr.span, &expr, is_question); - Ok((sp, ExprKind::Await(expr))) + Ok((expr.span, expr, is_question)) } fn error_on_incorrect_await(&self, lo: Span, hi: Span, expr: &Expr, is_question: bool) -> Span { @@ -1132,7 +1125,7 @@ impl<'a> Parser<'a> { err.emit(); // Recover from parse error, callers expect the closing delim to be consumed. self.consume_block(delim, ConsumeClosingDelim::Yes); - self.mk_expr(lo.to(self.prev_span), ExprKind::Err, ThinVec::new()) + self.mk_expr(lo.to(self.prev_span), ExprKind::Err, AttrVec::new()) } } } diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs index e4dff07e92c..159c2121d36 100644 --- a/src/librustc_parse/parser/expr.rs +++ b/src/librustc_parse/parser/expr.rs @@ -4,11 +4,10 @@ use super::pat::{GateOr, PARAM_EXPECTED}; use super::diagnostics::Error; use crate::maybe_recover_from_interpolated_ty_qpath; -use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::{PResult, Applicability}; -use syntax::ast::{self, DUMMY_NODE_ID, Attribute, AttrStyle, Ident, CaptureBy, BlockCheckMode}; -use syntax::ast::{Expr, ExprKind, RangeLimits, Label, Movability, IsAsync, Arm, Ty, TyKind}; -use syntax::ast::{FunctionRetTy, Param, FnDecl, BinOpKind, BinOp, UnOp, Mac, AnonConst, Field, Lit}; +use syntax::ast::{self, DUMMY_NODE_ID, AttrVec, AttrStyle, Ident, CaptureBy, Field, Lit}; +use syntax::ast::{BlockCheckMode, Expr, ExprKind, RangeLimits, Label, Movability, IsAsync, Arm}; +use syntax::ast::{Ty, TyKind, FunctionRetTy, Param, FnDecl, BinOpKind, BinOp, UnOp, Mac, AnonConst}; use syntax::token::{self, Token, TokenKind}; use syntax::print::pprust; use syntax::ptr::P; @@ -37,14 +36,14 @@ macro_rules! maybe_whole_expr { let path = path.clone(); $p.bump(); return Ok($p.mk_expr( - $p.token.span, ExprKind::Path(None, path), ThinVec::new() + $p.token.span, ExprKind::Path(None, path), AttrVec::new() )); } token::NtBlock(block) => { let block = block.clone(); $p.bump(); return Ok($p.mk_expr( - $p.token.span, ExprKind::Block(block, None), ThinVec::new() + $p.token.span, ExprKind::Block(block, None), AttrVec::new() )); } // N.B., `NtIdent(ident)` is normalized to `Ident` in `fn bump`. @@ -57,16 +56,16 @@ macro_rules! maybe_whole_expr { #[derive(Debug)] pub(super) enum LhsExpr { NotYetParsed, - AttributesParsed(ThinVec<Attribute>), + AttributesParsed(AttrVec), AlreadyParsed(P<Expr>), } -impl From<Option<ThinVec<Attribute>>> for LhsExpr { +impl From<Option<AttrVec>> for LhsExpr { /// Converts `Some(attrs)` into `LhsExpr::AttributesParsed(attrs)` /// and `None` into `LhsExpr::NotYetParsed`. /// /// This conversion does not allocate. - fn from(o: Option<ThinVec<Attribute>>) -> Self { + fn from(o: Option<AttrVec>) -> Self { if let Some(attrs) = o { LhsExpr::AttributesParsed(attrs) } else { @@ -91,24 +90,29 @@ impl<'a> Parser<'a> { self.parse_expr_res(Restrictions::empty(), None) } + fn parse_expr_catch_underscore(&mut self) -> PResult<'a, P<Expr>> { + match self.parse_expr() { + Ok(expr) => Ok(expr), + Err(mut err) => match self.token.kind { + token::Ident(name, false) + if name == kw::Underscore && self.look_ahead(1, |t| { + t == &token::Comma + }) => { + // Special-case handling of `foo(_, _, _)` + err.emit(); + let sp = self.token.span; + self.bump(); + Ok(self.mk_expr(sp, ExprKind::Err, AttrVec::new())) + } + _ => Err(err), + }, + } + } + + /// Parses a sequence of expressions bounded by parentheses. fn parse_paren_expr_seq(&mut self) -> PResult<'a, Vec<P<Expr>>> { self.parse_paren_comma_seq(|p| { - match p.parse_expr() { - Ok(expr) => Ok(expr), - Err(mut err) => match p.token.kind { - token::Ident(name, false) - if name == kw::Underscore && p.look_ahead(1, |t| { - t == &token::Comma - }) => { - // Special-case handling of `foo(_, _, _)` - err.emit(); - let sp = p.token.span; - p.bump(); - Ok(p.mk_expr(sp, ExprKind::Err, ThinVec::new())) - } - _ => Err(err), - }, - } + p.parse_expr_catch_underscore() }).map(|(r, _)| r) } @@ -117,7 +121,7 @@ impl<'a> Parser<'a> { pub(super) fn parse_expr_res( &mut self, r: Restrictions, - already_parsed_attrs: Option<ThinVec<Attribute>> + already_parsed_attrs: Option<AttrVec> ) -> PResult<'a, P<Expr>> { self.with_res(r, |this| this.parse_assoc_expr(already_parsed_attrs)) } @@ -129,7 +133,7 @@ impl<'a> Parser<'a> { #[inline] fn parse_assoc_expr( &mut self, - already_parsed_attrs: Option<ThinVec<Attribute>>, + already_parsed_attrs: Option<AttrVec>, ) -> PResult<'a, P<Expr>> { self.parse_assoc_expr_with(0, already_parsed_attrs.into()) } @@ -155,53 +159,13 @@ impl<'a> Parser<'a> { }; let last_type_ascription_set = self.last_type_ascription.is_some(); - match (self.expr_is_complete(&lhs), AssocOp::from_token(&self.token)) { - (true, None) => { - self.last_type_ascription = None; - // Semi-statement forms are odd. See https://github.com/rust-lang/rust/issues/29071 - return Ok(lhs); - } - (false, _) => {} // continue parsing the expression - // An exhaustive check is done in the following block, but these are checked first - // because they *are* ambiguous but also reasonable looking incorrect syntax, so we - // want to keep their span info to improve diagnostics in these cases in a later stage. - (true, Some(AssocOp::Multiply)) | // `{ 42 } *foo = bar;` or `{ 42 } * 3` - (true, Some(AssocOp::Subtract)) | // `{ 42 } -5` - (true, Some(AssocOp::LAnd)) | // `{ 42 } &&x` (#61475) - (true, Some(AssocOp::Add)) // `{ 42 } + 42 - // If the next token is a keyword, then the tokens above *are* unambiguously incorrect: - // `if x { a } else { b } && if y { c } else { d }` - if !self.look_ahead(1, |t| t.is_reserved_ident()) => { - self.last_type_ascription = None; - // These cases are ambiguous and can't be identified in the parser alone - let sp = self.sess.source_map().start_point(self.token.span); - self.sess.ambiguous_block_expr_parse.borrow_mut().insert(sp, lhs.span); - return Ok(lhs); - } - (true, Some(ref op)) if !op.can_continue_expr_unambiguously() => { - self.last_type_ascription = None; - return Ok(lhs); - } - (true, Some(_)) => { - // We've found an expression that would be parsed as a statement, but the next - // token implies this should be parsed as an expression. - // For example: `if let Some(x) = x { x } else { 0 } / 2` - let mut err = self.struct_span_err(self.token.span, &format!( - "expected expression, found `{}`", - pprust::token_to_string(&self.token), - )); - err.span_label(self.token.span, "expected expression"); - self.sess.expr_parentheses_needed( - &mut err, - lhs.span, - Some(pprust::expr_to_string(&lhs), - )); - err.emit(); - } + if !self.should_continue_as_assoc_expr(&lhs) { + self.last_type_ascription = None; + return Ok(lhs); } - self.expected_tokens.push(TokenType::Operator); - while let Some(op) = AssocOp::from_token(&self.token) { + self.expected_tokens.push(TokenType::Operator); + while let Some(op) = self.check_assoc_op() { // Adjust the span for interpolated LHS to point to the `$lhs` token and not to what // it refers to. Interpolated identifiers are unwrapped early and never show up here // as `PrevTokenKind::Interpolated` so if LHS is a single identifier we always process @@ -272,7 +236,7 @@ impl<'a> Parser<'a> { }; let r = self.mk_range(Some(lhs), rhs, limits)?; - lhs = self.mk_expr(lhs_span.to(rhs_span), r, ThinVec::new()); + lhs = self.mk_expr(lhs_span.to(rhs_span), r, AttrVec::new()); break } @@ -306,9 +270,9 @@ impl<'a> Parser<'a> { AssocOp::Greater | AssocOp::GreaterEqual => { let ast_op = op.to_ast_binop().unwrap(); let binary = self.mk_binary(source_map::respan(cur_op_span, ast_op), lhs, rhs); - self.mk_expr(span, binary, ThinVec::new()) + self.mk_expr(span, binary, AttrVec::new()) } - AssocOp::Assign => self.mk_expr(span, ExprKind::Assign(lhs, rhs), ThinVec::new()), + AssocOp::Assign => self.mk_expr(span, ExprKind::Assign(lhs, rhs), AttrVec::new()), AssocOp::AssignOp(k) => { let aop = match k { token::Plus => BinOpKind::Add, @@ -323,7 +287,7 @@ impl<'a> Parser<'a> { token::Shr => BinOpKind::Shr, }; let aopexpr = self.mk_assign_op(source_map::respan(cur_op_span, aop), lhs, rhs); - self.mk_expr(span, aopexpr, ThinVec::new()) + self.mk_expr(span, aopexpr, AttrVec::new()) } AssocOp::As | AssocOp::Colon | AssocOp::DotDot | AssocOp::DotDotEq => { self.bug("AssocOp should have been handled by special case") @@ -338,6 +302,80 @@ impl<'a> Parser<'a> { Ok(lhs) } + fn should_continue_as_assoc_expr(&mut self, lhs: &Expr) -> bool { + match (self.expr_is_complete(lhs), self.check_assoc_op()) { + // Semi-statement forms are odd: + // See https://github.com/rust-lang/rust/issues/29071 + (true, None) => false, + (false, _) => true, // Continue parsing the expression. + // An exhaustive check is done in the following block, but these are checked first + // because they *are* ambiguous but also reasonable looking incorrect syntax, so we + // want to keep their span info to improve diagnostics in these cases in a later stage. + (true, Some(AssocOp::Multiply)) | // `{ 42 } *foo = bar;` or `{ 42 } * 3` + (true, Some(AssocOp::Subtract)) | // `{ 42 } -5` + (true, Some(AssocOp::LAnd)) | // `{ 42 } &&x` (#61475) + (true, Some(AssocOp::Add)) // `{ 42 } + 42 + // If the next token is a keyword, then the tokens above *are* unambiguously incorrect: + // `if x { a } else { b } && if y { c } else { d }` + if !self.look_ahead(1, |t| t.is_reserved_ident()) => { + // These cases are ambiguous and can't be identified in the parser alone. + let sp = self.sess.source_map().start_point(self.token.span); + self.sess.ambiguous_block_expr_parse.borrow_mut().insert(sp, lhs.span); + false + } + (true, Some(ref op)) if !op.can_continue_expr_unambiguously() => false, + (true, Some(_)) => { + self.error_found_expr_would_be_stmt(lhs); + true + } + } + } + + /// We've found an expression that would be parsed as a statement, + /// but the next token implies this should be parsed as an expression. + /// For example: `if let Some(x) = x { x } else { 0 } / 2`. + fn error_found_expr_would_be_stmt(&self, lhs: &Expr) { + let mut err = self.struct_span_err(self.token.span, &format!( + "expected expression, found `{}`", + pprust::token_to_string(&self.token), + )); + err.span_label(self.token.span, "expected expression"); + self.sess.expr_parentheses_needed(&mut err, lhs.span, Some(pprust::expr_to_string(&lhs))); + err.emit(); + } + + /// Possibly translate the current token to an associative operator. + /// The method does not advance the current token. + /// + /// Also performs recovery for `and` / `or` which are mistaken for `&&` and `||` respectively. + fn check_assoc_op(&self) -> Option<AssocOp> { + match (AssocOp::from_token(&self.token), &self.token.kind) { + (op @ Some(_), _) => op, + (None, token::Ident(sym::and, false)) => { + self.error_bad_logical_op("and", "&&", "conjunction"); + Some(AssocOp::LAnd) + } + (None, token::Ident(sym::or, false)) => { + self.error_bad_logical_op("or", "||", "disjunction"); + Some(AssocOp::LOr) + } + _ => None, + } + } + + /// Error on `and` and `or` suggesting `&&` and `||` respectively. + fn error_bad_logical_op(&self, bad: &str, good: &str, english: &str) { + self.struct_span_err(self.token.span, &format!("`{}` is not a logical operator", bad)) + .span_suggestion_short( + self.token.span, + &format!("use `{}` to perform logical {}", good, english), + good.to_string(), + Applicability::MachineApplicable, + ) + .note("unlike in e.g., python and PHP, `&&` and `||` are used for logical operators") + .emit(); + } + /// Checks if this expression is a successfully parsed statement. fn expr_is_complete(&self, e: &Expr) -> bool { self.restrictions.contains(Restrictions::STMT_EXPR) && @@ -359,7 +397,7 @@ impl<'a> Parser<'a> { /// Parses prefix-forms of range notation: `..expr`, `..`, `..=expr`. fn parse_prefix_range_expr( &mut self, - already_parsed_attrs: Option<ThinVec<Attribute>> + already_parsed_attrs: Option<AttrVec> ) -> PResult<'a, P<Expr>> { // Check for deprecated `...` syntax. if self.token == token::DotDotDot { @@ -396,10 +434,7 @@ impl<'a> Parser<'a> { } /// Parses a prefix-unary-operator expr. - fn parse_prefix_expr( - &mut self, - already_parsed_attrs: Option<ThinVec<Attribute>> - ) -> PResult<'a, P<Expr>> { + fn parse_prefix_expr(&mut self, already_parsed_attrs: Option<AttrVec>) -> PResult<'a, P<Expr>> { let attrs = self.parse_or_use_outer_attributes(already_parsed_attrs)?; let lo = self.token.span; // Note: when adding new unary operators, don't forget to adjust TokenKind::can_begin_expr() @@ -510,7 +545,7 @@ impl<'a> Parser<'a> { expr_kind: fn(P<Expr>, P<Ty>) -> ExprKind) -> PResult<'a, P<Expr>> { let mk_expr = |this: &mut Self, rhs: P<Ty>| { - this.mk_expr(lhs_span.to(rhs.span), expr_kind(lhs, rhs), ThinVec::new()) + this.mk_expr(lhs_span.to(rhs.span), expr_kind(lhs, rhs), AttrVec::new()) }; // Save the state of the parser before parsing type normally, in case there is a @@ -611,7 +646,7 @@ impl<'a> Parser<'a> { /// Parses `a.b` or `a(13)` or `a[4]` or just `a`. fn parse_dot_or_call_expr( &mut self, - already_parsed_attrs: Option<ThinVec<Attribute>>, + already_parsed_attrs: Option<AttrVec>, ) -> PResult<'a, P<Expr>> { let attrs = self.parse_or_use_outer_attributes(already_parsed_attrs)?; @@ -624,7 +659,7 @@ impl<'a> Parser<'a> { &mut self, e0: P<Expr>, lo: Span, - mut attrs: ThinVec<Attribute>, + mut attrs: AttrVec, ) -> PResult<'a, P<Expr>> { // Stitch the list of outer attributes onto the return value. // A little bit ugly, but the best way given the current code @@ -633,19 +668,20 @@ impl<'a> Parser<'a> { expr.map(|mut expr| { attrs.extend::<Vec<_>>(expr.attrs.into()); expr.attrs = attrs; - match expr.kind { - ExprKind::If(..) if !expr.attrs.is_empty() => { - // Just point to the first attribute in there... - let span = expr.attrs[0].span; - self.span_err(span, "attributes are not yet allowed on `if` expressions"); - } - _ => {} - } + self.error_attr_on_if_expr(&expr); expr }) ) } + fn error_attr_on_if_expr(&self, expr: &Expr) { + if let (ExprKind::If(..), [a0, ..]) = (&expr.kind, &*expr.attrs) { + // Just point to the first attribute in there... + self.struct_span_err(a0.span, "attributes are not yet allowed on `if` expressions") + .emit(); + } + } + fn parse_dot_or_call_expr_with_(&mut self, e0: P<Expr>, lo: Span) -> PResult<'a, P<Expr>> { let mut e = e0; let mut hi; @@ -653,7 +689,7 @@ impl<'a> Parser<'a> { // expr? while self.eat(&token::Question) { let hi = self.prev_span; - e = self.mk_expr(lo.to(hi), ExprKind::Try(e), ThinVec::new()); + e = self.mk_expr(lo.to(hi), ExprKind::Try(e), AttrVec::new()); } // expr.f @@ -666,7 +702,7 @@ impl<'a> Parser<'a> { let span = self.token.span; self.bump(); let field = ExprKind::Field(e, Ident::new(symbol, span)); - e = self.mk_expr(lo.to(span), field, ThinVec::new()); + e = self.mk_expr(lo.to(span), field, AttrVec::new()); self.expect_no_suffix(span, "a tuple index", suffix); } @@ -715,7 +751,7 @@ impl<'a> Parser<'a> { let seq = self.parse_paren_expr_seq().map(|es| { let nd = self.mk_call(e, es); let hi = self.prev_span; - self.mk_expr(lo.to(hi), nd, ThinVec::new()) + self.mk_expr(lo.to(hi), nd, AttrVec::new()) }); e = self.recover_seq_parse_error(token::Paren, lo, seq); } @@ -728,7 +764,7 @@ impl<'a> Parser<'a> { hi = self.token.span; self.expect(&token::CloseDelim(token::Bracket))?; let index = self.mk_index(e, ix); - e = self.mk_expr(lo.to(hi), index, ThinVec::new()) + e = self.mk_expr(lo.to(hi), index, AttrVec::new()) } _ => return Ok(e) } @@ -752,7 +788,7 @@ impl<'a> Parser<'a> { args.insert(0, self_arg); let span = lo.to(self.prev_span); - self.mk_expr(span, ExprKind::MethodCall(segment, args), ThinVec::new()) + self.mk_expr(span, ExprKind::MethodCall(segment, args), AttrVec::new()) } _ => { // Field access `expr.f` @@ -762,7 +798,7 @@ impl<'a> Parser<'a> { } let span = lo.to(self.prev_span); - self.mk_expr(span, ExprKind::Field(self_arg, segment.ident), ThinVec::new()) + self.mk_expr(span, ExprKind::Field(self_arg, segment.ident), AttrVec::new()) } }) } @@ -781,305 +817,278 @@ impl<'a> Parser<'a> { // // Therefore, prevent sub-parser from parsing // attributes by giving them a empty "already-parsed" list. - let mut attrs = ThinVec::new(); + let attrs = AttrVec::new(); + // Note: when adding new syntax here, don't forget to adjust `TokenKind::can_begin_expr()`. let lo = self.token.span; - let mut hi = self.token.span; - - let ex: ExprKind; - - macro_rules! parse_lit { - () => { - match self.parse_opt_lit() { - Some(literal) => { - hi = self.prev_span; - ex = ExprKind::Lit(literal); - } - None => { - return Err(self.expected_expression_found()); - } + if let token::Literal(_) = self.token.kind { + // This match arm is a special-case of the `_` match arm below and + // could be removed without changing functionality, but it's faster + // to have it here, especially for programs with large constants. + self.parse_lit_expr(attrs) + } else if self.check(&token::OpenDelim(token::Paren)) { + self.parse_tuple_parens_expr(attrs) + } else if self.check(&token::OpenDelim(token::Brace)) { + self.parse_block_expr(None, lo, BlockCheckMode::Default, attrs) + } else if self.check(&token::BinOp(token::Or)) || self.check(&token::OrOr) { + self.parse_closure_expr(attrs) + } else if self.check(&token::OpenDelim(token::Bracket)) { + self.parse_array_or_repeat_expr(attrs) + } else if self.eat_lt() { + let (qself, path) = self.parse_qpath(PathStyle::Expr)?; + Ok(self.mk_expr(lo.to(path.span), ExprKind::Path(Some(qself), path), attrs)) + } else if self.token.is_path_start() { + self.parse_path_start_expr(attrs) + } else if self.check_keyword(kw::Move) || self.check_keyword(kw::Static) { + self.parse_closure_expr(attrs) + } else if self.eat_keyword(kw::If) { + self.parse_if_expr(attrs) + } else if self.eat_keyword(kw::For) { + self.parse_for_expr(None, self.prev_span, attrs) + } else if self.eat_keyword(kw::While) { + self.parse_while_expr(None, self.prev_span, attrs) + } else if let Some(label) = self.eat_label() { + self.parse_labeled_expr(label, attrs) + } else if self.eat_keyword(kw::Loop) { + self.parse_loop_expr(None, self.prev_span, attrs) + } else if self.eat_keyword(kw::Continue) { + let kind = ExprKind::Continue(self.eat_label()); + Ok(self.mk_expr(lo.to(self.prev_span), kind, attrs)) + } else if self.eat_keyword(kw::Match) { + let match_sp = self.prev_span; + self.parse_match_expr(attrs).map_err(|mut err| { + err.span_label(match_sp, "while parsing this match expression"); + err + }) + } else if self.eat_keyword(kw::Unsafe) { + self.parse_block_expr(None, lo, BlockCheckMode::Unsafe(ast::UserProvided), attrs) + } else if self.is_do_catch_block() { + self.recover_do_catch(attrs) + } else if self.is_try_block() { + self.expect_keyword(kw::Try)?; + self.parse_try_block(lo, attrs) + } else if self.eat_keyword(kw::Return) { + self.parse_return_expr(attrs) + } else if self.eat_keyword(kw::Break) { + self.parse_break_expr(attrs) + } else if self.eat_keyword(kw::Yield) { + self.parse_yield_expr(attrs) + } else if self.eat_keyword(kw::Let) { + self.parse_let_expr(attrs) + } else if !self.unclosed_delims.is_empty() && self.check(&token::Semi) { + // Don't complain about bare semicolons after unclosed braces + // recovery in order to keep the error count down. Fixing the + // delimiters will possibly also fix the bare semicolon found in + // expression context. For example, silence the following error: + // + // error: expected expression, found `;` + // --> file.rs:2:13 + // | + // 2 | foo(bar(; + // | ^ expected expression + self.bump(); + Ok(self.mk_expr_err(self.token.span)) + } else if self.token.span.rust_2018() { + // `Span::rust_2018()` is somewhat expensive; don't get it repeatedly. + if self.check_keyword(kw::Async) { + if self.is_async_block() { // Check for `async {` and `async move {`. + self.parse_async_block(attrs) + } else { + self.parse_closure_expr(attrs) } + } else if self.eat_keyword(kw::Await) { + self.recover_incorrect_await_syntax(lo, self.prev_span, attrs) + } else { + self.parse_lit_expr(attrs) } + } else { + self.parse_lit_expr(attrs) } + } - // Note: when adding new syntax here, don't forget to adjust `TokenKind::can_begin_expr()`. - match self.token.kind { - // This match arm is a special-case of the `_` match arm below and - // could be removed without changing functionality, but it's faster - // to have it here, especially for programs with large constants. - token::Literal(_) => { - parse_lit!() + fn parse_lit_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { + let lo = self.token.span; + match self.parse_opt_lit() { + Some(literal) => { + let expr = self.mk_expr(lo.to(self.prev_span), ExprKind::Lit(literal), attrs); + self.maybe_recover_from_bad_qpath(expr, true) } - token::OpenDelim(token::Paren) => { - self.bump(); + None => return Err(self.expected_expression_found()), + } + } - attrs.extend(self.parse_inner_attributes()?); - - // `(e)` is parenthesized `e`. - // `(e,)` is a tuple with only one field, `e`. - let mut es = vec![]; - let mut trailing_comma = false; - let mut recovered = false; - while self.token != token::CloseDelim(token::Paren) { - es.push(match self.parse_expr() { - Ok(es) => es, - Err(mut err) => { - // Recover from parse error in tuple list. - match self.token.kind { - token::Ident(name, false) - if name == kw::Underscore && self.look_ahead(1, |t| { - t == &token::Comma - }) => { - // Special-case handling of `Foo<(_, _, _)>` - err.emit(); - let sp = self.token.span; - self.bump(); - self.mk_expr(sp, ExprKind::Err, ThinVec::new()) - } - _ => return Ok( - self.recover_seq_parse_error(token::Paren, lo, Err(err)), - ), - } - } - }); - recovered = self.expect_one_of( - &[], - &[token::Comma, token::CloseDelim(token::Paren)], - )?; - if self.eat(&token::Comma) { - trailing_comma = true; - } else { - trailing_comma = false; - break; - } - } - if !recovered { - self.bump(); - } + fn parse_tuple_parens_expr(&mut self, mut attrs: AttrVec) -> PResult<'a, P<Expr>> { + let lo = self.token.span; + self.expect(&token::OpenDelim(token::Paren))?; + attrs.extend(self.parse_inner_attributes()?); // `(#![foo] a, b, ...)` is OK. + let (es, trailing_comma) = match self.parse_seq_to_end( + &token::CloseDelim(token::Paren), + SeqSep::trailing_allowed(token::Comma), + |p| p.parse_expr_catch_underscore(), + ) { + Ok(x) => x, + Err(err) => return Ok(self.recover_seq_parse_error(token::Paren, lo, Err(err))), + }; + let kind = if es.len() == 1 && !trailing_comma { + // `(e)` is parenthesized `e`. + ExprKind::Paren(es.into_iter().nth(0).unwrap()) + } else { + // `(e,)` is a tuple with only one field, `e`. + ExprKind::Tup(es) + }; + let expr = self.mk_expr(lo.to(self.prev_span), kind, attrs); + self.maybe_recover_from_bad_qpath(expr, true) + } - hi = self.prev_span; - ex = if es.len() == 1 && !trailing_comma { - ExprKind::Paren(es.into_iter().nth(0).unwrap()) - } else { - ExprKind::Tup(es) + fn parse_array_or_repeat_expr(&mut self, mut attrs: AttrVec) -> PResult<'a, P<Expr>> { + let lo = self.token.span; + self.bump(); // `[` + + attrs.extend(self.parse_inner_attributes()?); + + let close = &token::CloseDelim(token::Bracket); + let kind = if self.eat(close) { + // Empty vector + ExprKind::Array(Vec::new()) + } else { + // Non-empty vector + let first_expr = self.parse_expr()?; + if self.eat(&token::Semi) { + // Repeating array syntax: `[ 0; 512 ]` + let count = AnonConst { + id: DUMMY_NODE_ID, + value: self.parse_expr()?, }; + self.expect(close)?; + ExprKind::Repeat(first_expr, count) + } else if self.eat(&token::Comma) { + // Vector with two or more elements. + let sep = SeqSep::trailing_allowed(token::Comma); + let (remaining_exprs, _) = self.parse_seq_to_end(close, sep, |p| p.parse_expr())?; + let mut exprs = vec![first_expr]; + exprs.extend(remaining_exprs); + ExprKind::Array(exprs) + } else { + // Vector with one element + self.expect(close)?; + ExprKind::Array(vec![first_expr]) } - token::OpenDelim(token::Brace) => { - return self.parse_block_expr(None, lo, BlockCheckMode::Default, attrs); - } - token::BinOp(token::Or) | token::OrOr => { - return self.parse_closure_expr(attrs); + }; + let expr = self.mk_expr(lo.to(self.prev_span), kind, attrs); + self.maybe_recover_from_bad_qpath(expr, true) + } + + fn parse_path_start_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { + let lo = self.token.span; + let path = self.parse_path(PathStyle::Expr)?; + + // `!`, as an operator, is prefix, so we know this isn't that. + let (hi, kind) = if self.eat(&token::Not) { + // MACRO INVOCATION expression + let mac = Mac { + path, + args: self.parse_mac_args()?, + prior_type_ascription: self.last_type_ascription, + }; + (self.prev_span, ExprKind::Mac(mac)) + } else if self.check(&token::OpenDelim(token::Brace)) { + if let Some(expr) = self.maybe_parse_struct_expr(lo, &path, &attrs) { + return expr; + } else { + (path.span, ExprKind::Path(None, path)) } - token::OpenDelim(token::Bracket) => { - self.bump(); + } else { + (path.span, ExprKind::Path(None, path)) + }; - attrs.extend(self.parse_inner_attributes()?); + let expr = self.mk_expr(lo.to(hi), kind, attrs); + self.maybe_recover_from_bad_qpath(expr, true) + } - if self.eat(&token::CloseDelim(token::Bracket)) { - // Empty vector - ex = ExprKind::Array(Vec::new()); - } else { - // Non-empty vector - let first_expr = self.parse_expr()?; - if self.eat(&token::Semi) { - // Repeating array syntax: `[ 0; 512 ]` - let count = AnonConst { - id: DUMMY_NODE_ID, - value: self.parse_expr()?, - }; - self.expect(&token::CloseDelim(token::Bracket))?; - ex = ExprKind::Repeat(first_expr, count); - } else if self.eat(&token::Comma) { - // Vector with two or more elements - let remaining_exprs = self.parse_seq_to_end( - &token::CloseDelim(token::Bracket), - SeqSep::trailing_allowed(token::Comma), - |p| Ok(p.parse_expr()?) - )?; - let mut exprs = vec![first_expr]; - exprs.extend(remaining_exprs); - ex = ExprKind::Array(exprs); - } else { - // Vector with one element - self.expect(&token::CloseDelim(token::Bracket))?; - ex = ExprKind::Array(vec![first_expr]); - } - } - hi = self.prev_span; - } - _ => { - if self.eat_lt() { - let (qself, path) = self.parse_qpath(PathStyle::Expr)?; - hi = path.span; - return Ok(self.mk_expr(lo.to(hi), ExprKind::Path(Some(qself), path), attrs)); - } - if self.token.is_path_start() { - let path = self.parse_path(PathStyle::Expr)?; - - // `!`, as an operator, is prefix, so we know this isn't that. - if self.eat(&token::Not) { - // MACRO INVOCATION expression - let args = self.parse_mac_args()?; - hi = self.prev_span; - ex = ExprKind::Mac(Mac { - path, - args, - prior_type_ascription: self.last_type_ascription, - }); - } else if self.check(&token::OpenDelim(token::Brace)) { - if let Some(expr) = self.maybe_parse_struct_expr(lo, &path, &attrs) { - return expr; - } else { - hi = path.span; - ex = ExprKind::Path(None, path); - } - } else { - hi = path.span; - ex = ExprKind::Path(None, path); - } + fn parse_labeled_expr(&mut self, label: Label, attrs: AttrVec) -> PResult<'a, P<Expr>> { + let lo = label.ident.span; + self.expect(&token::Colon)?; + if self.eat_keyword(kw::While) { + return self.parse_while_expr(Some(label), lo, attrs) + } + if self.eat_keyword(kw::For) { + return self.parse_for_expr(Some(label), lo, attrs) + } + if self.eat_keyword(kw::Loop) { + return self.parse_loop_expr(Some(label), lo, attrs) + } + if self.token == token::OpenDelim(token::Brace) { + return self.parse_block_expr(Some(label), lo, BlockCheckMode::Default, attrs); + } - let expr = self.mk_expr(lo.to(hi), ex, attrs); - return self.maybe_recover_from_bad_qpath(expr, true); - } - if self.check_keyword(kw::Move) || self.check_keyword(kw::Static) { - return self.parse_closure_expr(attrs); - } - if self.eat_keyword(kw::If) { - return self.parse_if_expr(attrs); - } - if self.eat_keyword(kw::For) { - let lo = self.prev_span; - return self.parse_for_expr(None, lo, attrs); - } - if self.eat_keyword(kw::While) { - let lo = self.prev_span; - return self.parse_while_expr(None, lo, attrs); - } - if let Some(label) = self.eat_label() { - let lo = label.ident.span; - self.expect(&token::Colon)?; - if self.eat_keyword(kw::While) { - return self.parse_while_expr(Some(label), lo, attrs) - } - if self.eat_keyword(kw::For) { - return self.parse_for_expr(Some(label), lo, attrs) - } - if self.eat_keyword(kw::Loop) { - return self.parse_loop_expr(Some(label), lo, attrs) - } - if self.token == token::OpenDelim(token::Brace) { - return self.parse_block_expr(Some(label), - lo, - BlockCheckMode::Default, - attrs); - } - let msg = "expected `while`, `for`, `loop` or `{` after a label"; - let mut err = self.fatal(msg); - err.span_label(self.token.span, msg); - return Err(err); - } - if self.eat_keyword(kw::Loop) { - let lo = self.prev_span; - return self.parse_loop_expr(None, lo, attrs); - } - if self.eat_keyword(kw::Continue) { - let label = self.eat_label(); - let ex = ExprKind::Continue(label); - let hi = self.prev_span; - return Ok(self.mk_expr(lo.to(hi), ex, attrs)); - } - if self.eat_keyword(kw::Match) { - let match_sp = self.prev_span; - return self.parse_match_expr(attrs).map_err(|mut err| { - err.span_label(match_sp, "while parsing this match expression"); - err - }); - } - if self.eat_keyword(kw::Unsafe) { - return self.parse_block_expr( - None, - lo, - BlockCheckMode::Unsafe(ast::UserProvided), - attrs); - } - if self.is_do_catch_block() { - let mut db = self.fatal("found removed `do catch` syntax"); - db.help("following RFC #2388, the new non-placeholder syntax is `try`"); - return Err(db); - } - if self.is_try_block() { - let lo = self.token.span; - assert!(self.eat_keyword(kw::Try)); - return self.parse_try_block(lo, attrs); - } + let msg = "expected `while`, `for`, `loop` or `{` after a label"; + self.struct_span_err(self.token.span, msg) + .span_label(self.token.span, msg) + .emit(); + // Continue as an expression in an effort to recover on `'label: non_block_expr`. + self.parse_expr() + } - // `Span::rust_2018()` is somewhat expensive; don't get it repeatedly. - let is_span_rust_2018 = self.token.span.rust_2018(); - if is_span_rust_2018 && self.check_keyword(kw::Async) { - return if self.is_async_block() { // Check for `async {` and `async move {`. - self.parse_async_block(attrs) - } else { - self.parse_closure_expr(attrs) - }; - } - if self.eat_keyword(kw::Return) { - if self.token.can_begin_expr() { - let e = self.parse_expr()?; - hi = e.span; - ex = ExprKind::Ret(Some(e)); - } else { - ex = ExprKind::Ret(None); - } - } else if self.eat_keyword(kw::Break) { - let label = self.eat_label(); - let e = if self.token.can_begin_expr() - && !(self.token == token::OpenDelim(token::Brace) - && self.restrictions.contains( - Restrictions::NO_STRUCT_LITERAL)) { - Some(self.parse_expr()?) - } else { - None - }; - ex = ExprKind::Break(label, e); - hi = self.prev_span; - } else if self.eat_keyword(kw::Yield) { - if self.token.can_begin_expr() { - let e = self.parse_expr()?; - hi = e.span; - ex = ExprKind::Yield(Some(e)); - } else { - ex = ExprKind::Yield(None); - } + /// Recover on the syntax `do catch { ... }` suggesting `try { ... }` instead. + fn recover_do_catch(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { + let lo = self.token.span; - let span = lo.to(hi); - self.sess.gated_spans.gate(sym::generators, span); - } else if self.eat_keyword(kw::Let) { - return self.parse_let_expr(attrs); - } else if is_span_rust_2018 && self.eat_keyword(kw::Await) { - let (await_hi, e_kind) = self.parse_incorrect_await_syntax(lo, self.prev_span)?; - hi = await_hi; - ex = e_kind; - } else { - if !self.unclosed_delims.is_empty() && self.check(&token::Semi) { - // Don't complain about bare semicolons after unclosed braces - // recovery in order to keep the error count down. Fixing the - // delimiters will possibly also fix the bare semicolon found in - // expression context. For example, silence the following error: - // - // error: expected expression, found `;` - // --> file.rs:2:13 - // | - // 2 | foo(bar(; - // | ^ expected expression - self.bump(); - return Ok(self.mk_expr(self.token.span, ExprKind::Err, ThinVec::new())); - } - parse_lit!() - } - } - } + self.bump(); // `do` + self.bump(); // `catch` + + let span_dc = lo.to(self.prev_span); + self.struct_span_err(span_dc, "found removed `do catch` syntax") + .span_suggestion( + span_dc, + "replace with the new syntax", + "try".to_string(), + Applicability::MachineApplicable, + ) + .note("following RFC #2388, the new non-placeholder syntax is `try`") + .emit(); - let expr = self.mk_expr(lo.to(hi), ex, attrs); + self.parse_try_block(lo, attrs) + } + + /// Parse an expression if the token can begin one. + fn parse_expr_opt(&mut self) -> PResult<'a, Option<P<Expr>>> { + Ok(if self.token.can_begin_expr() { + Some(self.parse_expr()?) + } else { + None + }) + } + + /// Parse `"return" expr?`. + fn parse_return_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { + let lo = self.prev_span; + let kind = ExprKind::Ret(self.parse_expr_opt()?); + let expr = self.mk_expr(lo.to(self.prev_span), kind, attrs); + self.maybe_recover_from_bad_qpath(expr, true) + } + + /// Parse `"('label ":")? break expr?`. + fn parse_break_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { + let lo = self.prev_span; + let label = self.eat_label(); + let kind = if self.token != token::OpenDelim(token::Brace) + || !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL) + { + self.parse_expr_opt()? + } else { + None + }; + let expr = self.mk_expr(lo.to(self.prev_span), ExprKind::Break(label, kind), attrs); + self.maybe_recover_from_bad_qpath(expr, true) + } + + /// Parse `"yield" expr?`. + fn parse_yield_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { + let lo = self.prev_span; + let kind = ExprKind::Yield(self.parse_expr_opt()?); + let span = lo.to(self.prev_span); + self.sess.gated_spans.gate(sym::generators, span); + let expr = self.mk_expr(span, kind, attrs); self.maybe_recover_from_bad_qpath(expr, true) } @@ -1282,12 +1291,12 @@ impl<'a> Parser<'a> { let lo = self.token.span; let literal = self.parse_lit()?; let hi = self.prev_span; - let expr = self.mk_expr(lo.to(hi), ExprKind::Lit(literal), ThinVec::new()); + let expr = self.mk_expr(lo.to(hi), ExprKind::Lit(literal), AttrVec::new()); if minus_present { let minus_hi = self.prev_span; let unary = self.mk_unary(UnOp::Neg, expr); - Ok(self.mk_expr(minus_lo.to(minus_hi), unary, ThinVec::new())) + Ok(self.mk_expr(minus_lo.to(minus_hi), unary, AttrVec::new())) } else { Ok(expr) } @@ -1299,7 +1308,7 @@ impl<'a> Parser<'a> { opt_label: Option<Label>, lo: Span, blk_mode: BlockCheckMode, - outer_attrs: ThinVec<Attribute>, + outer_attrs: AttrVec, ) -> PResult<'a, P<Expr>> { if let Some(label) = opt_label { self.sess.gated_spans.gate(sym::label_break_value, label.ident.span); @@ -1315,7 +1324,7 @@ impl<'a> Parser<'a> { } /// Parses a closure expression (e.g., `move |args| expr`). - fn parse_closure_expr(&mut self, attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> { + fn parse_closure_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { let lo = self.token.span; let movability = if self.eat_keyword(kw::Static) { @@ -1345,7 +1354,7 @@ impl<'a> Parser<'a> { _ => { // If an explicit return type is given, require a block to appear (RFC 968). let body_lo = self.token.span; - self.parse_block_expr(None, body_lo, BlockCheckMode::Default, ThinVec::new())? + self.parse_block_expr(None, body_lo, BlockCheckMode::Default, AttrVec::new())? } }; @@ -1415,7 +1424,7 @@ impl<'a> Parser<'a> { } /// Parses an `if` expression (`if` token already eaten). - fn parse_if_expr(&mut self, attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> { + fn parse_if_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { let lo = self.prev_span; let cond = self.parse_cond_expr()?; @@ -1461,7 +1470,7 @@ impl<'a> Parser<'a> { /// Parses a `let $pat = $expr` pseudo-expression. /// The `let` token has already been eaten. - fn parse_let_expr(&mut self, attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> { + fn parse_let_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { let lo = self.prev_span; let pat = self.parse_top_pat(GateOr::No)?; self.expect(&token::Eq)?; @@ -1477,10 +1486,10 @@ impl<'a> Parser<'a> { /// Parses an `else { ... }` expression (`else` token already eaten). fn parse_else_expr(&mut self) -> PResult<'a, P<Expr>> { if self.eat_keyword(kw::If) { - return self.parse_if_expr(ThinVec::new()); + return self.parse_if_expr(AttrVec::new()); } else { let blk = self.parse_block()?; - return Ok(self.mk_expr(blk.span, ExprKind::Block(blk, None), ThinVec::new())); + return Ok(self.mk_expr(blk.span, ExprKind::Block(blk, None), AttrVec::new())); } } @@ -1489,7 +1498,7 @@ impl<'a> Parser<'a> { &mut self, opt_label: Option<Label>, span_lo: Span, - mut attrs: ThinVec<Attribute> + mut attrs: AttrVec ) -> PResult<'a, P<Expr>> { // Parse: `for <src_pat> in <src_expr> <src_loop_block>` @@ -1531,7 +1540,7 @@ impl<'a> Parser<'a> { &mut self, opt_label: Option<Label>, span_lo: Span, - mut attrs: ThinVec<Attribute> + mut attrs: AttrVec ) -> PResult<'a, P<Expr>> { let cond = self.parse_cond_expr()?; let (iattrs, body) = self.parse_inner_attrs_and_block()?; @@ -1545,7 +1554,7 @@ impl<'a> Parser<'a> { &mut self, opt_label: Option<Label>, span_lo: Span, - mut attrs: ThinVec<Attribute> + mut attrs: AttrVec ) -> PResult<'a, P<Expr>> { let (iattrs, body) = self.parse_inner_attrs_and_block()?; attrs.extend(iattrs); @@ -1564,7 +1573,7 @@ impl<'a> Parser<'a> { } /// Parses a `match ... { ... }` expression (`match` token already eaten). - fn parse_match_expr(&mut self, mut attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> { + fn parse_match_expr(&mut self, mut attrs: AttrVec) -> PResult<'a, P<Expr>> { let match_span = self.prev_span; let lo = self.prev_span; let discriminant = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?; @@ -1676,11 +1685,7 @@ impl<'a> Parser<'a> { } /// Parses a `try {...}` expression (`try` token already eaten). - fn parse_try_block( - &mut self, - span_lo: Span, - mut attrs: ThinVec<Attribute> - ) -> PResult<'a, P<Expr>> { + fn parse_try_block(&mut self, span_lo: Span, mut attrs: AttrVec) -> PResult<'a, P<Expr>> { let (iattrs, body) = self.parse_inner_attrs_and_block()?; attrs.extend(iattrs); if self.eat_keyword(kw::Catch) { @@ -1712,7 +1717,7 @@ impl<'a> Parser<'a> { } /// Parses an `async move? {...}` expression. - fn parse_async_block(&mut self, mut attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> { + fn parse_async_block(&mut self, mut attrs: AttrVec) -> PResult<'a, P<Expr>> { let span_lo = self.token.span; self.expect_keyword(kw::Async)?; let capture_clause = self.parse_capture_clause(); @@ -1739,7 +1744,7 @@ impl<'a> Parser<'a> { &mut self, lo: Span, path: &ast::Path, - attrs: &ThinVec<Attribute>, + attrs: &AttrVec, ) -> Option<PResult<'a, P<Expr>>> { let struct_allowed = !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL); let certainly_not_a_block = || self.look_ahead(1, |t| t.is_ident()) && ( @@ -1780,7 +1785,7 @@ impl<'a> Parser<'a> { &mut self, lo: Span, pth: ast::Path, - mut attrs: ThinVec<Attribute> + mut attrs: AttrVec ) -> PResult<'a, P<Expr>> { let struct_sp = lo.to(self.prev_span); self.bump(); @@ -1826,9 +1831,9 @@ impl<'a> Parser<'a> { recovery_field = Some(ast::Field { ident: Ident::new(name, self.token.span), span: self.token.span, - expr: self.mk_expr(self.token.span, ExprKind::Err, ThinVec::new()), + expr: self.mk_expr(self.token.span, ExprKind::Err, AttrVec::new()), is_shorthand: false, - attrs: ThinVec::new(), + attrs: AttrVec::new(), id: DUMMY_NODE_ID, is_placeholder: false, }); @@ -1907,7 +1912,7 @@ impl<'a> Parser<'a> { // Mimic `x: x` for the `x` field shorthand. let path = ast::Path::from_ident(fieldname); - let expr = self.mk_expr(fieldname.span, ExprKind::Path(None, path), ThinVec::new()); + let expr = self.mk_expr(fieldname.span, ExprKind::Path(None, path), AttrVec::new()); (fieldname, expr, true) }; Ok(ast::Field { @@ -1984,16 +1989,16 @@ impl<'a> Parser<'a> { fn mk_await_expr(&mut self, self_arg: P<Expr>, lo: Span) -> PResult<'a, P<Expr>> { let span = lo.to(self.prev_span); - let await_expr = self.mk_expr(span, ExprKind::Await(self_arg), ThinVec::new()); + let await_expr = self.mk_expr(span, ExprKind::Await(self_arg), AttrVec::new()); self.recover_from_await_method_call(); Ok(await_expr) } - crate fn mk_expr(&self, span: Span, kind: ExprKind, attrs: ThinVec<Attribute>) -> P<Expr> { + crate fn mk_expr(&self, span: Span, kind: ExprKind, attrs: AttrVec) -> P<Expr> { P(Expr { kind, span, attrs, id: DUMMY_NODE_ID }) } pub(super) fn mk_expr_err(&self, span: Span) -> P<Expr> { - self.mk_expr(span, ExprKind::Err, ThinVec::new()) + self.mk_expr(span, ExprKind::Err, AttrVec::new()) } } diff --git a/src/librustc_parse/parser/item.rs b/src/librustc_parse/parser/item.rs index 0840a1551db..229ca07f13b 100644 --- a/src/librustc_parse/parser/item.rs +++ b/src/librustc_parse/parser/item.rs @@ -5,15 +5,14 @@ use crate::maybe_whole; use rustc_errors::{PResult, Applicability, DiagnosticBuilder, StashKey}; use rustc_error_codes::*; -use syntax::ast::{self, DUMMY_NODE_ID, Ident, Attribute, AttrKind, AttrStyle, AnonConst, Item}; -use syntax::ast::{AssocItem, AssocItemKind, ItemKind, UseTree, UseTreeKind}; +use syntax::ast::{self, DUMMY_NODE_ID, Ident, AttrVec, Attribute, AttrKind, AttrStyle, AnonConst}; +use syntax::ast::{AssocItem, AssocItemKind, Item, ItemKind, UseTree, UseTreeKind}; use syntax::ast::{PathSegment, IsAuto, Constness, IsAsync, Unsafety, Defaultness, Extern, StrLit}; use syntax::ast::{Visibility, VisibilityKind, Mutability, FnHeader, ForeignItem, ForeignItemKind}; use syntax::ast::{Ty, TyKind, Generics, TraitRef, EnumDef, Variant, VariantData, StructField}; use syntax::ast::{Mac, MacArgs, MacDelimiter, Block, BindingMode, FnDecl, FnSig, SelfKind, Param}; use syntax::print::pprust; use syntax::ptr::P; -use syntax::ThinVec; use syntax::token; use syntax::tokenstream::{DelimSpan, TokenTree, TokenStream}; use syntax::struct_span_err; @@ -2095,7 +2094,7 @@ impl<'a> Parser<'a> { }; let eself = source_map::respan(eself_lo.to(eself_hi), eself); - Ok(Some(Param::from_self(ThinVec::default(), eself, eself_ident))) + Ok(Some(Param::from_self(AttrVec::default(), eself, eself_ident))) } fn is_named_param(&self) -> bool { diff --git a/src/librustc_parse/parser/mod.rs b/src/librustc_parse/parser/mod.rs index 07e99cfe012..255e789b58e 100644 --- a/src/librustc_parse/parser/mod.rs +++ b/src/librustc_parse/parser/mod.rs @@ -15,9 +15,8 @@ use crate::{Directory, DirectoryOwnership}; use crate::lexer::UnmatchedBrace; use rustc_errors::{PResult, Applicability, DiagnosticBuilder, FatalError}; -use rustc_data_structures::thin_vec::ThinVec; -use syntax::ast::{self, DUMMY_NODE_ID, AttrStyle, Attribute, CrateSugar, Extern, Ident, StrLit}; -use syntax::ast::{IsAsync, MacArgs, MacDelimiter, Mutability, Visibility, VisibilityKind, Unsafety}; +use syntax::ast::{self, DUMMY_NODE_ID, AttrVec, AttrStyle, CrateSugar, Extern, Ident, Unsafety}; +use syntax::ast::{StrLit, IsAsync, MacArgs, MacDelimiter, Mutability, Visibility, VisibilityKind}; use syntax::print::pprust; use syntax::ptr::P; use syntax::token::{self, Token, TokenKind, DelimToken}; @@ -740,34 +739,6 @@ impl<'a> Parser<'a> { } } - /// Parses a sequence, including the closing delimiter. The function - /// `f` must consume tokens until reaching the next separator or - /// closing bracket. - fn parse_seq_to_end<T>( - &mut self, - ket: &TokenKind, - sep: SeqSep, - f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, - ) -> PResult<'a, Vec<T>> { - let (val, _, recovered) = self.parse_seq_to_before_end(ket, sep, f)?; - if !recovered { - self.bump(); - } - Ok(val) - } - - /// Parses a sequence, not including the closing delimiter. The function - /// `f` must consume tokens until reaching the next separator or - /// closing bracket. - fn parse_seq_to_before_end<T>( - &mut self, - ket: &TokenKind, - sep: SeqSep, - f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, - ) -> PResult<'a, (Vec<T>, bool, bool)> { - self.parse_seq_to_before_tokens(&[ket], sep, TokenExpectType::Expect, f) - } - fn expect_any_with_type(&mut self, kets: &[&TokenKind], expect: TokenExpectType) -> bool { kets.iter().any(|k| { match expect { @@ -855,6 +826,34 @@ impl<'a> Parser<'a> { Ok((v, trailing, recovered)) } + /// Parses a sequence, not including the closing delimiter. The function + /// `f` must consume tokens until reaching the next separator or + /// closing bracket. + fn parse_seq_to_before_end<T>( + &mut self, + ket: &TokenKind, + sep: SeqSep, + f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, + ) -> PResult<'a, (Vec<T>, bool, bool)> { + self.parse_seq_to_before_tokens(&[ket], sep, TokenExpectType::Expect, f) + } + + /// Parses a sequence, including the closing delimiter. The function + /// `f` must consume tokens until reaching the next separator or + /// closing bracket. + fn parse_seq_to_end<T>( + &mut self, + ket: &TokenKind, + sep: SeqSep, + f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, + ) -> PResult<'a, (Vec<T>, bool /* trailing */)> { + let (val, trailing, recovered) = self.parse_seq_to_before_end(ket, sep, f)?; + if !recovered { + self.eat(ket); + } + Ok((val, trailing)) + } + /// Parses a sequence, including the closing delimiter. The function /// `f` must consume tokens until reaching the next separator or /// closing bracket. @@ -866,11 +865,7 @@ impl<'a> Parser<'a> { f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, ) -> PResult<'a, (Vec<T>, bool)> { self.expect(bra)?; - let (result, trailing, recovered) = self.parse_seq_to_before_end(ket, sep, f)?; - if !recovered { - self.eat(ket); - } - Ok((result, trailing)) + self.parse_seq_to_end(ket, sep, f) } fn parse_delim_comma_seq<T>( @@ -1054,8 +1049,8 @@ impl<'a> Parser<'a> { fn parse_or_use_outer_attributes( &mut self, - already_parsed_attrs: Option<ThinVec<Attribute>>, - ) -> PResult<'a, ThinVec<Attribute>> { + already_parsed_attrs: Option<AttrVec>, + ) -> PResult<'a, AttrVec> { if let Some(attrs) = already_parsed_attrs { Ok(attrs) } else { diff --git a/src/librustc_parse/parser/pat.rs b/src/librustc_parse/parser/pat.rs index 117b92dc9a5..593fb30bb75 100644 --- a/src/librustc_parse/parser/pat.rs +++ b/src/librustc_parse/parser/pat.rs @@ -1,12 +1,11 @@ use super::{Parser, PathStyle}; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; use rustc_errors::{PResult, Applicability, DiagnosticBuilder}; -use syntax::ast::{self, Attribute, Pat, PatKind, FieldPat, RangeEnd, RangeSyntax, Mac}; +use syntax::ast::{self, AttrVec, Attribute, Pat, PatKind, FieldPat, RangeEnd, RangeSyntax, Mac}; use syntax::ast::{BindingMode, Ident, Mutability, Path, QSelf, Expr, ExprKind}; use syntax::mut_visit::{noop_visit_pat, noop_visit_mac, MutVisitor}; use syntax::ptr::P; use syntax::print::pprust; -use syntax::ThinVec; use syntax::token; use syntax_pos::source_map::{respan, Span, Spanned}; use syntax_pos::symbol::{kw, sym}; @@ -636,7 +635,7 @@ impl<'a> Parser<'a> { let op_span = self.token.span; // Parse range let span = lo.to(self.prev_span); - let begin = self.mk_expr(span, ExprKind::Path(qself, path), ThinVec::new()); + let begin = self.mk_expr(span, ExprKind::Path(qself, path), AttrVec::new()); self.bump(); let end = self.parse_pat_range_end_opt(&begin, form)?; Ok(PatKind::Range(begin, end, respan(op_span, end_kind))) @@ -693,7 +692,7 @@ impl<'a> Parser<'a> { let lo = self.prev_span; let end = self.parse_pat_range_end()?; let range_span = lo.to(end.span); - let begin = self.mk_expr(range_span, ExprKind::Err, ThinVec::new()); + let begin = self.mk_expr(range_span, ExprKind::Err, AttrVec::new()); self.diagnostic() .struct_span_err(range_span, &format!("`{}X` range patterns are not supported", form)) @@ -731,7 +730,7 @@ impl<'a> Parser<'a> { ) .emit(); - Ok(self.mk_expr(range_span, ExprKind::Err, ThinVec::new())) + Ok(self.mk_expr(range_span, ExprKind::Err, AttrVec::new())) } } @@ -747,7 +746,7 @@ impl<'a> Parser<'a> { (None, self.parse_path(PathStyle::Expr)?) }; let hi = self.prev_span; - Ok(self.mk_expr(lo.to(hi), ExprKind::Path(qself, path), ThinVec::new())) + Ok(self.mk_expr(lo.to(hi), ExprKind::Path(qself, path), AttrVec::new())) } else { self.parse_literal_maybe_minus() } diff --git a/src/librustc_parse/parser/path.rs b/src/librustc_parse/parser/path.rs index aeba6dd2f67..802d38e2997 100644 --- a/src/librustc_parse/parser/path.rs +++ b/src/librustc_parse/parser/path.rs @@ -3,7 +3,6 @@ use crate::maybe_whole; use rustc_errors::{PResult, Applicability, pluralize}; use syntax::ast::{self, QSelf, Path, PathSegment, Ident, ParenthesizedArgs, AngleBracketedArgs}; use syntax::ast::{AnonConst, GenericArg, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode}; -use syntax::ThinVec; use syntax::token::{self, Token}; use syntax_pos::source_map::{Span, BytePos}; use syntax_pos::symbol::{kw, sym}; @@ -400,7 +399,7 @@ impl<'a> Parser<'a> { // Parse const argument. let expr = if let token::OpenDelim(token::Brace) = self.token.kind { self.parse_block_expr( - None, self.token.span, BlockCheckMode::Default, ThinVec::new() + None, self.token.span, BlockCheckMode::Default, ast::AttrVec::new() )? } else if self.token.is_ident() { // FIXME(const_generics): to distinguish between idents for types and consts, diff --git a/src/librustc_parse/parser/stmt.rs b/src/librustc_parse/parser/stmt.rs index 943b6ecc825..42d85e96aef 100644 --- a/src/librustc_parse/parser/stmt.rs +++ b/src/librustc_parse/parser/stmt.rs @@ -7,15 +7,14 @@ use crate::maybe_whole; use crate::DirectoryOwnership; use rustc_errors::{PResult, Applicability}; -use syntax::ThinVec; use syntax::ptr::P; use syntax::ast; use syntax::ast::{DUMMY_NODE_ID, Stmt, StmtKind, Local, Block, BlockCheckMode, Expr, ExprKind}; -use syntax::ast::{Attribute, AttrStyle, VisibilityKind, MacStmtStyle, Mac}; +use syntax::ast::{AttrVec, Attribute, AttrStyle, VisibilityKind, MacStmtStyle, Mac}; use syntax::util::classify; use syntax::token; use syntax_pos::source_map::{respan, Span}; -use syntax_pos::symbol::{kw, sym}; +use syntax_pos::symbol::{kw, sym, Symbol}; use std::mem; @@ -23,15 +22,11 @@ impl<'a> Parser<'a> { /// Parses 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| { + Ok(self.parse_stmt_without_recovery(true).unwrap_or_else(|mut e| { e.emit(); self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore); None - }) + })) } fn parse_stmt_without_recovery( @@ -43,171 +38,195 @@ impl<'a> Parser<'a> { let attrs = self.parse_outer_attributes()?; let lo = self.token.span; - Ok(Some(if self.eat_keyword(kw::Let) { - Stmt { - id: DUMMY_NODE_ID, - kind: 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: DUMMY_NODE_ID, - kind: StmtKind::Item(macro_def), - span: lo.to(self.prev_span), - } + if self.eat_keyword(kw::Let) { + return self.parse_local_mk(lo, attrs.into()).map(Some) + } + if self.is_kw_followed_by_ident(kw::Mut) { + return self.recover_stmt_local(lo, attrs.into(), "missing keyword", "let mut"); + } + if self.is_kw_followed_by_ident(kw::Auto) { + self.bump(); // `auto` + let msg = "write `let` instead of `auto` to introduce a new variable"; + return self.recover_stmt_local(lo, attrs.into(), msg, "let"); + } + if self.is_kw_followed_by_ident(sym::var) { + self.bump(); // `var` + let msg = "write `let` instead of `var` to introduce a new variable"; + return self.recover_stmt_local(lo, attrs.into(), msg, "let"); + } + + let mac_vis = respan(lo, VisibilityKind::Inherited); + if let Some(macro_def) = self.eat_macro_def(&attrs, &mac_vis, lo)? { + return Ok(Some(self.mk_stmt(lo.to(self.prev_span), StmtKind::Item(macro_def)))); + } + // 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() { + if self.token.is_path_start() + && !self.token.is_qpath_start() + && !self.is_union_item() // `union::b::c` - path, `union U { ... }` - not a path. + && !self.is_crate_vis() // `crate::b::c` - path, `crate struct S;` - not a path. + && !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: DUMMY_NODE_ID, - kind: StmtKind::Expr(expr), - span: lo.to(self.prev_span), - })); + if self.eat(&token::Not) { + return self.parse_stmt_mac(lo, attrs.into(), path, macro_legacy_warnings); } - let args = self.parse_mac_args()?; - let delim = args.delim(); - let hi = self.prev_span; - - let style = if delim == token::Brace { - MacStmtStyle::Braces + let expr = if self.check(&token::OpenDelim(token::Brace)) { + self.parse_struct_expr(lo, path, AttrVec::new())? } else { - MacStmtStyle::NoBraces + let hi = self.prev_span; + self.mk_expr(lo.to(hi), ExprKind::Path(None, path), AttrVec::new()) }; - let mac = Mac { - path, - args, - prior_type_ascription: self.last_type_ascription, - }; - let kind = if delim == token::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(lo.to(hi), 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: DUMMY_NODE_ID, - span: lo.to(hi), - kind, + 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(self.mk_stmt(lo.to(self.prev_span), StmtKind::Expr(expr)))); + } + + // 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; + + if let Some(item) = item { + return Ok(Some(self.mk_stmt(lo.to(item.span), StmtKind::Item(item)))); + } + + // Do not attempt to parse an expression if we're done here. + if self.token == token::Semi { + self.error_outer_attrs(&attrs); + self.bump(); + let mut last_semi = lo; + while self.token == token::Semi { + last_semi = self.token.span; + self.bump(); } + // We are encoding a string of semicolons as an an empty tuple that spans + // the excess semicolons to preserve this info until the lint stage. + let kind = StmtKind::Semi(self.mk_expr( + lo.to(last_semi), + ExprKind::Tup(Vec::new()), + AttrVec::new() + )); + return Ok(Some(self.mk_stmt(lo.to(last_semi), kind))); + } + + if self.token == token::CloseDelim(token::Brace) { + self.error_outer_attrs(&attrs); + return Ok(None); + } + + // Remainder are line-expr stmts. + let e = self.parse_expr_res(Restrictions::STMT_EXPR, Some(attrs.into()))?; + Ok(Some(self.mk_stmt(lo.to(e.span), StmtKind::Expr(e)))) + } + + /// Parses a statement macro `mac!(args)` provided a `path` representing `mac`. + /// At this point, the `!` token after the path has already been eaten. + fn parse_stmt_mac( + &mut self, + lo: Span, + attrs: AttrVec, + path: ast::Path, + legacy_warnings: bool, + ) -> PResult<'a, Option<Stmt>> { + let args = self.parse_mac_args()?; + let delim = args.delim(); + let hi = self.prev_span; + + let style = if delim == token::Brace { + MacStmtStyle::Braces } 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: DUMMY_NODE_ID, - span: lo.to(i.span), - kind: 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(); - let mut last_semi = lo; - while self.token == token::Semi { - last_semi = self.token.span; - self.bump(); - } - // We are encoding a string of semicolons as an - // an empty tuple that spans the excess semicolons - // to preserve this info until the lint stage - return Ok(Some(Stmt { - id: DUMMY_NODE_ID, - span: lo.to(last_semi), - kind: StmtKind::Semi(self.mk_expr(lo.to(last_semi), - ExprKind::Tup(Vec::new()), - ThinVec::new() - )), - })); - } + MacStmtStyle::NoBraces + }; - if self.token == token::CloseDelim(token::Brace) { - unused_attrs(&attrs, self); - return Ok(None); - } + let mac = Mac { + path, + args, + prior_type_ascription: self.last_type_ascription, + }; - // Remainder are line-expr stmts. - let e = self.parse_expr_res( - Restrictions::STMT_EXPR, Some(attrs.into()))?; - Stmt { - id: DUMMY_NODE_ID, - span: lo.to(e.span), - kind: StmtKind::Expr(e), - } - } + let kind = if delim == token::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 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))) + } else { + // Since none of the above applied, this is an expression statement macro. + let e = self.mk_expr(lo.to(hi), ExprKind::Mac(mac), AttrVec::new()); + let e = self.maybe_recover_from_bad_qpath(e, true)?; + let e = self.parse_dot_or_call_expr_with(e, lo, attrs)?; + let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?; + StmtKind::Expr(e) + }; + Ok(Some(self.mk_stmt(lo.to(hi), kind))) + } + + /// Error on outer attributes in this context. + /// Also error if the previous token was a doc comment. + fn error_outer_attrs(&self, attrs: &[Attribute]) { + if !attrs.is_empty() { + if self.prev_token_kind == PrevTokenKind::DocComment { + self.span_fatal_err(self.prev_span, Error::UselessDocComment).emit(); + } else if attrs.iter().any(|a| a.style == AttrStyle::Outer) { + self.span_err(self.token.span, "expected statement after outer attribute"); + } + } + } + + fn is_kw_followed_by_ident(&self, kw: Symbol) -> bool { + self.token.is_keyword(kw) + && self.look_ahead(1, |t| t.is_ident() && !t.is_reserved_ident()) + } + + fn recover_stmt_local( + &mut self, + lo: Span, + attrs: AttrVec, + msg: &str, + sugg: &str, + ) -> PResult<'a, Option<Stmt>> { + let stmt = self.parse_local_mk(lo, attrs)?; + self.struct_span_err(lo, "invalid variable declaration") + .span_suggestion(lo, msg, sugg.to_string(), Applicability::MachineApplicable) + .emit(); + Ok(Some(stmt)) + } + + fn parse_local_mk(&mut self, lo: Span, attrs: AttrVec) -> PResult<'a, Stmt> { + let local = self.parse_local(attrs.into())?; + Ok(self.mk_stmt(lo.to(self.prev_span), StmtKind::Local(local))) } /// Parses a local variable declaration. - fn parse_local(&mut self, attrs: ThinVec<Attribute>) -> PResult<'a, P<Local>> { + fn parse_local(&mut self, attrs: AttrVec) -> PResult<'a, P<Local>> { let lo = self.prev_span; let pat = self.parse_top_pat(GateOr::Yes)?; @@ -307,70 +326,58 @@ impl<'a> Parser<'a> { 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, - ); - } + return self.error_block_no_opening_brace(); + } - // 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, - ); - } + self.parse_block_tail(lo, BlockCheckMode::Default) + } + + fn error_block_no_opening_brace<T>(&mut self) -> PResult<'a, T> { + 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; + + // 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); } - Err(mut e) => { - self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore); - e.cancel(); + let stmt_span = if self.eat(&token::Semi) { + // Expand the span to include the semicolon. + stmt.span.with_hi(self.prev_span.hi()) + } else { + stmt.span + }; + 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, + ); } - _ => () } - e.span_label(sp, "expected `{`"); - return Err(e); + Err(mut e) => { + self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore); + e.cancel(); + } + _ => {} } - - self.parse_block_tail(lo, BlockCheckMode::Default) + e.span_label(sp, "expected `{`"); + return Err(e); } /// Parses a block. Inner attributes are allowed. @@ -402,11 +409,10 @@ impl<'a> Parser<'a> { self.maybe_annotate_with_ascription(&mut err, false); err.emit(); self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore); - Some(Stmt { - id: DUMMY_NODE_ID, - kind: StmtKind::Expr(self.mk_expr_err(self.token.span)), - span: self.token.span, - }) + Some(self.mk_stmt( + self.token.span, + StmtKind::Expr(self.mk_expr_err(self.token.span)), + )) } Ok(stmt) => stmt, }; @@ -478,4 +484,8 @@ impl<'a> Parser<'a> { "this was erroneously allowed and will become a hard error in a future release" }).emit(); } + + fn mk_stmt(&self, span: Span, kind: StmtKind) -> Stmt { + Stmt { id: DUMMY_NODE_ID, kind, span } + } } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index cf54fd2887a..c1458236788 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -338,7 +338,7 @@ pub enum GenericParamKind { pub struct GenericParam { pub id: NodeId, pub ident: Ident, - pub attrs: ThinVec<Attribute>, + pub attrs: AttrVec, pub bounds: GenericBounds, pub is_placeholder: bool, pub kind: GenericParamKind, @@ -599,7 +599,7 @@ pub struct FieldPat { /// The pattern the field is destructured to pub pat: P<Pat>, pub is_shorthand: bool, - pub attrs: ThinVec<Attribute>, + pub attrs: AttrVec, pub id: NodeId, pub span: Span, pub is_placeholder: bool, @@ -911,7 +911,7 @@ pub enum StmtKind { /// Expr with a trailing semi-colon. Semi(P<Expr>), /// Macro. - Mac(P<(Mac, MacStmtStyle, ThinVec<Attribute>)>), + Mac(P<(Mac, MacStmtStyle, AttrVec)>), } #[derive(Clone, Copy, PartialEq, RustcEncodable, RustcDecodable, Debug)] @@ -936,7 +936,7 @@ pub struct Local { /// Initializer expression to set the value, if any. pub init: Option<P<Expr>>, pub span: Span, - pub attrs: ThinVec<Attribute>, + pub attrs: AttrVec, } /// An arm of a 'match'. @@ -966,7 +966,7 @@ pub struct Arm { /// Access of a named (e.g., `obj.foo`) or unnamed (e.g., `obj.0`) struct field. #[derive(Clone, RustcEncodable, RustcDecodable, Debug)] pub struct Field { - pub attrs: ThinVec<Attribute>, + pub attrs: AttrVec, pub id: NodeId, pub span: Span, pub ident: Ident, @@ -1004,7 +1004,7 @@ pub struct Expr { pub id: NodeId, pub kind: ExprKind, pub span: Span, - pub attrs: ThinVec<Attribute>, + pub attrs: AttrVec, } // `Expr` is used a lot. Make sure it doesn't unintentionally get bigger. @@ -1961,7 +1961,7 @@ pub struct InlineAsm { /// E.g., `bar: usize` as in `fn foo(bar: usize)`. #[derive(Clone, RustcEncodable, RustcDecodable, Debug)] pub struct Param { - pub attrs: ThinVec<Attribute>, + pub attrs: AttrVec, pub ty: P<Ty>, pub pat: P<Pat>, pub id: NodeId, @@ -2014,7 +2014,7 @@ impl Param { } /// Builds a `Param` object from `ExplicitSelf`. - pub fn from_self(attrs: ThinVec<Attribute>, eself: ExplicitSelf, eself_ident: Ident) -> Param { + pub fn from_self(attrs: AttrVec, eself: ExplicitSelf, eself_ident: Ident) -> Param { let span = eself.span.to(eself_ident.span); let infer_ty = P(Ty { id: DUMMY_NODE_ID, @@ -2332,6 +2332,9 @@ pub struct AttrItem { pub args: MacArgs, } +/// A list of attributes. +pub type AttrVec = ThinVec<Attribute>; + /// Metadata associated with an item. #[derive(Clone, RustcEncodable, RustcDecodable, Debug)] pub struct Attribute { diff --git a/src/libsyntax/attr/mod.rs b/src/libsyntax/attr/mod.rs index 13a9ed5f215..ae6d50ba083 100644 --- a/src/libsyntax/attr/mod.rs +++ b/src/libsyntax/attr/mod.rs @@ -9,7 +9,7 @@ pub use StabilityLevel::*; pub use crate::ast::Attribute; use crate::ast; -use crate::ast::{AttrItem, AttrId, AttrKind, AttrStyle, Name, Ident, Path, PathSegment}; +use crate::ast::{AttrVec, AttrItem, AttrId, AttrKind, AttrStyle, Name, Ident, Path, PathSegment}; use crate::ast::{MacArgs, MacDelimiter, MetaItem, MetaItemKind, NestedMetaItem}; use crate::ast::{Lit, LitKind, Expr, Item, Local, Stmt, StmtKind, GenericParam}; use crate::mut_visit::visit_clobber; @@ -17,7 +17,6 @@ use crate::source_map::{BytePos, Spanned}; use crate::token::{self, Token}; use crate::ptr::P; use crate::symbol::{sym, Symbol}; -use crate::ThinVec; use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint}; use crate::GLOBALS; @@ -665,7 +664,7 @@ impl HasAttrs for Vec<Attribute> { } } -impl HasAttrs for ThinVec<Attribute> { +impl HasAttrs for AttrVec { fn attrs(&self) -> &[Attribute] { self } diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 36173801eae..30fd23ea909 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -24,7 +24,6 @@ pub use errors; use rustc_data_structures::sync::Lock; use rustc_index::bit_set::GrowableBitSet; -pub use rustc_data_structures::thin_vec::ThinVec; use ast::AttrId; use syntax_pos::edition::Edition; diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs index 2a6cff5971c..1d27f70f5a5 100644 --- a/src/libsyntax/mut_visit.rs +++ b/src/libsyntax/mut_visit.rs @@ -11,7 +11,6 @@ use crate::ast::*; use crate::source_map::{Spanned, respan}; use crate::token::{self, Token}; use crate::ptr::P; -use crate::ThinVec; use crate::tokenstream::*; use crate::util::map_in_place::MapInPlace; @@ -337,7 +336,7 @@ pub fn visit_attrs<T: MutVisitor>(attrs: &mut Vec<Attribute>, vis: &mut T) { } // No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. -pub fn visit_thin_attrs<T: MutVisitor>(attrs: &mut ThinVec<Attribute>, vis: &mut T) { +pub fn visit_thin_attrs<T: MutVisitor>(attrs: &mut AttrVec, vis: &mut T) { for attr in attrs.iter_mut() { vis.visit_attribute(attr); } diff --git a/src/libsyntax_expand/base.rs b/src/libsyntax_expand/base.rs index 75066a006bf..2ad327e872e 100644 --- a/src/libsyntax_expand/base.rs +++ b/src/libsyntax_expand/base.rs @@ -9,7 +9,6 @@ use syntax::mut_visit::{self, MutVisitor}; use syntax::ptr::P; use syntax::sess::ParseSess; use syntax::symbol::{kw, sym, Ident, Symbol}; -use syntax::ThinVec; use syntax::token; use syntax::tokenstream::{self, TokenStream}; use syntax::visit::Visitor; @@ -552,7 +551,7 @@ impl DummyResult { id: ast::DUMMY_NODE_ID, kind: if is_error { ast::ExprKind::Err } else { ast::ExprKind::Tup(Vec::new()) }, span: sp, - attrs: ThinVec::new(), + attrs: ast::AttrVec::new(), }) } diff --git a/src/libsyntax_expand/build.rs b/src/libsyntax_expand/build.rs index 3d082101c41..4c539cad111 100644 --- a/src/libsyntax_expand/build.rs +++ b/src/libsyntax_expand/build.rs @@ -1,11 +1,10 @@ use crate::base::ExtCtxt; -use syntax::ast::{self, Ident, Expr, BlockCheckMode, UnOp, PatKind}; +use syntax::ast::{self, AttrVec, Ident, Expr, BlockCheckMode, UnOp, PatKind}; use syntax::attr; use syntax::source_map::{respan, Spanned}; use syntax::ptr::P; use syntax::symbol::{kw, sym, Symbol}; -use syntax::ThinVec; use syntax_pos::{Pos, Span}; @@ -81,7 +80,7 @@ impl<'a> ExtCtxt<'a> { id: ast::DUMMY_NODE_ID, kind, span, - attrs: ThinVec::new(), + attrs: AttrVec::new(), }) } } @@ -190,7 +189,7 @@ impl<'a> ExtCtxt<'a> { init: Some(ex), id: ast::DUMMY_NODE_ID, span: sp, - attrs: ThinVec::new(), + attrs: AttrVec::new(), }); ast::Stmt { id: ast::DUMMY_NODE_ID, @@ -207,7 +206,7 @@ impl<'a> ExtCtxt<'a> { init: None, id: ast::DUMMY_NODE_ID, span, - attrs: ThinVec::new(), + attrs: AttrVec::new(), }); ast::Stmt { id: ast::DUMMY_NODE_ID, @@ -245,7 +244,7 @@ impl<'a> ExtCtxt<'a> { id: ast::DUMMY_NODE_ID, kind, span, - attrs: ThinVec::new(), + attrs: AttrVec::new(), }) } @@ -304,7 +303,7 @@ impl<'a> ExtCtxt<'a> { expr: e, span, is_shorthand: false, - attrs: ThinVec::new(), + attrs: AttrVec::new(), id: ast::DUMMY_NODE_ID, is_placeholder: false, } @@ -549,7 +548,7 @@ impl<'a> ExtCtxt<'a> { pub fn param(&self, span: Span, ident: ast::Ident, ty: P<ast::Ty>) -> ast::Param { let arg_pat = self.pat_ident(span, ident); ast::Param { - attrs: ThinVec::default(), + attrs: AttrVec::default(), id: ast::DUMMY_NODE_ID, pat: arg_pat, span, diff --git a/src/libsyntax_expand/mbe/macro_rules.rs b/src/libsyntax_expand/mbe/macro_rules.rs index 2dd15872a9f..107fe388ed0 100644 --- a/src/libsyntax_expand/mbe/macro_rules.rs +++ b/src/libsyntax_expand/mbe/macro_rules.rs @@ -63,6 +63,30 @@ crate fn annotate_err_with_kind( }; } +/// Instead of e.g. `vec![a, b, c]` in a pattern context, suggest `[a, b, c]`. +fn suggest_slice_pat(e: &mut DiagnosticBuilder<'_>, site_span: Span, parser: &Parser<'_>) { + let mut suggestion = None; + if let Ok(code) = parser.sess.source_map().span_to_snippet(site_span) { + if let Some(bang) = code.find('!') { + suggestion = Some(code[bang + 1..].to_string()); + } + } + if let Some(suggestion) = suggestion { + e.span_suggestion( + site_span, + "use a slice pattern here instead", + suggestion, + Applicability::MachineApplicable, + ); + } else { + e.span_label(site_span, "use a slice pattern here instead"); + } + e.help( + "for more information, see https://doc.rust-lang.org/edition-guide/\ + rust-2018/slice-patterns.html" + ); +} + impl<'a> ParserAnyMacro<'a> { crate fn make(mut self: Box<ParserAnyMacro<'a>>, kind: AstFragmentKind) -> AstFragment { let ParserAnyMacro { site_span, macro_ident, ref mut parser, arm_span } = *self; @@ -92,27 +116,7 @@ impl<'a> ParserAnyMacro<'a> { } match kind { AstFragmentKind::Pat if macro_ident.name == sym::vec => { - let mut suggestion = None; - if let Ok(code) = parser.sess.source_map().span_to_snippet(site_span) { - if let Some(bang) = code.find('!') { - suggestion = Some(code[bang + 1..].to_string()); - } - } - if let Some(suggestion) = suggestion { - e.span_suggestion( - site_span, - "use a slice pattern here instead", - suggestion, - Applicability::MachineApplicable, - ); - } else { - e.span_label( - site_span, - "use a slice pattern here instead", - ); - } - e.help("for more information, see https://doc.rust-lang.org/edition-guide/\ - rust-2018/slice-patterns.html"); + suggest_slice_pat(&mut e, site_span, parser); } _ => annotate_err_with_kind(&mut e, kind, site_span), }; diff --git a/src/libsyntax_expand/placeholders.rs b/src/libsyntax_expand/placeholders.rs index 22e99baae5b..4298e0e74b6 100644 --- a/src/libsyntax_expand/placeholders.rs +++ b/src/libsyntax_expand/placeholders.rs @@ -5,7 +5,6 @@ use syntax::ast; use syntax::source_map::{DUMMY_SP, dummy_spanned}; use syntax::mut_visit::*; use syntax::ptr::P; -use syntax::ThinVec; use smallvec::{smallvec, SmallVec}; @@ -28,7 +27,7 @@ pub fn placeholder(kind: AstFragmentKind, id: ast::NodeId, vis: Option<ast::Visi let span = DUMMY_SP; let expr_placeholder = || P(ast::Expr { id, span, - attrs: ThinVec::new(), + attrs: ast::AttrVec::new(), kind: ast::ExprKind::Mac(mac_placeholder()), }); let ty = || P(ast::Ty { @@ -75,7 +74,7 @@ pub fn placeholder(kind: AstFragmentKind, id: ast::NodeId, vis: Option<ast::Visi id, span, kind: ast::TyKind::Mac(mac_placeholder()), })), AstFragmentKind::Stmts => AstFragment::Stmts(smallvec![{ - let mac = P((mac_placeholder(), ast::MacStmtStyle::Braces, ThinVec::new())); + let mac = P((mac_placeholder(), ast::MacStmtStyle::Braces, ast::AttrVec::new())); ast::Stmt { id, span, kind: ast::StmtKind::Mac(mac) } }]), AstFragmentKind::Arms => AstFragment::Arms(smallvec![ diff --git a/src/libsyntax_ext/asm.rs b/src/libsyntax_ext/asm.rs index bd345a9a7da..5ec24b7a7ac 100644 --- a/src/libsyntax_ext/asm.rs +++ b/src/libsyntax_ext/asm.rs @@ -3,7 +3,6 @@ use State::*; use errors::{DiagnosticBuilder, PResult}; -use rustc_data_structures::thin_vec::ThinVec; use rustc_parse::parser::Parser; use syntax_expand::base::*; use syntax_pos::Span; @@ -63,7 +62,7 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt<'_>, id: ast::DUMMY_NODE_ID, kind: ast::ExprKind::InlineAsm(P(inline_asm)), span: cx.with_def_site_ctxt(sp), - attrs: ThinVec::new(), + attrs: ast::AttrVec::new(), })) } diff --git a/src/libsyntax_ext/concat_idents.rs b/src/libsyntax_ext/concat_idents.rs index 8a1bc56cf1c..7d8bc8b87bc 100644 --- a/src/libsyntax_ext/concat_idents.rs +++ b/src/libsyntax_ext/concat_idents.rs @@ -1,5 +1,3 @@ -use rustc_data_structures::thin_vec::ThinVec; - use syntax::ast; use syntax_expand::base::{self, *}; use syntax::token::{self, Token}; @@ -49,7 +47,7 @@ pub fn expand_concat_idents<'cx>(cx: &'cx mut ExtCtxt<'_>, id: ast::DUMMY_NODE_ID, kind: ast::ExprKind::Path(None, ast::Path::from_ident(self.ident)), span: self.ident.span, - attrs: ThinVec::new(), + attrs: ast::AttrVec::new(), })) } diff --git a/src/libsyntax_ext/deriving/debug.rs b/src/libsyntax_ext/deriving/debug.rs index 35298211e4d..132ce76e1bb 100644 --- a/src/libsyntax_ext/deriving/debug.rs +++ b/src/libsyntax_ext/deriving/debug.rs @@ -2,8 +2,6 @@ use crate::deriving::path_std; use crate::deriving::generic::*; use crate::deriving::generic::ty::*; -use rustc_data_structures::thin_vec::ThinVec; - use syntax::ast::{self, Ident}; use syntax::ast::{Expr, MetaItem}; use syntax_expand::base::{Annotatable, ExtCtxt}; @@ -127,7 +125,7 @@ fn stmt_let_undescore(cx: &mut ExtCtxt<'_>, sp: Span, expr: P<ast::Expr>) -> ast init: Some(expr), id: ast::DUMMY_NODE_ID, span: sp, - attrs: ThinVec::new(), + attrs: ast::AttrVec::new(), }); ast::Stmt { id: ast::DUMMY_NODE_ID, diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs index b7707bfb8e5..5158a5b3da2 100644 --- a/src/libsyntax_ext/deriving/generic/mod.rs +++ b/src/libsyntax_ext/deriving/generic/mod.rs @@ -181,7 +181,6 @@ use std::cell::RefCell; use std::iter; use std::vec; -use rustc_data_structures::thin_vec::ThinVec; use syntax::ast::{self, BinOpKind, EnumDef, Expr, Generics, Ident, PatKind}; use syntax::ast::{VariantData, GenericParamKind, GenericArg}; use syntax::attr; @@ -919,7 +918,7 @@ impl<'a> MethodDef<'a> { let args = { let self_args = explicit_self.map(|explicit_self| { let ident = Ident::with_dummy_span(kw::SelfLower).with_span_pos(trait_.span); - ast::Param::from_self(ThinVec::default(), explicit_self, ident) + ast::Param::from_self(ast::AttrVec::default(), explicit_self, ident) }); let nonself_args = arg_types.into_iter() .map(|(name, ty)| cx.param(trait_.span, name, ty)); @@ -1608,7 +1607,7 @@ impl<'a> TraitDef<'a> { ast::FieldPat { ident: ident.unwrap(), is_shorthand: false, - attrs: ThinVec::new(), + attrs: ast::AttrVec::new(), id: ast::DUMMY_NODE_ID, span: pat.span.with_ctxt(self.span.ctxt()), pat, diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index ae34064c926..d3e80fc4fdd 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -773,6 +773,7 @@ symbols! { usize, v1, val, + var, vec, Vec, vis, diff --git a/src/test/pretty/ast-stmt-expr-attr.rs b/src/test/pretty/ast-stmt-expr-attr.rs new file mode 100644 index 00000000000..5b975424512 --- /dev/null +++ b/src/test/pretty/ast-stmt-expr-attr.rs @@ -0,0 +1,175 @@ +// pp-exact + +fn main() { } + +#[cfg(FALSE)] +fn syntax() { + let _ = #[attr] box 0; + let _ = #[attr] [#![attr] ]; + let _ = #[attr] [#![attr] 0]; + let _ = #[attr] [#![attr] 0; 0]; + let _ = #[attr] [#![attr] 0, 0, 0]; + let _ = #[attr] foo(); + let _ = #[attr] x.foo(); + let _ = #[attr] (#![attr] ); + let _ = #[attr] (#![attr] #[attr] 0,); + let _ = #[attr] (#![attr] #[attr] 0, 0); + let _ = #[attr] 0 + #[attr] 0; + let _ = #[attr] 0 / #[attr] 0; + let _ = #[attr] 0 & #[attr] 0; + let _ = #[attr] 0 % #[attr] 0; + let _ = #[attr] (0 + 0); + let _ = #[attr] !0; + let _ = #[attr] -0; + let _ = #[attr] false; + let _ = #[attr] 0; + let _ = #[attr] 'c'; + let _ = #[attr] x as Y; + let _ = #[attr] (x as Y); + let _ = + #[attr] while true { + #![attr] + }; + let _ = + #[attr] while let Some(false) = true { + #![attr] + }; + let _ = + #[attr] for x in y { + #![attr] + }; + let _ = + #[attr] loop { + #![attr] + }; + let _ = + #[attr] match true { + #![attr] + #[attr] + _ => false, + }; + let _ = #[attr] || #[attr] foo; + let _ = #[attr] move || #[attr] foo; + let _ = + #[attr] || + #[attr] { + #![attr] + foo + }; + let _ = + #[attr] move || + #[attr] { + #![attr] + foo + }; + let _ = + #[attr] || + { + #![attr] + foo + }; + let _ = + #[attr] move || + { + #![attr] + foo + }; + let _ = + #[attr] { + #![attr] + }; + let _ = + #[attr] { + #![attr] + let _ = (); + }; + let _ = + #[attr] { + #![attr] + let _ = (); + foo + }; + let _ = #[attr] x = y; + let _ = #[attr] (x = y); + let _ = #[attr] x += y; + let _ = #[attr] (x += y); + let _ = #[attr] foo.bar; + let _ = (#[attr] foo).bar; + let _ = #[attr] foo.0; + let _ = (#[attr] foo).0; + let _ = #[attr] foo[bar]; + let _ = (#[attr] foo)[bar]; + let _ = #[attr] 0..#[attr] 0; + let _ = #[attr] 0..; + let _ = #[attr] (0..0); + let _ = #[attr] (0..); + let _ = #[attr] (..0); + let _ = #[attr] (..); + let _ = #[attr] foo::bar::baz; + let _ = #[attr] &0; + let _ = #[attr] &mut 0; + let _ = #[attr] &#[attr] 0; + let _ = #[attr] &mut #[attr] 0; + let _ = #[attr] break ; + let _ = #[attr] continue ; + let _ = #[attr] return; + let _ = #[attr] foo!(); + let _ = #[attr] foo!(# ! [attr]); + let _ = #[attr] foo![]; + let _ = #[attr] foo![# ! [attr]]; + let _ = #[attr] foo! { }; + let _ = #[attr] foo! { # ! [attr] }; + let _ = #[attr] Foo{#![attr] bar: baz,}; + let _ = #[attr] Foo{#![attr] ..foo}; + let _ = #[attr] Foo{#![attr] bar: baz, ..foo}; + let _ = #[attr] (#![attr] 0); + + { + #[attr] + let _ = 0; + + #[attr] + 0; + + #[attr] + foo!(); + + #[attr] + foo! { } + + #[attr] + foo![]; + } + + { + #[attr] + let _ = 0; + } + { + + #[attr] + 0 + } + { + + #[attr] + { + #![attr] + } + } + { + + #[attr] + foo!() + } + { + + #[attr] + foo![] + } + { + + #[attr] + foo! { } + } +} diff --git a/src/test/ui-fulldeps/ast_stmt_expr_attr.rs b/src/test/ui-fulldeps/ast_stmt_expr_attr.rs deleted file mode 100644 index d6d49df63ef..00000000000 --- a/src/test/ui-fulldeps/ast_stmt_expr_attr.rs +++ /dev/null @@ -1,311 +0,0 @@ -// run-pass - -#![allow(unused_imports)] -// ignore-cross-compile - -#![feature(rustc_private)] - -extern crate syntax; -extern crate syntax_expand; -extern crate rustc_parse; -extern crate rustc_errors; - -use rustc_errors::PResult; -use rustc_parse::parser::attr::*; -use rustc_parse::new_parser_from_source_str; -use rustc_parse::parser::Parser; -use syntax::ast::*; -use syntax::attr::*; -use syntax::ast; -use syntax::sess::ParseSess; -use syntax::source_map::{FilePathMapping, FileName}; -use syntax::ptr::P; -use syntax::print::pprust; -use syntax::token; -use std::fmt; - -// Copied out of syntax::util::parser_testing - -pub fn string_to_parser<'a>(ps: &'a ParseSess, source_str: String) -> Parser<'a> { - new_parser_from_source_str(ps, FileName::Custom(source_str.clone()), source_str) -} - -fn with_error_checking_parse<'a, T, F>(s: String, ps: &'a ParseSess, f: F) -> PResult<'a, T> where - F: FnOnce(&mut Parser<'a>) -> PResult<'a, T>, -{ - let mut p = string_to_parser(&ps, s); - let x = f(&mut p); - - if ps.span_diagnostic.has_errors() || p.token != token::Eof { - if let Err(mut e) = x { - e.cancel(); - } - return Err(p.fatal("parse error")); - } - - x -} - -fn expr<'a>(s: &str, ps: &'a ParseSess) -> PResult<'a, P<ast::Expr>> { - with_error_checking_parse(s.to_string(), ps, |p| { - p.parse_expr() - }) -} - -fn stmt<'a>(s: &str, ps: &'a ParseSess) -> PResult<'a, ast::Stmt> { - with_error_checking_parse(s.to_string(), ps, |p| { - p.parse_stmt().map(|s| s.unwrap()) - }) -} - -fn attr<'a>(s: &str, ps: &'a ParseSess) -> PResult<'a, ast::Attribute> { - with_error_checking_parse(s.to_string(), ps, |p| { - p.parse_attribute(true) - }) -} - -fn str_compare<T, F: Fn(&T) -> String>(e: &str, expected: &[T], actual: &[T], f: F) { - let expected: Vec<_> = expected.iter().map(|e| f(e)).collect(); - let actual: Vec<_> = actual.iter().map(|e| f(e)).collect(); - - if expected != actual { - panic!("parsed `{}` as {:?}, expected {:?}", e, actual, expected); - } -} - -fn sess() -> ParseSess { - ParseSess::new(FilePathMapping::empty()) -} - -fn check_expr_attrs(es: &str, expected: &[&str]) { - let ps = sess(); - let e = expr(es, &ps).expect("parse error"); - let actual = &e.attrs; - str_compare(es, - &expected.iter().map(|r| attr(r, &ps).unwrap()).collect::<Vec<_>>(), - &actual, - pprust::attribute_to_string); -} - -fn check_stmt_attrs(es: &str, expected: &[&str]) { - let ps = sess(); - let e = stmt(es, &ps).expect("parse error"); - let actual = e.kind.attrs(); - str_compare(es, - &expected.iter().map(|r| attr(r, &ps).unwrap()).collect::<Vec<_>>(), - actual, - pprust::attribute_to_string); -} - -fn reject_expr_parse(es: &str) { - let ps = sess(); - match expr(es, &ps) { - Ok(_) => panic!("parser did not reject `{}`", es), - Err(mut e) => e.cancel(), - }; -} - -fn reject_stmt_parse(es: &str) { - let ps = sess(); - match stmt(es, &ps) { - Ok(_) => panic!("parser did not reject `{}`", es), - Err(mut e) => e.cancel(), - }; -} - -fn main() { - syntax::with_default_globals(|| run()); -} - -fn run() { - let both = &["#[attr]", "#![attr]"]; - let outer = &["#[attr]"]; - let none = &[]; - - check_expr_attrs("#[attr] box 0", outer); - reject_expr_parse("box #![attr] 0"); - - check_expr_attrs("#[attr] [#![attr]]", both); - check_expr_attrs("#[attr] [#![attr] 0]", both); - check_expr_attrs("#[attr] [#![attr] 0; 0]", both); - check_expr_attrs("#[attr] [#![attr] 0, 0, 0]", both); - reject_expr_parse("[#[attr]]"); - - check_expr_attrs("#[attr] foo()", outer); - check_expr_attrs("#[attr] x.foo()", outer); - reject_expr_parse("foo#[attr]()"); - reject_expr_parse("foo(#![attr])"); - reject_expr_parse("x.foo(#![attr])"); - reject_expr_parse("x.#[attr]foo()"); - reject_expr_parse("x.#![attr]foo()"); - - check_expr_attrs("#[attr] (#![attr])", both); - check_expr_attrs("#[attr] (#![attr] #[attr] 0,)", both); - check_expr_attrs("#[attr] (#![attr] #[attr] 0, 0)", both); - - check_expr_attrs("#[attr] 0 + #[attr] 0", none); - check_expr_attrs("#[attr] 0 / #[attr] 0", none); - check_expr_attrs("#[attr] 0 & #[attr] 0", none); - check_expr_attrs("#[attr] 0 % #[attr] 0", none); - check_expr_attrs("#[attr] (0 + 0)", outer); - reject_expr_parse("0 + #![attr] 0"); - - check_expr_attrs("#[attr] !0", outer); - check_expr_attrs("#[attr] -0", outer); - reject_expr_parse("!#![attr] 0"); - reject_expr_parse("-#![attr] 0"); - - check_expr_attrs("#[attr] false", outer); - check_expr_attrs("#[attr] 0", outer); - check_expr_attrs("#[attr] 'c'", outer); - - check_expr_attrs("#[attr] x as Y", none); - check_expr_attrs("#[attr] (x as Y)", outer); - reject_expr_parse("x #![attr] as Y"); - - reject_expr_parse("#[attr] if false {}"); - reject_expr_parse("if false #[attr] {}"); - reject_expr_parse("if false {#![attr]}"); - reject_expr_parse("if false {} #[attr] else {}"); - reject_expr_parse("if false {} else #[attr] {}"); - reject_expr_parse("if false {} else {#![attr]}"); - reject_expr_parse("if false {} else #[attr] if true {}"); - reject_expr_parse("if false {} else if true #[attr] {}"); - reject_expr_parse("if false {} else if true {#![attr]}"); - - reject_expr_parse("#[attr] if let Some(false) = false {}"); - reject_expr_parse("if let Some(false) = false #[attr] {}"); - reject_expr_parse("if let Some(false) = false {#![attr]}"); - reject_expr_parse("if let Some(false) = false {} #[attr] else {}"); - reject_expr_parse("if let Some(false) = false {} else #[attr] {}"); - reject_expr_parse("if let Some(false) = false {} else {#![attr]}"); - reject_expr_parse("if let Some(false) = false {} else #[attr] if let Some(false) = true {}"); - reject_expr_parse("if let Some(false) = false {} else if let Some(false) = true #[attr] {}"); - reject_expr_parse("if let Some(false) = false {} else if let Some(false) = true {#![attr]}"); - - check_expr_attrs("#[attr] while true {#![attr]}", both); - - check_expr_attrs("#[attr] while let Some(false) = true {#![attr]}", both); - - check_expr_attrs("#[attr] for x in y {#![attr]}", both); - - check_expr_attrs("#[attr] loop {#![attr]}", both); - - check_expr_attrs("#[attr] match true {#![attr] #[attr] _ => false}", both); - - check_expr_attrs("#[attr] || #[attr] foo", outer); - check_expr_attrs("#[attr] move || #[attr] foo", outer); - check_expr_attrs("#[attr] || #[attr] { #![attr] foo }", outer); - check_expr_attrs("#[attr] move || #[attr] { #![attr] foo }", outer); - check_expr_attrs("#[attr] || { #![attr] foo }", outer); - check_expr_attrs("#[attr] move || { #![attr] foo }", outer); - reject_expr_parse("|| #![attr] foo"); - reject_expr_parse("move || #![attr] foo"); - reject_expr_parse("|| #![attr] {foo}"); - reject_expr_parse("move || #![attr] {foo}"); - - check_expr_attrs("#[attr] { #![attr] }", both); - check_expr_attrs("#[attr] { #![attr] let _ = (); }", both); - check_expr_attrs("#[attr] { #![attr] let _ = (); foo }", both); - - check_expr_attrs("#[attr] x = y", none); - check_expr_attrs("#[attr] (x = y)", outer); - - check_expr_attrs("#[attr] x += y", none); - check_expr_attrs("#[attr] (x += y)", outer); - - check_expr_attrs("#[attr] foo.bar", outer); - check_expr_attrs("(#[attr] foo).bar", none); - - check_expr_attrs("#[attr] foo.0", outer); - check_expr_attrs("(#[attr] foo).0", none); - - check_expr_attrs("#[attr] foo[bar]", outer); - check_expr_attrs("(#[attr] foo)[bar]", none); - - check_expr_attrs("#[attr] 0..#[attr] 0", none); - check_expr_attrs("#[attr] 0..", none); - reject_expr_parse("#[attr] ..#[attr] 0"); - reject_expr_parse("#[attr] .."); - - check_expr_attrs("#[attr] (0..0)", outer); - check_expr_attrs("#[attr] (0..)", outer); - check_expr_attrs("#[attr] (..0)", outer); - check_expr_attrs("#[attr] (..)", outer); - - check_expr_attrs("#[attr] foo::bar::baz", outer); - - check_expr_attrs("#[attr] &0", outer); - check_expr_attrs("#[attr] &mut 0", outer); - check_expr_attrs("#[attr] & #[attr] 0", outer); - check_expr_attrs("#[attr] &mut #[attr] 0", outer); - reject_expr_parse("#[attr] &#![attr] 0"); - reject_expr_parse("#[attr] &mut #![attr] 0"); - - check_expr_attrs("#[attr] break", outer); - check_expr_attrs("#[attr] continue", outer); - check_expr_attrs("#[attr] return", outer); - - check_expr_attrs("#[attr] foo!()", outer); - check_expr_attrs("#[attr] foo!(#![attr])", outer); - check_expr_attrs("#[attr] foo![]", outer); - check_expr_attrs("#[attr] foo![#![attr]]", outer); - check_expr_attrs("#[attr] foo!{}", outer); - check_expr_attrs("#[attr] foo!{#![attr]}", outer); - - check_expr_attrs("#[attr] Foo { #![attr] bar: baz }", both); - check_expr_attrs("#[attr] Foo { #![attr] ..foo }", both); - check_expr_attrs("#[attr] Foo { #![attr] bar: baz, ..foo }", both); - - check_expr_attrs("#[attr] (#![attr] 0)", both); - - // Look at statements in their natural habitat... - check_expr_attrs("{ - #[attr] let _ = 0; - #[attr] 0; - #[attr] foo!(); - #[attr] foo!{} - #[attr] foo![]; - }", none); - - check_stmt_attrs("#[attr] let _ = 0", outer); - check_stmt_attrs("#[attr] 0", outer); - check_stmt_attrs("#[attr] {#![attr]}", both); - check_stmt_attrs("#[attr] foo!()", outer); - check_stmt_attrs("#[attr] foo![]", outer); - check_stmt_attrs("#[attr] foo!{}", outer); - - reject_stmt_parse("#[attr] #![attr] let _ = 0"); - reject_stmt_parse("#[attr] #![attr] 0"); - reject_stmt_parse("#[attr] #![attr] foo!()"); - reject_stmt_parse("#[attr] #![attr] foo![]"); - reject_stmt_parse("#[attr] #![attr] foo!{}"); - - // FIXME: Allow attributes in pattern constexprs? - // note: requires parens in patterns to allow disambiguation - - reject_expr_parse("match 0 { - 0..=#[attr] 10 => () - }"); - reject_expr_parse("match 0 { - 0..=#[attr] -10 => () - }"); - reject_expr_parse("match 0 { - 0..=-#[attr] 10 => () - }"); - reject_expr_parse("match 0 { - 0..=#[attr] FOO => () - }"); - - // make sure we don't catch this bug again... - reject_expr_parse("{ - fn foo() { - #[attr]; - } - }"); - reject_expr_parse("{ - fn foo() { - #[attr] - } - }"); -} diff --git a/src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.rs b/src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.rs index 687479bad3f..44421b077fa 100644 --- a/src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.rs +++ b/src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.rs @@ -1,17 +1,25 @@ +fn main() {} + fn test_and() { let a = true; let b = false; - if a and b { - //~^ ERROR expected `{`, found `and` + + let _ = a and b; //~ ERROR `and` is not a logical operator + + if a and b { //~ ERROR `and` is not a logical operator println!("both"); } + + let _recovery_witness: () = 0; //~ ERROR mismatched types } fn test_or() { let a = true; let b = false; - if a or b { - //~^ ERROR expected `{`, found `or` + + let _ = a or b; //~ ERROR `or` is not a logical operator + + if a or b { //~ ERROR `or` is not a logical operator println!("both"); } } @@ -19,8 +27,7 @@ fn test_or() { fn test_and_par() { let a = true; let b = false; - if (a and b) { - //~^ ERROR expected one of `!`, `)`, `,`, `.`, `::`, `?`, `{`, or an operator, found `and` + if (a and b) { //~ ERROR `and` is not a logical operator println!("both"); } } @@ -28,8 +35,7 @@ fn test_and_par() { fn test_or_par() { let a = true; let b = false; - if (a or b) { - //~^ ERROR expected one of `!`, `)`, `,`, `.`, `::`, `?`, `{`, or an operator, found `or` + if (a or b) { //~ ERROR `or` is not a logical operator println!("both"); } } @@ -37,8 +43,7 @@ fn test_or_par() { fn test_while_and() { let a = true; let b = false; - while a and b { - //~^ ERROR expected one of `!`, `.`, `::`, `?`, `{`, or an operator, found `and` + while a and b { //~ ERROR `and` is not a logical operator println!("both"); } } @@ -46,11 +51,7 @@ fn test_while_and() { fn test_while_or() { let a = true; let b = false; - while a or b { - //~^ ERROR expected one of `!`, `.`, `::`, `?`, `{`, or an operator, found `or` + while a or b { //~ ERROR `or` is not a logical operator println!("both"); } } - -fn main() { -} diff --git a/src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.stderr b/src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.stderr index f230395f7a5..528c62f501e 100644 --- a/src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.stderr +++ b/src/test/ui/did_you_mean/issue-54109-and_instead_of_ampersands.stderr @@ -1,58 +1,75 @@ -error: expected `{`, found `and` - --> $DIR/issue-54109-and_instead_of_ampersands.rs:4:10 +error: `and` is not a logical operator + --> $DIR/issue-54109-and_instead_of_ampersands.rs:7:15 + | +LL | let _ = a and b; + | ^^^ help: use `&&` to perform logical conjunction + | + = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators + +error: `and` is not a logical operator + --> $DIR/issue-54109-and_instead_of_ampersands.rs:9:10 | LL | if a and b { - | -- ^^^ - | | | - | | expected `{` - | | help: use `&&` instead of `and` for the boolean operator - | this `if` statement has a condition, but no block + | ^^^ help: use `&&` to perform logical conjunction + | + = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators + +error: `or` is not a logical operator + --> $DIR/issue-54109-and_instead_of_ampersands.rs:20:15 + | +LL | let _ = a or b; + | ^^ help: use `||` to perform logical disjunction + | + = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators -error: expected `{`, found `or` - --> $DIR/issue-54109-and_instead_of_ampersands.rs:13:10 +error: `or` is not a logical operator + --> $DIR/issue-54109-and_instead_of_ampersands.rs:22:10 | LL | if a or b { - | -- ^^ - | | | - | | expected `{` - | | help: use `||` instead of `or` for the boolean operator - | this `if` statement has a condition, but no block + | ^^ help: use `||` to perform logical disjunction + | + = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators -error: expected one of `!`, `)`, `,`, `.`, `::`, `?`, `{`, or an operator, found `and` - --> $DIR/issue-54109-and_instead_of_ampersands.rs:22:11 +error: `and` is not a logical operator + --> $DIR/issue-54109-and_instead_of_ampersands.rs:30:11 | LL | if (a and b) { - | ^^^ - | | - | expected one of 8 possible tokens - | help: use `&&` instead of `and` for the boolean operator + | ^^^ help: use `&&` to perform logical conjunction + | + = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators -error: expected one of `!`, `)`, `,`, `.`, `::`, `?`, `{`, or an operator, found `or` - --> $DIR/issue-54109-and_instead_of_ampersands.rs:31:11 +error: `or` is not a logical operator + --> $DIR/issue-54109-and_instead_of_ampersands.rs:38:11 | LL | if (a or b) { - | ^^ - | | - | expected one of 8 possible tokens - | help: use `||` instead of `or` for the boolean operator + | ^^ help: use `||` to perform logical disjunction + | + = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators -error: expected one of `!`, `.`, `::`, `?`, `{`, or an operator, found `and` - --> $DIR/issue-54109-and_instead_of_ampersands.rs:40:13 +error: `and` is not a logical operator + --> $DIR/issue-54109-and_instead_of_ampersands.rs:46:13 | LL | while a and b { - | ^^^ - | | - | expected one of `!`, `.`, `::`, `?`, `{`, or an operator - | help: use `&&` instead of `and` for the boolean operator + | ^^^ help: use `&&` to perform logical conjunction + | + = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators -error: expected one of `!`, `.`, `::`, `?`, `{`, or an operator, found `or` - --> $DIR/issue-54109-and_instead_of_ampersands.rs:49:13 +error: `or` is not a logical operator + --> $DIR/issue-54109-and_instead_of_ampersands.rs:54:13 | LL | while a or b { - | ^^ - | | - | expected one of `!`, `.`, `::`, `?`, `{`, or an operator - | help: use `||` instead of `or` for the boolean operator + | ^^ help: use `||` to perform logical disjunction + | + = note: unlike in e.g., python and PHP, `&&` and `||` are used for logical operators + +error[E0308]: mismatched types + --> $DIR/issue-54109-and_instead_of_ampersands.rs:13:33 + | +LL | let _recovery_witness: () = 0; + | -- ^ expected `()`, found integer + | | + | expected due to this -error: aborting due to 6 previous errors +error: aborting due to 9 previous errors +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/parser/attr-stmt-expr-attr-bad-2.rs b/src/test/ui/parser/attr-stmt-expr-attr-bad-2.rs new file mode 100644 index 00000000000..e5ac59ae463 --- /dev/null +++ b/src/test/ui/parser/attr-stmt-expr-attr-bad-2.rs @@ -0,0 +1,2 @@ +#[cfg(FALSE)] fn e() { let _ = x.#![attr]foo(); } +//~^ ERROR unexpected token: `#` diff --git a/src/test/ui/parser/attr-stmt-expr-attr-bad-2.stderr b/src/test/ui/parser/attr-stmt-expr-attr-bad-2.stderr new file mode 100644 index 00000000000..ca1043250ba --- /dev/null +++ b/src/test/ui/parser/attr-stmt-expr-attr-bad-2.stderr @@ -0,0 +1,8 @@ +error: unexpected token: `#` + --> $DIR/attr-stmt-expr-attr-bad-2.rs:1:34 + | +LL | #[cfg(FALSE)] fn e() { let _ = x.#![attr]foo(); } + | ^ + +error: aborting due to previous error + diff --git a/src/test/ui/parser/attr-stmt-expr-attr-bad-3.rs b/src/test/ui/parser/attr-stmt-expr-attr-bad-3.rs new file mode 100644 index 00000000000..7dc71af52f4 --- /dev/null +++ b/src/test/ui/parser/attr-stmt-expr-attr-bad-3.rs @@ -0,0 +1,2 @@ +#[cfg(FALSE)] fn e() { let _ = x.#[attr]foo(); } +//~^ ERROR unexpected token: `#` diff --git a/src/test/ui/parser/attr-stmt-expr-attr-bad-3.stderr b/src/test/ui/parser/attr-stmt-expr-attr-bad-3.stderr new file mode 100644 index 00000000000..ab9366d042a --- /dev/null +++ b/src/test/ui/parser/attr-stmt-expr-attr-bad-3.stderr @@ -0,0 +1,8 @@ +error: unexpected token: `#` + --> $DIR/attr-stmt-expr-attr-bad-3.rs:1:34 + | +LL | #[cfg(FALSE)] fn e() { let _ = x.#[attr]foo(); } + | ^ + +error: aborting due to previous error + diff --git a/src/test/ui/parser/attr-stmt-expr-attr-bad.rs b/src/test/ui/parser/attr-stmt-expr-attr-bad.rs new file mode 100644 index 00000000000..ef10010ed0e --- /dev/null +++ b/src/test/ui/parser/attr-stmt-expr-attr-bad.rs @@ -0,0 +1,107 @@ +fn main() {} + +#[cfg(FALSE)] fn e() { let _ = box #![attr] 0; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = [#[attr]]; } +//~^ ERROR expected expression, found `]` +#[cfg(FALSE)] fn e() { let _ = foo#[attr](); } +//~^ ERROR expected one of +#[cfg(FALSE)] fn e() { let _ = foo(#![attr]); } +//~^ ERROR an inner attribute is not permitted in this context +//~| ERROR expected expression, found `)` +#[cfg(FALSE)] fn e() { let _ = x.foo(#![attr]); } +//~^ ERROR an inner attribute is not permitted in this context +//~| ERROR expected expression, found `)` +#[cfg(FALSE)] fn e() { let _ = 0 + #![attr] 0; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = !#![attr] 0; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = -#![attr] 0; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = x #![attr] as Y; } +//~^ ERROR expected one of +#[cfg(FALSE)] fn e() { let _ = || #![attr] foo; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = move || #![attr] foo; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = || #![attr] {foo}; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = move || #![attr] {foo}; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = #[attr] ..#[attr] 0; } +//~^ ERROR expected expression, found `..` +#[cfg(FALSE)] fn e() { let _ = #[attr] ..; } +//~^ ERROR expected expression, found `..` +#[cfg(FALSE)] fn e() { let _ = #[attr] &#![attr] 0; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = #[attr] &mut #![attr] 0; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = #[attr] if 0 {}; } +//~^ ERROR attributes are not yet allowed on `if` expressions +#[cfg(FALSE)] fn e() { let _ = if 0 #[attr] {}; } +//~^ ERROR expected `{`, found `#` +#[cfg(FALSE)] fn e() { let _ = if 0 {#![attr]}; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = if 0 {} #[attr] else {}; } +//~^ ERROR expected one of +#[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] {}; } +//~^ ERROR expected `{`, found `#` +#[cfg(FALSE)] fn e() { let _ = if 0 {} else {#![attr]}; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; } +//~^ ERROR attributes are not yet allowed on `if` expressions +//~| ERROR expected `{`, found `#` +#[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 #[attr] {}; } +//~^ ERROR expected `{`, found `#` +#[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 {#![attr]}; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = #[attr] if let _ = 0 {}; } +//~^ ERROR attributes are not yet allowed on `if` expressions +#[cfg(FALSE)] fn e() { let _ = if let _ = 0 #[attr] {}; } +//~^ ERROR expected `{`, found `#` +#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {#![attr]}; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} #[attr] else {}; } +//~^ ERROR expected one of +#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] {}; } +//~^ ERROR expected `{`, found `#` +#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else {#![attr]}; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {}; } +//~^ ERROR attributes are not yet allowed on `if` expressions +//~| ERROR expected `{`, found `#` +#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 #[attr] {}; } +//~^ ERROR expected `{`, found `#` +#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 {#![attr]}; } +//~^ ERROR an inner attribute is not permitted in this context + +#[cfg(FALSE)] fn s() { #[attr] #![attr] let _ = 0; } +//~^ ERROR an inner attribute is not permitted following an outer attribute +#[cfg(FALSE)] fn s() { #[attr] #![attr] 0; } +//~^ ERROR an inner attribute is not permitted following an outer attribute +#[cfg(FALSE)] fn s() { #[attr] #![attr] foo!(); } +//~^ ERROR an inner attribute is not permitted following an outer attribute +#[cfg(FALSE)] fn s() { #[attr] #![attr] foo![]; } +//~^ ERROR an inner attribute is not permitted following an outer attribute +#[cfg(FALSE)] fn s() { #[attr] #![attr] foo!{}; } +//~^ ERROR an inner attribute is not permitted following an outer attribute + +// FIXME: Allow attributes in pattern constexprs? +// note: requires parens in patterns to allow disambiguation + +#[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] 10 => () } } +//~^ ERROR `X..=` range patterns are not supported +//~| ERROR expected one of `=>`, `if`, or `|`, found `#` +#[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] -10 => () } } +//~^ ERROR `X..=` range patterns are not supported +//~| ERROR expected one of `=>`, `if`, or `|`, found `#` +#[cfg(FALSE)] fn e() { match 0 { 0..=-#[attr] 10 => () } } +//~^ ERROR unexpected token: `#` +#[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] FOO => () } } +//~^ ERROR `X..=` range patterns are not supported +//~| ERROR expected one of `=>`, `if`, or `|`, found `#` + +// make sure we don't catch this bug again... +#[cfg(FALSE)] fn e() { { fn foo() { #[attr]; } } } +//~^ ERROR expected statement after outer attribute +#[cfg(FALSE)] fn e() { { fn foo() { #[attr] } } } diff --git a/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr b/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr new file mode 100644 index 00000000000..9a0d3176714 --- /dev/null +++ b/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr @@ -0,0 +1,390 @@ +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:3:36 + | +LL | #[cfg(FALSE)] fn e() { let _ = box #![attr] 0; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: expected expression, found `]` + --> $DIR/attr-stmt-expr-attr-bad.rs:5:40 + | +LL | #[cfg(FALSE)] fn e() { let _ = [#[attr]]; } + | ^ expected expression + +error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:7:35 + | +LL | #[cfg(FALSE)] fn e() { let _ = foo#[attr](); } + | ^ expected one of 7 possible tokens + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:9:36 + | +LL | #[cfg(FALSE)] fn e() { let _ = foo(#![attr]); } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: expected expression, found `)` + --> $DIR/attr-stmt-expr-attr-bad.rs:9:44 + | +LL | #[cfg(FALSE)] fn e() { let _ = foo(#![attr]); } + | ^ expected expression + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:12:38 + | +LL | #[cfg(FALSE)] fn e() { let _ = x.foo(#![attr]); } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: expected expression, found `)` + --> $DIR/attr-stmt-expr-attr-bad.rs:12:46 + | +LL | #[cfg(FALSE)] fn e() { let _ = x.foo(#![attr]); } + | ^ expected expression + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:15:36 + | +LL | #[cfg(FALSE)] fn e() { let _ = 0 + #![attr] 0; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:17:33 + | +LL | #[cfg(FALSE)] fn e() { let _ = !#![attr] 0; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:19:33 + | +LL | #[cfg(FALSE)] fn e() { let _ = -#![attr] 0; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:21:34 + | +LL | #[cfg(FALSE)] fn e() { let _ = x #![attr] as Y; } + | ^ expected one of 7 possible tokens + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:23:35 + | +LL | #[cfg(FALSE)] fn e() { let _ = || #![attr] foo; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:25:40 + | +LL | #[cfg(FALSE)] fn e() { let _ = move || #![attr] foo; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:27:35 + | +LL | #[cfg(FALSE)] fn e() { let _ = || #![attr] {foo}; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:29:40 + | +LL | #[cfg(FALSE)] fn e() { let _ = move || #![attr] {foo}; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: expected expression, found `..` + --> $DIR/attr-stmt-expr-attr-bad.rs:31:40 + | +LL | #[cfg(FALSE)] fn e() { let _ = #[attr] ..#[attr] 0; } + | ^^ expected expression + +error: expected expression, found `..` + --> $DIR/attr-stmt-expr-attr-bad.rs:33:40 + | +LL | #[cfg(FALSE)] fn e() { let _ = #[attr] ..; } + | ^^ expected expression + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:35:41 + | +LL | #[cfg(FALSE)] fn e() { let _ = #[attr] &#![attr] 0; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:37:45 + | +LL | #[cfg(FALSE)] fn e() { let _ = #[attr] &mut #![attr] 0; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: attributes are not yet allowed on `if` expressions + --> $DIR/attr-stmt-expr-attr-bad.rs:39:32 + | +LL | #[cfg(FALSE)] fn e() { let _ = #[attr] if 0 {}; } + | ^^^^^^^ + +error: expected `{`, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:41:37 + | +LL | #[cfg(FALSE)] fn e() { let _ = if 0 #[attr] {}; } + | -- ^ --- help: try placing this code inside a block: `{ {}; }` + | | | + | | expected `{` + | this `if` statement has a condition, but no block + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:43:38 + | +LL | #[cfg(FALSE)] fn e() { let _ = if 0 {#![attr]}; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:45:40 + | +LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} #[attr] else {}; } + | ^ expected one of `.`, `;`, `?`, `else`, or an operator + +error: expected `{`, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:47:45 + | +LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] {}; } + | ^ --- help: try placing this code inside a block: `{ {}; }` + | | + | expected `{` + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:49:46 + | +LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else {#![attr]}; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: attributes are not yet allowed on `if` expressions + --> $DIR/attr-stmt-expr-attr-bad.rs:51:45 + | +LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; } + | ^^^^^^^ + +error: expected `{`, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:51:45 + | +LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; } + | ^ -------- help: try placing this code inside a block: `{ if 0 {}; }` + | | + | expected `{` + +error: expected `{`, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:54:50 + | +LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 #[attr] {}; } + | -- ^ --- help: try placing this code inside a block: `{ {}; }` + | | | + | | expected `{` + | this `if` statement has a condition, but no block + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:56:51 + | +LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 {#![attr]}; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: attributes are not yet allowed on `if` expressions + --> $DIR/attr-stmt-expr-attr-bad.rs:58:32 + | +LL | #[cfg(FALSE)] fn e() { let _ = #[attr] if let _ = 0 {}; } + | ^^^^^^^ + +error: expected `{`, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:60:45 + | +LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 #[attr] {}; } + | -- ^ --- help: try placing this code inside a block: `{ {}; }` + | | | + | | expected `{` + | this `if` statement has a condition, but no block + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:62:46 + | +LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {#![attr]}; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:64:48 + | +LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} #[attr] else {}; } + | ^ expected one of `.`, `;`, `?`, `else`, or an operator + +error: expected `{`, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:66:53 + | +LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] {}; } + | ^ --- help: try placing this code inside a block: `{ {}; }` + | | + | expected `{` + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:68:54 + | +LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else {#![attr]}; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: attributes are not yet allowed on `if` expressions + --> $DIR/attr-stmt-expr-attr-bad.rs:70:53 + | +LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {}; } + | ^^^^^^^ + +error: expected `{`, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:70:53 + | +LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {}; } + | ^ ---------------- help: try placing this code inside a block: `{ if let _ = 0 {}; }` + | | + | expected `{` + +error: expected `{`, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:73:66 + | +LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 #[attr] {}; } + | -- ^ --- help: try placing this code inside a block: `{ {}; }` + | | | + | | expected `{` + | this `if` statement has a condition, but no block + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:75:67 + | +LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 {#![attr]}; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: an inner attribute is not permitted following an outer attribute + --> $DIR/attr-stmt-expr-attr-bad.rs:78:32 + | +LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] let _ = 0; } + | ------- ^^^^^^^^ not permitted following an outer attibute + | | + | previous outer attribute + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: an inner attribute is not permitted following an outer attribute + --> $DIR/attr-stmt-expr-attr-bad.rs:80:32 + | +LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] 0; } + | ------- ^^^^^^^^ not permitted following an outer attibute + | | + | previous outer attribute + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: an inner attribute is not permitted following an outer attribute + --> $DIR/attr-stmt-expr-attr-bad.rs:82:32 + | +LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] foo!(); } + | ------- ^^^^^^^^ not permitted following an outer attibute + | | + | previous outer attribute + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: an inner attribute is not permitted following an outer attribute + --> $DIR/attr-stmt-expr-attr-bad.rs:84:32 + | +LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] foo![]; } + | ------- ^^^^^^^^ not permitted following an outer attibute + | | + | previous outer attribute + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: an inner attribute is not permitted following an outer attribute + --> $DIR/attr-stmt-expr-attr-bad.rs:86:32 + | +LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] foo!{}; } + | ------- ^^^^^^^^ not permitted following an outer attibute + | | + | previous outer attribute + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: `X..=` range patterns are not supported + --> $DIR/attr-stmt-expr-attr-bad.rs:92:34 + | +LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] 10 => () } } + | ^^^^ help: try using the maximum value for the type: `0..=MAX` + +error: expected one of `=>`, `if`, or `|`, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:92:38 + | +LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] 10 => () } } + | ^ expected one of `=>`, `if`, or `|` + +error: `X..=` range patterns are not supported + --> $DIR/attr-stmt-expr-attr-bad.rs:95:34 + | +LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] -10 => () } } + | ^^^^ help: try using the maximum value for the type: `0..=MAX` + +error: expected one of `=>`, `if`, or `|`, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:95:38 + | +LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] -10 => () } } + | ^ expected one of `=>`, `if`, or `|` + +error: unexpected token: `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:98:39 + | +LL | #[cfg(FALSE)] fn e() { match 0 { 0..=-#[attr] 10 => () } } + | ^ + +error: `X..=` range patterns are not supported + --> $DIR/attr-stmt-expr-attr-bad.rs:100:34 + | +LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] FOO => () } } + | ^^^^ help: try using the maximum value for the type: `0..=MAX` + +error: expected one of `=>`, `if`, or `|`, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:100:38 + | +LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] FOO => () } } + | ^ expected one of `=>`, `if`, or `|` + +error: expected statement after outer attribute + --> $DIR/attr-stmt-expr-attr-bad.rs:105:44 + | +LL | #[cfg(FALSE)] fn e() { { fn foo() { #[attr]; } } } + | ^ + +error: aborting due to 52 previous errors + diff --git a/src/test/ui/parser/do-catch-suggests-try.rs b/src/test/ui/parser/do-catch-suggests-try.rs index d805ab75882..f64568d06e9 100644 --- a/src/test/ui/parser/do-catch-suggests-try.rs +++ b/src/test/ui/parser/do-catch-suggests-try.rs @@ -1,5 +1,10 @@ +#![feature(try_blocks)] + fn main() { let _: Option<()> = do catch {}; //~^ ERROR found removed `do catch` syntax - //~^^ HELP following RFC #2388, the new non-placeholder syntax is `try` + //~| replace with the new syntax + //~| following RFC #2388, the new non-placeholder syntax is `try` + + let _recovery_witness: () = 1; //~ ERROR mismatched types } diff --git a/src/test/ui/parser/do-catch-suggests-try.stderr b/src/test/ui/parser/do-catch-suggests-try.stderr index e151d4cf8a6..cd8907b7eac 100644 --- a/src/test/ui/parser/do-catch-suggests-try.stderr +++ b/src/test/ui/parser/do-catch-suggests-try.stderr @@ -1,10 +1,19 @@ error: found removed `do catch` syntax - --> $DIR/do-catch-suggests-try.rs:2:25 + --> $DIR/do-catch-suggests-try.rs:4:25 | LL | let _: Option<()> = do catch {}; - | ^^ + | ^^^^^^^^ help: replace with the new syntax: `try` | - = help: following RFC #2388, the new non-placeholder syntax is `try` + = note: following RFC #2388, the new non-placeholder syntax is `try` -error: aborting due to previous error +error[E0308]: mismatched types + --> $DIR/do-catch-suggests-try.rs:9:33 + | +LL | let _recovery_witness: () = 1; + | -- ^ expected `()`, found integer + | | + | expected due to this + +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/parser/issue-65257-invalid-var-decl-recovery.rs b/src/test/ui/parser/issue-65257-invalid-var-decl-recovery.rs new file mode 100644 index 00000000000..c1826f8caae --- /dev/null +++ b/src/test/ui/parser/issue-65257-invalid-var-decl-recovery.rs @@ -0,0 +1,21 @@ +fn main() { + auto n = 0;//~ ERROR invalid variable declaration + //~^ HELP write `let` instead of `auto` to introduce a new variable + auto m;//~ ERROR invalid variable declaration + //~^ HELP write `let` instead of `auto` to introduce a new variable + m = 0; + + var n = 0;//~ ERROR invalid variable declaration + //~^ HELP write `let` instead of `var` to introduce a new variable + var m;//~ ERROR invalid variable declaration + //~^ HELP write `let` instead of `var` to introduce a new variable + m = 0; + + mut n = 0;//~ ERROR invalid variable declaration + //~^ HELP missing keyword + mut var;//~ ERROR invalid variable declaration + //~^ HELP missing keyword + var = 0; + + let _recovery_witness: () = 0; //~ ERROR mismatched types +} diff --git a/src/test/ui/parser/issue-65257-invalid-var-decl-recovery.stderr b/src/test/ui/parser/issue-65257-invalid-var-decl-recovery.stderr new file mode 100644 index 00000000000..ad72dd30542 --- /dev/null +++ b/src/test/ui/parser/issue-65257-invalid-var-decl-recovery.stderr @@ -0,0 +1,67 @@ +error: invalid variable declaration + --> $DIR/issue-65257-invalid-var-decl-recovery.rs:2:5 + | +LL | auto n = 0; + | ^^^^ + | +help: write `let` instead of `auto` to introduce a new variable + | +LL | let n = 0; + | ^^^ + +error: invalid variable declaration + --> $DIR/issue-65257-invalid-var-decl-recovery.rs:4:5 + | +LL | auto m; + | ^^^^ + | +help: write `let` instead of `auto` to introduce a new variable + | +LL | let m; + | ^^^ + +error: invalid variable declaration + --> $DIR/issue-65257-invalid-var-decl-recovery.rs:8:5 + | +LL | var n = 0; + | ^^^ + | +help: write `let` instead of `var` to introduce a new variable + | +LL | let n = 0; + | ^^^ + +error: invalid variable declaration + --> $DIR/issue-65257-invalid-var-decl-recovery.rs:10:5 + | +LL | var m; + | ^^^ + | +help: write `let` instead of `var` to introduce a new variable + | +LL | let m; + | ^^^ + +error: invalid variable declaration + --> $DIR/issue-65257-invalid-var-decl-recovery.rs:14:5 + | +LL | mut n = 0; + | ^^^ help: missing keyword: `let mut` + +error: invalid variable declaration + --> $DIR/issue-65257-invalid-var-decl-recovery.rs:16:5 + | +LL | mut var; + | ^^^ help: missing keyword: `let mut` + +error[E0308]: mismatched types + --> $DIR/issue-65257-invalid-var-decl-recovery.rs:20:33 + | +LL | let _recovery_witness: () = 0; + | -- ^ expected `()`, found integer + | | + | expected due to this + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/parser/recover-labeled-non-block-expr.rs b/src/test/ui/parser/recover-labeled-non-block-expr.rs new file mode 100644 index 00000000000..be92170acf0 --- /dev/null +++ b/src/test/ui/parser/recover-labeled-non-block-expr.rs @@ -0,0 +1,5 @@ +fn main() { + 'label: 1 + 1; //~ ERROR expected `while`, `for`, `loop` or `{` after a label + + let _recovery_witness: () = 0; //~ ERROR mismatched types +} diff --git a/src/test/ui/parser/recover-labeled-non-block-expr.stderr b/src/test/ui/parser/recover-labeled-non-block-expr.stderr new file mode 100644 index 00000000000..771a915288c --- /dev/null +++ b/src/test/ui/parser/recover-labeled-non-block-expr.stderr @@ -0,0 +1,17 @@ +error: expected `while`, `for`, `loop` or `{` after a label + --> $DIR/recover-labeled-non-block-expr.rs:2:13 + | +LL | 'label: 1 + 1; + | ^ expected `while`, `for`, `loop` or `{` after a label + +error[E0308]: mismatched types + --> $DIR/recover-labeled-non-block-expr.rs:4:33 + | +LL | let _recovery_witness: () = 0; + | -- ^ expected `()`, found integer + | | + | expected due to this + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/parser/recovery-attr-on-if.rs b/src/test/ui/parser/recovery-attr-on-if.rs new file mode 100644 index 00000000000..0d1f5be7b49 --- /dev/null +++ b/src/test/ui/parser/recovery-attr-on-if.rs @@ -0,0 +1,9 @@ +fn main() { + #[attr] if true {}; + //~^ ERROR cannot find attribute + //~| ERROR attributes are not yet allowed on `if` expressions + #[attr] if true {}; + //~^ ERROR cannot find attribute + //~| ERROR attributes are not yet allowed on `if` expressions + let _recovery_witness: () = 0; //~ ERROR mismatched types +} diff --git a/src/test/ui/parser/recovery-attr-on-if.stderr b/src/test/ui/parser/recovery-attr-on-if.stderr new file mode 100644 index 00000000000..a02846827c9 --- /dev/null +++ b/src/test/ui/parser/recovery-attr-on-if.stderr @@ -0,0 +1,35 @@ +error: attributes are not yet allowed on `if` expressions + --> $DIR/recovery-attr-on-if.rs:2:5 + | +LL | #[attr] if true {}; + | ^^^^^^^ + +error: attributes are not yet allowed on `if` expressions + --> $DIR/recovery-attr-on-if.rs:5:5 + | +LL | #[attr] if true {}; + | ^^^^^^^ + +error: cannot find attribute `attr` in this scope + --> $DIR/recovery-attr-on-if.rs:5:7 + | +LL | #[attr] if true {}; + | ^^^^ + +error: cannot find attribute `attr` in this scope + --> $DIR/recovery-attr-on-if.rs:2:7 + | +LL | #[attr] if true {}; + | ^^^^ + +error[E0308]: mismatched types + --> $DIR/recovery-attr-on-if.rs:8:33 + | +LL | let _recovery_witness: () = 0; + | -- ^ expected `()`, found integer + | | + | expected due to this + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/parser/stmt_expr_attrs_placement.rs b/src/test/ui/parser/stmt_expr_attrs_placement.rs new file mode 100644 index 00000000000..b8a794f4b92 --- /dev/null +++ b/src/test/ui/parser/stmt_expr_attrs_placement.rs @@ -0,0 +1,22 @@ +#![feature(stmt_expr_attributes)] + +// Test that various placements of the inner attribute are parsed correctly, +// or not. + +fn main() { + let a = #![allow(warnings)] (1, 2); + //~^ ERROR an inner attribute is not permitted in this context + + let b = (#![allow(warnings)] 1, 2); + + let c = { + #![allow(warnings)] + (#![allow(warnings)] 1, 2) + }; + + let d = { + #![allow(warnings)] + let e = (#![allow(warnings)] 1, 2); + e + }; +} diff --git a/src/test/ui/parser/stmt_expr_attrs_placement.stderr b/src/test/ui/parser/stmt_expr_attrs_placement.stderr new file mode 100644 index 00000000000..1886a0f9ba0 --- /dev/null +++ b/src/test/ui/parser/stmt_expr_attrs_placement.stderr @@ -0,0 +1,10 @@ +error: an inner attribute is not permitted in this context + --> $DIR/stmt_expr_attrs_placement.rs:7:13 + | +LL | let a = #![allow(warnings)] (1, 2); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: aborting due to previous error + |
