use super::*; pub(super) const ATTRIBUTE_FIRST: TokenSet = TokenSet::new(&[T![#]]); pub(super) fn inner_attrs(p: &mut Parser<'_>) { while p.at(T![#]) && p.nth(1) == T![!] { attr(p, true); } } pub(super) fn outer_attrs(p: &mut Parser<'_>) { while p.at(T![#]) { attr(p, false); } } fn attr(p: &mut Parser<'_>, inner: bool) { assert!(p.at(T![#])); let attr = p.start(); p.bump(T![#]); if inner { p.bump(T![!]); } if p.eat(T!['[']) { meta(p); if !p.eat(T![']']) { p.error("expected `]`"); } } else { p.error("expected `[`"); } attr.complete(p, ATTR); } // test_err meta_recovery // #![] // #![p = ] // #![p::] // #![p:: =] // #![unsafe] // #![unsafe =] // test metas // #![simple_ident] // #![simple::path] // #![simple_ident_expr = ""] // #![simple::path::Expr = ""] // #![simple_ident_tt(a b c)] // #![simple_ident_tt[a b c]] // #![simple_ident_tt{a b c}] // #![simple::path::tt(a b c)] // #![simple::path::tt[a b c]] // #![simple::path::tt{a b c}] // #![unsafe(simple_ident)] // #![unsafe(simple::path)] // #![unsafe(simple_ident_expr = "")] // #![unsafe(simple::path::Expr = "")] // #![unsafe(simple_ident_tt(a b c))] // #![unsafe(simple_ident_tt[a b c])] // #![unsafe(simple_ident_tt{a b c})] // #![unsafe(simple::path::tt(a b c))] // #![unsafe(simple::path::tt[a b c])] // #![unsafe(simple::path::tt{a b c})] pub(super) fn meta(p: &mut Parser<'_>) { let meta = p.start(); let is_unsafe = p.eat(T![unsafe]); if is_unsafe { p.expect(T!['(']); } paths::attr_path(p); match p.current() { T![=] => { p.bump(T![=]); if expressions::expr(p).is_none() { p.error("expected expression"); } } T!['('] | T!['['] | T!['{'] => items::token_tree(p), _ => {} } if is_unsafe { p.expect(T![')']); } meta.complete(p, META); }