let tt_result = match kind {
"ident" => input
.expect_ident()
+ .and_then(|ident| if ident.text == "_" { Err(()) } else { Ok(ident) })
.map(|ident| Some(tt::Leaf::from(ident.clone()).into()))
.map_err(|()| err!("expected ident")),
"tt" => input.expect_tt().map(Some).map_err(|()| err!()),
Op::Repeat { tokens: MetaTemplate(tokens), separator, kind }
}
tt::TokenTree::Leaf(leaf) => match leaf {
- tt::Leaf::Punct(punct) => {
- static UNDERSCORE: SmolStr = SmolStr::new_inline("_");
-
- if punct.char != '_' {
- return Err(ParseError::Expected("_".to_string()));
- }
- let name = UNDERSCORE.clone();
- let kind = eat_fragment_kind(src, mode)?;
- let id = punct.id;
- Op::Var { name, kind, id }
+ tt::Leaf::Punct(_) => {
+ return Err(ParseError::Expected("ident".to_string()));
}
tt::Leaf::Ident(ident) if ident.text == "crate" => {
// We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path.
let kind = match ident.text.as_ref() {
"true" => T![true],
"false" => T![false],
+ "_" => UNDERSCORE,
i if i.starts_with('\'') => LIFETIME_IDENT,
_ => SyntaxKind::from_keyword(ident.text.as_str()).unwrap_or(IDENT),
};
return;
}
- result.push(if k.is_punct() {
+ result.push(if k.is_punct() && k != UNDERSCORE {
assert_eq!(range.len(), TextSize::of('.'));
let delim = match k {
T!['('] => Some((tt::DelimiterKind::Parenthesis, T![')'])),
{
tt::Spacing::Alone
}
- Some(next) if next.kind().is_punct() => tt::Spacing::Joint,
+ Some(next) if next.kind().is_punct() && next.kind() != UNDERSCORE => {
+ tt::Spacing::Joint
+ }
_ => tt::Spacing::Alone,
};
let char = match token.to_char() {
let leaf: tt::Leaf = match k {
T![true] | T![false] => make_leaf!(Ident),
IDENT => make_leaf!(Ident),
+ UNDERSCORE => make_leaf!(Ident),
k if k.is_keyword() => make_leaf!(Ident),
k if k.is_literal() => make_leaf!(Literal),
LIFETIME_IDENT => {
.assert_expand_items(r#"q![_]"#, r#"0"#);
}
+#[test]
+fn test_underscore_lifetime() {
+ parse_macro(r#"macro_rules! q { ($a:lifetime) => {0}; }"#)
+ .assert_expand_items(r#"q!['_]"#, r#"0"#);
+}
+
#[test]
fn test_vertical_bar_with_pat() {
parse_macro(
let t2 = TokenStream::from_str("(a);").unwrap();
assert_eq!(t2.token_trees.len(), 2);
assert_eq!(t2.token_trees[0], subtree_paren_a);
+
+ let underscore = TokenStream::from_str("_").unwrap();
+ assert_eq!(
+ underscore.token_trees[0],
+ tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
+ text: "_".into(),
+ id: tt::TokenId::unspecified(),
+ }))
+ );
}
}