1 //! Eager expansion related utils
2 //!
3 //! Here is a dump of a discussion from Vadim Petrochenkov about Eager Expansion and
4 //! Its name resolution :
5 //!
6 //! > Eagerly expanded macros (and also macros eagerly expanded by eagerly expanded macros,
7 //! > which actually happens in practice too!) are resolved at the location of the "root" macro
8 //! > that performs the eager expansion on its arguments.
9 //! > If some name cannot be resolved at the eager expansion time it's considered unresolved,
10 //! > even if becomes available later (e.g. from a glob import or other macro).
11 //!
12 //! > Eagerly expanded macros don't add anything to the module structure of the crate and
13 //! > don't build any speculative module structures, i.e. they are expanded in a "flat"
14 //! > way even if tokens in them look like modules.
15 //!
16 //! > In other words, it kinda works for simple cases for which it was originally intended,
17 //! > and we need to live with it because it's available on stable and widely relied upon.
18 //!
19 //!
20 //! See the full discussion : <https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros>
21 use base_db::CrateId;
22 use syntax::{ted, Parse, SyntaxNode};
23 use triomphe::Arc;
24
25 use crate::{
26 ast::{self, AstNode},
27 db::ExpandDatabase,
28 hygiene::Hygiene,
29 mod_path::ModPath,
30 EagerCallInfo, ExpandError, ExpandResult, ExpandTo, InFile, MacroCallId, MacroCallKind,
31 MacroCallLoc, MacroDefId, MacroDefKind, UnresolvedMacro,
32 };
33
expand_eager_macro_input( db: &dyn ExpandDatabase, krate: CrateId, macro_call: InFile<ast::MacroCall>, def: MacroDefId, resolver: &dyn Fn(ModPath) -> Option<MacroDefId>, ) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro>34 pub fn expand_eager_macro_input(
35 db: &dyn ExpandDatabase,
36 krate: CrateId,
37 macro_call: InFile<ast::MacroCall>,
38 def: MacroDefId,
39 resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
40 ) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
41 assert!(matches!(def.kind, MacroDefKind::BuiltInEager(..)));
42 let token_tree = macro_call.value.token_tree();
43
44 let Some(token_tree) = token_tree else {
45 return Ok(ExpandResult { value: None, err:
46 Some(ExpandError::other(
47 "invalid token tree"
48 )),
49 });
50 };
51 let (parsed_args, arg_token_map) = mbe::syntax_node_to_token_tree(token_tree.syntax());
52
53 let ast_map = db.ast_id_map(macro_call.file_id);
54 let call_id = InFile::new(macro_call.file_id, ast_map.ast_id(¯o_call.value));
55 let expand_to = ExpandTo::from_call_site(¯o_call.value);
56
57 // Note:
58 // When `lazy_expand` is called, its *parent* file must already exist.
59 // Here we store an eager macro id for the argument expanded subtree
60 // for that purpose.
61 let arg_id = db.intern_macro_call(MacroCallLoc {
62 def,
63 krate,
64 eager: Some(Box::new(EagerCallInfo {
65 arg: Arc::new((parsed_args, arg_token_map)),
66 arg_id: None,
67 error: None,
68 })),
69 kind: MacroCallKind::FnLike { ast_id: call_id, expand_to: ExpandTo::Expr },
70 });
71 let arg_as_expr = match db.macro_arg_text(arg_id) {
72 Some(it) => it,
73 None => {
74 return Ok(ExpandResult {
75 value: None,
76 err: Some(ExpandError::other("invalid token tree")),
77 })
78 }
79 };
80 let ExpandResult { value: expanded_eager_input, err } = eager_macro_recur(
81 db,
82 &Hygiene::new(db, macro_call.file_id),
83 InFile::new(arg_id.as_file(), SyntaxNode::new_root(arg_as_expr)),
84 krate,
85 resolver,
86 )?;
87 let Some(expanded_eager_input) = expanded_eager_input else {
88 return Ok(ExpandResult { value: None, err })
89 };
90 let (mut subtree, token_map) = mbe::syntax_node_to_token_tree(&expanded_eager_input);
91 subtree.delimiter = crate::tt::Delimiter::unspecified();
92
93 let loc = MacroCallLoc {
94 def,
95 krate,
96 eager: Some(Box::new(EagerCallInfo {
97 arg: Arc::new((subtree, token_map)),
98 arg_id: Some(arg_id),
99 error: err.clone(),
100 })),
101 kind: MacroCallKind::FnLike { ast_id: call_id, expand_to },
102 };
103
104 Ok(ExpandResult { value: Some(db.intern_macro_call(loc)), err })
105 }
106
lazy_expand( db: &dyn ExpandDatabase, def: &MacroDefId, macro_call: InFile<ast::MacroCall>, krate: CrateId, ) -> ExpandResult<InFile<Parse<SyntaxNode>>>107 fn lazy_expand(
108 db: &dyn ExpandDatabase,
109 def: &MacroDefId,
110 macro_call: InFile<ast::MacroCall>,
111 krate: CrateId,
112 ) -> ExpandResult<InFile<Parse<SyntaxNode>>> {
113 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(¯o_call.value);
114
115 let expand_to = ExpandTo::from_call_site(¯o_call.value);
116 let id = def.as_lazy_macro(
117 db,
118 krate,
119 MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id), expand_to },
120 );
121
122 let macro_file = id.as_macro_file();
123
124 db.parse_macro_expansion(macro_file).map(|parse| InFile::new(macro_file.into(), parse.0))
125 }
126
eager_macro_recur( db: &dyn ExpandDatabase, hygiene: &Hygiene, curr: InFile<SyntaxNode>, krate: CrateId, macro_resolver: &dyn Fn(ModPath) -> Option<MacroDefId>, ) -> Result<ExpandResult<Option<SyntaxNode>>, UnresolvedMacro>127 fn eager_macro_recur(
128 db: &dyn ExpandDatabase,
129 hygiene: &Hygiene,
130 curr: InFile<SyntaxNode>,
131 krate: CrateId,
132 macro_resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
133 ) -> Result<ExpandResult<Option<SyntaxNode>>, UnresolvedMacro> {
134 let original = curr.value.clone_for_update();
135
136 let children = original.descendants().filter_map(ast::MacroCall::cast);
137 let mut replacements = Vec::new();
138
139 // Note: We only report a single error inside of eager expansions
140 let mut error = None;
141
142 // Collect replacement
143 for child in children {
144 let def = match child.path().and_then(|path| ModPath::from_src(db, path, hygiene)) {
145 Some(path) => macro_resolver(path.clone()).ok_or(UnresolvedMacro { path })?,
146 None => {
147 error = Some(ExpandError::other("malformed macro invocation"));
148 continue;
149 }
150 };
151 let ExpandResult { value, err } = match def.kind {
152 MacroDefKind::BuiltInEager(..) => {
153 let ExpandResult { value, err } = match expand_eager_macro_input(
154 db,
155 krate,
156 curr.with_value(child.clone()),
157 def,
158 macro_resolver,
159 ) {
160 Ok(it) => it,
161 Err(err) => return Err(err),
162 };
163 match value {
164 Some(call) => {
165 let ExpandResult { value, err: err2 } =
166 db.parse_macro_expansion(call.as_macro_file());
167 ExpandResult {
168 value: Some(value.0.syntax_node().clone_for_update()),
169 err: err.or(err2),
170 }
171 }
172 None => ExpandResult { value: None, err },
173 }
174 }
175 MacroDefKind::Declarative(_)
176 | MacroDefKind::BuiltIn(..)
177 | MacroDefKind::BuiltInAttr(..)
178 | MacroDefKind::BuiltInDerive(..)
179 | MacroDefKind::ProcMacro(..) => {
180 let ExpandResult { value, err } =
181 lazy_expand(db, &def, curr.with_value(child.clone()), krate);
182
183 // replace macro inside
184 let hygiene = Hygiene::new(db, value.file_id);
185 let ExpandResult { value, err: error } = eager_macro_recur(
186 db,
187 &hygiene,
188 // FIXME: We discard parse errors here
189 value.map(|it| it.syntax_node()),
190 krate,
191 macro_resolver,
192 )?;
193 let err = err.or(error);
194 ExpandResult { value, err }
195 }
196 };
197 if err.is_some() {
198 error = err;
199 }
200 // check if the whole original syntax is replaced
201 if child.syntax() == &original {
202 return Ok(ExpandResult { value, err: error });
203 }
204
205 if let Some(insert) = value {
206 replacements.push((child, insert));
207 }
208 }
209
210 replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new));
211 Ok(ExpandResult { value: Some(original), err: error })
212 }
213