use super::ptr::P; use super::token::Nonterminal; use super::tokenstream::LazyTokenStream; use super::{Arm, Crate, ExprField, FieldDef, GenericParam, Param, PatField, Variant}; use super::{AssocItem, Expr, ForeignItem, Item, Local, MacCallStmt}; use super::{AttrItem, AttrKind, Block, Pat, Path, Ty, Visibility}; use super::{AttrVec, Attribute, Stmt, StmtKind}; use std::fmt; use std::marker::PhantomData; /// An `AstLike` represents an AST node (or some wrapper around /// and AST node) which stores some combination of attributes /// and tokens. pub trait AstLike: Sized + fmt::Debug { /// This is `true` if this `AstLike` might support 'custom' (proc-macro) inner /// attributes. Attributes like `#![cfg]` and `#![cfg_attr]` are not /// considered 'custom' attributes /// /// If this is `false`, then this `AstLike` definitely does /// not support 'custom' inner attributes, which enables some optimizations /// during token collection. const SUPPORTS_CUSTOM_INNER_ATTRS: bool; fn attrs(&self) -> &[Attribute]; fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)); fn tokens_mut(&mut self) -> Option<&mut Option>; } impl AstLike for P { const SUPPORTS_CUSTOM_INNER_ATTRS: bool = T::SUPPORTS_CUSTOM_INNER_ATTRS; fn attrs(&self) -> &[Attribute] { (**self).attrs() } fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { (**self).visit_attrs(f); } fn tokens_mut(&mut self) -> Option<&mut Option> { (**self).tokens_mut() } } impl AstLike for crate::token::Nonterminal { const SUPPORTS_CUSTOM_INNER_ATTRS: bool = true; fn attrs(&self) -> &[Attribute] { match self { Nonterminal::NtItem(item) => item.attrs(), Nonterminal::NtStmt(stmt) => stmt.attrs(), Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.attrs(), Nonterminal::NtPat(_) | Nonterminal::NtTy(_) | Nonterminal::NtMeta(_) | Nonterminal::NtPath(_) | Nonterminal::NtVis(_) | Nonterminal::NtBlock(_) | Nonterminal::NtIdent(..) | Nonterminal::NtLifetime(_) => &[], } } fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { match self { Nonterminal::NtItem(item) => item.visit_attrs(f), Nonterminal::NtStmt(stmt) => stmt.visit_attrs(f), Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.visit_attrs(f), Nonterminal::NtPat(_) | Nonterminal::NtTy(_) | Nonterminal::NtMeta(_) | Nonterminal::NtPath(_) | Nonterminal::NtVis(_) | Nonterminal::NtBlock(_) | Nonterminal::NtIdent(..) | Nonterminal::NtLifetime(_) => {} } } fn tokens_mut(&mut self) -> Option<&mut Option> { match self { Nonterminal::NtItem(item) => item.tokens_mut(), Nonterminal::NtStmt(stmt) => stmt.tokens_mut(), Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.tokens_mut(), Nonterminal::NtPat(pat) => pat.tokens_mut(), Nonterminal::NtTy(ty) => ty.tokens_mut(), Nonterminal::NtMeta(attr_item) => attr_item.tokens_mut(), Nonterminal::NtPath(path) => path.tokens_mut(), Nonterminal::NtVis(vis) => vis.tokens_mut(), Nonterminal::NtBlock(block) => block.tokens_mut(), Nonterminal::NtIdent(..) | Nonterminal::NtLifetime(..) => None, } } } fn visit_attrvec(attrs: &mut AttrVec, f: impl FnOnce(&mut Vec)) { crate::mut_visit::visit_clobber(attrs, |attrs| { let mut vec = attrs.into(); f(&mut vec); vec.into() }); } impl AstLike for StmtKind { // This might be an `StmtKind::Item`, which contains // an item that supports inner attrs const SUPPORTS_CUSTOM_INNER_ATTRS: bool = true; fn attrs(&self) -> &[Attribute] { match self { StmtKind::Local(local) => local.attrs(), StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.attrs(), StmtKind::Item(item) => item.attrs(), StmtKind::Empty => &[], StmtKind::MacCall(mac) => &mac.attrs, } } fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { match self { StmtKind::Local(local) => local.visit_attrs(f), StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.visit_attrs(f), StmtKind::Item(item) => item.visit_attrs(f), StmtKind::Empty => {} StmtKind::MacCall(mac) => visit_attrvec(&mut mac.attrs, f), } } fn tokens_mut(&mut self) -> Option<&mut Option> { Some(match self { StmtKind::Local(local) => &mut local.tokens, StmtKind::Item(item) => &mut item.tokens, StmtKind::Expr(expr) | StmtKind::Semi(expr) => &mut expr.tokens, StmtKind::Empty => return None, StmtKind::MacCall(mac) => &mut mac.tokens, }) } } impl AstLike for Stmt { const SUPPORTS_CUSTOM_INNER_ATTRS: bool = StmtKind::SUPPORTS_CUSTOM_INNER_ATTRS; fn attrs(&self) -> &[Attribute] { self.kind.attrs() } fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { self.kind.visit_attrs(f); } fn tokens_mut(&mut self) -> Option<&mut Option> { self.kind.tokens_mut() } } impl AstLike for Attribute { const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false; fn attrs(&self) -> &[Attribute] { &[] } fn visit_attrs(&mut self, _f: impl FnOnce(&mut Vec)) {} fn tokens_mut(&mut self) -> Option<&mut Option> { Some(match &mut self.kind { AttrKind::Normal(_, tokens) => tokens, kind @ AttrKind::DocComment(..) => { panic!("Called tokens_mut on doc comment attr {:?}", kind) } }) } } impl AstLike for Option { const SUPPORTS_CUSTOM_INNER_ATTRS: bool = T::SUPPORTS_CUSTOM_INNER_ATTRS; fn attrs(&self) -> &[Attribute] { self.as_ref().map(|inner| inner.attrs()).unwrap_or(&[]) } fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { if let Some(inner) = self.as_mut() { inner.visit_attrs(f); } } fn tokens_mut(&mut self) -> Option<&mut Option> { self.as_mut().and_then(|inner| inner.tokens_mut()) } } /// Helper trait for the macros below. Abstracts over /// the two types of attribute fields that AST nodes /// may have (`Vec` or `AttrVec`) trait VecOrAttrVec { fn visit(&mut self, f: impl FnOnce(&mut Vec)); } impl VecOrAttrVec for Vec { fn visit(&mut self, f: impl FnOnce(&mut Vec)) { f(self) } } impl VecOrAttrVec for AttrVec { fn visit(&mut self, f: impl FnOnce(&mut Vec)) { visit_attrvec(self, f) } } macro_rules! derive_has_tokens_and_attrs { ( const SUPPORTS_CUSTOM_INNER_ATTRS: bool = $inner_attrs:literal; $($ty:path),* ) => { $( impl AstLike for $ty { const SUPPORTS_CUSTOM_INNER_ATTRS: bool = $inner_attrs; fn attrs(&self) -> &[Attribute] { &self.attrs } fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { VecOrAttrVec::visit(&mut self.attrs, f) } fn tokens_mut(&mut self) -> Option<&mut Option> { Some(&mut self.tokens) } } )* } } macro_rules! derive_has_attrs_no_tokens { ($($ty:path),*) => { $( impl AstLike for $ty { const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false; fn attrs(&self) -> &[Attribute] { &self.attrs } fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { VecOrAttrVec::visit(&mut self.attrs, f) } fn tokens_mut(&mut self) -> Option<&mut Option> { None } } )* } } macro_rules! derive_has_tokens_no_attrs { ($($ty:path),*) => { $( impl AstLike for $ty { const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false; fn attrs(&self) -> &[Attribute] { &[] } fn visit_attrs(&mut self, _f: impl FnOnce(&mut Vec)) {} fn tokens_mut(&mut self) -> Option<&mut Option> { Some(&mut self.tokens) } } )* } } // These ast nodes support both active and inert attributes, // so they have tokens collected to pass to proc macros derive_has_tokens_and_attrs! { // Both `Item` and `AssocItem` can have bodies, which // can contain inner attributes const SUPPORTS_CUSTOM_INNER_ATTRS: bool = true; Item, AssocItem, ForeignItem } derive_has_tokens_and_attrs! { const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false; Local, MacCallStmt, Expr } // These ast nodes only support inert attributes, so they don't // store tokens (since nothing can observe them) derive_has_attrs_no_tokens! { FieldDef, Arm, ExprField, PatField, Variant, Param, GenericParam, Crate } // These AST nodes don't support attributes, but can // be captured by a `macro_rules!` matcher. Therefore, // they need to store tokens. derive_has_tokens_no_attrs! { Ty, Block, AttrItem, Pat, Path, Visibility } /// A newtype around an `AstLike` node that implements `AstLike` itself. pub struct AstLikeWrapper { pub wrapped: Wrapped, pub tag: PhantomData, } impl AstLikeWrapper { pub fn new(wrapped: Wrapped, _tag: Tag) -> AstLikeWrapper { AstLikeWrapper { wrapped, tag: Default::default() } } } impl fmt::Debug for AstLikeWrapper { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("AstLikeWrapper") .field("wrapped", &self.wrapped) .field("tag", &self.tag) .finish() } } impl AstLike for AstLikeWrapper { const SUPPORTS_CUSTOM_INNER_ATTRS: bool = Wrapped::SUPPORTS_CUSTOM_INNER_ATTRS; fn attrs(&self) -> &[Attribute] { self.wrapped.attrs() } fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { self.wrapped.visit_attrs(f) } fn tokens_mut(&mut self) -> Option<&mut Option> { self.wrapped.tokens_mut() } }