From 6d1071962f17cb2f8ded66bae28b571c8f49f76b Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 19 Mar 2024 19:48:09 +0100 Subject: [PATCH] =?UTF-8?q?Resolve=20whether=20$pat=20is=20$pat=5Fparam=20?= =?UTF-8?q?or=20not=20via=20=F0=9F=8C=9Fhygiene=F0=9F=8C=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hir-def/src/macro_expansion_tests/mbe.rs | 85 +++++++++++++++++++ crates/hir-expand/src/declarative.rs | 34 +++++--- crates/mbe/src/benchmark.rs | 10 ++- crates/mbe/src/expander.rs | 3 +- crates/mbe/src/expander/matcher.rs | 14 ++- crates/mbe/src/lib.rs | 30 +++---- crates/mbe/src/parser.rs | 36 +++++--- 7 files changed, 158 insertions(+), 54 deletions(-) diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs index 965f329acb9e..c5c26e26bc0e 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs @@ -1449,6 +1449,7 @@ ok!(); #[test] fn test_new_std_matches() { check( + //- edition:2021 r#" macro_rules! matches { ($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => { @@ -1480,6 +1481,90 @@ fn main() { ); } +#[test] +fn test_hygienic_pat() { + check( + r#" +//- /new.rs crate:new deps:old edition:2015 +old::make!(); +fn main() { + matches!(0, 0 | 1 if true); +} +//- /old.rs crate:old edition:2021 +#[macro_export] +macro_rules! make { + () => { + macro_rules! matches { + ($expression:expr, $pattern:pat if $guard:expr ) => { + match $expression { + $pattern if $guard => true, + _ => false + } + }; + } + } +} + "#, + expect![[r#" +macro_rules !matches { + ($expression: expr, $pattern: pat if $guard: expr) = > { + match $expression { + $pattern if $guard = > true , _ = > false + } + } + ; +} +fn main() { + match 0 { + 0|1 if true =>true , _=>false + }; +} +"#]], + ); + check( + r#" +//- /new.rs crate:new deps:old edition:2021 +old::make!(); +fn main() { + matches/*+errors*/!(0, 0 | 1 if true); +} +//- /old.rs crate:old edition:2015 +#[macro_export] +macro_rules! make { + () => { + macro_rules! matches { + ($expression:expr, $pattern:pat if $guard:expr ) => { + match $expression { + $pattern if $guard => true, + _ => false + } + }; + } + } +} + "#, + expect![[r#" +macro_rules !matches { + ($expression: expr, $pattern: pat if $guard: expr) = > { + match $expression { + $pattern if $guard = > true , _ = > false + } + } + ; +} +fn main() { + /* error: unexpected token in input *//* parse error: expected expression */ +/* parse error: expected FAT_ARROW */ +/* parse error: expected `,` */ +/* parse error: expected pattern */ +match 0 { + 0 if $guard=>true , _=>false + }; +} +"#]], + ); +} + #[test] fn test_dollar_crate_lhs_is_not_meta() { check( diff --git a/crates/hir-expand/src/declarative.rs b/crates/hir-expand/src/declarative.rs index eceb20df5f84..9a0b218e6d1d 100644 --- a/crates/hir-expand/src/declarative.rs +++ b/crates/hir-expand/src/declarative.rs @@ -2,7 +2,7 @@ use std::sync::OnceLock; use base_db::{CrateId, VersionReq}; -use span::{Edition, MacroCallId, Span}; +use span::{MacroCallId, Span, SyntaxContextId}; use syntax::{ast, AstNode}; use triomphe::Arc; @@ -10,7 +10,7 @@ use crate::{ attrs::RawAttrs, db::ExpandDatabase, hygiene::{apply_mark, Transparency}, - tt, AstId, ExpandError, ExpandResult, + tt, AstId, ExpandError, ExpandResult, Lookup, }; /// Old-style `macro_rules` or the new macros 2.0 @@ -94,8 +94,6 @@ impl DeclarativeMacroExpander { def_crate: CrateId, id: AstId, ) -> Arc { - let crate_data = &db.crate_graph()[def_crate]; - let is_2021 = crate_data.edition >= Edition::Edition2021; let (root, map) = crate::db::parse_with_map(db, id.file_id); let root = root.syntax_node(); @@ -133,6 +131,16 @@ impl DeclarativeMacroExpander { ) }); + let edition = |ctx: SyntaxContextId| { + let crate_graph = db.crate_graph(); + if ctx.is_root() { + crate_graph[def_crate].edition + } else { + let data = db.lookup_intern_syntax_context(ctx); + // UNWRAP-SAFETY: Only the root context has no outer expansion + crate_graph[data.outer_expn.unwrap().lookup(db).def.krate].edition + } + }; let (mac, transparency) = match id.to_ptr(db).to_node(&root) { ast::Macro::MacroRules(macro_rules) => ( match macro_rules.token_tree() { @@ -145,12 +153,11 @@ impl DeclarativeMacroExpander { ), ); - mbe::DeclarativeMacro::parse_macro_rules(&tt, is_2021, new_meta_vars) + mbe::DeclarativeMacro::parse_macro_rules(&tt, edition, new_meta_vars) } - None => mbe::DeclarativeMacro::from_err( - mbe::ParseError::Expected("expected a token tree".into()), - is_2021, - ), + None => mbe::DeclarativeMacro::from_err(mbe::ParseError::Expected( + "expected a token tree".into(), + )), }, transparency(¯o_rules).unwrap_or(Transparency::SemiTransparent), ), @@ -163,12 +170,11 @@ impl DeclarativeMacroExpander { map.span_for_range(macro_def.macro_token().unwrap().text_range()), ); - mbe::DeclarativeMacro::parse_macro2(&tt, is_2021, new_meta_vars) + mbe::DeclarativeMacro::parse_macro2(&tt, edition, new_meta_vars) } - None => mbe::DeclarativeMacro::from_err( - mbe::ParseError::Expected("expected a token tree".into()), - is_2021, - ), + None => mbe::DeclarativeMacro::from_err(mbe::ParseError::Expected( + "expected a token tree".into(), + )), }, transparency(¯o_def).unwrap_or(Transparency::Opaque), ), diff --git a/crates/mbe/src/benchmark.rs b/crates/mbe/src/benchmark.rs index cacd5ed81a52..4d5531ae307f 100644 --- a/crates/mbe/src/benchmark.rs +++ b/crates/mbe/src/benchmark.rs @@ -23,7 +23,11 @@ fn benchmark_parse_macro_rules() { let _pt = bench("mbe parse macro rules"); rules .values() - .map(|it| DeclarativeMacro::parse_macro_rules(it, true, true).rules.len()) + .map(|it| { + DeclarativeMacro::parse_macro_rules(it, |_| span::Edition::CURRENT, true) + .rules + .len() + }) .sum() }; assert_eq!(hash, 1144); @@ -54,7 +58,9 @@ fn benchmark_expand_macro_rules() { fn macro_rules_fixtures() -> FxHashMap { macro_rules_fixtures_tt() .into_iter() - .map(|(id, tt)| (id, DeclarativeMacro::parse_macro_rules(&tt, true, true))) + .map(|(id, tt)| { + (id, DeclarativeMacro::parse_macro_rules(&tt, |_| span::Edition::CURRENT, true)) + }) .collect() } diff --git a/crates/mbe/src/expander.rs b/crates/mbe/src/expander.rs index 9e365232656d..2f2c0aa6ff59 100644 --- a/crates/mbe/src/expander.rs +++ b/crates/mbe/src/expander.rs @@ -15,13 +15,12 @@ pub(crate) fn expand_rules( rules: &[crate::Rule], input: &tt::Subtree, marker: impl Fn(&mut Span) + Copy, - is_2021: bool, new_meta_vars: bool, call_site: Span, ) -> ExpandResult> { let mut match_: Option<(matcher::Match, &crate::Rule)> = None; for rule in rules { - let new_match = matcher::match_(&rule.lhs, input, is_2021); + let new_match = matcher::match_(&rule.lhs, input); if new_match.err.is_none() { // If we find a rule that applies without errors, we're done. diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs index e14e13856fd2..3170834d54ff 100644 --- a/crates/mbe/src/expander/matcher.rs +++ b/crates/mbe/src/expander/matcher.rs @@ -108,8 +108,8 @@ impl Match { } /// Matching errors are added to the `Match`. -pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree, is_2021: bool) -> Match { - let mut res = match_loop(pattern, input, is_2021); +pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree) -> Match { + let mut res = match_loop(pattern, input); res.bound_count = count(res.bindings.bindings()); return res; @@ -362,7 +362,6 @@ fn match_loop_inner<'t>( next_items: &mut Vec>, eof_items: &mut SmallVec<[MatchState<'t>; 1]>, error_items: &mut SmallVec<[MatchState<'t>; 1]>, - is_2021: bool, delim_span: tt::DelimSpan, ) { macro_rules! try_push { @@ -474,7 +473,7 @@ fn match_loop_inner<'t>( OpDelimited::Op(Op::Var { kind, name, .. }) => { if let &Some(kind) = kind { let mut fork = src.clone(); - let match_res = match_meta_var(kind, &mut fork, is_2021, delim_span); + let match_res = match_meta_var(kind, &mut fork, delim_span); match match_res.err { None => { // Some meta variables are optional (e.g. vis) @@ -587,7 +586,7 @@ fn match_loop_inner<'t>( } } -fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, is_2021: bool) -> Match { +fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match { let span = src.delimiter.delim_span(); let mut src = TtIter::new(src); let mut stack: SmallVec<[TtIter<'_, Span>; 1]> = SmallVec::new(); @@ -627,7 +626,6 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, is_2021: bool) -> &mut next_items, &mut eof_items, &mut error_items, - is_2021, span, ); stdx::always!(cur_items.is_empty()); @@ -741,7 +739,6 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, is_2021: bool) -> fn match_meta_var( kind: MetaVarKind, input: &mut TtIter<'_, Span>, - is_2021: bool, delim_span: DelimSpan, ) -> ExpandResult> { let fragment = match kind { @@ -751,8 +748,7 @@ fn match_meta_var( }); } MetaVarKind::Ty => parser::PrefixEntryPoint::Ty, - MetaVarKind::Pat if is_2021 => parser::PrefixEntryPoint::PatTop, - MetaVarKind::Pat => parser::PrefixEntryPoint::Pat, + MetaVarKind::Pat => parser::PrefixEntryPoint::PatTop, MetaVarKind::PatParam => parser::PrefixEntryPoint::Pat, MetaVarKind::Stmt => parser::PrefixEntryPoint::Stmt, MetaVarKind::Block => parser::PrefixEntryPoint::Block, diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs index 2d68f6f6a53d..3a853512660e 100644 --- a/crates/mbe/src/lib.rs +++ b/crates/mbe/src/lib.rs @@ -17,7 +17,7 @@ mod tt_iter; #[cfg(test)] mod benchmark; -use span::Span; +use span::{Edition, Span, SyntaxContextId}; use stdx::impl_from; use std::fmt; @@ -129,9 +129,6 @@ impl fmt::Display for CountError { #[derive(Clone, Debug, PartialEq, Eq)] pub struct DeclarativeMacro { rules: Box<[Rule]>, - // This is used for correctly determining the behavior of the pat fragment - // FIXME: This should be tracked by hygiene of the fragment identifier! - is_2021: bool, err: Option>, } @@ -142,14 +139,14 @@ struct Rule { } impl DeclarativeMacro { - pub fn from_err(err: ParseError, is_2021: bool) -> DeclarativeMacro { - DeclarativeMacro { rules: Box::default(), is_2021, err: Some(Box::new(err)) } + pub fn from_err(err: ParseError) -> DeclarativeMacro { + DeclarativeMacro { rules: Box::default(), err: Some(Box::new(err)) } } /// The old, `macro_rules! m {}` flavor. pub fn parse_macro_rules( tt: &tt::Subtree, - is_2021: bool, + edition: impl Copy + Fn(SyntaxContextId) -> Edition, // FIXME: Remove this once we drop support for rust 1.76 (defaults to true then) new_meta_vars: bool, ) -> DeclarativeMacro { @@ -161,7 +158,7 @@ impl DeclarativeMacro { let mut err = None; while src.len() > 0 { - let rule = match Rule::parse(&mut src, true, new_meta_vars) { + let rule = match Rule::parse(edition, &mut src, true, new_meta_vars) { Ok(it) => it, Err(e) => { err = Some(Box::new(e)); @@ -184,13 +181,13 @@ impl DeclarativeMacro { } } - DeclarativeMacro { rules: rules.into_boxed_slice(), is_2021, err } + DeclarativeMacro { rules: rules.into_boxed_slice(), err } } /// The new, unstable `macro m {}` flavor. pub fn parse_macro2( tt: &tt::Subtree, - is_2021: bool, + edition: impl Copy + Fn(SyntaxContextId) -> Edition, // FIXME: Remove this once we drop support for rust 1.76 (defaults to true then) new_meta_vars: bool, ) -> DeclarativeMacro { @@ -201,7 +198,7 @@ impl DeclarativeMacro { if tt::DelimiterKind::Brace == tt.delimiter.kind { cov_mark::hit!(parse_macro_def_rules); while src.len() > 0 { - let rule = match Rule::parse(&mut src, true, new_meta_vars) { + let rule = match Rule::parse(edition, &mut src, true, new_meta_vars) { Ok(it) => it, Err(e) => { err = Some(Box::new(e)); @@ -220,7 +217,7 @@ impl DeclarativeMacro { } } else { cov_mark::hit!(parse_macro_def_simple); - match Rule::parse(&mut src, false, new_meta_vars) { + match Rule::parse(edition, &mut src, false, new_meta_vars) { Ok(rule) => { if src.len() != 0 { err = Some(Box::new(ParseError::expected("remaining tokens in macro def"))); @@ -240,7 +237,7 @@ impl DeclarativeMacro { } } - DeclarativeMacro { rules: rules.into_boxed_slice(), is_2021, err } + DeclarativeMacro { rules: rules.into_boxed_slice(), err } } pub fn err(&self) -> Option<&ParseError> { @@ -254,12 +251,13 @@ impl DeclarativeMacro { new_meta_vars: bool, call_site: Span, ) -> ExpandResult> { - expander::expand_rules(&self.rules, tt, marker, self.is_2021, new_meta_vars, call_site) + expander::expand_rules(&self.rules, tt, marker, new_meta_vars, call_site) } } impl Rule { fn parse( + edition: impl Copy + Fn(SyntaxContextId) -> Edition, src: &mut TtIter<'_, Span>, expect_arrow: bool, new_meta_vars: bool, @@ -271,8 +269,8 @@ impl Rule { } let rhs = src.expect_subtree().map_err(|()| ParseError::expected("expected subtree"))?; - let lhs = MetaTemplate::parse_pattern(lhs)?; - let rhs = MetaTemplate::parse_template(rhs, new_meta_vars)?; + let lhs = MetaTemplate::parse_pattern(edition, lhs)?; + let rhs = MetaTemplate::parse_template(edition, rhs, new_meta_vars)?; Ok(crate::Rule { lhs, rhs }) } diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs index 560dcf61a822..eaf2fd8c273f 100644 --- a/crates/mbe/src/parser.rs +++ b/crates/mbe/src/parser.rs @@ -2,7 +2,7 @@ //! trees. use smallvec::{smallvec, SmallVec}; -use span::Span; +use span::{Edition, Span, SyntaxContextId}; use syntax::SmolStr; use crate::{tt_iter::TtIter, ParseError}; @@ -24,27 +24,36 @@ use crate::{tt_iter::TtIter, ParseError}; pub(crate) struct MetaTemplate(pub(crate) Box<[Op]>); impl MetaTemplate { - pub(crate) fn parse_pattern(pattern: &tt::Subtree) -> Result { - MetaTemplate::parse(pattern, Mode::Pattern, false) + pub(crate) fn parse_pattern( + edition: impl Copy + Fn(SyntaxContextId) -> Edition, + pattern: &tt::Subtree, + ) -> Result { + MetaTemplate::parse(edition, pattern, Mode::Pattern, false) } pub(crate) fn parse_template( + edition: impl Copy + Fn(SyntaxContextId) -> Edition, template: &tt::Subtree, new_meta_vars: bool, ) -> Result { - MetaTemplate::parse(template, Mode::Template, new_meta_vars) + MetaTemplate::parse(edition, template, Mode::Template, new_meta_vars) } pub(crate) fn iter(&self) -> impl Iterator { self.0.iter() } - fn parse(tt: &tt::Subtree, mode: Mode, new_meta_vars: bool) -> Result { + fn parse( + edition: impl Copy + Fn(SyntaxContextId) -> Edition, + tt: &tt::Subtree, + mode: Mode, + new_meta_vars: bool, + ) -> Result { let mut src = TtIter::new(tt); let mut res = Vec::new(); while let Some(first) = src.peek_n(0) { - let op = next_op(first, &mut src, mode, new_meta_vars)?; + let op = next_op(edition, first, &mut src, mode, new_meta_vars)?; res.push(op); } @@ -145,6 +154,7 @@ enum Mode { } fn next_op( + edition: impl Copy + Fn(SyntaxContextId) -> Edition, first_peeked: &tt::TokenTree, src: &mut TtIter<'_, Span>, mode: Mode, @@ -162,7 +172,7 @@ fn next_op( tt::TokenTree::Subtree(subtree) => match subtree.delimiter.kind { tt::DelimiterKind::Parenthesis => { let (separator, kind) = parse_repeat(src)?; - let tokens = MetaTemplate::parse(subtree, mode, new_meta_vars)?; + let tokens = MetaTemplate::parse(edition, subtree, mode, new_meta_vars)?; Op::Repeat { tokens, separator, kind } } tt::DelimiterKind::Brace => match mode { @@ -189,13 +199,13 @@ fn next_op( Op::Ident(tt::Ident { text: "$crate".into(), span: ident.span }) } tt::Leaf::Ident(ident) => { - let kind = eat_fragment_kind(src, mode)?; + let kind = eat_fragment_kind(edition, src, mode)?; let name = ident.text.clone(); let id = ident.span; Op::Var { name, kind, id } } tt::Leaf::Literal(lit) if is_boolean_literal(lit) => { - let kind = eat_fragment_kind(src, mode)?; + let kind = eat_fragment_kind(edition, src, mode)?; let name = lit.text.clone(); let id = lit.span; Op::Var { name, kind, id } @@ -233,7 +243,7 @@ fn next_op( tt::TokenTree::Subtree(subtree) => { src.next().expect("first token already peeked"); - let tokens = MetaTemplate::parse(subtree, mode, new_meta_vars)?; + let tokens = MetaTemplate::parse(edition, subtree, mode, new_meta_vars)?; Op::Subtree { tokens, delimiter: subtree.delimiter } } }; @@ -241,6 +251,7 @@ fn next_op( } fn eat_fragment_kind( + edition: impl Copy + Fn(SyntaxContextId) -> Edition, src: &mut TtIter<'_, Span>, mode: Mode, ) -> Result, ParseError> { @@ -252,7 +263,10 @@ fn eat_fragment_kind( let kind = match ident.text.as_str() { "path" => MetaVarKind::Path, "ty" => MetaVarKind::Ty, - "pat" => MetaVarKind::Pat, + "pat" => match edition(ident.span.ctx) { + Edition::Edition2015 | Edition::Edition2018 => MetaVarKind::PatParam, + Edition::Edition2021 | Edition::Edition2024 => MetaVarKind::Pat, + }, "pat_param" => MetaVarKind::PatParam, "stmt" => MetaVarKind::Stmt, "block" => MetaVarKind::Block,