diff options
Diffstat (limited to 'compiler/rustc_parse/src')
| -rw-r--r-- | compiler/rustc_parse/src/lib.rs | 394 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/diagnostics.rs | 18 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 40 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/generics.rs | 16 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/item.rs | 31 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/mod.rs | 20 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/nonterminal.rs | 16 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/pat.rs | 10 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/path.rs | 36 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/stmt.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/validate_attr.rs | 5 |
11 files changed, 174 insertions, 416 deletions
diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 44999c9b63a..62fd6936d21 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -7,22 +7,18 @@ use rustc_ast as ast; use rustc_ast::attr::HasAttrs; -use rustc_ast::token::{self, DelimToken, Nonterminal, Token, TokenKind}; -use rustc_ast::tokenstream::{self, LazyTokenStream, TokenStream, TokenTree}; +use rustc_ast::token::{self, Nonterminal}; +use rustc_ast::tokenstream::{self, CanSynthesizeMissingTokens, LazyTokenStream, TokenStream}; use rustc_ast_pretty::pprust; -use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lrc; use rustc_errors::{Diagnostic, FatalError, Level, PResult}; use rustc_session::parse::ParseSess; -use rustc_span::{symbol::kw, FileName, SourceFile, Span, DUMMY_SP}; +use rustc_span::{FileName, SourceFile, Span}; -use smallvec::SmallVec; -use std::cell::RefCell; -use std::mem; use std::path::Path; use std::str; -use tracing::{debug, info}; +use tracing::debug; pub const MACRO_ARGUMENTS: Option<&str> = Some("macro arguments"); @@ -237,7 +233,11 @@ pub fn parse_in<'a, T>( // NOTE(Centril): The following probably shouldn't be here but it acknowledges the // fact that architecturally, we are using parsing (read on below to understand why). -pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> TokenStream { +pub fn nt_to_tokenstream( + nt: &Nonterminal, + sess: &ParseSess, + synthesize_tokens: CanSynthesizeMissingTokens, +) -> TokenStream { // A `Nonterminal` is often a parsed AST item. At this point we now // need to convert the parsed AST to an actual token stream, e.g. // un-parse it basically. @@ -255,9 +255,18 @@ pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> Toke |tokens: Option<&LazyTokenStream>| tokens.as_ref().map(|t| t.create_token_stream()); let tokens = match *nt { - Nonterminal::NtItem(ref item) => prepend_attrs(&item.attrs, item.tokens.as_ref()), + Nonterminal::NtItem(ref item) => prepend_attrs(sess, &item.attrs, nt, item.tokens.as_ref()), Nonterminal::NtBlock(ref block) => convert_tokens(block.tokens.as_ref()), - Nonterminal::NtStmt(ref stmt) => prepend_attrs(stmt.attrs(), stmt.tokens()), + Nonterminal::NtStmt(ref stmt) => { + let do_prepend = |tokens| prepend_attrs(sess, stmt.attrs(), nt, tokens); + if let ast::StmtKind::Empty = stmt.kind { + let tokens: TokenStream = + tokenstream::TokenTree::token(token::Semi, stmt.span).into(); + do_prepend(Some(&LazyTokenStream::new(tokens))) + } else { + do_prepend(stmt.tokens()) + } + } Nonterminal::NtPat(ref pat) => convert_tokens(pat.tokens.as_ref()), Nonterminal::NtTy(ref ty) => convert_tokens(ty.tokens.as_ref()), Nonterminal::NtIdent(ident, is_raw) => { @@ -274,376 +283,43 @@ pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> Toke if expr.tokens.is_none() { debug!("missing tokens for expr {:?}", expr); } - prepend_attrs(&expr.attrs, expr.tokens.as_ref()) + prepend_attrs(sess, &expr.attrs, nt, expr.tokens.as_ref()) } }; - // Caches the stringification of 'good' `TokenStreams` which passed - // `tokenstream_probably_equal_for_proc_macro`. This allows us to avoid - // repeatedly stringifying and comparing the same `TokenStream` for deeply - // nested nonterminals. - // - // We cache by the strinification instead of the `TokenStream` to avoid - // needing to implement `Hash` for `TokenStream`. Note that it's possible to - // have two distinct `TokenStream`s that stringify to the same result - // (e.g. if they differ only in hygiene information). However, any - // information lost during the stringification process is also intentionally - // ignored by `tokenstream_probably_equal_for_proc_macro`, so it's fine - // that a single cache entry may 'map' to multiple distinct `TokenStream`s. - // - // This is a temporary hack to prevent compilation blowup on certain inputs. - // The entire pretty-print/retokenize process will be removed soon. - thread_local! { - static GOOD_TOKEN_CACHE: RefCell<FxHashSet<String>> = Default::default(); - } - - // FIXME(#43081): Avoid this pretty-print + reparse hack - // Pretty-print the AST struct without inserting any parenthesis - // beyond those explicitly written by the user (e.g. `ExpnKind::Paren`). - // The resulting stream may have incorrect precedence, but it's only - // ever used for a comparison against the capture tokenstream. - let source = pprust::nonterminal_to_string_no_extra_parens(nt); - let filename = FileName::macro_expansion_source_code(&source); - let reparsed_tokens = parse_stream_from_source_str(filename, source.clone(), sess, Some(span)); - - // During early phases of the compiler the AST could get modified - // directly (e.g., attributes added or removed) and the internal cache - // of tokens my not be invalidated or updated. Consequently if the - // "lossless" token stream disagrees with our actual stringification - // (which has historically been much more battle-tested) then we go - // with the lossy stream anyway (losing span information). - // - // Note that the comparison isn't `==` here to avoid comparing spans, - // but it *also* is a "probable" equality which is a pretty weird - // definition. We mostly want to catch actual changes to the AST - // like a `#[cfg]` being processed or some weird `macro_rules!` - // expansion. - // - // What we *don't* want to catch is the fact that a user-defined - // literal like `0xf` is stringified as `15`, causing the cached token - // stream to not be literal `==` token-wise (ignoring spans) to the - // token stream we got from stringification. - // - // Instead the "probably equal" check here is "does each token - // recursively have the same discriminant?" We basically don't look at - // the token values here and assume that such fine grained token stream - // modifications, including adding/removing typically non-semantic - // tokens such as extra braces and commas, don't happen. if let Some(tokens) = tokens { - if GOOD_TOKEN_CACHE.with(|cache| cache.borrow().contains(&source)) { - return tokens; - } - - // Compare with a non-relaxed delim match to start. - if tokenstream_probably_equal_for_proc_macro(&tokens, &reparsed_tokens, sess, false) { - GOOD_TOKEN_CACHE.with(|cache| cache.borrow_mut().insert(source.clone())); - return tokens; - } - - // The check failed. This time, we pretty-print the AST struct with parenthesis - // inserted to preserve precedence. This may cause `None`-delimiters in the captured - // token stream to match up with inserted parenthesis in the reparsed stream. - let source_with_parens = pprust::nonterminal_to_string(nt); - let filename_with_parens = FileName::macro_expansion_source_code(&source_with_parens); - - if GOOD_TOKEN_CACHE.with(|cache| cache.borrow().contains(&source_with_parens)) { - return tokens; - } - - let reparsed_tokens_with_parens = parse_stream_from_source_str( - filename_with_parens, - source_with_parens, - sess, - Some(span), - ); - - // Compare with a relaxed delim match - we want inserted parenthesis in the - // reparsed stream to match `None`-delimiters in the original stream. - if tokenstream_probably_equal_for_proc_macro( - &tokens, - &reparsed_tokens_with_parens, - sess, - true, - ) { - GOOD_TOKEN_CACHE.with(|cache| cache.borrow_mut().insert(source.clone())); - return tokens; - } - - info!( - "cached tokens found, but they're not \"probably equal\", \ - going with stringified version" - ); - info!("cached tokens: {}", pprust::tts_to_string(&tokens)); - info!("reparsed tokens: {}", pprust::tts_to_string(&reparsed_tokens_with_parens)); - - info!("cached tokens debug: {:?}", tokens); - info!("reparsed tokens debug: {:?}", reparsed_tokens_with_parens); + return tokens; + } else if matches!(synthesize_tokens, CanSynthesizeMissingTokens::Yes) { + return fake_token_stream(sess, nt); + } else { + panic!("Missing tokens for nt {:?}", pprust::nonterminal_to_string(nt)); } - reparsed_tokens } -// See comments in `Nonterminal::to_tokenstream` for why we care about -// *probably* equal here rather than actual equality -// -// This is otherwise the same as `eq_unspanned`, only recursing with a -// different method. -pub fn tokenstream_probably_equal_for_proc_macro( - tokens: &TokenStream, - reparsed_tokens: &TokenStream, - sess: &ParseSess, - relaxed_delim_match: bool, -) -> bool { - // When checking for `probably_eq`, we ignore certain tokens that aren't - // preserved in the AST. Because they are not preserved, the pretty - // printer arbitrarily adds or removes them when printing as token - // streams, making a comparison between a token stream generated from an - // AST and a token stream which was parsed into an AST more reliable. - fn semantic_tree(tree: &TokenTree) -> bool { - if let TokenTree::Token(token) = tree { - if let - // The pretty printer tends to add trailing commas to - // everything, and in particular, after struct fields. - | token::Comma - // The pretty printer collapses many semicolons into one. - | token::Semi - // We don't preserve leading `|` tokens in patterns, so - // we ignore them entirely - | token::BinOp(token::BinOpToken::Or) - // We don't preserve trailing '+' tokens in trait bounds, - // so we ignore them entirely - | token::BinOp(token::BinOpToken::Plus) - // The pretty printer can turn `$crate` into `::crate_name` - | token::ModSep = token.kind { - return false; - } - } - true - } - - // When comparing two `TokenStream`s, we ignore the `IsJoint` information. - // - // However, `rustc_parse::lexer::tokentrees::TokenStreamBuilder` will - // use `Token.glue` on adjacent tokens with the proper `IsJoint`. - // Since we are ignoreing `IsJoint`, a 'glued' token (e.g. `BinOp(Shr)`) - // and its 'split'/'unglued' compoenents (e.g. `Gt, Gt`) are equivalent - // when determining if two `TokenStream`s are 'probably equal'. - // - // Therefore, we use `break_two_token_op` to convert all tokens - // to the 'unglued' form (if it exists). This ensures that two - // `TokenStream`s which differ only in how their tokens are glued - // will be considered 'probably equal', which allows us to keep spans. - // - // This is important when the original `TokenStream` contained - // extra spaces (e.g. `f :: < Vec < _ > > ( ) ;'). These extra spaces - // will be omitted when we pretty-print, which can cause the original - // and reparsed `TokenStream`s to differ in the assignment of `IsJoint`, - // leading to some tokens being 'glued' together in one stream but not - // the other. See #68489 for more details. - fn break_tokens(tree: TokenTree) -> impl Iterator<Item = TokenTree> { - // In almost all cases, we should have either zero or one levels - // of 'unglueing'. However, in some unusual cases, we may need - // to iterate breaking tokens mutliple times. For example: - // '[BinOpEq(Shr)] => [Gt, Ge] -> [Gt, Gt, Eq]' - let mut token_trees: SmallVec<[_; 2]>; - if let TokenTree::Token(token) = tree { - let mut out = SmallVec::<[_; 2]>::new(); - out.push(token); - // Iterate to fixpoint: - // * We start off with 'out' containing our initial token, and `temp` empty - // * If we are able to break any tokens in `out`, then `out` will have - // at least one more element than 'temp', so we will try to break tokens - // again. - // * If we cannot break any tokens in 'out', we are done - loop { - let mut temp = SmallVec::<[_; 2]>::new(); - let mut changed = false; - - for token in out.into_iter() { - if let Some((first, second)) = token.kind.break_two_token_op() { - temp.push(Token::new(first, DUMMY_SP)); - temp.push(Token::new(second, DUMMY_SP)); - changed = true; - } else { - temp.push(token); - } - } - out = temp; - if !changed { - break; - } - } - token_trees = out.into_iter().map(TokenTree::Token).collect(); - } else { - token_trees = SmallVec::new(); - token_trees.push(tree); - } - token_trees.into_iter() - } - - fn expand_token(tree: TokenTree, sess: &ParseSess) -> impl Iterator<Item = TokenTree> { - // When checking tokenstreams for 'probable equality', we are comparing - // a captured (from parsing) `TokenStream` to a reparsed tokenstream. - // The reparsed Tokenstream will never have `None`-delimited groups, - // since they are only ever inserted as a result of macro expansion. - // Therefore, inserting a `None`-delimtied group here (when we - // convert a nested `Nonterminal` to a tokenstream) would cause - // a mismatch with the reparsed tokenstream. - // - // Note that we currently do not handle the case where the - // reparsed stream has a `Parenthesis`-delimited group - // inserted. This will cause a spurious mismatch: - // issue #75734 tracks resolving this. - - let expanded: SmallVec<[_; 1]> = - if let TokenTree::Token(Token { kind: TokenKind::Interpolated(nt), span }) = &tree { - nt_to_tokenstream(nt, sess, *span) - .into_trees() - .flat_map(|t| expand_token(t, sess)) - .collect() - } else { - // Filter before and after breaking tokens, - // since we may want to ignore both glued and unglued tokens. - std::iter::once(tree) - .filter(semantic_tree) - .flat_map(break_tokens) - .filter(semantic_tree) - .collect() - }; - expanded.into_iter() - } - - // Break tokens after we expand any nonterminals, so that we break tokens - // that are produced as a result of nonterminal expansion. - let tokens = tokens.trees().flat_map(|t| expand_token(t, sess)); - let reparsed_tokens = reparsed_tokens.trees().flat_map(|t| expand_token(t, sess)); - - tokens.eq_by(reparsed_tokens, |t, rt| { - tokentree_probably_equal_for_proc_macro(&t, &rt, sess, relaxed_delim_match) - }) -} - -// See comments in `Nonterminal::to_tokenstream` for why we care about -// *probably* equal here rather than actual equality -// -// This is otherwise the same as `eq_unspanned`, only recursing with a -// different method. -pub fn tokentree_probably_equal_for_proc_macro( - token: &TokenTree, - reparsed_token: &TokenTree, - sess: &ParseSess, - relaxed_delim_match: bool, -) -> bool { - match (token, reparsed_token) { - (TokenTree::Token(token), TokenTree::Token(reparsed_token)) => { - token_probably_equal_for_proc_macro(token, reparsed_token) - } - ( - TokenTree::Delimited(_, delim, tokens), - TokenTree::Delimited(_, reparsed_delim, reparsed_tokens), - ) if delim == reparsed_delim => tokenstream_probably_equal_for_proc_macro( - tokens, - reparsed_tokens, - sess, - relaxed_delim_match, - ), - (TokenTree::Delimited(_, DelimToken::NoDelim, tokens), reparsed_token) => { - if relaxed_delim_match { - if let TokenTree::Delimited(_, DelimToken::Paren, reparsed_tokens) = reparsed_token - { - if tokenstream_probably_equal_for_proc_macro( - tokens, - reparsed_tokens, - sess, - relaxed_delim_match, - ) { - return true; - } - } - } - tokens.len() == 1 - && tokentree_probably_equal_for_proc_macro( - &tokens.trees().next().unwrap(), - reparsed_token, - sess, - relaxed_delim_match, - ) - } - _ => false, - } -} - -// See comments in `Nonterminal::to_tokenstream` for why we care about -// *probably* equal here rather than actual equality -fn token_probably_equal_for_proc_macro(first: &Token, other: &Token) -> bool { - if mem::discriminant(&first.kind) != mem::discriminant(&other.kind) { - return false; - } - use rustc_ast::token::TokenKind::*; - match (&first.kind, &other.kind) { - (&Eq, &Eq) - | (&Lt, &Lt) - | (&Le, &Le) - | (&EqEq, &EqEq) - | (&Ne, &Ne) - | (&Ge, &Ge) - | (&Gt, &Gt) - | (&AndAnd, &AndAnd) - | (&OrOr, &OrOr) - | (&Not, &Not) - | (&Tilde, &Tilde) - | (&At, &At) - | (&Dot, &Dot) - | (&DotDot, &DotDot) - | (&DotDotDot, &DotDotDot) - | (&DotDotEq, &DotDotEq) - | (&Comma, &Comma) - | (&Semi, &Semi) - | (&Colon, &Colon) - | (&ModSep, &ModSep) - | (&RArrow, &RArrow) - | (&LArrow, &LArrow) - | (&FatArrow, &FatArrow) - | (&Pound, &Pound) - | (&Dollar, &Dollar) - | (&Question, &Question) - | (&Eof, &Eof) => true, - - (&BinOp(a), &BinOp(b)) | (&BinOpEq(a), &BinOpEq(b)) => a == b, - - (&OpenDelim(a), &OpenDelim(b)) | (&CloseDelim(a), &CloseDelim(b)) => a == b, - - (&DocComment(a1, a2, a3), &DocComment(b1, b2, b3)) => a1 == b1 && a2 == b2 && a3 == b3, - - (&Literal(a), &Literal(b)) => a == b, - - (&Lifetime(a), &Lifetime(b)) => a == b, - (&Ident(a, b), &Ident(c, d)) => { - b == d && (a == c || a == kw::DollarCrate || c == kw::DollarCrate) - } - - (&Interpolated(..), &Interpolated(..)) => panic!("Unexpanded Interpolated!"), - - _ => panic!("forgot to add a token?"), - } +pub fn fake_token_stream(sess: &ParseSess, nt: &Nonterminal) -> TokenStream { + let source = pprust::nonterminal_to_string(nt); + let filename = FileName::macro_expansion_source_code(&source); + parse_stream_from_source_str(filename, source, sess, Some(nt.span())) } fn prepend_attrs( + sess: &ParseSess, attrs: &[ast::Attribute], + nt: &Nonterminal, tokens: Option<&tokenstream::LazyTokenStream>, ) -> Option<tokenstream::TokenStream> { - let tokens = tokens?.create_token_stream(); if attrs.is_empty() { - return Some(tokens); + return Some(tokens?.create_token_stream()); } let mut builder = tokenstream::TokenStreamBuilder::new(); for attr in attrs { // FIXME: Correctly handle tokens for inner attributes. // For now, we fall back to reparsing the original AST node if attr.style == ast::AttrStyle::Inner { - return None; + return Some(fake_token_stream(sess, nt)); } builder.push(attr.tokens()); } - builder.push(tokens); + builder.push(tokens?.create_token_stream()); Some(builder.build()) } diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 350a372a684..98c7b9a63a5 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1912,4 +1912,22 @@ impl<'a> Parser<'a> { *self = snapshot; Err(err) } + + /// Get the diagnostics for the cases where `move async` is found. + /// + /// `move_async_span` starts at the 'm' of the move keyword and ends with the 'c' of the async keyword + pub(super) fn incorrect_move_async_order_found( + &self, + move_async_span: Span, + ) -> DiagnosticBuilder<'a> { + let mut err = + self.struct_span_err(move_async_span, "the order of `move` and `async` is incorrect"); + err.span_suggestion_verbose( + move_async_span, + "try switching the order", + "async move".to_owned(), + Applicability::MaybeIncorrect, + ); + err + } } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 4d2167442be..f4332e4548a 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}; @@ -15,6 +15,7 @@ use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits}; use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, DiagnosticBuilder, PResult}; +use rustc_span::edition::LATEST_STABLE_EDITION; use rustc_span::source_map::{self, Span, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, Pos}; @@ -1598,12 +1599,8 @@ impl<'a> Parser<'a> { } else { Async::No }; - if let Async::Yes { span, .. } = asyncness { - // Feature-gate `async ||` closures. - self.sess.gated_spans.gate(sym::async_closure, span); - } - let capture_clause = self.parse_capture_clause(); + let capture_clause = self.parse_capture_clause()?; let decl = self.parse_fn_block_decl()?; let decl_hi = self.prev_token.span; let body = match decl.output { @@ -1618,6 +1615,11 @@ impl<'a> Parser<'a> { } }; + if let Async::Yes { span, .. } = asyncness { + // Feature-gate `async ||` closures. + self.sess.gated_spans.gate(sym::async_closure, span); + } + Ok(self.mk_expr( lo.to(body.span), ExprKind::Closure(capture_clause, asyncness, movability, decl, body, lo.to(decl_hi)), @@ -1626,8 +1628,18 @@ impl<'a> Parser<'a> { } /// Parses an optional `move` prefix to a closure-like construct. - fn parse_capture_clause(&mut self) -> CaptureBy { - if self.eat_keyword(kw::Move) { CaptureBy::Value } else { CaptureBy::Ref } + fn parse_capture_clause(&mut self) -> PResult<'a, CaptureBy> { + if self.eat_keyword(kw::Move) { + // Check for `move async` and recover + if self.check_keyword(kw::Async) { + let move_async_span = self.token.span.with_lo(self.prev_token.span.data().lo); + Err(self.incorrect_move_async_order_found(move_async_span)) + } else { + Ok(CaptureBy::Value) + } + } else { + Ok(CaptureBy::Ref) + } } /// Parses the `|arg, arg|` header of a closure. @@ -1729,7 +1741,7 @@ impl<'a> Parser<'a> { /// The `let` token has already been eaten. fn parse_let_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { 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 +1804,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 +1914,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()?; @@ -2019,7 +2031,7 @@ impl<'a> Parser<'a> { fn parse_async_block(&mut self, mut attrs: AttrVec) -> PResult<'a, P<Expr>> { let lo = self.token.span; self.expect_keyword(kw::Async)?; - let capture_clause = self.parse_capture_clause(); + let capture_clause = self.parse_capture_clause()?; let (iattrs, body) = self.parse_inner_attrs_and_block()?; attrs.extend(iattrs); let kind = ExprKind::Async(capture_clause, DUMMY_NODE_ID, body); @@ -2098,8 +2110,8 @@ impl<'a> Parser<'a> { let mut async_block_err = |e: &mut DiagnosticBuilder<'_>, span: Span| { recover_async = true; - e.span_label(span, "`async` blocks are only allowed in the 2018 edition"); - e.help("set `edition = \"2018\"` in `Cargo.toml`"); + e.span_label(span, "`async` blocks are only allowed in Rust 2018 or later"); + e.help(&format!("set `edition = \"{}\"` in `Cargo.toml`", LATEST_STABLE_EDITION)); e.note("for more on editions, read https://doc.rust-lang.org/edition-guide"); }; diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index ed8d4f78426..42a13376863 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -56,14 +56,26 @@ impl<'a> Parser<'a> { self.expect(&token::Colon)?; let ty = self.parse_ty()?; - self.sess.gated_spans.gate(sym::min_const_generics, const_span.to(self.prev_token.span)); + // Parse optional const generics default value, taking care of feature gating the spans + // with the unstable syntax mechanism. + let default = if self.eat(&token::Eq) { + // The gated span goes from the `=` to the end of the const argument that follows (and + // which could be a block expression). + let start = self.prev_token.span; + let const_arg = self.parse_const_arg()?; + let span = start.to(const_arg.value.span); + self.sess.gated_spans.gate(sym::const_generics_defaults, span); + Some(const_arg) + } else { + None + }; Ok(GenericParam { ident, id: ast::DUMMY_NODE_ID, attrs: preceding_attrs.into(), bounds: Vec::new(), - kind: GenericParamKind::Const { ty, kw_span: const_span }, + kind: GenericParamKind::Const { ty, kw_span: const_span, default }, is_placeholder: false, }) } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 634cce403df..4fcc9edb7d9 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -16,7 +16,7 @@ use rustc_ast::{FnHeader, ForeignItem, Path, PathSegment, Visibility, Visibility use rustc_ast::{MacArgs, MacCall, MacDelimiter}; use rustc_ast_pretty::pprust; use rustc_errors::{struct_span_err, Applicability, PResult, StashKey}; -use rustc_span::edition::Edition; +use rustc_span::edition::{Edition, LATEST_STABLE_EDITION}; use rustc_span::source_map::{self, Span}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -220,7 +220,22 @@ impl<'a> Parser<'a> { let info = if self.eat_keyword(kw::Use) { // USE ITEM let tree = self.parse_use_tree()?; - self.expect_semi()?; + + // If wildcard or glob-like brace syntax doesn't have `;`, + // the user may not know `*` or `{}` should be the last. + if let Err(mut e) = self.expect_semi() { + match tree.kind { + UseTreeKind::Glob => { + e.note("the wildcard token must be last on the path").emit(); + } + UseTreeKind::Nested(..) => { + e.note("glob-like brace syntax must be last on the path").emit(); + } + _ => (), + } + return Err(e); + } + (Ident::invalid(), ItemKind::Use(P(tree))) } else if self.check_fn_front_matter() { // FUNCTION ITEM @@ -494,7 +509,7 @@ impl<'a> Parser<'a> { let polarity = self.parse_polarity(); // Parse both types and traits as a type, then reinterpret if necessary. - let err_path = |span| ast::Path::from_ident(Ident::new(kw::Invalid, span)); + let err_path = |span| ast::Path::from_ident(Ident::new(kw::Empty, span)); let ty_first = if self.token.is_keyword(kw::For) && self.look_ahead(1, |t| t != &token::Lt) { let span = self.prev_token.span.between(self.token.span); @@ -1667,9 +1682,9 @@ impl<'a> Parser<'a> { fn ban_async_in_2015(&self, span: Span) { if span.rust_2015() { let diag = self.diagnostic(); - struct_span_err!(diag, span, E0670, "`async fn` is not permitted in the 2015 edition") - .span_label(span, "to use `async fn`, switch to Rust 2018") - .help("set `edition = \"2018\"` in `Cargo.toml`") + struct_span_err!(diag, span, E0670, "`async fn` is not permitted in Rust 2015") + .span_label(span, "to use `async fn`, switch to Rust 2018 or later") + .help(&format!("set `edition = \"{}\"` in `Cargo.toml`", LATEST_STABLE_EDITION)) .note("for more on editions, read https://doc.rust-lang.org/edition-guide") .emit(); } @@ -1699,7 +1714,7 @@ impl<'a> Parser<'a> { // Skip every token until next possible arg or end. p.eat_to_tokens(&[&token::Comma, &token::CloseDelim(token::Paren)]); // Create a placeholder argument for proper arg count (issue #34264). - Ok(dummy_arg(Ident::new(kw::Invalid, lo.to(p.prev_token.span)))) + Ok(dummy_arg(Ident::new(kw::Empty, lo.to(p.prev_token.span)))) }); // ...now that we've parsed the first argument, `self` is no longer allowed. first_param = false; @@ -1759,7 +1774,7 @@ impl<'a> Parser<'a> { } match ty { Ok(ty) => { - let ident = Ident::new(kw::Invalid, self.prev_token.span); + let ident = Ident::new(kw::Empty, self.prev_token.span); let bm = BindingMode::ByValue(Mutability::Not); let pat = self.mk_pat_ident(ty.span, bm, ident); (pat, ty) diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index d51a0fcbf09..45964b1c988 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -720,13 +720,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, @@ -987,8 +983,8 @@ impl<'a> Parser<'a> { _ => self.sess.gated_spans.gate(sym::extended_key_value_attributes, span), } - let token = token::Interpolated(Lrc::new(token::NtExpr(expr))); - MacArgs::Eq(eq_span, TokenTree::token(token, span).into()) + let token_kind = token::Interpolated(Lrc::new(token::NtExpr(expr))); + MacArgs::Eq(eq_span, Token::new(token_kind, span)) } else { MacArgs::Empty } @@ -1243,7 +1239,15 @@ impl<'a> Parser<'a> { f: impl FnOnce(&mut Self) -> PResult<'a, R>, ) -> PResult<'a, (R, Option<LazyTokenStream>)> { let start_token = (self.token.clone(), self.token_spacing); - let cursor_snapshot = self.token_cursor.clone(); + let cursor_snapshot = TokenCursor { + frame: self.token_cursor.frame.clone(), + // We only ever capture tokens within our current frame, + // so we can just use an empty frame stack + stack: vec![], + desugar_doc_comments: self.token_cursor.desugar_doc_comments, + num_next_calls: self.token_cursor.num_next_calls, + append_unglued_token: self.token_cursor.append_unglued_token.clone(), + }; let ret = f(self)?; diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 76ad5acd530..eb5d7075f00 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, RecoverComma}; use crate::parser::{FollowedByType, Parser, PathStyle}; impl<'a> Parser<'a> { @@ -57,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 @@ -70,6 +71,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!(kind, NonterminalKind::Pat2021 {..}), token::Interpolated(ref nt) => may_be_ident(nt), _ => false, }, @@ -86,6 +89,7 @@ impl<'a> Parser<'a> { } } + /// Parse a non-terminal (e.g. MBE `:pat` or `:ident`). 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. @@ -129,8 +133,14 @@ impl<'a> Parser<'a> { } } } - NonterminalKind::Pat => { - let (mut pat, tokens) = self.collect_tokens(|this| this.parse_pat(None))?; + 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) + } + _ => unreachable!(), + })?; // 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..456e32680fe 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -26,7 +26,7 @@ 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, } @@ -43,13 +43,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<Pat>> { + pub(super) fn parse_top_pat( + &mut self, + gate_or: GateOr, + rc: RecoverComma, + ) -> PResult<'a, P<Pat>> { // 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/path.rs b/compiler/rustc_parse/src/parser/path.rs index 4510e86e034..43dee391c17 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -501,10 +501,9 @@ impl<'a> Parser<'a> { pub(super) fn expr_is_valid_const_arg(&self, expr: &P<rustc_ast::Expr>) -> bool { match &expr.kind { ast::ExprKind::Block(_, _) | ast::ExprKind::Lit(_) => true, - ast::ExprKind::Unary(ast::UnOp::Neg, expr) => match &expr.kind { - ast::ExprKind::Lit(_) => true, - _ => false, - }, + ast::ExprKind::Unary(ast::UnOp::Neg, expr) => { + matches!(expr.kind, ast::ExprKind::Lit(_)) + } // We can only resolve single-segment paths at the moment, because multi-segment paths // require type-checking: see `visit_generic_arg` in `src/librustc_resolve/late.rs`. ast::ExprKind::Path(None, path) @@ -516,6 +515,23 @@ impl<'a> Parser<'a> { } } + /// Parse a const argument, e.g. `<3>`. It is assumed the angle brackets will be parsed by + /// the caller. + pub(super) fn parse_const_arg(&mut self) -> PResult<'a, AnonConst> { + // Parse const argument. + let value = if let token::OpenDelim(token::Brace) = self.token.kind { + self.parse_block_expr( + None, + self.token.span, + BlockCheckMode::Default, + ast::AttrVec::new(), + )? + } else { + self.handle_unambiguous_unbraced_const_arg()? + }; + Ok(AnonConst { id: ast::DUMMY_NODE_ID, value }) + } + /// Parse a generic argument in a path segment. /// This does not include constraints, e.g., `Item = u8`, which is handled in `parse_angle_arg`. fn parse_generic_arg(&mut self) -> PResult<'a, Option<GenericArg>> { @@ -525,17 +541,7 @@ impl<'a> Parser<'a> { GenericArg::Lifetime(self.expect_lifetime()) } else if self.check_const_arg() { // Parse const argument. - let value = if let token::OpenDelim(token::Brace) = self.token.kind { - self.parse_block_expr( - None, - self.token.span, - BlockCheckMode::Default, - ast::AttrVec::new(), - )? - } else { - self.handle_unambiguous_unbraced_const_arg()? - }; - GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }) + GenericArg::Const(self.parse_const_arg()?) } else if self.check_type() { // Parse type argument. match self.parse_ty() { 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<Local>> { 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/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index f4bb9610940..21372725a68 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -2,7 +2,7 @@ use crate::parse_in; -use rustc_ast::tokenstream::DelimSpan; +use rustc_ast::tokenstream::{DelimSpan, TokenTree}; use rustc_ast::{self as ast, Attribute, MacArgs, MacDelimiter, MetaItem, MetaItemKind}; use rustc_errors::{Applicability, PResult}; use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP}; @@ -45,7 +45,8 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta kind: match &item.args { MacArgs::Empty => MetaItemKind::Word, MacArgs::Eq(_, t) => { - let v = parse_in(sess, t.clone(), "name value", |p| p.parse_unsuffixed_lit())?; + let t = TokenTree::Token(t.clone()).into(); + let v = parse_in(sess, t, "name value", |p| p.parse_unsuffixed_lit())?; MetaItemKind::NameValue(v) } MacArgs::Delimited(dspan, delim, t) => { |
