1use base_db::Crate;
4use intern::sym;
5use span::{Edition, Span, SyntaxContext};
6use stdx::TupleExt;
7use syntax::{AstNode, ast};
8use syntax_bridge::DocCommentDesugarMode;
9use triomphe::Arc;
10
11use crate::{
12 AstId, ExpandError, ExpandErrorKind, ExpandResult, HirFileId, Lookup, MacroCallId,
13 attrs::RawAttrs,
14 db::ExpandDatabase,
15 hygiene::{Transparency, apply_mark},
16 tt,
17};
18
19#[derive(Debug, Clone, Eq, PartialEq)]
21pub struct DeclarativeMacroExpander {
22 pub mac: mbe::DeclarativeMacro,
23 pub transparency: Transparency,
24 edition: Edition,
25}
26
27impl DeclarativeMacroExpander {
28 pub fn expand(
29 &self,
30 db: &dyn ExpandDatabase,
31 tt: tt::TopSubtree,
32 call_id: MacroCallId,
33 span: Span,
34 ) -> ExpandResult<(tt::TopSubtree, Option<u32>)> {
35 let loc = db.lookup_intern_macro_call(call_id);
36 match self.mac.err() {
37 Some(_) => ExpandResult::new(
38 (tt::TopSubtree::empty(tt::DelimSpan { open: span, close: span }), None),
39 ExpandError::new(span, ExpandErrorKind::MacroDefinition),
40 ),
41 None => self
42 .mac
43 .expand(
44 &tt,
45 |s| {
46 s.ctx =
47 apply_mark(db, s.ctx, call_id.into(), self.transparency, self.edition)
48 },
49 span,
50 loc.def.edition,
51 )
52 .map_err(Into::into),
53 }
54 }
55
56 pub fn expand_unhygienic(
57 &self,
58 tt: tt::TopSubtree,
59 call_site: Span,
60 def_site_edition: Edition,
61 ) -> ExpandResult<tt::TopSubtree> {
62 match self.mac.err() {
63 Some(_) => ExpandResult::new(
64 tt::TopSubtree::empty(tt::DelimSpan { open: call_site, close: call_site }),
65 ExpandError::new(call_site, ExpandErrorKind::MacroDefinition),
66 ),
67 None => self
68 .mac
69 .expand(&tt, |_| (), call_site, def_site_edition)
70 .map(TupleExt::head)
71 .map_err(Into::into),
72 }
73 }
74
75 pub(crate) fn expander(
76 db: &dyn ExpandDatabase,
77 def_crate: Crate,
78 id: AstId<ast::Macro>,
79 ) -> Arc<DeclarativeMacroExpander> {
80 let (root, map) = crate::db::parse_with_map(db, id.file_id);
81 let root = root.syntax_node();
82
83 let transparency = |node| {
84 let attrs = RawAttrs::new_expanded(db, node, map.as_ref(), def_crate.cfg_options(db));
86 match attrs
87 .iter()
88 .find(|it| {
89 it.path
90 .as_ident()
91 .map(|it| *it == sym::rustc_macro_transparency)
92 .unwrap_or(false)
93 })?
94 .token_tree_value()?
95 .token_trees()
96 .flat_tokens()
97 {
98 [tt::TokenTree::Leaf(tt::Leaf::Ident(i)), ..] => match &i.sym {
99 s if *s == sym::transparent => Some(Transparency::Transparent),
100 s if *s == sym::semitransparent => Some(Transparency::SemiTransparent),
101 s if *s == sym::opaque => Some(Transparency::Opaque),
102 _ => None,
103 },
104 _ => None,
105 }
106 };
107 let ctx_edition = |ctx: SyntaxContext| {
108 if ctx.is_root() {
109 def_crate.data(db).edition
110 } else {
111 let krate =
113 db.lookup_intern_macro_call(ctx.outer_expn(db).unwrap().into()).def.krate;
114 krate.data(db).edition
115 }
116 };
117 let (mac, transparency) = match id.to_ptr(db).to_node(&root) {
118 ast::Macro::MacroRules(macro_rules) => (
119 match macro_rules.token_tree() {
120 Some(arg) => {
121 let tt = syntax_bridge::syntax_node_to_token_tree(
122 arg.syntax(),
123 map.as_ref(),
124 map.span_for_range(
125 macro_rules.macro_rules_token().unwrap().text_range(),
126 ),
127 DocCommentDesugarMode::Mbe,
128 );
129
130 mbe::DeclarativeMacro::parse_macro_rules(&tt, ctx_edition)
131 }
132 None => mbe::DeclarativeMacro::from_err(mbe::ParseError::Expected(
133 "expected a token tree".into(),
134 )),
135 },
136 transparency(¯o_rules).unwrap_or(Transparency::SemiTransparent),
137 ),
138 ast::Macro::MacroDef(macro_def) => (
139 match macro_def.body() {
140 Some(body) => {
141 let span =
142 map.span_for_range(macro_def.macro_token().unwrap().text_range());
143 let args = macro_def.args().map(|args| {
144 syntax_bridge::syntax_node_to_token_tree(
145 args.syntax(),
146 map.as_ref(),
147 span,
148 DocCommentDesugarMode::Mbe,
149 )
150 });
151 let body = syntax_bridge::syntax_node_to_token_tree(
152 body.syntax(),
153 map.as_ref(),
154 span,
155 DocCommentDesugarMode::Mbe,
156 );
157
158 mbe::DeclarativeMacro::parse_macro2(args.as_ref(), &body, ctx_edition)
159 }
160 None => mbe::DeclarativeMacro::from_err(mbe::ParseError::Expected(
161 "expected a token tree".into(),
162 )),
163 },
164 transparency(¯o_def).unwrap_or(Transparency::Opaque),
165 ),
166 };
167 let edition = ctx_edition(match id.file_id {
168 HirFileId::MacroFile(macro_file) => macro_file.lookup(db).ctxt,
169 HirFileId::FileId(file) => SyntaxContext::root(file.edition(db)),
170 });
171 Arc::new(DeclarativeMacroExpander { mac, transparency, edition })
172 }
173}