]> git.proxmox.com Git - rustc.git/blame - src/tools/rust-analyzer/crates/hir-expand/src/eager.rs
New upstream version 1.72.1+dfsg1
[rustc.git] / src / tools / rust-analyzer / crates / hir-expand / src / eager.rs
CommitLineData
064997fb
FG
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>
064997fb 21use base_db::CrateId;
fe692bf9
FG
22use syntax::{ted, Parse, SyntaxNode};
23use triomphe::Arc;
064997fb
FG
24
25use crate::{
26 ast::{self, AstNode},
353b0b11 27 db::ExpandDatabase,
064997fb
FG
28 hygiene::Hygiene,
29 mod_path::ModPath,
30 EagerCallInfo, ExpandError, ExpandResult, ExpandTo, InFile, MacroCallId, MacroCallKind,
31 MacroCallLoc, MacroDefId, MacroDefKind, UnresolvedMacro,
32};
33
fe692bf9 34pub fn expand_eager_macro_input(
353b0b11 35 db: &dyn ExpandDatabase,
064997fb
FG
36 krate: CrateId,
37 macro_call: InFile<ast::MacroCall>,
38 def: MacroDefId,
39 resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
fe692bf9
FG
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());
064997fb
FG
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(&macro_call.value));
55 let expand_to = ExpandTo::from_call_site(&macro_call.value);
56
57 // Note:
fe692bf9
FG
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
064997fb
FG
60 // for that purpose.
61 let arg_id = db.intern_macro_call(MacroCallLoc {
62 def,
63 krate,
fe692bf9
FG
64 eager: Some(Box::new(EagerCallInfo {
65 arg: Arc::new((parsed_args, arg_token_map)),
66 arg_id: None,
67 error: None,
68 })),
064997fb
FG
69 kind: MacroCallKind::FnLike { ast_id: call_id, expand_to: ExpandTo::Expr },
70 });
fe692bf9
FG
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(
064997fb 81 db,
fe692bf9
FG
82 &Hygiene::new(db, macro_call.file_id),
83 InFile::new(arg_id.as_file(), SyntaxNode::new_root(arg_as_expr)),
064997fb
FG
84 krate,
85 resolver,
fe692bf9
FG
86 )?;
87 let Some(expanded_eager_input) = expanded_eager_input else {
88 return Ok(ExpandResult { value: None, err })
064997fb 89 };
fe692bf9
FG
90 let (mut subtree, token_map) = mbe::syntax_node_to_token_tree(&expanded_eager_input);
91 subtree.delimiter = crate::tt::Delimiter::unspecified();
064997fb 92
fe692bf9
FG
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 };
064997fb 103
fe692bf9 104 Ok(ExpandResult { value: Some(db.intern_macro_call(loc)), err })
064997fb
FG
105}
106
107fn lazy_expand(
353b0b11 108 db: &dyn ExpandDatabase,
064997fb
FG
109 def: &MacroDefId,
110 macro_call: InFile<ast::MacroCall>,
111 krate: CrateId,
fe692bf9 112) -> ExpandResult<InFile<Parse<SyntaxNode>>> {
064997fb
FG
113 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value);
114
115 let expand_to = ExpandTo::from_call_site(&macro_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
fe692bf9 122 let macro_file = id.as_macro_file();
064997fb 123
fe692bf9 124 db.parse_macro_expansion(macro_file).map(|parse| InFile::new(macro_file.into(), parse.0))
064997fb
FG
125}
126
127fn eager_macro_recur(
353b0b11 128 db: &dyn ExpandDatabase,
064997fb
FG
129 hygiene: &Hygiene,
130 curr: InFile<SyntaxNode>,
131 krate: CrateId,
132 macro_resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
fe692bf9 133) -> Result<ExpandResult<Option<SyntaxNode>>, UnresolvedMacro> {
064997fb
FG
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
fe692bf9
FG
139 // Note: We only report a single error inside of eager expansions
140 let mut error = None;
141
064997fb
FG
142 // Collect replacement
143 for child in children {
144 let def = match child.path().and_then(|path| ModPath::from_src(db, path, hygiene)) {
9c376795 145 Some(path) => macro_resolver(path.clone()).ok_or(UnresolvedMacro { path })?,
064997fb 146 None => {
fe692bf9 147 error = Some(ExpandError::other("malformed macro invocation"));
064997fb
FG
148 continue;
149 }
150 };
fe692bf9 151 let ExpandResult { value, err } = match def.kind {
064997fb 152 MacroDefKind::BuiltInEager(..) => {
fe692bf9 153 let ExpandResult { value, err } = match expand_eager_macro_input(
064997fb
FG
154 db,
155 krate,
156 curr.with_value(child.clone()),
157 def,
158 macro_resolver,
064997fb 159 ) {
fe692bf9 160 Ok(it) => it,
064997fb
FG
161 Err(err) => return Err(err),
162 };
fe692bf9
FG
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 }
064997fb
FG
174 }
175 MacroDefKind::Declarative(_)
176 | MacroDefKind::BuiltIn(..)
177 | MacroDefKind::BuiltInAttr(..)
178 | MacroDefKind::BuiltInDerive(..)
179 | MacroDefKind::ProcMacro(..) => {
fe692bf9
FG
180 let ExpandResult { value, err } =
181 lazy_expand(db, &def, curr.with_value(child.clone()), krate);
064997fb
FG
182
183 // replace macro inside
fe692bf9
FG
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 }
064997fb
FG
195 }
196 };
fe692bf9
FG
197 if err.is_some() {
198 error = err;
199 }
064997fb
FG
200 // check if the whole original syntax is replaced
201 if child.syntax() == &original {
fe692bf9 202 return Ok(ExpandResult { value, err: error });
064997fb
FG
203 }
204
fe692bf9
FG
205 if let Some(insert) = value {
206 replacements.push((child, insert));
207 }
064997fb
FG
208 }
209
210 replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new));
fe692bf9 211 Ok(ExpandResult { value: Some(original), err: error })
064997fb 212}