mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-08-04 18:58:41 +00:00
Allow lifetime repeats in macros: $($x)'a*
This works in rustc. This change isn't motivated by any real code. I just learned about it and decided to see why it doesn't work with rust-analyzer.
This commit is contained in:
parent
a497f4114c
commit
a7c09532a0
6 changed files with 56 additions and 8 deletions
|
@ -2029,3 +2029,25 @@ fn f() {
|
|||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lifetime_repeat() {
|
||||
check(
|
||||
r#"
|
||||
macro_rules! m {
|
||||
($($x:expr)'a*) => (stringify!($($x)'b*));
|
||||
}
|
||||
fn f() {
|
||||
let _ = m!(0 'a 1 'a 2);
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
macro_rules! m {
|
||||
($($x:expr)'a*) => (stringify!($($x)'b*));
|
||||
}
|
||||
fn f() {
|
||||
let _ = stringify!(0 'b1 'b2);
|
||||
}
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ macro_rules! m {
|
|||
($(x),*) => ();
|
||||
($(x)_*) => ();
|
||||
($(x)i*) => ();
|
||||
($(x)'a*) => ();
|
||||
($(x)'_*) => ();
|
||||
($($i:ident)*) => ($_);
|
||||
($($true:ident)*) => ($true);
|
||||
($($false:ident)*) => ($false);
|
||||
|
@ -28,6 +30,8 @@ macro_rules! m {
|
|||
($(x),*) => ();
|
||||
($(x)_*) => ();
|
||||
($(x)i*) => ();
|
||||
($(x)'a*) => ();
|
||||
($(x)'_*) => ();
|
||||
($($i:ident)*) => ($_);
|
||||
($($true:ident)*) => ($true);
|
||||
($($false:ident)*) => ($false);
|
||||
|
|
|
@ -197,6 +197,10 @@ fn invocation_fixtures(
|
|||
builder.push(tt::Leaf::Punct(*it))
|
||||
}
|
||||
}
|
||||
Separator::Lifetime(punct, ident) => {
|
||||
builder.push(tt::Leaf::Punct(*punct));
|
||||
builder.push(tt::Leaf::Ident(ident.clone()));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -823,7 +823,7 @@ fn match_meta_var<'t>(
|
|||
"expected token tree",
|
||||
)
|
||||
}),
|
||||
MetaVarKind::Lifetime => expect_lifetime(input).map_err(|()| {
|
||||
MetaVarKind::Lifetime => expect_lifetime(input).map(drop).map_err(|()| {
|
||||
ExpandError::binding_error(
|
||||
span.unwrap_or(delim_span.close),
|
||||
"expected lifetime",
|
||||
|
@ -963,6 +963,10 @@ fn expect_separator<S: Copy>(iter: &mut TtIter<'_, S>, separator: &Separator) ->
|
|||
}
|
||||
Err(_) => false,
|
||||
},
|
||||
Separator::Lifetime(_punct, ident) => match expect_lifetime(&mut fork) {
|
||||
Ok(lifetime) => lifetime.sym == ident.sym,
|
||||
Err(_) => false,
|
||||
},
|
||||
};
|
||||
if ok {
|
||||
*iter = fork;
|
||||
|
@ -983,13 +987,12 @@ fn expect_tt<S: Copy>(iter: &mut TtIter<'_, S>) -> Result<(), ()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn expect_lifetime<S: Copy>(iter: &mut TtIter<'_, S>) -> Result<(), ()> {
|
||||
fn expect_lifetime<'a, S: Copy>(iter: &mut TtIter<'a, S>) -> Result<&'a tt::Ident<S>, ()> {
|
||||
let punct = iter.expect_single_punct()?;
|
||||
if punct.char != '\'' {
|
||||
return Err(());
|
||||
}
|
||||
iter.expect_ident_or_underscore()?;
|
||||
Ok(())
|
||||
iter.expect_ident_or_underscore()
|
||||
}
|
||||
|
||||
fn eat_char<S: Copy>(iter: &mut TtIter<'_, S>, c: char) {
|
||||
|
|
|
@ -497,6 +497,10 @@ fn expand_repeat(
|
|||
builder.push(tt::Leaf::from(punct));
|
||||
}
|
||||
}
|
||||
Separator::Lifetime(punct, ident) => {
|
||||
builder.push(tt::Leaf::from(*punct));
|
||||
builder.push(tt::Leaf::from(ident.clone()));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -155,6 +155,7 @@ pub(crate) enum Separator {
|
|||
Literal(tt::Literal<Span>),
|
||||
Ident(tt::Ident<Span>),
|
||||
Puncts(ArrayVec<tt::Punct<Span>, MAX_GLUED_PUNCT_LEN>),
|
||||
Lifetime(tt::Punct<Span>, tt::Ident<Span>),
|
||||
}
|
||||
|
||||
// Note that when we compare a Separator, we just care about its textual value.
|
||||
|
@ -170,6 +171,7 @@ impl PartialEq for Separator {
|
|||
let b_iter = b.iter().map(|b| b.char);
|
||||
a_iter.eq(b_iter)
|
||||
}
|
||||
(Lifetime(_, a), Lifetime(_, b)) => a.sym == b.sym,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -350,10 +352,19 @@ fn parse_repeat(src: &mut TtIter<'_, Span>) -> Result<(Option<Separator>, Repeat
|
|||
_ => true,
|
||||
};
|
||||
match tt {
|
||||
tt::Leaf::Ident(_) | tt::Leaf::Literal(_) if has_sep => {
|
||||
return Err(ParseError::InvalidRepeat);
|
||||
}
|
||||
tt::Leaf::Ident(ident) => separator = Separator::Ident(ident.clone()),
|
||||
tt::Leaf::Ident(ident) => match separator {
|
||||
Separator::Puncts(puncts) if puncts.is_empty() => {
|
||||
separator = Separator::Ident(ident.clone());
|
||||
}
|
||||
Separator::Puncts(puncts) => match puncts.as_slice() {
|
||||
[tt::Punct { char: '\'', .. }] => {
|
||||
separator = Separator::Lifetime(puncts[0], ident.clone());
|
||||
}
|
||||
_ => return Err(ParseError::InvalidRepeat),
|
||||
},
|
||||
_ => return Err(ParseError::InvalidRepeat),
|
||||
},
|
||||
tt::Leaf::Literal(_) if has_sep => return Err(ParseError::InvalidRepeat),
|
||||
tt::Leaf::Literal(lit) => separator = Separator::Literal(lit.clone()),
|
||||
tt::Leaf::Punct(punct) => {
|
||||
let repeat_kind = match punct.char {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue