From c7981d64117a4e1228e94ddc47a16d171a011c0b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 17 Apr 2024 12:17:09 +1000 Subject: [PATCH 1/3] Remove `NtVis`. We now use invisible delimiters for expanded `vis` fragments, instead of `Token::Interpolated`. --- compiler/rustc_ast/src/ast_traits.rs | 2 - compiler/rustc_ast/src/mut_visit.rs | 1 - compiler/rustc_ast/src/token.rs | 13 ++++-- compiler/rustc_ast/src/tokenstream.rs | 1 - compiler/rustc_expand/src/mbe/transcribe.rs | 35 +++++++++++++- compiler/rustc_parse/src/parser/mod.rs | 46 ++++++++++++++++++- .../rustc_parse/src/parser/nonterminal.rs | 13 +++--- tests/ui/macros/block-to-expr-metavar.rs | 17 +++++++ 8 files changed, 110 insertions(+), 18 deletions(-) create mode 100644 tests/ui/macros/block-to-expr-metavar.rs diff --git a/compiler/rustc_ast/src/ast_traits.rs b/compiler/rustc_ast/src/ast_traits.rs index 60f8c6e10481b..a2923d6448ae9 100644 --- a/compiler/rustc_ast/src/ast_traits.rs +++ b/compiler/rustc_ast/src/ast_traits.rs @@ -206,7 +206,6 @@ impl HasTokens for Nonterminal { Nonterminal::NtTy(ty) => ty.tokens(), Nonterminal::NtMeta(attr_item) => attr_item.tokens(), Nonterminal::NtPath(path) => path.tokens(), - Nonterminal::NtVis(vis) => vis.tokens(), Nonterminal::NtBlock(block) => block.tokens(), } } @@ -219,7 +218,6 @@ impl HasTokens for Nonterminal { Nonterminal::NtTy(ty) => ty.tokens_mut(), Nonterminal::NtMeta(attr_item) => attr_item.tokens_mut(), Nonterminal::NtPath(path) => path.tokens_mut(), - Nonterminal::NtVis(vis) => vis.tokens_mut(), Nonterminal::NtBlock(block) => block.tokens_mut(), } } diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index de9f049704a4f..47cf77cefa7b3 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -916,7 +916,6 @@ fn visit_nonterminal(vis: &mut T, nt: &mut token::Nonterminal) { visit_lazy_tts(vis, tokens); } token::NtPath(path) => vis.visit_path(path), - token::NtVis(visib) => vis.visit_vis(visib), } } diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 36a7b7d87892f..661f2c3eca24e 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -969,6 +969,15 @@ impl Token { } } + /// Is this an invisible open delimiter at the start of a token sequence + /// from an expanded metavar? + pub fn is_metavar_seq(&self) -> Option { + match self.kind { + OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(kind))) => Some(kind), + _ => None, + } + } + pub fn glue(&self, joint: &Token) -> Option { let kind = match self.kind { Eq => match joint.kind { @@ -1072,7 +1081,6 @@ pub enum Nonterminal { /// Stuff inside brackets for attributes NtMeta(P), NtPath(P), - NtVis(P), } #[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, HashStable_Generic)] @@ -1169,7 +1177,6 @@ impl Nonterminal { NtTy(ty) => ty.span, NtMeta(attr_item) => attr_item.span(), NtPath(path) => path.span, - NtVis(vis) => vis.span, } } @@ -1184,7 +1191,6 @@ impl Nonterminal { NtTy(..) => "type", NtMeta(..) => "attribute", NtPath(..) => "path", - NtVis(..) => "visibility", } } } @@ -1211,7 +1217,6 @@ impl fmt::Debug for Nonterminal { NtLiteral(..) => f.pad("NtLiteral(..)"), NtMeta(..) => f.pad("NtMeta(..)"), NtPath(..) => f.pad("NtPath(..)"), - NtVis(..) => f.pad("NtVis(..)"), } } } diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 50f10d083a041..1b6d825a22c8b 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -472,7 +472,6 @@ impl TokenStream { Nonterminal::NtTy(ty) => TokenStream::from_ast(ty), Nonterminal::NtMeta(attr) => TokenStream::from_ast(attr), Nonterminal::NtPath(path) => TokenStream::from_ast(path), - Nonterminal::NtVis(vis) => TokenStream::from_ast(vis), Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => TokenStream::from_ast(expr), } } diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index 595c8c3279f41..1723b47798b08 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -3,7 +3,10 @@ use std::sync::Arc; use rustc_ast::ExprKind; use rustc_ast::mut_visit::{self, MutVisitor}; -use rustc_ast::token::{self, Delimiter, IdentIsRaw, Lit, LitKind, Nonterminal, Token, TokenKind}; +use rustc_ast::token::{ + self, Delimiter, IdentIsRaw, InvisibleOrigin, Lit, LitKind, MetaVarKind, Nonterminal, Token, + TokenKind, +}; use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Diag, DiagCtxtHandle, PResult, pluralize}; @@ -274,6 +277,33 @@ pub(super) fn transcribe<'a>( // some of the unnecessary whitespace. let ident = MacroRulesNormalizedIdent::new(original_ident); if let Some(cur_matched) = lookup_cur_matched(ident, interp, &repeats) { + // We wrap the tokens in invisible delimiters, unless they are already wrapped + // in invisible delimiters with the same `MetaVarKind`. Because some proc + // macros can't multiple layers of invisible delimiters of the same + // `MetaVarKind`. This loses some span info, though it hopefully won't matter. + let mut mk_delimited = |mv_kind, mut stream: TokenStream| { + if stream.len() == 1 { + let tree = stream.iter().next().unwrap(); + if let TokenTree::Delimited(_, _, delim, inner) = tree + && let Delimiter::Invisible(InvisibleOrigin::MetaVar(mvk)) = delim + && mv_kind == *mvk + { + stream = inner.clone(); + } + } + + // Emit as a token stream within `Delimiter::Invisible` to maintain + // parsing priorities. + marker.visit_span(&mut sp); + // Both the open delim and close delim get the same span, which covers the + // `$foo` in the decl macro RHS. + TokenTree::Delimited( + DelimSpan::from_single(sp), + DelimSpacing::new(Spacing::Alone, Spacing::Alone), + Delimiter::Invisible(InvisibleOrigin::MetaVar(mv_kind)), + stream, + ) + }; let tt = match cur_matched { MatchedSingle(ParseNtResult::Tt(tt)) => { // `tt`s are emitted into the output stream directly as "raw tokens", @@ -292,6 +322,9 @@ pub(super) fn transcribe<'a>( let kind = token::NtLifetime(*ident, *is_raw); TokenTree::token_alone(kind, sp) } + MatchedSingle(ParseNtResult::Vis(vis)) => { + mk_delimited(MetaVarKind::Vis, TokenStream::from_ast(vis)) + } MatchedSingle(ParseNtResult::Nt(nt)) => { // Other variables are emitted into the output stream as groups with // `Delimiter::Invisible` to maintain parsing priorities. diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 1de452dcf3965..ecbc4e396ceea 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -721,6 +721,43 @@ impl<'a> Parser<'a> { if !self.eat_keyword(exp) { self.unexpected() } else { Ok(()) } } + /// Consume a sequence produced by a metavar expansion, if present. + fn eat_metavar_seq( + &mut self, + mv_kind: MetaVarKind, + f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, + ) -> Option { + self.eat_metavar_seq_with_matcher(|mvk| mvk == mv_kind, f) + } + + /// A slightly more general form of `eat_metavar_seq`, for use with the + /// `MetaVarKind` variants that have parameters, where an exact match isn't + /// desired. + fn eat_metavar_seq_with_matcher( + &mut self, + match_mv_kind: impl Fn(MetaVarKind) -> bool, + mut f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, + ) -> Option { + if let token::OpenDelim(delim) = self.token.kind + && let Delimiter::Invisible(InvisibleOrigin::MetaVar(mv_kind)) = delim + && match_mv_kind(mv_kind) + { + self.bump(); + let res = f(self).expect("failed to reparse {mv_kind:?}"); + if let token::CloseDelim(delim) = self.token.kind + && let Delimiter::Invisible(InvisibleOrigin::MetaVar(mv_kind)) = delim + && match_mv_kind(mv_kind) + { + self.bump(); + Some(res) + } else { + panic!("no close delim when reparsing {mv_kind:?}"); + } + } else { + None + } + } + /// Is the given keyword `kw` followed by a non-reserved identifier? 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()) @@ -1455,7 +1492,11 @@ impl<'a> Parser<'a> { /// so emit a proper diagnostic. // Public for rustfmt usage. pub fn parse_visibility(&mut self, fbt: FollowedByType) -> PResult<'a, Visibility> { - maybe_whole!(self, NtVis, |vis| vis.into_inner()); + if let Some(vis) = self + .eat_metavar_seq(MetaVarKind::Vis, |this| this.parse_visibility(FollowedByType::Yes)) + { + return Ok(vis); + } if !self.eat_keyword(exp!(Pub)) { // We need a span for our `Spanned`, but there's inherently no @@ -1683,7 +1724,8 @@ pub enum ParseNtResult { Tt(TokenTree), Ident(Ident, IdentIsRaw), Lifetime(Ident, IdentIsRaw), + Vis(P), - /// This case will eventually be removed, along with `Token::Interpolate`. + /// This variant will eventually be removed, along with `Token::Interpolate`. Nt(Arc), } diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index eefdb641da22a..f102821adf7ce 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -56,9 +56,7 @@ impl<'a> Parser<'a> { | NtMeta(_) | NtPath(_) => true, - NtItem(_) - | NtBlock(_) - | NtVis(_) => false, + NtItem(_) | NtBlock(_) => false, } } @@ -88,7 +86,7 @@ impl<'a> Parser<'a> { NonterminalKind::Ident => get_macro_ident(token).is_some(), NonterminalKind::Literal => token.can_begin_literal_maybe_minus(), NonterminalKind::Vis => match token.kind { - // The follow-set of :vis + "priv" keyword + interpolated + // The follow-set of :vis + "priv" keyword + interpolated/metavar-expansion. token::Comma | token::Ident(..) | token::NtIdent(..) @@ -102,7 +100,7 @@ impl<'a> Parser<'a> { token::NtLifetime(..) => true, token::Interpolated(nt) => match &**nt { NtBlock(_) | NtStmt(_) | NtExpr(_) | NtLiteral(_) => true, - NtItem(_) | NtPat(_) | NtTy(_) | NtMeta(_) | NtPath(_) | NtVis(_) => false, + NtItem(_) | NtPat(_) | NtTy(_) | NtMeta(_) | NtPath(_) => false, }, token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(k))) => match k { MetaVarKind::Block @@ -208,8 +206,9 @@ impl<'a> Parser<'a> { } NonterminalKind::Meta => NtMeta(P(self.parse_attr_item(ForceCollect::Yes)?)), NonterminalKind::Vis => { - NtVis(P(self - .collect_tokens_no_attrs(|this| this.parse_visibility(FollowedByType::Yes))?)) + return Ok(ParseNtResult::Vis(P(self.collect_tokens_no_attrs(|this| { + this.parse_visibility(FollowedByType::Yes) + })?))); } NonterminalKind::Lifetime => { // We want to keep `'keyword` parsing, just like `keyword` is still diff --git a/tests/ui/macros/block-to-expr-metavar.rs b/tests/ui/macros/block-to-expr-metavar.rs new file mode 100644 index 0000000000000..04f10ad0f9886 --- /dev/null +++ b/tests/ui/macros/block-to-expr-metavar.rs @@ -0,0 +1,17 @@ +//@ check-pass +// +// A test case where a `block` fragment specifier is interpreted as an `expr` +// fragment specifier. It's an interesting case for the handling of invisible +// delimiters. + +macro_rules! m_expr { + ($e:expr) => { const _CURRENT: u32 = $e; }; +} + +macro_rules! m_block { + ($b:block) => ( m_expr!($b); ); +} + +fn main() { + m_block!({ 1 }); +} From 76b04437be91069260c72a6d59d130a4e127a9a8 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 17 Apr 2024 13:17:44 +1000 Subject: [PATCH 2/3] Remove `NtTy`. Notes about tests: - tests/ui/parser/macro/trait-object-macro-matcher.rs: the syntax error is duplicated, because it occurs now when parsing the decl macro input, and also when parsing the expanded decl macro. But this won't show up for normal users due to error de-duplication. - tests/ui/associated-consts/issue-93835.rs: similar, plus there are some additional errors about this very broken code. - The changes to metavariable descriptions in #132629 are now visible in error message for several tests. --- compiler/rustc_ast/src/ast_traits.rs | 2 - compiler/rustc_ast/src/mut_visit.rs | 1 - compiler/rustc_ast/src/token.rs | 7 +--- compiler/rustc_ast/src/tokenstream.rs | 1 - compiler/rustc_expand/src/mbe/transcribe.rs | 3 ++ compiler/rustc_parse/src/parser/mod.rs | 33 ++++++++++++++--- .../rustc_parse/src/parser/nonterminal.rs | 7 ++-- compiler/rustc_parse/src/parser/path.rs | 17 +++++---- compiler/rustc_parse/src/parser/ty.rs | 14 +++++-- tests/ui/associated-consts/issue-93835.rs | 4 +- tests/ui/associated-consts/issue-93835.stderr | 37 ++++++++++++++++--- tests/ui/macros/macro-interpolation.rs | 2 +- tests/ui/macros/macro-interpolation.stderr | 4 +- tests/ui/macros/syntax-error-recovery.rs | 4 +- tests/ui/macros/syntax-error-recovery.stderr | 4 +- tests/ui/parser/macro/issue-37113.rs | 2 +- tests/ui/parser/macro/issue-37113.stderr | 4 +- .../macro/trait-object-macro-matcher.rs | 1 + .../macro/trait-object-macro-matcher.stderr | 10 ++++- 19 files changed, 109 insertions(+), 48 deletions(-) diff --git a/compiler/rustc_ast/src/ast_traits.rs b/compiler/rustc_ast/src/ast_traits.rs index a2923d6448ae9..346edc87c86bc 100644 --- a/compiler/rustc_ast/src/ast_traits.rs +++ b/compiler/rustc_ast/src/ast_traits.rs @@ -203,7 +203,6 @@ impl HasTokens for Nonterminal { Nonterminal::NtStmt(stmt) => stmt.tokens(), Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.tokens(), Nonterminal::NtPat(pat) => pat.tokens(), - Nonterminal::NtTy(ty) => ty.tokens(), Nonterminal::NtMeta(attr_item) => attr_item.tokens(), Nonterminal::NtPath(path) => path.tokens(), Nonterminal::NtBlock(block) => block.tokens(), @@ -215,7 +214,6 @@ impl HasTokens for Nonterminal { Nonterminal::NtStmt(stmt) => stmt.tokens_mut(), Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.tokens_mut(), Nonterminal::NtPat(pat) => pat.tokens_mut(), - Nonterminal::NtTy(ty) => ty.tokens_mut(), Nonterminal::NtMeta(attr_item) => attr_item.tokens_mut(), Nonterminal::NtPath(path) => path.tokens_mut(), Nonterminal::NtBlock(block) => block.tokens_mut(), diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 47cf77cefa7b3..40b29fdba2506 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -907,7 +907,6 @@ fn visit_nonterminal(vis: &mut T, nt: &mut token::Nonterminal) { }), token::NtPat(pat) => vis.visit_pat(pat), token::NtExpr(expr) => vis.visit_expr(expr), - token::NtTy(ty) => vis.visit_ty(ty), token::NtLiteral(expr) => vis.visit_expr(expr), token::NtMeta(item) => { let AttrItem { unsafety: _, path, args, tokens } = item.deref_mut(); diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 661f2c3eca24e..8ac6dc3b4d8ca 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -659,7 +659,6 @@ impl Token { | NtMeta(..) | NtPat(..) | NtPath(..) - | NtTy(..) ), OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar( MetaVarKind::Expr { .. } | @@ -688,7 +687,7 @@ impl Token { Lifetime(..) | // lifetime bound in trait object Lt | BinOp(Shl) | // associated path PathSep => true, // global path - Interpolated(ref nt) => matches!(&**nt, NtTy(..) | NtPath(..)), + Interpolated(ref nt) => matches!(&**nt, NtPath(..)), OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar( MetaVarKind::Ty | MetaVarKind::Path @@ -1076,7 +1075,6 @@ pub enum Nonterminal { NtStmt(P), NtPat(P), NtExpr(P), - NtTy(P), NtLiteral(P), /// Stuff inside brackets for attributes NtMeta(P), @@ -1174,7 +1172,6 @@ impl Nonterminal { NtStmt(stmt) => stmt.span, NtPat(pat) => pat.span, NtExpr(expr) | NtLiteral(expr) => expr.span, - NtTy(ty) => ty.span, NtMeta(attr_item) => attr_item.span(), NtPath(path) => path.span, } @@ -1188,7 +1185,6 @@ impl Nonterminal { NtPat(..) => "pattern", NtExpr(..) => "expression", NtLiteral(..) => "literal", - NtTy(..) => "type", NtMeta(..) => "attribute", NtPath(..) => "path", } @@ -1213,7 +1209,6 @@ impl fmt::Debug for Nonterminal { NtStmt(..) => f.pad("NtStmt(..)"), NtPat(..) => f.pad("NtPat(..)"), NtExpr(..) => f.pad("NtExpr(..)"), - NtTy(..) => f.pad("NtTy(..)"), NtLiteral(..) => f.pad("NtLiteral(..)"), NtMeta(..) => f.pad("NtMeta(..)"), NtPath(..) => f.pad("NtPath(..)"), diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 1b6d825a22c8b..1123ea3a449b6 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -469,7 +469,6 @@ impl TokenStream { } Nonterminal::NtStmt(stmt) => TokenStream::from_ast(stmt), Nonterminal::NtPat(pat) => TokenStream::from_ast(pat), - Nonterminal::NtTy(ty) => TokenStream::from_ast(ty), Nonterminal::NtMeta(attr) => TokenStream::from_ast(attr), Nonterminal::NtPath(path) => TokenStream::from_ast(path), Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => TokenStream::from_ast(expr), diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index 1723b47798b08..fa0c2c7da781c 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -322,6 +322,9 @@ pub(super) fn transcribe<'a>( let kind = token::NtLifetime(*ident, *is_raw); TokenTree::token_alone(kind, sp) } + MatchedSingle(ParseNtResult::Ty(ty)) => { + mk_delimited(MetaVarKind::Ty, TokenStream::from_ast(ty)) + } MatchedSingle(ParseNtResult::Vis(vis)) => { mk_delimited(MetaVarKind::Vis, TokenStream::from_ast(vis)) } diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index ecbc4e396ceea..4b26100c46dee 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -117,12 +117,16 @@ macro_rules! maybe_recover_from_interpolated_ty_qpath { ($self: expr, $allow_qpath_recovery: expr) => { if $allow_qpath_recovery && $self.may_recover() - && $self.look_ahead(1, |t| t == &token::PathSep) - && let token::Interpolated(nt) = &$self.token.kind - && let token::NtTy(ty) = &**nt + && let Some(token::MetaVarKind::Ty) = $self.token.is_metavar_seq() + && $self.check_noexpect_past_close_delim(&token::PathSep) { - let ty = ty.clone(); - $self.bump(); + // Reparse the type, then move to recovery. + let ty = $self + .eat_metavar_seq(token::MetaVarKind::Ty, |this| { + this.parse_ty_no_question_mark_recover() + }) + .expect("metavar seq ty"); + return $self.maybe_recover_from_bad_qpath_stage_2($self.prev_token.span, ty); } }; @@ -614,6 +618,24 @@ impl<'a> Parser<'a> { self.token == *tok } + // Check the first token after the delimiter that closes the current + // delimited sequence. (Panics if used in the outermost token stream, which + // has no delimiters.) It uses a clone of the relevant tree cursor to skip + // past the entire `TokenTree::Delimited` in a single step, avoiding the + // need for unbounded token lookahead. + // + // Primarily used when `self.token` matches + // `OpenDelim(Delimiter::Invisible(_))`, to look ahead through the current + // metavar expansion. + fn check_noexpect_past_close_delim(&self, tok: &TokenKind) -> bool { + let mut tree_cursor = self.token_cursor.stack.last().unwrap().clone(); + tree_cursor.bump(); + matches!( + tree_cursor.curr(), + Some(TokenTree::Token(token::Token { kind, .. }, _)) if kind == tok + ) + } + /// Consumes a token 'tok' if it exists. Returns whether the given token was present. /// /// the main purpose of this function is to reduce the cluttering of the suggestions list @@ -1724,6 +1746,7 @@ pub enum ParseNtResult { Tt(TokenTree), Ident(Ident, IdentIsRaw), Lifetime(Ident, IdentIsRaw), + Ty(P), Vis(P), /// This variant will eventually be removed, along with `Token::Interpolate`. diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index f102821adf7ce..e9cf8f1bbba99 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -51,7 +51,6 @@ impl<'a> Parser<'a> { NtStmt(_) | NtPat(_) | NtExpr(_) - | NtTy(_) | NtLiteral(_) // `true`, `false` | NtMeta(_) | NtPath(_) => true, @@ -100,7 +99,7 @@ impl<'a> Parser<'a> { token::NtLifetime(..) => true, token::Interpolated(nt) => match &**nt { NtBlock(_) | NtStmt(_) | NtExpr(_) | NtLiteral(_) => true, - NtItem(_) | NtPat(_) | NtTy(_) | NtMeta(_) | NtPath(_) => false, + NtItem(_) | NtPat(_) | NtMeta(_) | NtPath(_) => false, }, token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(k))) => match k { MetaVarKind::Block @@ -187,7 +186,9 @@ impl<'a> Parser<'a> { NtLiteral(self.collect_tokens_no_attrs(|this| this.parse_literal_maybe_minus())?) } NonterminalKind::Ty => { - NtTy(self.collect_tokens_no_attrs(|this| this.parse_ty_no_question_mark_recover())?) + return Ok(ParseNtResult::Ty( + self.collect_tokens_no_attrs(|this| this.parse_ty_no_question_mark_recover())?, + )); } // this could be handled like a token, since it is one NonterminalKind::Ident => { diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index b241aa892db92..b68eb9c99f52a 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -2,7 +2,7 @@ use std::mem; use ast::token::IdentIsRaw; use rustc_ast::ptr::P; -use rustc_ast::token::{self, Delimiter, Token, TokenKind}; +use rustc_ast::token::{self, Delimiter, MetaVarKind, Token, TokenKind}; use rustc_ast::{ self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AssocItemConstraint, AssocItemConstraintKind, BlockCheckMode, GenericArg, GenericArgs, Generics, ParenthesizedArgs, @@ -196,13 +196,14 @@ impl<'a> Parser<'a> { maybe_whole!(self, NtPath, |path| reject_generics_if_mod_style(self, path.into_inner())); - if let token::Interpolated(nt) = &self.token.kind { - if let token::NtTy(ty) = &**nt { - if let ast::TyKind::Path(None, path) = &ty.kind { - let path = path.clone(); - self.bump(); - return Ok(reject_generics_if_mod_style(self, path)); - } + if let Some(MetaVarKind::Ty) = self.token.is_metavar_seq() { + let mut snapshot = self.create_snapshot_for_diagnostic(); + let ty = snapshot + .eat_metavar_seq(MetaVarKind::Ty, |this| this.parse_ty_no_question_mark_recover()) + .expect("metavar seq ty"); + if let ast::TyKind::Path(None, path) = ty.into_inner().kind { + self.restore_snapshot(snapshot); + return Ok(reject_generics_if_mod_style(self, path)); } } diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index dc5919b3630ce..c78c7bff9b57a 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -1,5 +1,5 @@ use rustc_ast::ptr::P; -use rustc_ast::token::{self, BinOpToken, Delimiter, IdentIsRaw, Token, TokenKind}; +use rustc_ast::token::{self, BinOpToken, Delimiter, IdentIsRaw, MetaVarKind, Token, TokenKind}; use rustc_ast::util::case::Case; use rustc_ast::{ self as ast, BareFnTy, BoundAsyncness, BoundConstness, BoundPolarity, DUMMY_NODE_ID, FnRetTy, @@ -18,7 +18,7 @@ use crate::errors::{ HelpUseLatestEdition, InvalidDynKeyword, LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime, NestedCVariadicType, ReturnTypesUseThinArrow, }; -use crate::{exp, maybe_recover_from_interpolated_ty_qpath, maybe_whole}; +use crate::{exp, maybe_recover_from_interpolated_ty_qpath}; /// Signals whether parsing a type should allow `+`. /// @@ -183,7 +183,8 @@ impl<'a> Parser<'a> { ) } - /// Parse a type without recovering `:` as `->` to avoid breaking code such as `where fn() : for<'a>` + /// Parse a type without recovering `:` as `->` to avoid breaking code such + /// as `where fn() : for<'a>`. pub(super) fn parse_ty_for_where_clause(&mut self) -> PResult<'a, P> { self.parse_ty_common( AllowPlus::Yes, @@ -247,7 +248,12 @@ impl<'a> Parser<'a> { ) -> PResult<'a, P> { let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes; maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery); - maybe_whole!(self, NtTy, |ty| ty); + + if let Some(ty) = + self.eat_metavar_seq(MetaVarKind::Ty, |this| this.parse_ty_no_question_mark_recover()) + { + return Ok(ty); + } let lo = self.token.span; let mut impl_dyn_multi = false; diff --git a/tests/ui/associated-consts/issue-93835.rs b/tests/ui/associated-consts/issue-93835.rs index 9cc33d53f9cd5..048681f047789 100644 --- a/tests/ui/associated-consts/issue-93835.rs +++ b/tests/ui/associated-consts/issue-93835.rs @@ -3,9 +3,11 @@ fn e() { type_ascribe!(p, a>); //~^ ERROR cannot find type `a` in this scope + //~| ERROR path separator must be a double colon //~| ERROR cannot find value //~| ERROR associated const equality - //~| ERROR cannot find trait `p` in this scope + //~| ERROR associated const equality + //~| ERROR failed to resolve: use of unresolved module or unlinked crate `p` } fn main() {} diff --git a/tests/ui/associated-consts/issue-93835.stderr b/tests/ui/associated-consts/issue-93835.stderr index dfe78b3d1f380..e154ae25de26c 100644 --- a/tests/ui/associated-consts/issue-93835.stderr +++ b/tests/ui/associated-consts/issue-93835.stderr @@ -1,3 +1,15 @@ +error: path separator must be a double colon + --> $DIR/issue-93835.rs:4:25 + | +LL | type_ascribe!(p, a>); + | ^ + | + = note: if you meant to annotate an expression with a type, the type ascription syntax has been removed, see issue #101728 +help: use a double colon instead + | +LL | type_ascribe!(p, a>); + | + + error[E0425]: cannot find value `p` in this scope --> $DIR/issue-93835.rs:4:19 | @@ -10,11 +22,15 @@ error[E0412]: cannot find type `a` in this scope LL | type_ascribe!(p, a>); | ^ not found in this scope -error[E0405]: cannot find trait `p` in this scope - --> $DIR/issue-93835.rs:4:26 +error[E0658]: associated const equality is incomplete + --> $DIR/issue-93835.rs:4:28 | LL | type_ascribe!(p, a>); - | ^ not found in this scope + | ^^^ + | + = note: see issue #92827 for more information + = help: add `#![feature(associated_const_equality)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: associated const equality is incomplete --> $DIR/issue-93835.rs:4:28 @@ -25,8 +41,17 @@ LL | type_ascribe!(p, a>); = note: see issue #92827 for more information = help: add `#![feature(associated_const_equality)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `p` + --> $DIR/issue-93835.rs:4:24 + | +LL | type_ascribe!(p, a>); + | ^ use of unresolved module or unlinked crate `p` + | + = help: you might be missing a crate named `p` -error: aborting due to 4 previous errors +error: aborting due to 6 previous errors -Some errors have detailed explanations: E0405, E0412, E0425, E0658. -For more information about an error, try `rustc --explain E0405`. +Some errors have detailed explanations: E0412, E0425, E0433, E0658. +For more information about an error, try `rustc --explain E0412`. diff --git a/tests/ui/macros/macro-interpolation.rs b/tests/ui/macros/macro-interpolation.rs index 48c1f19e777f7..b5d2322c8052c 100644 --- a/tests/ui/macros/macro-interpolation.rs +++ b/tests/ui/macros/macro-interpolation.rs @@ -19,7 +19,7 @@ macro_rules! qpath { (ty, <$type:ty as $trait:ty>::$name:ident) => { <$type as $trait>::$name - //~^ ERROR expected identifier, found `!` + //~^ ERROR expected identifier, found metavariable }; } diff --git a/tests/ui/macros/macro-interpolation.stderr b/tests/ui/macros/macro-interpolation.stderr index e6b39dfef8581..bc24a15861295 100644 --- a/tests/ui/macros/macro-interpolation.stderr +++ b/tests/ui/macros/macro-interpolation.stderr @@ -1,8 +1,8 @@ -error: expected identifier, found `!` +error: expected identifier, found metavariable --> $DIR/macro-interpolation.rs:21:19 | LL | <$type as $trait>::$name - | ^^^^^^ expected identifier + | ^^^^^^ expected identifier, found metavariable ... LL | let _: qpath!(ty, ::Owned); | ----------------------------- diff --git a/tests/ui/macros/syntax-error-recovery.rs b/tests/ui/macros/syntax-error-recovery.rs index 016e4def28497..6cf9d54e82636 100644 --- a/tests/ui/macros/syntax-error-recovery.rs +++ b/tests/ui/macros/syntax-error-recovery.rs @@ -9,8 +9,8 @@ macro_rules! values { } }; } -//~^^^^^ ERROR expected one of `(`, `,`, `=`, `{`, or `}`, found type `(String)` -//~| ERROR macro expansion ignores type `(String)` and any tokens following +//~^^^^^ ERROR expected one of `(`, `,`, `=`, `{`, or `}`, found `ty` metavariable +//~| ERROR macro expansion ignores `ty` metavariable and any tokens following values!(STRING(1) as (String) => cfg(test),); //~^ ERROR expected one of `!` or `::`, found `` diff --git a/tests/ui/macros/syntax-error-recovery.stderr b/tests/ui/macros/syntax-error-recovery.stderr index 3cfbd8ce82b56..61758fb9d7dc6 100644 --- a/tests/ui/macros/syntax-error-recovery.stderr +++ b/tests/ui/macros/syntax-error-recovery.stderr @@ -1,4 +1,4 @@ -error: expected one of `(`, `,`, `=`, `{`, or `}`, found type `(String)` +error: expected one of `(`, `,`, `=`, `{`, or `}`, found `ty` metavariable --> $DIR/syntax-error-recovery.rs:7:26 | LL | $token $($inner)? = $value, @@ -10,7 +10,7 @@ LL | values!(STRING(1) as (String) => cfg(test),); = help: enum variants can be `Variant`, `Variant = `, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }` = note: this error originates in the macro `values` (in Nightly builds, run with -Z macro-backtrace for more info) -error: macro expansion ignores type `(String)` and any tokens following +error: macro expansion ignores `ty` metavariable and any tokens following --> $DIR/syntax-error-recovery.rs:7:26 | LL | $token $($inner)? = $value, diff --git a/tests/ui/parser/macro/issue-37113.rs b/tests/ui/parser/macro/issue-37113.rs index 0044aa5610f5f..e0957542f8f19 100644 --- a/tests/ui/parser/macro/issue-37113.rs +++ b/tests/ui/parser/macro/issue-37113.rs @@ -1,7 +1,7 @@ macro_rules! test_macro { ( $( $t:ty ),* $(),*) => { enum SomeEnum { - $( $t, )* //~ ERROR expected identifier, found `String` + $( $t, )* //~ ERROR expected identifier, found metavariable }; }; } diff --git a/tests/ui/parser/macro/issue-37113.stderr b/tests/ui/parser/macro/issue-37113.stderr index 1f2fe23106ae3..560329df5ccbd 100644 --- a/tests/ui/parser/macro/issue-37113.stderr +++ b/tests/ui/parser/macro/issue-37113.stderr @@ -1,10 +1,10 @@ -error: expected identifier, found `String` +error: expected identifier, found metavariable --> $DIR/issue-37113.rs:4:16 | LL | enum SomeEnum { | -------- while parsing this enum LL | $( $t, )* - | ^^ expected identifier + | ^^ expected identifier, found metavariable ... LL | test_macro!(String,); | -------------------- in this macro invocation diff --git a/tests/ui/parser/macro/trait-object-macro-matcher.rs b/tests/ui/parser/macro/trait-object-macro-matcher.rs index 560195977d03a..d4ec199070e7c 100644 --- a/tests/ui/parser/macro/trait-object-macro-matcher.rs +++ b/tests/ui/parser/macro/trait-object-macro-matcher.rs @@ -10,5 +10,6 @@ macro_rules! m { fn main() { m!('static); //~^ ERROR lifetime in trait object type must be followed by `+` + //~| ERROR lifetime in trait object type must be followed by `+` //~| ERROR at least one trait is required for an object type } diff --git a/tests/ui/parser/macro/trait-object-macro-matcher.stderr b/tests/ui/parser/macro/trait-object-macro-matcher.stderr index 40082564bad4c..81dca6f71c436 100644 --- a/tests/ui/parser/macro/trait-object-macro-matcher.stderr +++ b/tests/ui/parser/macro/trait-object-macro-matcher.stderr @@ -4,12 +4,20 @@ error: lifetime in trait object type must be followed by `+` LL | m!('static); | ^^^^^^^ +error: lifetime in trait object type must be followed by `+` + --> $DIR/trait-object-macro-matcher.rs:11:8 + | +LL | m!('static); + | ^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + error[E0224]: at least one trait is required for an object type --> $DIR/trait-object-macro-matcher.rs:11:8 | LL | m!('static); | ^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0224`. From 0f490b040af82820e423a7e71e0e2e1f6ca7ab57 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 21 Feb 2025 16:47:07 +1100 Subject: [PATCH 3/3] Avoid snapshotting the parser in `parse_path_inner`. --- compiler/rustc_ast/src/token.rs | 10 ++++++---- compiler/rustc_expand/src/mbe/transcribe.rs | 5 +++-- compiler/rustc_parse/src/parser/mod.rs | 7 +++---- compiler/rustc_parse/src/parser/nonterminal.rs | 4 ++-- compiler/rustc_parse/src/parser/path.rs | 15 ++++++--------- compiler/rustc_parse/src/parser/ty.rs | 7 ++++--- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 8ac6dc3b4d8ca..97d121909f8dd 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -84,7 +84,9 @@ pub enum MetaVarKind { // This field is needed for `Token::can_begin_string_literal`. can_begin_string_literal: bool, }, - Ty, + Ty { + is_path: bool, + }, Ident, Lifetime, Literal, @@ -104,7 +106,7 @@ impl fmt::Display for MetaVarKind { MetaVarKind::Pat(PatParam { inferred: false }) => sym::pat_param, MetaVarKind::Expr { kind: Expr2021 { inferred: true } | Expr, .. } => sym::expr, MetaVarKind::Expr { kind: Expr2021 { inferred: false }, .. } => sym::expr_2021, - MetaVarKind::Ty => sym::ty, + MetaVarKind::Ty { .. } => sym::ty, MetaVarKind::Ident => sym::ident, MetaVarKind::Lifetime => sym::lifetime, MetaVarKind::Literal => sym::literal, @@ -666,7 +668,7 @@ impl Token { MetaVarKind::Meta | MetaVarKind::Pat(_) | MetaVarKind::Path | - MetaVarKind::Ty + MetaVarKind::Ty { .. } ))) => true, _ => false, } @@ -689,7 +691,7 @@ impl Token { PathSep => true, // global path Interpolated(ref nt) => matches!(&**nt, NtPath(..)), OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar( - MetaVarKind::Ty | + MetaVarKind::Ty { .. } | MetaVarKind::Path ))) => true, // For anonymous structs or unions, which only appear in specific positions diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index fa0c2c7da781c..721798b0ce464 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -1,13 +1,13 @@ use std::mem; use std::sync::Arc; -use rustc_ast::ExprKind; use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::token::{ self, Delimiter, IdentIsRaw, InvisibleOrigin, Lit, LitKind, MetaVarKind, Nonterminal, Token, TokenKind, }; use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; +use rustc_ast::{ExprKind, TyKind}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Diag, DiagCtxtHandle, PResult, pluralize}; use rustc_parse::lexer::nfc_normalize; @@ -323,7 +323,8 @@ pub(super) fn transcribe<'a>( TokenTree::token_alone(kind, sp) } MatchedSingle(ParseNtResult::Ty(ty)) => { - mk_delimited(MetaVarKind::Ty, TokenStream::from_ast(ty)) + let is_path = matches!(&ty.kind, TyKind::Path(None, _path)); + mk_delimited(MetaVarKind::Ty { is_path }, TokenStream::from_ast(ty)) } MatchedSingle(ParseNtResult::Vis(vis)) => { mk_delimited(MetaVarKind::Vis, TokenStream::from_ast(vis)) diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 4b26100c46dee..bbd73dec2e472 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -117,14 +117,13 @@ macro_rules! maybe_recover_from_interpolated_ty_qpath { ($self: expr, $allow_qpath_recovery: expr) => { if $allow_qpath_recovery && $self.may_recover() - && let Some(token::MetaVarKind::Ty) = $self.token.is_metavar_seq() + && let Some(mv_kind) = $self.token.is_metavar_seq() + && let token::MetaVarKind::Ty { .. } = mv_kind && $self.check_noexpect_past_close_delim(&token::PathSep) { // Reparse the type, then move to recovery. let ty = $self - .eat_metavar_seq(token::MetaVarKind::Ty, |this| { - this.parse_ty_no_question_mark_recover() - }) + .eat_metavar_seq(mv_kind, |this| this.parse_ty_no_question_mark_recover()) .expect("metavar seq ty"); return $self.maybe_recover_from_bad_qpath_stage_2($self.prev_token.span, ty); diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index e9cf8f1bbba99..f202f85752e19 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -30,7 +30,7 @@ impl<'a> Parser<'a> { MetaVarKind::Stmt | MetaVarKind::Pat(_) | MetaVarKind::Expr { .. } - | MetaVarKind::Ty + | MetaVarKind::Ty { .. } | MetaVarKind::Literal // `true`, `false` | MetaVarKind::Meta | MetaVarKind::Path => true, @@ -108,7 +108,7 @@ impl<'a> Parser<'a> { | MetaVarKind::Literal => true, MetaVarKind::Item | MetaVarKind::Pat(_) - | MetaVarKind::Ty + | MetaVarKind::Ty { .. } | MetaVarKind::Meta | MetaVarKind::Path | MetaVarKind::Vis => false, diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index b68eb9c99f52a..c24305ea9a866 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -196,15 +196,12 @@ impl<'a> Parser<'a> { maybe_whole!(self, NtPath, |path| reject_generics_if_mod_style(self, path.into_inner())); - if let Some(MetaVarKind::Ty) = self.token.is_metavar_seq() { - let mut snapshot = self.create_snapshot_for_diagnostic(); - let ty = snapshot - .eat_metavar_seq(MetaVarKind::Ty, |this| this.parse_ty_no_question_mark_recover()) - .expect("metavar seq ty"); - if let ast::TyKind::Path(None, path) = ty.into_inner().kind { - self.restore_snapshot(snapshot); - return Ok(reject_generics_if_mod_style(self, path)); - } + // If we have a `ty` metavar in the form of a path, reparse it directly as a path, instead + // of reparsing it as a `ty` and then extracting the path. + if let Some(path) = self.eat_metavar_seq(MetaVarKind::Ty { is_path: true }, |this| { + this.parse_path(PathStyle::Type) + }) { + return Ok(reject_generics_if_mod_style(self, path)); } let lo = self.token.span; diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index c78c7bff9b57a..18af0a1c79aa2 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -249,9 +249,10 @@ impl<'a> Parser<'a> { let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes; maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery); - if let Some(ty) = - self.eat_metavar_seq(MetaVarKind::Ty, |this| this.parse_ty_no_question_mark_recover()) - { + if let Some(ty) = self.eat_metavar_seq_with_matcher( + |mv_kind| matches!(mv_kind, MetaVarKind::Ty { .. }), + |this| this.parse_ty_no_question_mark_recover(), + ) { return Ok(ty); }