Skip to content

fix: Fix pat fragment handling in 2021 edition #14652

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 37 additions & 3 deletions crates/hir-def/src/macro_expansion_tests/mbe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1293,19 +1293,53 @@ ok!();
}

#[test]
fn test_vertical_bar_with_pat() {
fn test_vertical_bar_with_pat_param() {
check(
r#"
macro_rules! m { (|$pat:pat| ) => { ok!(); } }
macro_rules! m { (|$pat:pat_param| ) => { ok!(); } }
m! { |x| }
"#,
expect![[r#"
macro_rules! m { (|$pat:pat| ) => { ok!(); } }
macro_rules! m { (|$pat:pat_param| ) => { ok!(); } }
ok!();
"#]],
);
}

#[test]
fn test_new_std_matches() {
check(
r#"
macro_rules! matches {
($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => {
match $expression {
$pattern $(if $guard)? => true,
_ => false
}
};
}
fn main() {
matches!(0, 0 | 1 if true);
}
"#,
expect![[r#"
macro_rules! matches {
($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => {
match $expression {
$pattern $(if $guard)? => true,
_ => false
}
};
}
fn main() {
match 0 {
0|1if true =>true , _=>false
};
}
"#]],
);
}

#[test]
fn test_dollar_crate_lhs_is_not_meta() {
check(
Expand Down
7 changes: 4 additions & 3 deletions crates/hir-expand/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use std::sync::Arc;

use base_db::{salsa, SourceDatabase};
use base_db::{salsa, Edition, SourceDatabase};
use either::Either;
use limit::Limit;
use mbe::syntax_node_to_token_tree;
Expand Down Expand Up @@ -406,21 +406,22 @@ fn macro_def(
) -> Result<Arc<TokenExpander>, mbe::ParseError> {
match id.kind {
MacroDefKind::Declarative(ast_id) => {
let is_2021 = db.crate_graph()[id.krate].edition >= Edition::Edition2021;
let (mac, def_site_token_map) = match ast_id.to_node(db) {
ast::Macro::MacroRules(macro_rules) => {
let arg = macro_rules
.token_tree()
.ok_or_else(|| mbe::ParseError::Expected("expected a token tree".into()))?;
let (tt, def_site_token_map) = mbe::syntax_node_to_token_tree(arg.syntax());
let mac = mbe::DeclarativeMacro::parse_macro_rules(&tt)?;
let mac = mbe::DeclarativeMacro::parse_macro_rules(&tt, is_2021)?;
(mac, def_site_token_map)
}
ast::Macro::MacroDef(macro_def) => {
let arg = macro_def
.body()
.ok_or_else(|| mbe::ParseError::Expected("expected a token tree".into()))?;
let (tt, def_site_token_map) = mbe::syntax_node_to_token_tree(arg.syntax());
let mac = mbe::DeclarativeMacro::parse_macro2(&tt)?;
let mac = mbe::DeclarativeMacro::parse_macro2(&tt, is_2021)?;
(mac, def_site_token_map)
}
};
Expand Down
7 changes: 5 additions & 2 deletions crates/mbe/src/benchmark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ fn benchmark_parse_macro_rules() {
let rules = macro_rules_fixtures_tt();
let hash: usize = {
let _pt = bench("mbe parse macro rules");
rules.values().map(|it| DeclarativeMacro::parse_macro_rules(it).unwrap().rules.len()).sum()
rules
.values()
.map(|it| DeclarativeMacro::parse_macro_rules(it, true).unwrap().rules.len())
.sum()
};
assert_eq!(hash, 1144);
}
Expand Down Expand Up @@ -50,7 +53,7 @@ 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).unwrap()))
.map(|(id, tt)| (id, DeclarativeMacro::parse_macro_rules(&tt, true).unwrap()))
.collect()
}

Expand Down
3 changes: 2 additions & 1 deletion crates/mbe/src/expander.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ use crate::{parser::MetaVarKind, tt, ExpandError, ExpandResult};
pub(crate) fn expand_rules(
rules: &[crate::Rule],
input: &tt::Subtree,
is_2021: bool,
) -> ExpandResult<tt::Subtree> {
let mut match_: Option<(matcher::Match, &crate::Rule)> = None;
for rule in rules {
let new_match = matcher::match_(&rule.lhs, input);
let new_match = matcher::match_(&rule.lhs, input, is_2021);

if new_match.err.is_none() {
// If we find a rule that applies without errors, we're done.
Expand Down
24 changes: 15 additions & 9 deletions crates/mbe/src/expander/matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@ impl Match {
}

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

Expand Down Expand Up @@ -354,6 +354,7 @@ struct MatchState<'t> {
/// - `eof_items`: the set of items that would be valid if this was the EOF.
/// - `bb_items`: the set of items that are waiting for the black-box parser.
/// - `error_items`: the set of items in errors, used for error-resilient parsing
#[inline]
fn match_loop_inner<'t>(
src: TtIter<'t>,
stack: &[TtIter<'t>],
Expand All @@ -364,6 +365,7 @@ 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,
) {
macro_rules! try_push {
($items: expr, $it:expr) => {
Expand Down Expand Up @@ -474,7 +476,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);
let match_res = match_meta_var(kind, &mut fork, is_2021);
match match_res.err {
None => {
// Some meta variables are optional (e.g. vis)
Expand Down Expand Up @@ -583,7 +585,7 @@ fn match_loop_inner<'t>(
}
}

fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match {
fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, is_2021: bool) -> Match {
let mut src = TtIter::new(src);
let mut stack: SmallVec<[TtIter<'_>; 1]> = SmallVec::new();
let mut res = Match::default();
Expand Down Expand Up @@ -622,6 +624,7 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match {
&mut next_items,
&mut eof_items,
&mut error_items,
is_2021,
);
stdx::always!(cur_items.is_empty());

Expand Down Expand Up @@ -731,14 +734,17 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match {
}
}

fn match_meta_var(kind: MetaVarKind, input: &mut TtIter<'_>) -> ExpandResult<Option<Fragment>> {
fn match_meta_var(
kind: MetaVarKind,
input: &mut TtIter<'_>,
is_2021: bool,
) -> ExpandResult<Option<Fragment>> {
let fragment = match kind {
MetaVarKind::Path => parser::PrefixEntryPoint::Path,
MetaVarKind::Ty => parser::PrefixEntryPoint::Ty,
// FIXME: These two should actually behave differently depending on the edition.
//
// https://doc.rust-lang.org/edition-guide/rust-2021/or-patterns-macro-rules.html
MetaVarKind::Pat | MetaVarKind::PatParam => parser::PrefixEntryPoint::Pat,
MetaVarKind::Pat if is_2021 => parser::PrefixEntryPoint::PatTop,
MetaVarKind::Pat => parser::PrefixEntryPoint::Pat,
MetaVarKind::PatParam => parser::PrefixEntryPoint::Pat,
MetaVarKind::Stmt => parser::PrefixEntryPoint::Stmt,
MetaVarKind::Block => parser::PrefixEntryPoint::Block,
MetaVarKind::Meta => parser::PrefixEntryPoint::MetaItem,
Expand Down
16 changes: 11 additions & 5 deletions crates/mbe/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ pub struct DeclarativeMacro {
rules: Vec<Rule>,
/// Highest id of the token we have in TokenMap
shift: Shift,
// 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,
}

#[derive(Clone, Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -190,7 +193,10 @@ pub enum Origin {

impl DeclarativeMacro {
/// The old, `macro_rules! m {}` flavor.
pub fn parse_macro_rules(tt: &tt::Subtree) -> Result<DeclarativeMacro, ParseError> {
pub fn parse_macro_rules(
tt: &tt::Subtree,
is_2021: bool,
) -> Result<DeclarativeMacro, ParseError> {
// Note: this parsing can be implemented using mbe machinery itself, by
// matching against `$($lhs:tt => $rhs:tt);*` pattern, but implementing
// manually seems easier.
Expand All @@ -211,11 +217,11 @@ impl DeclarativeMacro {
validate(lhs)?;
}

Ok(DeclarativeMacro { rules, shift: Shift::new(tt) })
Ok(DeclarativeMacro { rules, shift: Shift::new(tt), is_2021 })
}

/// The new, unstable `macro m {}` flavor.
pub fn parse_macro2(tt: &tt::Subtree) -> Result<DeclarativeMacro, ParseError> {
pub fn parse_macro2(tt: &tt::Subtree, is_2021: bool) -> Result<DeclarativeMacro, ParseError> {
let mut src = TtIter::new(tt);
let mut rules = Vec::new();

Expand Down Expand Up @@ -244,14 +250,14 @@ impl DeclarativeMacro {
validate(lhs)?;
}

Ok(DeclarativeMacro { rules, shift: Shift::new(tt) })
Ok(DeclarativeMacro { rules, shift: Shift::new(tt), is_2021 })
}

pub fn expand(&self, tt: &tt::Subtree) -> ExpandResult<tt::Subtree> {
// apply shift
let mut tt = tt.clone();
self.shift.shift_all(&mut tt);
expander::expand_rules(&self.rules, &tt)
expander::expand_rules(&self.rules, &tt, self.is_2021)
}

pub fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId {
Expand Down
4 changes: 4 additions & 0 deletions crates/parser/src/grammar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ pub(crate) mod entry {
patterns::pattern_single(p);
}

pub(crate) fn pat_top(p: &mut Parser<'_>) {
patterns::pattern_top(p);
}

pub(crate) fn ty(p: &mut Parser<'_>) {
types::type_(p);
}
Expand Down
2 changes: 2 additions & 0 deletions crates/parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ pub enum PrefixEntryPoint {
Block,
Stmt,
Pat,
PatTop,
Ty,
Expr,
Path,
Expand All @@ -145,6 +146,7 @@ impl PrefixEntryPoint {
PrefixEntryPoint::Block => grammar::entry::prefix::block,
PrefixEntryPoint::Stmt => grammar::entry::prefix::stmt,
PrefixEntryPoint::Pat => grammar::entry::prefix::pat,
PrefixEntryPoint::PatTop => grammar::entry::prefix::pat_top,
PrefixEntryPoint::Ty => grammar::entry::prefix::ty,
PrefixEntryPoint::Expr => grammar::entry::prefix::expr,
PrefixEntryPoint::Path => grammar::entry::prefix::path,
Expand Down