//! Compiled declarative macro expanders (`macro_rules!`` and `macro`) use std::sync::OnceLock; use base_db::{CrateId, Edition, VersionReq}; use span::{MacroCallId, Span}; use syntax::{ast, AstNode}; use triomphe::Arc; use crate::{ attrs::RawAttrs, db::ExpandDatabase, hygiene::{apply_mark, Transparency}, tt, AstId, ExpandError, ExpandResult, }; /// Old-style `macro_rules` or the new macros 2.0 #[derive(Debug, Clone, Eq, PartialEq)] pub struct DeclarativeMacroExpander { pub mac: mbe::DeclarativeMacro, pub transparency: Transparency, } // FIXME: Remove this once we drop support for 1.76 static REQUIREMENT: OnceLock = OnceLock::new(); impl DeclarativeMacroExpander { pub fn expand( &self, db: &dyn ExpandDatabase, tt: tt::Subtree, call_id: MacroCallId, ) -> ExpandResult { let loc = db.lookup_intern_macro_call(call_id); let toolchain = &db.crate_graph()[loc.def.krate].toolchain; let new_meta_vars = toolchain.as_ref().map_or(false, |version| { REQUIREMENT.get_or_init(|| VersionReq::parse(">=1.76").unwrap()).matches( &base_db::Version { pre: base_db::Prerelease::EMPTY, build: base_db::BuildMetadata::EMPTY, major: version.major, minor: version.minor, patch: version.patch, }, ) }); match self.mac.err() { Some(_) => ExpandResult::new( tt::Subtree::empty(tt::DelimSpan { open: loc.call_site, close: loc.call_site }), ExpandError::MacroDefinition, ), None => self .mac .expand( &tt, |s| s.ctx = apply_mark(db, s.ctx, call_id, self.transparency), new_meta_vars, loc.call_site, ) .map_err(Into::into), } } pub fn expand_unhygienic( &self, db: &dyn ExpandDatabase, tt: tt::Subtree, krate: CrateId, call_site: Span, ) -> ExpandResult { let toolchain = &db.crate_graph()[krate].toolchain; let new_meta_vars = toolchain.as_ref().map_or(false, |version| { REQUIREMENT.get_or_init(|| VersionReq::parse(">=1.76").unwrap()).matches( &base_db::Version { pre: base_db::Prerelease::EMPTY, build: base_db::BuildMetadata::EMPTY, major: version.major, minor: version.minor, patch: version.patch, }, ) }); match self.mac.err() { Some(_) => ExpandResult::new( tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }), ExpandError::MacroDefinition, ), None => self.mac.expand(&tt, |_| (), new_meta_vars, call_site).map_err(Into::into), } } pub(crate) fn expander( db: &dyn ExpandDatabase, def_crate: CrateId, id: AstId, ) -> Arc { let crate_data = &db.crate_graph()[def_crate]; let is_2021 = crate_data.edition >= Edition::Edition2021; let (root, map) = crate::db::parse_with_map(db, id.file_id); let root = root.syntax_node(); let transparency = |node| { // ... would be nice to have the item tree here let attrs = RawAttrs::new(db, node, map.as_ref()).filter(db, def_crate); match &*attrs .iter() .find(|it| { it.path.as_ident().and_then(|it| it.as_str()) == Some("rustc_macro_transparency") })? .token_tree_value()? .token_trees { [tt::TokenTree::Leaf(tt::Leaf::Ident(i)), ..] => match &*i.text { "transparent" => Some(Transparency::Transparent), "semitransparent" => Some(Transparency::SemiTransparent), "opaque" => Some(Transparency::Opaque), _ => None, }, _ => None, } }; let toolchain = crate_data.toolchain.as_ref(); let new_meta_vars = toolchain.as_ref().map_or(false, |version| { REQUIREMENT.get_or_init(|| VersionReq::parse(">=1.76").unwrap()).matches( &base_db::Version { pre: base_db::Prerelease::EMPTY, build: base_db::BuildMetadata::EMPTY, major: version.major, minor: version.minor, patch: version.patch, }, ) }); let (mac, transparency) = match id.to_ptr(db).to_node(&root) { ast::Macro::MacroRules(macro_rules) => ( match macro_rules.token_tree() { Some(arg) => { let tt = mbe::syntax_node_to_token_tree( arg.syntax(), map.as_ref(), map.span_for_range( macro_rules.macro_rules_token().unwrap().text_range(), ), ); mbe::DeclarativeMacro::parse_macro_rules(&tt, is_2021, new_meta_vars) } None => mbe::DeclarativeMacro::from_err( mbe::ParseError::Expected("expected a token tree".into()), is_2021, ), }, transparency(¯o_rules).unwrap_or(Transparency::SemiTransparent), ), ast::Macro::MacroDef(macro_def) => ( match macro_def.body() { Some(arg) => { let tt = mbe::syntax_node_to_token_tree( arg.syntax(), map.as_ref(), map.span_for_range(macro_def.macro_token().unwrap().text_range()), ); mbe::DeclarativeMacro::parse_macro2(&tt, is_2021, new_meta_vars) } None => mbe::DeclarativeMacro::from_err( mbe::ParseError::Expected("expected a token tree".into()), is_2021, ), }, transparency(¯o_def).unwrap_or(Transparency::Opaque), ), }; Arc::new(DeclarativeMacroExpander { mac, transparency }) } }