Skip to content

Commit

Permalink
Resolve whether $pat is $pat_param or not via 🌟hygiene🌟
Browse files Browse the repository at this point in the history
  • Loading branch information
Veykril committed Mar 21, 2024
1 parent 7e88fa5 commit 6d10719
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 54 deletions.
85 changes: 85 additions & 0 deletions crates/hir-def/src/macro_expansion_tests/mbe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)? $(,)?) => {
Expand Down Expand Up @@ -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(
Expand Down
34 changes: 20 additions & 14 deletions crates/hir-expand/src/declarative.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
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;

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
Expand Down Expand Up @@ -94,8 +94,6 @@ impl DeclarativeMacroExpander {
def_crate: CrateId,
id: AstId<ast::Macro>,
) -> Arc<DeclarativeMacroExpander> {
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();

Expand Down Expand Up @@ -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() {
Expand All @@ -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(&macro_rules).unwrap_or(Transparency::SemiTransparent),
),
Expand All @@ -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(&macro_def).unwrap_or(Transparency::Opaque),
),
Expand Down
10 changes: 8 additions & 2 deletions crates/mbe/src/benchmark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -54,7 +58,9 @@ fn benchmark_expand_macro_rules() {
fn macro_rules_fixtures() -> FxHashMap<String, DeclarativeMacro> {
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()
}

Expand Down
3 changes: 1 addition & 2 deletions crates/mbe/src/expander.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,12 @@ pub(crate) fn expand_rules(
rules: &[crate::Rule],
input: &tt::Subtree<Span>,
marker: impl Fn(&mut Span) + Copy,
is_2021: bool,
new_meta_vars: bool,
call_site: Span,
) -> ExpandResult<tt::Subtree<Span>> {
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.
Expand Down
14 changes: 5 additions & 9 deletions crates/mbe/src/expander/matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ impl Match {
}

/// Matching errors are added to the `Match`.
pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree<Span>, is_2021: bool) -> Match {
let mut res = match_loop(pattern, input, is_2021);
pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree<Span>) -> Match {
let mut res = match_loop(pattern, input);
res.bound_count = count(res.bindings.bindings());
return res;

Expand Down Expand Up @@ -362,7 +362,6 @@ fn match_loop_inner<'t>(
next_items: &mut Vec<MatchState<'t>>,
eof_items: &mut SmallVec<[MatchState<'t>; 1]>,
error_items: &mut SmallVec<[MatchState<'t>; 1]>,
is_2021: bool,
delim_span: tt::DelimSpan<Span>,
) {
macro_rules! try_push {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -587,7 +586,7 @@ fn match_loop_inner<'t>(
}
}

fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree<Span>, is_2021: bool) -> Match {
fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree<Span>) -> Match {
let span = src.delimiter.delim_span();
let mut src = TtIter::new(src);
let mut stack: SmallVec<[TtIter<'_, Span>; 1]> = SmallVec::new();
Expand Down Expand Up @@ -627,7 +626,6 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree<Span>, is_2021: bool) ->
&mut next_items,
&mut eof_items,
&mut error_items,
is_2021,
span,
);
stdx::always!(cur_items.is_empty());
Expand Down Expand Up @@ -741,7 +739,6 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree<Span>, is_2021: bool) ->
fn match_meta_var(
kind: MetaVarKind,
input: &mut TtIter<'_, Span>,
is_2021: bool,
delim_span: DelimSpan<Span>,
) -> ExpandResult<Option<Fragment>> {
let fragment = match kind {
Expand All @@ -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,
Expand Down
30 changes: 14 additions & 16 deletions crates/mbe/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<Box<ParseError>>,
}

Expand All @@ -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<Span>,
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 {
Expand All @@ -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));
Expand All @@ -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<Span>,
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 {
Expand All @@ -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));
Expand All @@ -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")));
Expand All @@ -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> {
Expand All @@ -254,12 +251,13 @@ impl DeclarativeMacro {
new_meta_vars: bool,
call_site: Span,
) -> ExpandResult<tt::Subtree<Span>> {
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,
Expand All @@ -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 })
}
Expand Down
Loading

0 comments on commit 6d10719

Please sign in to comment.