From e6fa6334dd54f7c96514b520c5b9f261df3fc16b Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sat, 12 Dec 2020 15:20:22 -0500 Subject: Properly capture trailing 'unglued' token If we try to capture the `Vec` in `Option>`, we'll need to capture a `>` token which was 'unglued' from a `>>` token. The processing of unglueing a token for parsing purposes bypasses the usual capturing infrastructure, so we currently lose the trailing `>`. As a result, we fall back to the reparsed `TokenStream`, causing us to lose spans. This commit makes token capturing keep track of a trailing 'unglued' token. Note that we don't need to care about unglueing except at the end of the captured tokens - if we capture both the first and second unglued tokens, then we'll end up capturing the full 'glued' token, which already works correctly. --- compiler/rustc_parse/src/parser/mod.rs | 67 +++++++++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 9 deletions(-) (limited to 'compiler/rustc_parse/src/parser/mod.rs') diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index df4695b18e7..d51a0fcbf09 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -17,7 +17,7 @@ pub use path::PathStyle; use rustc_ast::ptr::P; use rustc_ast::token::{self, DelimToken, Token, TokenKind}; use rustc_ast::tokenstream::{self, DelimSpan, LazyTokenStream, Spacing}; -use rustc_ast::tokenstream::{CreateTokenStream, TokenStream, TokenTree}; +use rustc_ast::tokenstream::{CreateTokenStream, TokenStream, TokenTree, TreeAndSpacing}; use rustc_ast::DUMMY_NODE_ID; use rustc_ast::{self as ast, AnonConst, AttrStyle, AttrVec, Const, CrateSugar, Extern, Unsafe}; use rustc_ast::{Async, Expr, ExprKind, MacArgs, MacDelimiter, Mutability, StrLit}; @@ -132,6 +132,28 @@ struct TokenCursor { // Counts the number of calls to `next` or `next_desugared`, // depending on whether `desugar_doc_comments` is set. num_next_calls: usize, + // During parsing, we may sometimes need to 'unglue' a + // glued token into two component tokens + // (e.g. '>>' into '>' and '>), so that the parser + // can consume them one at a time. This process + // bypasses the normal capturing mechanism + // (e.g. `num_next_calls` will not be incremented), + // since the 'unglued' tokens due not exist in + // the original `TokenStream`. + // + // If we end up consuming both unglued tokens, + // then this is not an issue - we'll end up + // capturing the single 'glued' token. + // + // However, in certain circumstances, we may + // want to capture just the first 'unglued' token. + // For example, capturing the `Vec` + // in `Option>` requires us to unglue + // the trailing `>>` token. The `append_unglued_token` + // field is used to track this token - it gets + // appended to the captured stream when + // we evaluate a `LazyTokenStream` + append_unglued_token: Option, } #[derive(Clone)] @@ -336,6 +358,7 @@ impl<'a> Parser<'a> { stack: Vec::new(), num_next_calls: 0, desugar_doc_comments, + append_unglued_token: None, }, desugar_doc_comments, unmatched_angle_bracket_count: 0, @@ -359,6 +382,10 @@ impl<'a> Parser<'a> { self.token_cursor.next() }; self.token_cursor.num_next_calls += 1; + // We've retrieved an token from the underlying + // cursor, so we no longer need to worry about + // an unglued token. See `break_and_eat` for more details + self.token_cursor.append_unglued_token = None; if next.span.is_dummy() { // Tweak the location for better diagnostics, but keep syntactic context intact. next.span = fallback_span.with_ctxt(next.span.ctxt()); @@ -555,6 +582,14 @@ impl<'a> Parser<'a> { let first_span = self.sess.source_map().start_point(self.token.span); let second_span = self.token.span.with_lo(first_span.hi()); self.token = Token::new(first, first_span); + // Keep track of this token - if we end token capturing now, + // we'll want to append this token to the captured stream. + // + // If we consume any additional tokens, then this token + // is not needed (we'll capture the entire 'glued' token), + // and `next_tok` will set this field to `None` + self.token_cursor.append_unglued_token = + Some((TokenTree::Token(self.token.clone()), Spacing::Alone)); // Use the spacing of the glued token as the spacing // of the unglued second token. self.bump_with((Token::new(second, second_span), self.token_spacing)); @@ -1230,6 +1265,7 @@ impl<'a> Parser<'a> { num_calls: usize, desugar_doc_comments: bool, trailing_semi: bool, + append_unglued_token: Option, } impl CreateTokenStream for LazyTokenStreamImpl { fn create_token_stream(&self) -> TokenStream { @@ -1253,12 +1289,18 @@ impl<'a> Parser<'a> { })) .take(num_calls); - make_token_stream(tokens) + make_token_stream(tokens, self.append_unglued_token.clone()) } fn add_trailing_semi(&self) -> Box { if self.trailing_semi { panic!("Called `add_trailing_semi` twice!"); } + if self.append_unglued_token.is_some() { + panic!( + "Cannot call `add_trailing_semi` when we have an unglued token {:?}", + self.append_unglued_token + ); + } let mut new = self.clone(); new.trailing_semi = true; Box::new(new) @@ -1271,6 +1313,7 @@ impl<'a> Parser<'a> { cursor_snapshot, desugar_doc_comments: self.desugar_doc_comments, trailing_semi: false, + append_unglued_token: self.token_cursor.append_unglued_token.clone(), }; Ok((ret, Some(LazyTokenStream::new(lazy_impl)))) } @@ -1325,7 +1368,10 @@ pub fn emit_unclosed_delims(unclosed_delims: &mut Vec, sess: &Pa /// Converts a flattened iterator of tokens (including open and close delimiter tokens) /// into a `TokenStream`, creating a `TokenTree::Delimited` for each matching pair /// of open and close delims. -fn make_token_stream(tokens: impl Iterator) -> TokenStream { +fn make_token_stream( + tokens: impl Iterator, + append_unglued_token: Option, +) -> TokenStream { #[derive(Debug)] struct FrameData { open: Span, @@ -1348,14 +1394,17 @@ fn make_token_stream(tokens: impl Iterator) -> TokenStr .inner .push((delimited, Spacing::Alone)); } - token => stack - .last_mut() - .expect("Bottom token frame is missing!") - .inner - .push((TokenTree::Token(token), spacing)), + token => { + stack + .last_mut() + .expect("Bottom token frame is missing!") + .inner + .push((TokenTree::Token(token), spacing)); + } } } - let final_buf = stack.pop().expect("Missing final buf!"); + let mut final_buf = stack.pop().expect("Missing final buf!"); + final_buf.inner.extend(append_unglued_token); assert!(stack.is_empty(), "Stack should be empty: final_buf={:?} stack={:?}", final_buf, stack); TokenStream::new(final_buf.inner) } -- cgit 1.4.1-3-g733a5 From 1a7d00a529503ac38a6b1ae28e8e779e434e02e0 Mon Sep 17 00:00:00 2001 From: mark Date: Tue, 10 Nov 2020 18:00:53 -0600 Subject: implement edition-specific :pat behavior for 2015/18 --- compiler/rustc_expand/src/mbe/macro_parser.rs | 33 +++++++++++---- compiler/rustc_parse/src/parser/expr.rs | 8 ++-- compiler/rustc_parse/src/parser/mod.rs | 1 + compiler/rustc_parse/src/parser/nonterminal.rs | 23 +++++++++-- compiler/rustc_parse/src/parser/pat.rs | 17 ++++++-- compiler/rustc_parse/src/parser/stmt.rs | 4 +- src/test/ui/macros/macro-pat-follow-2018.rs | 15 +++++++ src/test/ui/macros/macro-pat-follow.rs | 16 ++------ .../or-patterns/or-patterns-syntactic-fail-2018.rs | 15 +++++++ .../or-patterns-syntactic-fail-2018.stderr | 20 +++++++++ .../ui/or-patterns/or-patterns-syntactic-fail.rs | 10 ----- .../or-patterns/or-patterns-syntactic-fail.stderr | 48 +++++++--------------- .../or-patterns/or-patterns-syntactic-pass-2021.rs | 14 +++++++ src/test/ui/pattern/or-pattern-macro-pat.rs | 44 ++++++++++++++++++++ 14 files changed, 193 insertions(+), 75 deletions(-) create mode 100644 src/test/ui/macros/macro-pat-follow-2018.rs create mode 100644 src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.rs create mode 100644 src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.stderr create mode 100644 src/test/ui/or-patterns/or-patterns-syntactic-pass-2021.rs create mode 100644 src/test/ui/pattern/or-pattern-macro-pat.rs (limited to 'compiler/rustc_parse/src/parser/mod.rs') diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index c37f9125675..3cf2d8f8ac1 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -77,9 +77,9 @@ use TokenTreeOrTokenTreeSlice::*; use crate::mbe::{self, TokenTree}; use rustc_ast::token::{self, DocComment, Nonterminal, Token}; -use rustc_parse::parser::Parser; +use rustc_parse::parser::{OrPatNonterminalMode, Parser}; use rustc_session::parse::ParseSess; -use rustc_span::symbol::MacroRulesNormalizedIdent; +use rustc_span::{edition::Edition, symbol::MacroRulesNormalizedIdent}; use smallvec::{smallvec, SmallVec}; @@ -414,6 +414,18 @@ fn token_name_eq(t1: &Token, t2: &Token) -> bool { } } +/// In edition 2015/18, `:pat` can only match `pat` because otherwise, we have +/// breakage. As of edition 2021, `:pat` matches `top_pat`. +/// +/// See for more info. +fn or_pat_mode(edition: Edition) -> OrPatNonterminalMode { + match edition { + Edition::Edition2015 | Edition::Edition2018 => OrPatNonterminalMode::NoTopAlt, + // FIXME(mark-i-m): uncomment this when edition 2021 machinery is added. + // Edition::Edition2021 => OrPatNonterminalMode::TopPat, + } +} + /// Process the matcher positions of `cur_items` until it is empty. In the process, this will /// produce more items in `next_items`, `eof_items`, and `bb_items`. /// @@ -553,10 +565,14 @@ fn inner_parse_loop<'root, 'tt>( // We need to match a metavar with a valid ident... call out to the black-box // parser by adding an item to `bb_items`. - TokenTree::MetaVarDecl(_, _, kind) => { - // Built-in nonterminals never start with these tokens, - // so we can eliminate them from consideration. - if Parser::nonterminal_may_begin_with(kind, token) { + TokenTree::MetaVarDecl(span, _, kind) => { + // Built-in nonterminals never start with these tokens, so we can eliminate + // them from consideration. + // + // We use the span of the metavariable declaration to determine any + // edition-specific matching behavior for non-terminals. + if Parser::nonterminal_may_begin_with(kind, token, or_pat_mode(span.edition())) + { bb_items.push(item); } } @@ -717,7 +733,10 @@ pub(super) fn parse_tt(parser: &mut Cow<'_, Parser<'_>>, ms: &[TokenTree]) -> Na let mut item = bb_items.pop().unwrap(); if let TokenTree::MetaVarDecl(span, _, kind) = item.top_elts.get_tt(item.idx) { let match_cur = item.match_cur; - let nt = match parser.to_mut().parse_nonterminal(kind) { + // We use the span of the metavariable declaration to determine any + // edition-specific matching behavior for non-terminals. + let nt = match parser.to_mut().parse_nonterminal(kind, or_pat_mode(span.edition())) + { Err(mut err) => { err.span_label( span, diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 4d2167442be..eed3e9947b2 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1,4 +1,4 @@ -use super::pat::{GateOr, PARAM_EXPECTED}; +use super::pat::{GateOr, RecoverComma, PARAM_EXPECTED}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{BlockMode, Parser, PathStyle, Restrictions, TokenType}; use super::{SemiColonMode, SeqSep, TokenExpectType}; @@ -1729,7 +1729,7 @@ impl<'a> Parser<'a> { /// The `let` token has already been eaten. fn parse_let_expr(&mut self, attrs: AttrVec) -> PResult<'a, P> { let lo = self.prev_token.span; - let pat = self.parse_top_pat(GateOr::No)?; + let pat = self.parse_top_pat(GateOr::No, RecoverComma::Yes)?; self.expect(&token::Eq)?; let expr = self.with_res(self.restrictions | Restrictions::NO_STRUCT_LITERAL, |this| { this.parse_assoc_expr_with(1 + prec_let_scrutinee_needs_par(), None.into()) @@ -1792,7 +1792,7 @@ impl<'a> Parser<'a> { _ => None, }; - let pat = self.parse_top_pat(GateOr::Yes)?; + let pat = self.parse_top_pat(GateOr::Yes, RecoverComma::Yes)?; if !self.eat_keyword(kw::In) { self.error_missing_in_for_loop(); } @@ -1902,7 +1902,7 @@ impl<'a> Parser<'a> { pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> { let attrs = self.parse_outer_attributes()?; let lo = self.token.span; - let pat = self.parse_top_pat(GateOr::No)?; + let pat = self.parse_top_pat(GateOr::No, RecoverComma::Yes)?; let guard = if self.eat_keyword(kw::If) { let if_span = self.prev_token.span; let cond = self.parse_expr()?; diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index d51a0fcbf09..e19ebb8fd2f 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -12,6 +12,7 @@ mod ty; use crate::lexer::UnmatchedBrace; pub use diagnostics::AttemptLocalParseRecovery; use diagnostics::Error; +pub use pat::OrPatNonterminalMode; pub use path::PathStyle; use rustc_ast::ptr::P; diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 76ad5acd530..a6b9ac1014e 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -4,6 +4,7 @@ use rustc_ast_pretty::pprust; use rustc_errors::PResult; use rustc_span::symbol::{kw, Ident}; +use crate::parser::pat::{GateOr, OrPatNonterminalMode, RecoverComma}; use crate::parser::{FollowedByType, Parser, PathStyle}; impl<'a> Parser<'a> { @@ -11,7 +12,11 @@ impl<'a> Parser<'a> { /// /// Returning `false` is a *stability guarantee* that such a matcher will *never* begin with that /// token. Be conservative (return true) if not sure. - pub fn nonterminal_may_begin_with(kind: NonterminalKind, token: &Token) -> bool { + pub fn nonterminal_may_begin_with( + kind: NonterminalKind, + token: &Token, + or_pat_mode: OrPatNonterminalMode, + ) -> bool { /// Checks whether the non-terminal may contain a single (non-keyword) identifier. fn may_be_ident(nt: &token::Nonterminal) -> bool { match *nt { @@ -70,6 +75,8 @@ impl<'a> Parser<'a> { token::ModSep | // path token::Lt | // path (UFCS constant) token::BinOp(token::Shl) => true, // path (double UFCS) + // leading vert `|` or-pattern + token::BinOp(token::Or) => matches!(or_pat_mode, OrPatNonterminalMode::TopPat), token::Interpolated(ref nt) => may_be_ident(nt), _ => false, }, @@ -86,7 +93,12 @@ impl<'a> Parser<'a> { } } - pub fn parse_nonterminal(&mut self, kind: NonterminalKind) -> PResult<'a, Nonterminal> { + /// Parse a non-terminal (e.g. MBE `:pat` or `:ident`). + pub fn parse_nonterminal( + &mut self, + kind: NonterminalKind, + or_pat_mode: OrPatNonterminalMode, + ) -> PResult<'a, Nonterminal> { // Any `Nonterminal` which stores its tokens (currently `NtItem` and `NtExpr`) // needs to have them force-captured here. // A `macro_rules!` invocation may pass a captured item/expr to a proc-macro, @@ -130,7 +142,12 @@ impl<'a> Parser<'a> { } } NonterminalKind::Pat => { - let (mut pat, tokens) = self.collect_tokens(|this| this.parse_pat(None))?; + let (mut pat, tokens) = self.collect_tokens(|this| match or_pat_mode { + OrPatNonterminalMode::TopPat => { + this.parse_top_pat(GateOr::Yes, RecoverComma::No) + } + OrPatNonterminalMode::NoTopAlt => this.parse_pat(None), + })?; // We have have eaten an NtPat, which could already have tokens if pat.tokens.is_none() { pat.tokens = tokens; diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index b62c7373800..1da371e0b72 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -26,11 +26,18 @@ pub(super) enum GateOr { /// Whether or not to recover a `,` when parsing or-patterns. #[derive(PartialEq, Copy, Clone)] -enum RecoverComma { +pub(super) enum RecoverComma { Yes, No, } +/// Used when parsing a non-terminal (see `parse_nonterminal`) to determine if `:pat` should match +/// `top_pat` or `pat`. See issue . +pub enum OrPatNonterminalMode { + TopPat, + NoTopAlt, +} + impl<'a> Parser<'a> { /// Parses a pattern. /// @@ -43,13 +50,17 @@ impl<'a> Parser<'a> { /// Entry point to the main pattern parser. /// Corresponds to `top_pat` in RFC 2535 and allows or-pattern at the top level. - pub(super) fn parse_top_pat(&mut self, gate_or: GateOr) -> PResult<'a, P> { + pub(super) fn parse_top_pat( + &mut self, + gate_or: GateOr, + rc: RecoverComma, + ) -> PResult<'a, P> { // Allow a '|' before the pats (RFCs 1925, 2530, and 2535). let gated_leading_vert = self.eat_or_separator(None) && gate_or == GateOr::Yes; let leading_vert_span = self.prev_token.span; // Parse the possibly-or-pattern. - let pat = self.parse_pat_with_or(None, gate_or, RecoverComma::Yes)?; + let pat = self.parse_pat_with_or(None, gate_or, rc)?; // If we parsed a leading `|` which should be gated, // and no other gated or-pattern has been parsed thus far, diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index e974556f43a..2942747991a 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -1,7 +1,7 @@ use super::attr::DEFAULT_INNER_ATTR_FORBIDDEN; use super::diagnostics::{AttemptLocalParseRecovery, Error}; use super::expr::LhsExpr; -use super::pat::GateOr; +use super::pat::{GateOr, RecoverComma}; use super::path::PathStyle; use super::{BlockMode, Parser, Restrictions, SemiColonMode}; use crate::maybe_whole; @@ -185,7 +185,7 @@ impl<'a> Parser<'a> { /// Parses a local variable declaration. fn parse_local(&mut self, attrs: AttrVec) -> PResult<'a, P> { let lo = self.prev_token.span; - let pat = self.parse_top_pat(GateOr::Yes)?; + let pat = self.parse_top_pat(GateOr::Yes, RecoverComma::Yes)?; let (err, ty) = if self.eat(&token::Colon) { // Save the state of the parser before parsing type normally, in case there is a `:` diff --git a/src/test/ui/macros/macro-pat-follow-2018.rs b/src/test/ui/macros/macro-pat-follow-2018.rs new file mode 100644 index 00000000000..ce2911de986 --- /dev/null +++ b/src/test/ui/macros/macro-pat-follow-2018.rs @@ -0,0 +1,15 @@ +// run-pass +// edition:2018 + +macro_rules! pat_bar { + ($p:pat | $p2:pat) => {{ + match Some(1u8) { + $p | $p2 => {} + _ => {} + } + }}; +} + +fn main() { + pat_bar!(Some(1u8) | None); +} diff --git a/src/test/ui/macros/macro-pat-follow.rs b/src/test/ui/macros/macro-pat-follow.rs index 8673cf79467..8e02789fdd8 100644 --- a/src/test/ui/macros/macro-pat-follow.rs +++ b/src/test/ui/macros/macro-pat-follow.rs @@ -3,29 +3,19 @@ macro_rules! pat_in { ($p:pat in $e:expr) => {{ let mut iter = $e.into_iter(); while let $p = iter.next() {} - }} + }}; } macro_rules! pat_if { ($p:pat if $e:expr) => {{ match Some(1u8) { - $p if $e => {}, + $p if $e => {} _ => {} } - }} -} - -macro_rules! pat_bar { - ($p:pat | $p2:pat) => {{ - match Some(1u8) { - $p | $p2 => {}, - _ => {} - } - }} + }}; } fn main() { pat_in!(Some(_) in 0..10); pat_if!(Some(x) if x > 0); - pat_bar!(Some(1u8) | None); } diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.rs b/src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.rs new file mode 100644 index 00000000000..9c3c5dd360e --- /dev/null +++ b/src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.rs @@ -0,0 +1,15 @@ +// Test that :pat doesn't accept top-level or-patterns in edition 2018. + +// edition:2018 + +#![feature(or_patterns)] + +fn main() {} + +// Test the `pat` macro fragment parser: +macro_rules! accept_pat { + ($p:pat) => {}; +} + +accept_pat!(p | q); //~ ERROR no rules expected the token `|` +accept_pat!(|p| q); //~ ERROR no rules expected the token `|` diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.stderr b/src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.stderr new file mode 100644 index 00000000000..7dbc3087663 --- /dev/null +++ b/src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.stderr @@ -0,0 +1,20 @@ +error: no rules expected the token `|` + --> $DIR/or-patterns-syntactic-fail-2018.rs:14:15 + | +LL | macro_rules! accept_pat { + | ----------------------- when calling this macro +... +LL | accept_pat!(p | q); + | ^ no rules expected this token in macro call + +error: no rules expected the token `|` + --> $DIR/or-patterns-syntactic-fail-2018.rs:15:13 + | +LL | macro_rules! accept_pat { + | ----------------------- when calling this macro +... +LL | accept_pat!(|p| q); + | ^ no rules expected this token in macro call + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-fail.rs b/src/test/ui/or-patterns/or-patterns-syntactic-fail.rs index d2322005652..efe90b3e3c6 100644 --- a/src/test/ui/or-patterns/or-patterns-syntactic-fail.rs +++ b/src/test/ui/or-patterns/or-patterns-syntactic-fail.rs @@ -5,16 +5,6 @@ fn main() {} -// Test the `pat` macro fragment parser: -macro_rules! accept_pat { - ($p:pat) => {} -} - -accept_pat!(p | q); //~ ERROR no rules expected the token `|` -accept_pat!(| p | q); //~ ERROR no rules expected the token `|` - -// Non-macro tests: - enum E { A, B } use E::*; diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr b/src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr index 861d274ab5c..989aeb52006 100644 --- a/src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr +++ b/src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr @@ -1,53 +1,53 @@ error: an or-pattern parameter must be wrapped in parenthesis - --> $DIR/or-patterns-syntactic-fail.rs:27:13 + --> $DIR/or-patterns-syntactic-fail.rs:17:13 | LL | fn fun1(A | B: E) {} | ^^^^^ help: wrap the pattern in parenthesis: `(A | B)` error: a leading `|` is not allowed in a parameter pattern - --> $DIR/or-patterns-syntactic-fail.rs:29:13 + --> $DIR/or-patterns-syntactic-fail.rs:19:13 | LL | fn fun2(| A | B: E) {} | ^ help: remove the `|` error: an or-pattern parameter must be wrapped in parenthesis - --> $DIR/or-patterns-syntactic-fail.rs:29:15 + --> $DIR/or-patterns-syntactic-fail.rs:19:15 | LL | fn fun2(| A | B: E) {} | ^^^^^ help: wrap the pattern in parenthesis: `(A | B)` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:40:11 + --> $DIR/or-patterns-syntactic-fail.rs:30:11 | LL | let ( | A | B) = E::A; | ^ help: remove the `|` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:41:11 + --> $DIR/or-patterns-syntactic-fail.rs:31:11 | LL | let ( | A | B,) = (E::B,); | ^ help: remove the `|` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:42:11 + --> $DIR/or-patterns-syntactic-fail.rs:32:11 | LL | let [ | A | B ] = [E::A]; | ^ help: remove the `|` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:43:13 + --> $DIR/or-patterns-syntactic-fail.rs:33:13 | LL | let TS( | A | B ); | ^ help: remove the `|` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:44:17 + --> $DIR/or-patterns-syntactic-fail.rs:34:17 | LL | let NS { f: | A | B }; | ^ help: remove the `|` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:46:11 + --> $DIR/or-patterns-syntactic-fail.rs:36:11 | LL | let ( || A | B) = E::A; | ^^ help: remove the `||` @@ -55,7 +55,7 @@ LL | let ( || A | B) = E::A; = note: alternatives in or-patterns are separated with `|`, not `||` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:47:11 + --> $DIR/or-patterns-syntactic-fail.rs:37:11 | LL | let [ || A | B ] = [E::A]; | ^^ help: remove the `||` @@ -63,7 +63,7 @@ LL | let [ || A | B ] = [E::A]; = note: alternatives in or-patterns are separated with `|`, not `||` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:48:13 + --> $DIR/or-patterns-syntactic-fail.rs:38:13 | LL | let TS( || A | B ); | ^^ help: remove the `||` @@ -71,33 +71,15 @@ LL | let TS( || A | B ); = note: alternatives in or-patterns are separated with `|`, not `||` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:49:17 + --> $DIR/or-patterns-syntactic-fail.rs:39:17 | LL | let NS { f: || A | B }; | ^^ help: remove the `||` | = note: alternatives in or-patterns are separated with `|`, not `||` -error: no rules expected the token `|` - --> $DIR/or-patterns-syntactic-fail.rs:13:15 - | -LL | macro_rules! accept_pat { - | ----------------------- when calling this macro -... -LL | accept_pat!(p | q); - | ^ no rules expected this token in macro call - -error: no rules expected the token `|` - --> $DIR/or-patterns-syntactic-fail.rs:14:13 - | -LL | macro_rules! accept_pat { - | ----------------------- when calling this macro -... -LL | accept_pat!(| p | q); - | ^ no rules expected this token in macro call - error[E0369]: no implementation for `E | ()` - --> $DIR/or-patterns-syntactic-fail.rs:23:22 + --> $DIR/or-patterns-syntactic-fail.rs:13:22 | LL | let _ = |A | B: E| (); | ----^ -- () @@ -107,7 +89,7 @@ LL | let _ = |A | B: E| (); = note: an implementation of `std::ops::BitOr` might be missing for `E` error[E0308]: mismatched types - --> $DIR/or-patterns-syntactic-fail.rs:51:36 + --> $DIR/or-patterns-syntactic-fail.rs:41:36 | LL | let recovery_witness: String = 0; | ------ ^ @@ -116,7 +98,7 @@ LL | let recovery_witness: String = 0; | | help: try using a conversion method: `0.to_string()` | expected due to this -error: aborting due to 16 previous errors +error: aborting due to 14 previous errors Some errors have detailed explanations: E0308, E0369. For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-pass-2021.rs b/src/test/ui/or-patterns/or-patterns-syntactic-pass-2021.rs new file mode 100644 index 00000000000..f0ce7597aee --- /dev/null +++ b/src/test/ui/or-patterns/or-patterns-syntactic-pass-2021.rs @@ -0,0 +1,14 @@ +// Tests that :pat in macros in edition 2021 allows top-level or-patterns. + +// run-pass +// ignore-test +// edition:2021 +// FIXME(mark-i-m): unignore when 2021 machinery is in place. + +macro_rules! accept_pat { + ($p:pat) => {}; +} + +accept_pat!(p | q); + +fn main() {} diff --git a/src/test/ui/pattern/or-pattern-macro-pat.rs b/src/test/ui/pattern/or-pattern-macro-pat.rs new file mode 100644 index 00000000000..8749407675b --- /dev/null +++ b/src/test/ui/pattern/or-pattern-macro-pat.rs @@ -0,0 +1,44 @@ +// run-pass +// edition:2021 +// ignore-test +// FIXME(mark-i-m): enable this test again when 2021 machinery is available + +#![feature(or_patterns)] + +use Foo::*; + +#[derive(Eq, PartialEq, Debug)] +enum Foo { + A(u64), + B(u64), + C, + D, +} + +macro_rules! foo { + ($orpat:pat, $val:expr) => { + match $val { + x @ ($orpat) => x, // leading vert would not be allowed in $orpat + _ => B(0xDEADBEEFu64), + } + }; +} + +macro_rules! bar { + ($orpat:pat, $val:expr) => { + match $val { + $orpat => 42, // leading vert allowed here + _ => 0xDEADBEEFu64, + } + }; +} + +fn main() { + // Test or-pattern. + let y = foo!(A(_)|B(_), A(32)); + assert_eq!(y, A(32)); + + // Leading vert in or-pattern. + let y = bar!(|C| D, C); + assert_eq!(y, 42u64); +} -- cgit 1.4.1-3-g733a5 From 4ae99cc843b923d05e5c0993f732c5e0a9097852 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sat, 19 Dec 2020 19:29:56 +0900 Subject: Fix ICE when pointing at multi bytes character --- compiler/rustc_parse/src/parser/mod.rs | 6 +-- .../multibyte-char-use-seperator-issue-80134.rs | 12 +++++ ...multibyte-char-use-seperator-issue-80134.stderr | 52 ++++++++++++++++++++++ src/test/ui/similar-tokens.fixed | 13 ------ src/test/ui/similar-tokens.rs | 2 - src/test/ui/similar-tokens.stderr | 2 +- 6 files changed, 66 insertions(+), 21 deletions(-) create mode 100644 src/test/ui/parser/multibyte-char-use-seperator-issue-80134.rs create mode 100644 src/test/ui/parser/multibyte-char-use-seperator-issue-80134.stderr delete mode 100644 src/test/ui/similar-tokens.fixed (limited to 'compiler/rustc_parse/src/parser/mod.rs') diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index e19ebb8fd2f..1062000fede 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -721,13 +721,9 @@ impl<'a> Parser<'a> { Ok(t) => { // Parsed successfully, therefore most probably the code only // misses a separator. - let mut exp_span = self.sess.source_map().next_point(sp); - if self.sess.source_map().is_multiline(exp_span) { - exp_span = sp; - } expect_err .span_suggestion_short( - exp_span, + sp, &format!("missing `{}`", token_str), token_str, Applicability::MaybeIncorrect, diff --git a/src/test/ui/parser/multibyte-char-use-seperator-issue-80134.rs b/src/test/ui/parser/multibyte-char-use-seperator-issue-80134.rs new file mode 100644 index 00000000000..f3ae3aba9b9 --- /dev/null +++ b/src/test/ui/parser/multibyte-char-use-seperator-issue-80134.rs @@ -0,0 +1,12 @@ +// Regression test for #80134. + +fn main() { + (()é); + //~^ ERROR: expected one of `)`, `,`, `.`, `?`, or an operator + //~| ERROR: cannot find value `é` in this scope + //~| ERROR: non-ascii idents are not fully supported + (()氷); + //~^ ERROR: expected one of `)`, `,`, `.`, `?`, or an operator + //~| ERROR: cannot find value `氷` in this scope + //~| ERROR: non-ascii idents are not fully supported +} diff --git a/src/test/ui/parser/multibyte-char-use-seperator-issue-80134.stderr b/src/test/ui/parser/multibyte-char-use-seperator-issue-80134.stderr new file mode 100644 index 00000000000..892cc92b1bd --- /dev/null +++ b/src/test/ui/parser/multibyte-char-use-seperator-issue-80134.stderr @@ -0,0 +1,52 @@ +error: expected one of `)`, `,`, `.`, `?`, or an operator, found `é` + --> $DIR/multibyte-char-use-seperator-issue-80134.rs:4:8 + | +LL | (()é); + | ^ + | | + | expected one of `)`, `,`, `.`, `?`, or an operator + | help: missing `,` + +error: expected one of `)`, `,`, `.`, `?`, or an operator, found `氷` + --> $DIR/multibyte-char-use-seperator-issue-80134.rs:8:8 + | +LL | (()氷); + | -^ + | | + | expected one of `)`, `,`, `.`, `?`, or an operator + | help: missing `,` + +error[E0425]: cannot find value `é` in this scope + --> $DIR/multibyte-char-use-seperator-issue-80134.rs:4:8 + | +LL | (()é); + | ^ not found in this scope + +error[E0425]: cannot find value `氷` in this scope + --> $DIR/multibyte-char-use-seperator-issue-80134.rs:8:8 + | +LL | (()氷); + | ^^ not found in this scope + +error[E0658]: non-ascii idents are not fully supported + --> $DIR/multibyte-char-use-seperator-issue-80134.rs:4:8 + | +LL | (()é); + | ^ + | + = note: see issue #55467 for more information + = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable + +error[E0658]: non-ascii idents are not fully supported + --> $DIR/multibyte-char-use-seperator-issue-80134.rs:8:8 + | +LL | (()氷); + | ^^ + | + = note: see issue #55467 for more information + = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0425, E0658. +For more information about an error, try `rustc --explain E0425`. diff --git a/src/test/ui/similar-tokens.fixed b/src/test/ui/similar-tokens.fixed deleted file mode 100644 index addba76ae3b..00000000000 --- a/src/test/ui/similar-tokens.fixed +++ /dev/null @@ -1,13 +0,0 @@ -// run-rustfix - -#![allow(unused_imports)] - -pub mod x { - pub struct A; - pub struct B; -} - -// `.` is similar to `,` so list parsing should continue to closing `}` -use x::{A, B}; //~ ERROR expected one of `,`, `::`, `as`, or `}`, found `.` - -fn main() {} diff --git a/src/test/ui/similar-tokens.rs b/src/test/ui/similar-tokens.rs index 3d1bf5fe54a..e3024c61ad2 100644 --- a/src/test/ui/similar-tokens.rs +++ b/src/test/ui/similar-tokens.rs @@ -1,5 +1,3 @@ -// run-rustfix - #![allow(unused_imports)] pub mod x { diff --git a/src/test/ui/similar-tokens.stderr b/src/test/ui/similar-tokens.stderr index 6a8d09ebae6..90acfc052dd 100644 --- a/src/test/ui/similar-tokens.stderr +++ b/src/test/ui/similar-tokens.stderr @@ -1,5 +1,5 @@ error: expected one of `,`, `::`, `as`, or `}`, found `.` - --> $DIR/similar-tokens.rs:11:10 + --> $DIR/similar-tokens.rs:9:10 | LL | use x::{A. B}; | ^ -- cgit 1.4.1-3-g733a5 From 40bf3c0f09240836d55431165f20cf221816c958 Mon Sep 17 00:00:00 2001 From: mark Date: Mon, 28 Dec 2020 16:57:13 -0600 Subject: Implement edition-based macro pat feature --- compiler/rustc_ast/src/token.rs | 35 +++++++++-- compiler/rustc_expand/src/mbe/macro_parser.rs | 24 ++------ compiler/rustc_expand/src/mbe/macro_rules.rs | 16 +++-- compiler/rustc_expand/src/mbe/quoted.rs | 68 +++++++++++++++------- compiler/rustc_feature/src/active.rs | 3 + compiler/rustc_parse/src/parser/mod.rs | 1 - compiler/rustc_parse/src/parser/nonterminal.rs | 27 ++++----- compiler/rustc_parse/src/parser/pat.rs | 7 --- compiler/rustc_span/src/symbol.rs | 3 + src/test/ui/feature-gate-edition_macro_pats.rs | 8 +++ src/test/ui/feature-gate-edition_macro_pats.stderr | 21 +++++++ src/test/ui/macros/edition-macro-pats.rs | 14 +++++ 12 files changed, 151 insertions(+), 76 deletions(-) create mode 100644 src/test/ui/feature-gate-edition_macro_pats.rs create mode 100644 src/test/ui/feature-gate-edition_macro_pats.stderr create mode 100644 src/test/ui/macros/edition-macro-pats.rs (limited to 'compiler/rustc_parse/src/parser/mod.rs') diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index cd1e444bcf7..6dde304e8cf 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -15,7 +15,7 @@ use rustc_span::hygiene::ExpnKind; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{kw, sym}; use rustc_span::symbol::{Ident, Symbol}; -use rustc_span::{self, FileName, RealFileName, Span, DUMMY_SP}; +use rustc_span::{self, edition::Edition, FileName, RealFileName, Span, DUMMY_SP}; use std::borrow::Cow; use std::{fmt, mem}; @@ -690,7 +690,16 @@ pub enum NonterminalKind { Item, Block, Stmt, - Pat, + Pat2018 { + /// Keep track of whether the user used `:pat2018` or `:pat` and we inferred it from the + /// edition of the span. This is used for diagnostics. + inferred: bool, + }, + Pat2021 { + /// Keep track of whether the user used `:pat2018` or `:pat` and we inferred it from the + /// edition of the span. This is used for diagnostics. + inferred: bool, + }, Expr, Ty, Ident, @@ -703,12 +712,25 @@ pub enum NonterminalKind { } impl NonterminalKind { - pub fn from_symbol(symbol: Symbol) -> Option { + /// The `edition` closure is used to get the edition for the given symbol. Doing + /// `span.edition()` is expensive, so we do it lazily. + pub fn from_symbol( + symbol: Symbol, + edition: impl FnOnce() -> Edition, + ) -> Option { Some(match symbol { sym::item => NonterminalKind::Item, sym::block => NonterminalKind::Block, sym::stmt => NonterminalKind::Stmt, - sym::pat => NonterminalKind::Pat, + sym::pat => match edition() { + Edition::Edition2015 | Edition::Edition2018 => { + NonterminalKind::Pat2018 { inferred: true } + } + // FIXME(mark-i-m): uncomment when 2021 machinery is available. + //Edition::Edition2021 => NonterminalKind::Pat2021{inferred:true}, + }, + sym::pat2018 => NonterminalKind::Pat2018 { inferred: false }, + sym::pat2021 => NonterminalKind::Pat2021 { inferred: false }, sym::expr => NonterminalKind::Expr, sym::ty => NonterminalKind::Ty, sym::ident => NonterminalKind::Ident, @@ -726,7 +748,10 @@ impl NonterminalKind { NonterminalKind::Item => sym::item, NonterminalKind::Block => sym::block, NonterminalKind::Stmt => sym::stmt, - NonterminalKind::Pat => sym::pat, + NonterminalKind::Pat2018 { inferred: false } => sym::pat2018, + NonterminalKind::Pat2021 { inferred: false } => sym::pat2021, + NonterminalKind::Pat2018 { inferred: true } + | NonterminalKind::Pat2021 { inferred: true } => sym::pat, NonterminalKind::Expr => sym::expr, NonterminalKind::Ty => sym::ty, NonterminalKind::Ident => sym::ident, diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index 0c44f5fe9e1..e76cc6f1fed 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -77,9 +77,9 @@ use TokenTreeOrTokenTreeSlice::*; use crate::mbe::{self, TokenTree}; use rustc_ast::token::{self, DocComment, Nonterminal, Token}; -use rustc_parse::parser::{OrPatNonterminalMode, Parser}; +use rustc_parse::parser::Parser; use rustc_session::parse::ParseSess; -use rustc_span::{edition::Edition, symbol::MacroRulesNormalizedIdent}; +use rustc_span::symbol::MacroRulesNormalizedIdent; use smallvec::{smallvec, SmallVec}; @@ -419,18 +419,6 @@ fn token_name_eq(t1: &Token, t2: &Token) -> bool { } } -/// In edition 2015/18, `:pat` can only match `pat` because otherwise, we have -/// breakage. As of edition 2021, `:pat` matches `top_pat`. -/// -/// See for more info. -fn or_pat_mode(edition: Edition) -> OrPatNonterminalMode { - match edition { - Edition::Edition2015 | Edition::Edition2018 => OrPatNonterminalMode::NoTopAlt, - // FIXME(mark-i-m): uncomment this when edition 2021 machinery is added. - // Edition::Edition2021 => OrPatNonterminalMode::TopPat, - } -} - /// Process the matcher positions of `cur_items` until it is empty. In the process, this will /// produce more items in `next_items`, `eof_items`, and `bb_items`. /// @@ -578,14 +566,13 @@ fn inner_parse_loop<'root, 'tt>( // We need to match a metavar with a valid ident... call out to the black-box // parser by adding an item to `bb_items`. - TokenTree::MetaVarDecl(span, _, Some(kind)) => { + TokenTree::MetaVarDecl(_, _, Some(kind)) => { // Built-in nonterminals never start with these tokens, so we can eliminate // them from consideration. // // We use the span of the metavariable declaration to determine any // edition-specific matching behavior for non-terminals. - if Parser::nonterminal_may_begin_with(kind, token, or_pat_mode(span.edition())) - { + if Parser::nonterminal_may_begin_with(kind, token) { bb_items.push(item); } } @@ -749,8 +736,7 @@ pub(super) fn parse_tt(parser: &mut Cow<'_, Parser<'_>>, ms: &[TokenTree]) -> Na let match_cur = item.match_cur; // We use the span of the metavariable declaration to determine any // edition-specific matching behavior for non-terminals. - let nt = match parser.to_mut().parse_nonterminal(kind, or_pat_mode(span.edition())) - { + let nt = match parser.to_mut().parse_nonterminal(kind) { Err(mut err) => { err.span_label( span, diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 89d375b257d..3d126749d54 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -476,10 +476,15 @@ pub fn compile_declarative_macro( .map(|m| { if let MatchedNonterminal(ref nt) = *m { if let NtTT(ref tt) = **nt { - let tt = - mbe::quoted::parse(tt.clone().into(), true, &sess.parse_sess, def.id) - .pop() - .unwrap(); + let tt = mbe::quoted::parse( + tt.clone().into(), + true, + &sess.parse_sess, + def.id, + features, + ) + .pop() + .unwrap(); valid &= check_lhs_nt_follows(&sess.parse_sess, features, &def.attrs, &tt); return tt; } @@ -501,6 +506,7 @@ pub fn compile_declarative_macro( false, &sess.parse_sess, def.id, + features, ) .pop() .unwrap(); @@ -1090,7 +1096,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { _ => IsInFollow::No(TOKENS), } } - NonterminalKind::Pat => { + NonterminalKind::Pat2018 { .. } | NonterminalKind::Pat2021 { .. } => { const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`|`", "`if`", "`in`"]; match tok { TokenTree::Token(token) => match token.kind { diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index 01b11bb979d..a4b44931fc1 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -5,8 +5,9 @@ use rustc_ast::token::{self, Token}; use rustc_ast::tokenstream; use rustc_ast::{NodeId, DUMMY_NODE_ID}; use rustc_ast_pretty::pprust; -use rustc_session::parse::ParseSess; -use rustc_span::symbol::{kw, Ident}; +use rustc_feature::Features; +use rustc_session::parse::{feature_err, ParseSess}; +use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Span; @@ -29,10 +30,8 @@ const VALID_FRAGMENT_NAMES_MSG: &str = "valid fragment specifiers are \ /// `ident` are "matchers". They are not present in the body of a macro rule -- just in the /// pattern, so we pass a parameter to indicate whether to expect them or not. /// - `sess`: the parsing session. Any errors will be emitted to this session. -/// - `features`, `attrs`: language feature flags and attributes so that we know whether to use -/// unstable features or not. -/// - `edition`: which edition are we in. -/// - `macro_node_id`: the NodeId of the macro we are parsing. +/// - `node_id`: the NodeId of the macro we are parsing. +/// - `features`: language features so we can do feature gating. /// /// # Returns /// @@ -42,6 +41,7 @@ pub(super) fn parse( expect_matchers: bool, sess: &ParseSess, node_id: NodeId, + features: &Features, ) -> Vec { // Will contain the final collection of `self::TokenTree` let mut result = Vec::new(); @@ -52,7 +52,7 @@ pub(super) fn parse( while let Some(tree) = trees.next() { // Given the parsed tree, if there is a metavar and we are expecting matchers, actually // parse out the matcher (i.e., in `$id:ident` this would parse the `:` and `ident`). - let tree = parse_tree(tree, &mut trees, expect_matchers, sess, node_id); + let tree = parse_tree(tree, &mut trees, expect_matchers, sess, node_id, features); match tree { TokenTree::MetaVar(start_sp, ident) if expect_matchers => { let span = match trees.next() { @@ -61,18 +61,39 @@ pub(super) fn parse( Some(tokenstream::TokenTree::Token(token)) => match token.ident() { Some((frag, _)) => { let span = token.span.with_lo(start_sp.lo()); - let kind = token::NonterminalKind::from_symbol(frag.name) - .unwrap_or_else(|| { - let msg = format!( - "invalid fragment specifier `{}`", - frag.name - ); - sess.span_diagnostic - .struct_span_err(span, &msg) - .help(VALID_FRAGMENT_NAMES_MSG) + + match frag.name { + sym::pat2018 | sym::pat2021 => { + if !features.edition_macro_pats { + feature_err( + sess, + sym::edition_macro_pats, + frag.span, + "`pat2018` and `pat2021` are unstable.", + ) .emit(); - token::NonterminalKind::Ident - }); + } + } + _ => {} + } + + let kind = + token::NonterminalKind::from_symbol(frag.name, || { + span.edition() + }) + .unwrap_or_else( + || { + let msg = format!( + "invalid fragment specifier `{}`", + frag.name + ); + sess.span_diagnostic + .struct_span_err(span, &msg) + .help(VALID_FRAGMENT_NAMES_MSG) + .emit(); + token::NonterminalKind::Ident + }, + ); result.push(TokenTree::MetaVarDecl(span, ident, Some(kind))); continue; } @@ -110,14 +131,14 @@ pub(super) fn parse( /// converting `tree` /// - `expect_matchers`: same as for `parse` (see above). /// - `sess`: the parsing session. Any errors will be emitted to this session. -/// - `features`, `attrs`: language feature flags and attributes so that we know whether to use -/// unstable features or not. +/// - `features`: language features so we can do feature gating. fn parse_tree( tree: tokenstream::TokenTree, outer_trees: &mut impl Iterator, expect_matchers: bool, sess: &ParseSess, node_id: NodeId, + features: &Features, ) -> TokenTree { // Depending on what `tree` is, we could be parsing different parts of a macro match tree { @@ -145,7 +166,7 @@ fn parse_tree( sess.span_diagnostic.span_err(span.entire(), &msg); } // Parse the contents of the sequence itself - let sequence = parse(tts, expect_matchers, sess, node_id); + let sequence = parse(tts, expect_matchers, sess, node_id, features); // Get the Kleene operator and optional separator let (separator, kleene) = parse_sep_and_kleene_op(&mut trees, span.entire(), sess); @@ -196,7 +217,10 @@ fn parse_tree( // descend into the delimited set and further parse it. tokenstream::TokenTree::Delimited(span, delim, tts) => TokenTree::Delimited( span, - Lrc::new(Delimited { delim, tts: parse(tts, expect_matchers, sess, node_id) }), + Lrc::new(Delimited { + delim, + tts: parse(tts, expect_matchers, sess, node_id, features), + }), ), } } diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index d60ae8ef75b..c61857a1cd0 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -620,6 +620,9 @@ declare_features! ( /// Allows arbitrary expressions in key-value attributes at parse time. (active, extended_key_value_attributes, "1.50.0", Some(78835), None), + /// `:pat2018` and `:pat2021` macro matchers. + (active, edition_macro_pats, "1.51.0", Some(54883), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index e19ebb8fd2f..d51a0fcbf09 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -12,7 +12,6 @@ mod ty; use crate::lexer::UnmatchedBrace; pub use diagnostics::AttemptLocalParseRecovery; use diagnostics::Error; -pub use pat::OrPatNonterminalMode; pub use path::PathStyle; use rustc_ast::ptr::P; diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index a6b9ac1014e..eb5d7075f00 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -4,7 +4,7 @@ use rustc_ast_pretty::pprust; use rustc_errors::PResult; use rustc_span::symbol::{kw, Ident}; -use crate::parser::pat::{GateOr, OrPatNonterminalMode, RecoverComma}; +use crate::parser::pat::{GateOr, RecoverComma}; use crate::parser::{FollowedByType, Parser, PathStyle}; impl<'a> Parser<'a> { @@ -12,11 +12,7 @@ impl<'a> Parser<'a> { /// /// Returning `false` is a *stability guarantee* that such a matcher will *never* begin with that /// token. Be conservative (return true) if not sure. - pub fn nonterminal_may_begin_with( - kind: NonterminalKind, - token: &Token, - or_pat_mode: OrPatNonterminalMode, - ) -> bool { + pub fn nonterminal_may_begin_with(kind: NonterminalKind, token: &Token) -> bool { /// Checks whether the non-terminal may contain a single (non-keyword) identifier. fn may_be_ident(nt: &token::Nonterminal) -> bool { match *nt { @@ -62,7 +58,7 @@ impl<'a> Parser<'a> { }, _ => false, }, - NonterminalKind::Pat => match token.kind { + NonterminalKind::Pat2018 { .. } | NonterminalKind::Pat2021 { .. } => match token.kind { token::Ident(..) | // box, ref, mut, and other identifiers (can stricten) token::OpenDelim(token::Paren) | // tuple pattern token::OpenDelim(token::Bracket) | // slice pattern @@ -76,7 +72,7 @@ impl<'a> Parser<'a> { token::Lt | // path (UFCS constant) token::BinOp(token::Shl) => true, // path (double UFCS) // leading vert `|` or-pattern - token::BinOp(token::Or) => matches!(or_pat_mode, OrPatNonterminalMode::TopPat), + token::BinOp(token::Or) => matches!(kind, NonterminalKind::Pat2021 {..}), token::Interpolated(ref nt) => may_be_ident(nt), _ => false, }, @@ -94,11 +90,7 @@ impl<'a> Parser<'a> { } /// Parse a non-terminal (e.g. MBE `:pat` or `:ident`). - pub fn parse_nonterminal( - &mut self, - kind: NonterminalKind, - or_pat_mode: OrPatNonterminalMode, - ) -> PResult<'a, Nonterminal> { + pub fn parse_nonterminal(&mut self, kind: NonterminalKind) -> PResult<'a, Nonterminal> { // Any `Nonterminal` which stores its tokens (currently `NtItem` and `NtExpr`) // needs to have them force-captured here. // A `macro_rules!` invocation may pass a captured item/expr to a proc-macro, @@ -141,12 +133,13 @@ impl<'a> Parser<'a> { } } } - NonterminalKind::Pat => { - let (mut pat, tokens) = self.collect_tokens(|this| match or_pat_mode { - OrPatNonterminalMode::TopPat => { + NonterminalKind::Pat2018 { .. } | NonterminalKind::Pat2021 { .. } => { + let (mut pat, tokens) = self.collect_tokens(|this| match kind { + NonterminalKind::Pat2018 { .. } => this.parse_pat(None), + NonterminalKind::Pat2021 { .. } => { this.parse_top_pat(GateOr::Yes, RecoverComma::No) } - OrPatNonterminalMode::NoTopAlt => this.parse_pat(None), + _ => unreachable!(), })?; // We have have eaten an NtPat, which could already have tokens if pat.tokens.is_none() { diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 1da371e0b72..456e32680fe 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -31,13 +31,6 @@ pub(super) enum RecoverComma { No, } -/// Used when parsing a non-terminal (see `parse_nonterminal`) to determine if `:pat` should match -/// `top_pat` or `pat`. See issue . -pub enum OrPatNonterminalMode { - TopPat, - NoTopAlt, -} - impl<'a> Parser<'a> { /// Parses a pattern. /// diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index bc57a00e31b..eb3588d5775 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -470,6 +470,7 @@ symbols! { dropck_parametricity, dylib, dyn_trait, + edition_macro_pats, eh_catch_typeinfo, eh_personality, emit_enum, @@ -808,6 +809,8 @@ symbols! { partial_ord, passes, pat, + pat2018, + pat2021, path, pattern_parentheses, phantom_data, diff --git a/src/test/ui/feature-gate-edition_macro_pats.rs b/src/test/ui/feature-gate-edition_macro_pats.rs new file mode 100644 index 00000000000..bd8a21ea36a --- /dev/null +++ b/src/test/ui/feature-gate-edition_macro_pats.rs @@ -0,0 +1,8 @@ +// Feature gate test for `edition_macro_pats` feature. + +macro_rules! foo { + ($x:pat2018) => {}; //~ERROR `pat2018` and `pat2021` are unstable + ($x:pat2021) => {}; //~ERROR `pat2018` and `pat2021` are unstable +} + +fn main() {} diff --git a/src/test/ui/feature-gate-edition_macro_pats.stderr b/src/test/ui/feature-gate-edition_macro_pats.stderr new file mode 100644 index 00000000000..89bfb239d9e --- /dev/null +++ b/src/test/ui/feature-gate-edition_macro_pats.stderr @@ -0,0 +1,21 @@ +error[E0658]: `pat2018` and `pat2021` are unstable. + --> $DIR/feature-gate-edition_macro_pats.rs:4:9 + | +LL | ($x:pat2018) => {}; + | ^^^^^^^ + | + = note: see issue #54883 for more information + = help: add `#![feature(edition_macro_pats)]` to the crate attributes to enable + +error[E0658]: `pat2018` and `pat2021` are unstable. + --> $DIR/feature-gate-edition_macro_pats.rs:5:9 + | +LL | ($x:pat2021) => {}; + | ^^^^^^^ + | + = note: see issue #54883 for more information + = help: add `#![feature(edition_macro_pats)]` to the crate attributes to enable + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/macros/edition-macro-pats.rs b/src/test/ui/macros/edition-macro-pats.rs new file mode 100644 index 00000000000..ea1f9bff6bf --- /dev/null +++ b/src/test/ui/macros/edition-macro-pats.rs @@ -0,0 +1,14 @@ +// run-pass + +#![feature(or_patterns)] +#![feature(edition_macro_pats)] + +macro_rules! foo { + (a $x:pat2018) => {}; + (b $x:pat2021) => {}; +} + +fn main() { + foo!(a None); + foo!(b 1 | 2); +} -- cgit 1.4.1-3-g733a5