mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-26 20:09:19 +00:00
fix: Update metavariable expression implementation
This commit is contained in:
parent
002e611d09
commit
8753ca5360
7 changed files with 83 additions and 65 deletions
|
@ -18,7 +18,7 @@ macro_rules! m {
|
||||||
($($false:ident)*) => ($false);
|
($($false:ident)*) => ($false);
|
||||||
(double_dollar) => ($$);
|
(double_dollar) => ($$);
|
||||||
($) => (m!($););
|
($) => (m!($););
|
||||||
($($t:tt)*) => ($( ${ignore(t)} ${index()} )-*);
|
($($t:tt)*) => ($( ${ignore($t)} ${index()} )-*);
|
||||||
}
|
}
|
||||||
m!($);
|
m!($);
|
||||||
"#,
|
"#,
|
||||||
|
@ -33,7 +33,7 @@ macro_rules! m {
|
||||||
($($false:ident)*) => ($false);
|
($($false:ident)*) => ($false);
|
||||||
(double_dollar) => ($$);
|
(double_dollar) => ($$);
|
||||||
($) => (m!($););
|
($) => (m!($););
|
||||||
($($t:tt)*) => ($( ${ignore(t)} ${index()} )-*);
|
($($t:tt)*) => ($( ${ignore($t)} ${index()} )-*);
|
||||||
}
|
}
|
||||||
m!($);
|
m!($);
|
||||||
"#]],
|
"#]],
|
||||||
|
|
|
@ -77,13 +77,13 @@ fn test_metavar_exprs() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
macro_rules! m {
|
macro_rules! m {
|
||||||
( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* );
|
( $( $t:tt )* ) => ( $( ${ignore($t)} -${index()} )-* );
|
||||||
}
|
}
|
||||||
const _: i32 = m!(a b c);
|
const _: i32 = m!(a b c);
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
macro_rules! m {
|
macro_rules! m {
|
||||||
( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* );
|
( $( $t:tt )* ) => ( $( ${ignore($t)} -${index()} )-* );
|
||||||
}
|
}
|
||||||
const _: i32 = -0--1--2;
|
const _: i32 = -0--1--2;
|
||||||
"#]],
|
"#]],
|
||||||
|
@ -96,7 +96,7 @@ fn count_basic() {
|
||||||
r#"
|
r#"
|
||||||
macro_rules! m {
|
macro_rules! m {
|
||||||
($($t:ident),*) => {
|
($($t:ident),*) => {
|
||||||
${count(t)}
|
${count($t)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ fn test() {
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
macro_rules! m {
|
macro_rules! m {
|
||||||
($($t:ident),*) => {
|
($($t:ident),*) => {
|
||||||
${count(t)}
|
${count($t)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,9 +130,9 @@ macro_rules! foo {
|
||||||
($( $( $($t:ident)* ),* );*) => {
|
($( $( $($t:ident)* ),* );*) => {
|
||||||
$(
|
$(
|
||||||
{
|
{
|
||||||
let depth_none = ${count(t)};
|
let depth_none = ${count($t)};
|
||||||
let depth_zero = ${count(t, 0)};
|
let depth_zero = ${count($t, 0)};
|
||||||
let depth_one = ${count(t, 1)};
|
let depth_one = ${count($t, 1)};
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
|
@ -150,9 +150,9 @@ macro_rules! foo {
|
||||||
($( $( $($t:ident)* ),* );*) => {
|
($( $( $($t:ident)* ),* );*) => {
|
||||||
$(
|
$(
|
||||||
{
|
{
|
||||||
let depth_none = ${count(t)};
|
let depth_none = ${count($t)};
|
||||||
let depth_zero = ${count(t, 0)};
|
let depth_zero = ${count($t, 0)};
|
||||||
let depth_one = ${count(t, 1)};
|
let depth_one = ${count($t, 1)};
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
|
@ -160,11 +160,11 @@ macro_rules! foo {
|
||||||
|
|
||||||
fn bar() {
|
fn bar() {
|
||||||
{
|
{
|
||||||
let depth_none = 6;
|
let depth_none = 3;
|
||||||
let depth_zero = 3;
|
let depth_zero = 3;
|
||||||
let depth_one = 6;
|
let depth_one = 6;
|
||||||
} {
|
} {
|
||||||
let depth_none = 3;
|
let depth_none = 1;
|
||||||
let depth_zero = 1;
|
let depth_zero = 1;
|
||||||
let depth_one = 3;
|
let depth_one = 3;
|
||||||
}
|
}
|
||||||
|
@ -178,12 +178,12 @@ fn count_depth_out_of_bounds() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
macro_rules! foo {
|
macro_rules! foo {
|
||||||
($($t:ident)*) => { ${count(t, 1)} };
|
($($t:ident)*) => { ${count($t, 1)} };
|
||||||
($( $( $l:literal )* );*) => { $(${count(l, 1)};)* }
|
($( $( $l:literal )* );*) => { $(${count($l, 1)};)* }
|
||||||
}
|
}
|
||||||
macro_rules! bar {
|
macro_rules! bar {
|
||||||
($($t:ident)*) => { ${count(t, 1024)} };
|
($($t:ident)*) => { ${count($t, 1024)} };
|
||||||
($( $( $l:literal )* );*) => { $(${count(l, 8192)};)* }
|
($( $( $l:literal )* );*) => { $(${count($l, 8192)};)* }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test() {
|
fn test() {
|
||||||
|
@ -195,19 +195,21 @@ fn test() {
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
macro_rules! foo {
|
macro_rules! foo {
|
||||||
($($t:ident)*) => { ${count(t, 1)} };
|
($($t:ident)*) => { ${count($t, 1)} };
|
||||||
($( $( $l:literal )* );*) => { $(${count(l, 1)};)* }
|
($( $( $l:literal )* );*) => { $(${count($l, 1)};)* }
|
||||||
}
|
}
|
||||||
macro_rules! bar {
|
macro_rules! bar {
|
||||||
($($t:ident)*) => { ${count(t, 1024)} };
|
($($t:ident)*) => { ${count($t, 1024)} };
|
||||||
($( $( $l:literal )* );*) => { $(${count(l, 8192)};)* }
|
($( $( $l:literal )* );*) => { $(${count($l, 8192)};)* }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test() {
|
fn test() {
|
||||||
/* error: ${count} out of bounds */;
|
2;
|
||||||
/* error: ${count} out of bounds */;
|
2;
|
||||||
/* error: ${count} out of bounds */;
|
1;;
|
||||||
/* error: ${count} out of bounds */;
|
2;
|
||||||
|
2;
|
||||||
|
1;;
|
||||||
}
|
}
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
|
@ -218,8 +220,8 @@ fn misplaced_count() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
macro_rules! foo {
|
macro_rules! foo {
|
||||||
($($t:ident)*) => { $(${count(t)})* };
|
($($t:ident)*) => { $(${count($t)})* };
|
||||||
($l:literal) => { ${count(l)} }
|
($l:literal) => { ${count($l)} }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test() {
|
fn test() {
|
||||||
|
@ -229,13 +231,13 @@ fn test() {
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
macro_rules! foo {
|
macro_rules! foo {
|
||||||
($($t:ident)*) => { $(${count(t)})* };
|
($($t:ident)*) => { $(${count($t)})* };
|
||||||
($l:literal) => { ${count(l)} }
|
($l:literal) => { ${count($l)} }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test() {
|
fn test() {
|
||||||
/* error: ${count} misplaced */;
|
1 1 1;
|
||||||
/* error: ${count} misplaced */;
|
1;
|
||||||
}
|
}
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
|
@ -246,13 +248,13 @@ fn malformed_count() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
macro_rules! too_many_args {
|
macro_rules! too_many_args {
|
||||||
($($t:ident)*) => { ${count(t, 1, leftover)} }
|
($($t:ident)*) => { ${count($t, 1, leftover)} }
|
||||||
}
|
}
|
||||||
macro_rules! depth_suffixed {
|
macro_rules! depth_suffixed {
|
||||||
($($t:ident)*) => { ${count(t, 0usize)} }
|
($($t:ident)*) => { ${count($t, 0usize)} }
|
||||||
}
|
}
|
||||||
macro_rules! depth_too_large {
|
macro_rules! depth_too_large {
|
||||||
($($t:ident)*) => { ${count(t, 18446744073709551616)} }
|
($($t:ident)*) => { ${count($t, 18446744073709551616)} }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test() {
|
fn test() {
|
||||||
|
@ -263,13 +265,13 @@ fn test() {
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
macro_rules! too_many_args {
|
macro_rules! too_many_args {
|
||||||
($($t:ident)*) => { ${count(t, 1, leftover)} }
|
($($t:ident)*) => { ${count($t, 1, leftover)} }
|
||||||
}
|
}
|
||||||
macro_rules! depth_suffixed {
|
macro_rules! depth_suffixed {
|
||||||
($($t:ident)*) => { ${count(t, 0usize)} }
|
($($t:ident)*) => { ${count($t, 0usize)} }
|
||||||
}
|
}
|
||||||
macro_rules! depth_too_large {
|
macro_rules! depth_too_large {
|
||||||
($($t:ident)*) => { ${count(t, 18446744073709551616)} }
|
($($t:ident)*) => { ${count($t, 18446744073709551616)} }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test() {
|
fn test() {
|
||||||
|
@ -288,7 +290,7 @@ fn count_interaction_with_empty_binding() {
|
||||||
r#"
|
r#"
|
||||||
macro_rules! m {
|
macro_rules! m {
|
||||||
($($t:ident),*) => {
|
($($t:ident),*) => {
|
||||||
${count(t, 100)}
|
${count($t, 100)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -299,7 +301,7 @@ fn test() {
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
macro_rules! m {
|
macro_rules! m {
|
||||||
($($t:ident),*) => {
|
($($t:ident),*) => {
|
||||||
${count(t, 100)}
|
${count($t, 100)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -199,7 +199,7 @@ fn invocation_fixtures(
|
||||||
});
|
});
|
||||||
parent.token_trees.push(subtree.into());
|
parent.token_trees.push(subtree.into());
|
||||||
}
|
}
|
||||||
Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } => {}
|
Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } | Op::Length { .. } => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Simple linear congruential generator for deterministic result
|
// Simple linear congruential generator for deterministic result
|
||||||
|
|
|
@ -588,7 +588,9 @@ fn match_loop_inner<'t, S: Span>(
|
||||||
item.is_error = true;
|
item.is_error = true;
|
||||||
error_items.push(item);
|
error_items.push(item);
|
||||||
}
|
}
|
||||||
OpDelimited::Op(Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. }) => {
|
OpDelimited::Op(
|
||||||
|
Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } | Op::Length { .. },
|
||||||
|
) => {
|
||||||
stdx::never!("metavariable expression in lhs found");
|
stdx::never!("metavariable expression in lhs found");
|
||||||
}
|
}
|
||||||
OpDelimited::Open => {
|
OpDelimited::Open => {
|
||||||
|
@ -851,7 +853,7 @@ fn collect_vars<S: Span>(collector_fun: &mut impl FnMut(SmolStr), pattern: &Meta
|
||||||
Op::Subtree { tokens, .. } => collect_vars(collector_fun, tokens),
|
Op::Subtree { tokens, .. } => collect_vars(collector_fun, tokens),
|
||||||
Op::Repeat { tokens, .. } => collect_vars(collector_fun, tokens),
|
Op::Repeat { tokens, .. } => collect_vars(collector_fun, tokens),
|
||||||
Op::Literal(_) | Op::Ident(_) | Op::Punct(_) => {}
|
Op::Literal(_) | Op::Ident(_) | Op::Punct(_) => {}
|
||||||
Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } => {
|
Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } | Op::Length { .. } => {
|
||||||
stdx::never!("metavariable expression in lhs found");
|
stdx::never!("metavariable expression in lhs found");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -232,6 +232,21 @@ fn expand_subtree<S: Span>(
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Op::Length { depth } => {
|
||||||
|
let length = ctx.nesting.get(ctx.nesting.len() - 1 - depth).map_or(0, |_nest| {
|
||||||
|
// FIXME: to be implemented
|
||||||
|
0
|
||||||
|
});
|
||||||
|
arena.push(
|
||||||
|
tt::Leaf::Literal(tt::Literal {
|
||||||
|
text: length.to_string().into(),
|
||||||
|
// FIXME
|
||||||
|
#[allow(deprecated)]
|
||||||
|
span: S::DUMMY,
|
||||||
|
})
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
Op::Count { name, depth } => {
|
Op::Count { name, depth } => {
|
||||||
let mut binding = match ctx.bindings.get(name.as_str()) {
|
let mut binding = match ctx.bindings.get(name.as_str()) {
|
||||||
Ok(b) => b,
|
Ok(b) => b,
|
||||||
|
@ -518,28 +533,18 @@ fn fix_up_and_push_path_tt<S: Span>(buf: &mut Vec<tt::TokenTree<S>>, subtree: tt
|
||||||
fn count<S>(
|
fn count<S>(
|
||||||
ctx: &ExpandCtx<'_, S>,
|
ctx: &ExpandCtx<'_, S>,
|
||||||
binding: &Binding<S>,
|
binding: &Binding<S>,
|
||||||
our_depth: usize,
|
depth_curr: usize,
|
||||||
count_depth: Option<usize>,
|
depth_max: usize,
|
||||||
) -> Result<usize, CountError> {
|
) -> Result<usize, CountError> {
|
||||||
match binding {
|
match binding {
|
||||||
Binding::Nested(bs) => match count_depth {
|
Binding::Nested(bs) => {
|
||||||
None => bs.iter().map(|b| count(ctx, b, our_depth + 1, None)).sum(),
|
if depth_curr == depth_max {
|
||||||
Some(0) => Ok(bs.len()),
|
Ok(bs.len())
|
||||||
Some(d) => bs.iter().map(|b| count(ctx, b, our_depth + 1, Some(d - 1))).sum(),
|
|
||||||
},
|
|
||||||
Binding::Empty => Ok(0),
|
|
||||||
Binding::Fragment(_) | Binding::Missing(_) => {
|
|
||||||
if our_depth == 0 {
|
|
||||||
// `${count(t)}` is placed inside the innermost repetition. This includes cases
|
|
||||||
// where `t` is not a repeated fragment.
|
|
||||||
Err(CountError::Misplaced)
|
|
||||||
} else if count_depth.is_none() {
|
|
||||||
Ok(1)
|
|
||||||
} else {
|
} else {
|
||||||
// We've reached at the innermost repeated fragment, but the user wants us to go
|
bs.iter().map(|b| count(ctx, b, depth_curr + 1, depth_max)).sum()
|
||||||
// further!
|
|
||||||
Err(CountError::OutOfBounds)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Binding::Empty => Ok(0),
|
||||||
|
Binding::Fragment(_) | Binding::Missing(_) => Ok(1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,8 @@ pub(crate) enum Op<S> {
|
||||||
Var { name: SmolStr, kind: Option<MetaVarKind>, id: S },
|
Var { name: SmolStr, kind: Option<MetaVarKind>, id: S },
|
||||||
Ignore { name: SmolStr, id: S },
|
Ignore { name: SmolStr, id: S },
|
||||||
Index { depth: usize },
|
Index { depth: usize },
|
||||||
Count { name: SmolStr, depth: Option<usize> },
|
Length { depth: usize },
|
||||||
|
Count { name: SmolStr, depth: usize },
|
||||||
Repeat { tokens: MetaTemplate<S>, kind: RepeatKind, separator: Option<Separator<S>> },
|
Repeat { tokens: MetaTemplate<S>, kind: RepeatKind, separator: Option<Separator<S>> },
|
||||||
Subtree { tokens: MetaTemplate<S>, delimiter: tt::Delimiter<S> },
|
Subtree { tokens: MetaTemplate<S>, delimiter: tt::Delimiter<S> },
|
||||||
Literal(tt::Literal<S>),
|
Literal(tt::Literal<S>),
|
||||||
|
@ -299,15 +300,16 @@ fn parse_metavar_expr<S: Span>(src: &mut TtIter<'_, S>) -> Result<Op<S>, ()> {
|
||||||
|
|
||||||
let op = match &*func.text {
|
let op = match &*func.text {
|
||||||
"ignore" => {
|
"ignore" => {
|
||||||
|
args.expect_dollar()?;
|
||||||
let ident = args.expect_ident()?;
|
let ident = args.expect_ident()?;
|
||||||
Op::Ignore { name: ident.text.clone(), id: ident.span }
|
Op::Ignore { name: ident.text.clone(), id: ident.span }
|
||||||
}
|
}
|
||||||
"index" => Op::Index { depth: parse_depth(&mut args)? },
|
"index" => Op::Index { depth: parse_depth(&mut args)? },
|
||||||
|
"length" => Op::Length { depth: parse_depth(&mut args)? },
|
||||||
"count" => {
|
"count" => {
|
||||||
|
args.expect_dollar()?;
|
||||||
let ident = args.expect_ident()?;
|
let ident = args.expect_ident()?;
|
||||||
// `${count(t)}` and `${count(t,)}` have different meanings. Not sure if this is a bug
|
let depth = if try_eat_comma(&mut args) { parse_depth(&mut args)? } else { 0 };
|
||||||
// but that's how it's implemented in rustc as of this writing. See rust-lang/rust#111904.
|
|
||||||
let depth = if try_eat_comma(&mut args) { Some(parse_depth(&mut args)?) } else { None };
|
|
||||||
Op::Count { name: ident.text.clone(), depth }
|
Op::Count { name: ident.text.clone(), depth }
|
||||||
}
|
}
|
||||||
_ => return Err(()),
|
_ => return Err(()),
|
||||||
|
|
|
@ -51,6 +51,13 @@ impl<'a, S: Span> TtIter<'a, S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn expect_dollar(&mut self) -> Result<(), ()> {
|
||||||
|
match self.expect_leaf()? {
|
||||||
|
tt::Leaf::Punct(tt::Punct { char: '$', .. }) => Ok(()),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn expect_ident(&mut self) -> Result<&'a tt::Ident<S>, ()> {
|
pub(crate) fn expect_ident(&mut self) -> Result<&'a tt::Ident<S>, ()> {
|
||||||
match self.expect_leaf()? {
|
match self.expect_leaf()? {
|
||||||
tt::Leaf::Ident(it) if it.text != "_" => Ok(it),
|
tt::Leaf::Ident(it) if it.text != "_" => Ok(it),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue