mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 14:21:44 +00:00
Auto merge of #14652 - Veykril:pat2021, r=Veykril
fix: Fix pat fragment handling in 2021 edition Fixes https://github.com/rust-lang/rust-analyzer/issues/9055 The fix isn't that great, but we are kind of forced to do it the quick and hacky way right now since std has changed the `matches` macro to make use of this now. And for a proper fix we need to track hygiene for identifiers which is a long way off anyways
This commit is contained in:
commit
707382c21d
8 changed files with 80 additions and 23 deletions
|
@ -1293,19 +1293,53 @@ ok!();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_vertical_bar_with_pat() {
|
fn test_vertical_bar_with_pat_param() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
macro_rules! m { (|$pat:pat| ) => { ok!(); } }
|
macro_rules! m { (|$pat:pat_param| ) => { ok!(); } }
|
||||||
m! { |x| }
|
m! { |x| }
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
macro_rules! m { (|$pat:pat| ) => { ok!(); } }
|
macro_rules! m { (|$pat:pat_param| ) => { ok!(); } }
|
||||||
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]
|
#[test]
|
||||||
fn test_dollar_crate_lhs_is_not_meta() {
|
fn test_dollar_crate_lhs_is_not_meta() {
|
||||||
check(
|
check(
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use base_db::{salsa, SourceDatabase};
|
use base_db::{salsa, Edition, SourceDatabase};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use limit::Limit;
|
use limit::Limit;
|
||||||
use mbe::syntax_node_to_token_tree;
|
use mbe::syntax_node_to_token_tree;
|
||||||
|
@ -406,13 +406,14 @@ fn macro_def(
|
||||||
) -> Result<Arc<TokenExpander>, mbe::ParseError> {
|
) -> Result<Arc<TokenExpander>, mbe::ParseError> {
|
||||||
match id.kind {
|
match id.kind {
|
||||||
MacroDefKind::Declarative(ast_id) => {
|
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) {
|
let (mac, def_site_token_map) = match ast_id.to_node(db) {
|
||||||
ast::Macro::MacroRules(macro_rules) => {
|
ast::Macro::MacroRules(macro_rules) => {
|
||||||
let arg = macro_rules
|
let arg = macro_rules
|
||||||
.token_tree()
|
.token_tree()
|
||||||
.ok_or_else(|| mbe::ParseError::Expected("expected a token tree".into()))?;
|
.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 (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)
|
(mac, def_site_token_map)
|
||||||
}
|
}
|
||||||
ast::Macro::MacroDef(macro_def) => {
|
ast::Macro::MacroDef(macro_def) => {
|
||||||
|
@ -420,7 +421,7 @@ fn macro_def(
|
||||||
.body()
|
.body()
|
||||||
.ok_or_else(|| mbe::ParseError::Expected("expected a token tree".into()))?;
|
.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 (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)
|
(mac, def_site_token_map)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,7 +20,10 @@ fn benchmark_parse_macro_rules() {
|
||||||
let rules = macro_rules_fixtures_tt();
|
let rules = macro_rules_fixtures_tt();
|
||||||
let hash: usize = {
|
let hash: usize = {
|
||||||
let _pt = bench("mbe parse macro rules");
|
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);
|
assert_eq!(hash, 1144);
|
||||||
}
|
}
|
||||||
|
@ -50,7 +53,7 @@ fn benchmark_expand_macro_rules() {
|
||||||
fn macro_rules_fixtures() -> FxHashMap<String, DeclarativeMacro> {
|
fn macro_rules_fixtures() -> FxHashMap<String, DeclarativeMacro> {
|
||||||
macro_rules_fixtures_tt()
|
macro_rules_fixtures_tt()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(id, tt)| (id, DeclarativeMacro::parse_macro_rules(&tt).unwrap()))
|
.map(|(id, tt)| (id, DeclarativeMacro::parse_macro_rules(&tt, true).unwrap()))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,10 +13,11 @@ use crate::{parser::MetaVarKind, tt, ExpandError, ExpandResult};
|
||||||
pub(crate) fn expand_rules(
|
pub(crate) fn expand_rules(
|
||||||
rules: &[crate::Rule],
|
rules: &[crate::Rule],
|
||||||
input: &tt::Subtree,
|
input: &tt::Subtree,
|
||||||
|
is_2021: bool,
|
||||||
) -> ExpandResult<tt::Subtree> {
|
) -> ExpandResult<tt::Subtree> {
|
||||||
let mut match_: Option<(matcher::Match, &crate::Rule)> = None;
|
let mut match_: Option<(matcher::Match, &crate::Rule)> = None;
|
||||||
for rule in rules {
|
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 new_match.err.is_none() {
|
||||||
// If we find a rule that applies without errors, we're done.
|
// If we find a rule that applies without errors, we're done.
|
||||||
|
|
|
@ -111,8 +111,8 @@ impl Match {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Matching errors are added to the `Match`.
|
/// Matching errors are added to the `Match`.
|
||||||
pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree) -> Match {
|
pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree, is_2021: bool) -> Match {
|
||||||
let mut res = match_loop(pattern, input);
|
let mut res = match_loop(pattern, input, is_2021);
|
||||||
res.bound_count = count(res.bindings.bindings());
|
res.bound_count = count(res.bindings.bindings());
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
|
@ -354,6 +354,7 @@ struct MatchState<'t> {
|
||||||
/// - `eof_items`: the set of items that would be valid if this was the EOF.
|
/// - `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.
|
/// - `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
|
/// - `error_items`: the set of items in errors, used for error-resilient parsing
|
||||||
|
#[inline]
|
||||||
fn match_loop_inner<'t>(
|
fn match_loop_inner<'t>(
|
||||||
src: TtIter<'t>,
|
src: TtIter<'t>,
|
||||||
stack: &[TtIter<'t>],
|
stack: &[TtIter<'t>],
|
||||||
|
@ -364,6 +365,7 @@ fn match_loop_inner<'t>(
|
||||||
next_items: &mut Vec<MatchState<'t>>,
|
next_items: &mut Vec<MatchState<'t>>,
|
||||||
eof_items: &mut SmallVec<[MatchState<'t>; 1]>,
|
eof_items: &mut SmallVec<[MatchState<'t>; 1]>,
|
||||||
error_items: &mut SmallVec<[MatchState<'t>; 1]>,
|
error_items: &mut SmallVec<[MatchState<'t>; 1]>,
|
||||||
|
is_2021: bool,
|
||||||
) {
|
) {
|
||||||
macro_rules! try_push {
|
macro_rules! try_push {
|
||||||
($items: expr, $it:expr) => {
|
($items: expr, $it:expr) => {
|
||||||
|
@ -474,7 +476,7 @@ fn match_loop_inner<'t>(
|
||||||
OpDelimited::Op(Op::Var { kind, name, .. }) => {
|
OpDelimited::Op(Op::Var { kind, name, .. }) => {
|
||||||
if let &Some(kind) = kind {
|
if let &Some(kind) = kind {
|
||||||
let mut fork = src.clone();
|
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 {
|
match match_res.err {
|
||||||
None => {
|
None => {
|
||||||
// Some meta variables are optional (e.g. vis)
|
// Some meta variables are optional (e.g. vis)
|
||||||
|
@ -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 src = TtIter::new(src);
|
||||||
let mut stack: SmallVec<[TtIter<'_>; 1]> = SmallVec::new();
|
let mut stack: SmallVec<[TtIter<'_>; 1]> = SmallVec::new();
|
||||||
let mut res = Match::default();
|
let mut res = Match::default();
|
||||||
|
@ -622,6 +624,7 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match {
|
||||||
&mut next_items,
|
&mut next_items,
|
||||||
&mut eof_items,
|
&mut eof_items,
|
||||||
&mut error_items,
|
&mut error_items,
|
||||||
|
is_2021,
|
||||||
);
|
);
|
||||||
stdx::always!(cur_items.is_empty());
|
stdx::always!(cur_items.is_empty());
|
||||||
|
|
||||||
|
@ -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 {
|
let fragment = match kind {
|
||||||
MetaVarKind::Path => parser::PrefixEntryPoint::Path,
|
MetaVarKind::Path => parser::PrefixEntryPoint::Path,
|
||||||
MetaVarKind::Ty => parser::PrefixEntryPoint::Ty,
|
MetaVarKind::Ty => parser::PrefixEntryPoint::Ty,
|
||||||
// FIXME: These two should actually behave differently depending on the edition.
|
MetaVarKind::Pat if is_2021 => parser::PrefixEntryPoint::PatTop,
|
||||||
//
|
MetaVarKind::Pat => parser::PrefixEntryPoint::Pat,
|
||||||
// https://doc.rust-lang.org/edition-guide/rust-2021/or-patterns-macro-rules.html
|
MetaVarKind::PatParam => parser::PrefixEntryPoint::Pat,
|
||||||
MetaVarKind::Pat | MetaVarKind::PatParam => parser::PrefixEntryPoint::Pat,
|
|
||||||
MetaVarKind::Stmt => parser::PrefixEntryPoint::Stmt,
|
MetaVarKind::Stmt => parser::PrefixEntryPoint::Stmt,
|
||||||
MetaVarKind::Block => parser::PrefixEntryPoint::Block,
|
MetaVarKind::Block => parser::PrefixEntryPoint::Block,
|
||||||
MetaVarKind::Meta => parser::PrefixEntryPoint::MetaItem,
|
MetaVarKind::Meta => parser::PrefixEntryPoint::MetaItem,
|
||||||
|
|
|
@ -107,6 +107,9 @@ pub struct DeclarativeMacro {
|
||||||
rules: Vec<Rule>,
|
rules: Vec<Rule>,
|
||||||
/// Highest id of the token we have in TokenMap
|
/// Highest id of the token we have in TokenMap
|
||||||
shift: Shift,
|
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)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
@ -190,7 +193,10 @@ pub enum Origin {
|
||||||
|
|
||||||
impl DeclarativeMacro {
|
impl DeclarativeMacro {
|
||||||
/// The old, `macro_rules! m {}` flavor.
|
/// 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
|
// Note: this parsing can be implemented using mbe machinery itself, by
|
||||||
// matching against `$($lhs:tt => $rhs:tt);*` pattern, but implementing
|
// matching against `$($lhs:tt => $rhs:tt);*` pattern, but implementing
|
||||||
// manually seems easier.
|
// manually seems easier.
|
||||||
|
@ -211,11 +217,11 @@ impl DeclarativeMacro {
|
||||||
validate(lhs)?;
|
validate(lhs)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(DeclarativeMacro { rules, shift: Shift::new(tt) })
|
Ok(DeclarativeMacro { rules, shift: Shift::new(tt), is_2021 })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The new, unstable `macro m {}` flavor.
|
/// 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 src = TtIter::new(tt);
|
||||||
let mut rules = Vec::new();
|
let mut rules = Vec::new();
|
||||||
|
|
||||||
|
@ -244,14 +250,14 @@ impl DeclarativeMacro {
|
||||||
validate(lhs)?;
|
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> {
|
pub fn expand(&self, tt: &tt::Subtree) -> ExpandResult<tt::Subtree> {
|
||||||
// apply shift
|
// apply shift
|
||||||
let mut tt = tt.clone();
|
let mut tt = tt.clone();
|
||||||
self.shift.shift_all(&mut tt);
|
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 {
|
pub fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId {
|
||||||
|
|
|
@ -66,6 +66,10 @@ pub(crate) mod entry {
|
||||||
patterns::pattern_single(p);
|
patterns::pattern_single(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn pat_top(p: &mut Parser<'_>) {
|
||||||
|
patterns::pattern_top(p);
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn ty(p: &mut Parser<'_>) {
|
pub(crate) fn ty(p: &mut Parser<'_>) {
|
||||||
types::type_(p);
|
types::type_(p);
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,6 +131,7 @@ pub enum PrefixEntryPoint {
|
||||||
Block,
|
Block,
|
||||||
Stmt,
|
Stmt,
|
||||||
Pat,
|
Pat,
|
||||||
|
PatTop,
|
||||||
Ty,
|
Ty,
|
||||||
Expr,
|
Expr,
|
||||||
Path,
|
Path,
|
||||||
|
@ -145,6 +146,7 @@ impl PrefixEntryPoint {
|
||||||
PrefixEntryPoint::Block => grammar::entry::prefix::block,
|
PrefixEntryPoint::Block => grammar::entry::prefix::block,
|
||||||
PrefixEntryPoint::Stmt => grammar::entry::prefix::stmt,
|
PrefixEntryPoint::Stmt => grammar::entry::prefix::stmt,
|
||||||
PrefixEntryPoint::Pat => grammar::entry::prefix::pat,
|
PrefixEntryPoint::Pat => grammar::entry::prefix::pat,
|
||||||
|
PrefixEntryPoint::PatTop => grammar::entry::prefix::pat_top,
|
||||||
PrefixEntryPoint::Ty => grammar::entry::prefix::ty,
|
PrefixEntryPoint::Ty => grammar::entry::prefix::ty,
|
||||||
PrefixEntryPoint::Expr => grammar::entry::prefix::expr,
|
PrefixEntryPoint::Expr => grammar::entry::prefix::expr,
|
||||||
PrefixEntryPoint::Path => grammar::entry::prefix::path,
|
PrefixEntryPoint::Path => grammar::entry::prefix::path,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue