diff options
Diffstat (limited to 'crates/hir_expand/src')
-rw-r--r-- | crates/hir_expand/src/db.rs | 141 | ||||
-rw-r--r-- | crates/hir_expand/src/lib.rs | 2 |
2 files changed, 88 insertions, 55 deletions
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index ade57ac1b..a9099eb22 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs | |||
@@ -13,6 +13,19 @@ use crate::{ | |||
13 | MacroFile, ProcMacroExpander, | 13 | MacroFile, ProcMacroExpander, |
14 | }; | 14 | }; |
15 | 15 | ||
16 | /// A result of some macro expansion. | ||
17 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
18 | pub struct MacroResult<T> { | ||
19 | /// The result of the expansion. Might be `None` when error recovery was impossible and no | ||
20 | /// usable result was produced. | ||
21 | pub value: Option<T>, | ||
22 | |||
23 | /// The error that occurred during expansion or processing. | ||
24 | /// | ||
25 | /// Since we do error recovery, getting an error here does not mean that `value` will be absent. | ||
26 | pub error: Option<String>, | ||
27 | } | ||
28 | |||
16 | #[derive(Debug, Clone, Eq, PartialEq)] | 29 | #[derive(Debug, Clone, Eq, PartialEq)] |
17 | pub enum TokenExpander { | 30 | pub enum TokenExpander { |
18 | MacroRules(mbe::MacroRules), | 31 | MacroRules(mbe::MacroRules), |
@@ -75,9 +88,11 @@ pub trait AstDatabase: SourceDatabase { | |||
75 | #[salsa::transparent] | 88 | #[salsa::transparent] |
76 | fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>; | 89 | fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>; |
77 | fn macro_def(&self, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>>; | 90 | fn macro_def(&self, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>>; |
78 | fn parse_macro(&self, macro_file: MacroFile) | 91 | fn parse_macro_expansion( |
79 | -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>; | 92 | &self, |
80 | fn macro_expand(&self, macro_call: MacroCallId) -> (Option<Arc<tt::Subtree>>, Option<String>); | 93 | macro_file: MacroFile, |
94 | ) -> MacroResult<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>; | ||
95 | fn macro_expand(&self, macro_call: MacroCallId) -> MacroResult<Arc<tt::Subtree>>; | ||
81 | 96 | ||
82 | #[salsa::interned] | 97 | #[salsa::interned] |
83 | fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; | 98 | fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; |
@@ -85,6 +100,20 @@ pub trait AstDatabase: SourceDatabase { | |||
85 | fn expand_proc_macro(&self, call: MacroCallId) -> Result<tt::Subtree, mbe::ExpandError>; | 100 | fn expand_proc_macro(&self, call: MacroCallId) -> Result<tt::Subtree, mbe::ExpandError>; |
86 | } | 101 | } |
87 | 102 | ||
103 | impl<T> MacroResult<T> { | ||
104 | fn error(message: String) -> Self { | ||
105 | Self { value: None, error: Some(message) } | ||
106 | } | ||
107 | |||
108 | fn map<U>(self, f: impl FnOnce(T) -> U) -> MacroResult<U> { | ||
109 | MacroResult { value: self.value.map(f), error: self.error } | ||
110 | } | ||
111 | |||
112 | fn drop_value<U>(self) -> MacroResult<U> { | ||
113 | MacroResult { value: None, error: self.error } | ||
114 | } | ||
115 | } | ||
116 | |||
88 | /// This expands the given macro call, but with different arguments. This is | 117 | /// This expands the given macro call, but with different arguments. This is |
89 | /// used for completion, where we want to see what 'would happen' if we insert a | 118 | /// used for completion, where we want to see what 'would happen' if we insert a |
90 | /// token. The `token_to_map` mapped down into the expansion, with the mapped | 119 | /// token. The `token_to_map` mapped down into the expansion, with the mapped |
@@ -102,23 +131,20 @@ pub fn expand_hypothetical( | |||
102 | let token_id = tmap_1.token_by_range(range)?; | 131 | let token_id = tmap_1.token_by_range(range)?; |
103 | let macro_def = expander(db, actual_macro_call)?; | 132 | let macro_def = expander(db, actual_macro_call)?; |
104 | let (node, tmap_2) = | 133 | let (node, tmap_2) = |
105 | parse_macro_with_arg(db, macro_file, Some(std::sync::Arc::new((tt, tmap_1))))?; | 134 | parse_macro_with_arg(db, macro_file, Some(std::sync::Arc::new((tt, tmap_1)))).value?; |
106 | let token_id = macro_def.0.map_id_down(token_id); | 135 | let token_id = macro_def.0.map_id_down(token_id); |
107 | let range = tmap_2.range_by_token(token_id)?.by_kind(token_to_map.kind())?; | 136 | let range = tmap_2.range_by_token(token_id)?.by_kind(token_to_map.kind())?; |
108 | let token = syntax::algo::find_covering_element(&node.syntax_node(), range).into_token()?; | 137 | let token = syntax::algo::find_covering_element(&node.syntax_node(), range).into_token()?; |
109 | Some((node.syntax_node(), token)) | 138 | Some((node.syntax_node(), token)) |
110 | } | 139 | } |
111 | 140 | ||
112 | pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> { | 141 | fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> { |
113 | let map = | 142 | let map = |
114 | db.parse_or_expand(file_id).map_or_else(AstIdMap::default, |it| AstIdMap::from_source(&it)); | 143 | db.parse_or_expand(file_id).map_or_else(AstIdMap::default, |it| AstIdMap::from_source(&it)); |
115 | Arc::new(map) | 144 | Arc::new(map) |
116 | } | 145 | } |
117 | 146 | ||
118 | pub(crate) fn macro_def( | 147 | fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> { |
119 | db: &dyn AstDatabase, | ||
120 | id: MacroDefId, | ||
121 | ) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> { | ||
122 | match id.kind { | 148 | match id.kind { |
123 | MacroDefKind::Declarative => { | 149 | MacroDefKind::Declarative => { |
124 | let macro_call = id.ast_id?.to_node(db); | 150 | let macro_call = id.ast_id?.to_node(db); |
@@ -149,7 +175,7 @@ pub(crate) fn macro_def( | |||
149 | } | 175 | } |
150 | } | 176 | } |
151 | 177 | ||
152 | pub(crate) fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> { | 178 | fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> { |
153 | let id = match id { | 179 | let id = match id { |
154 | MacroCallId::LazyMacro(id) => id, | 180 | MacroCallId::LazyMacro(id) => id, |
155 | MacroCallId::EagerMacro(_id) => { | 181 | MacroCallId::EagerMacro(_id) => { |
@@ -162,19 +188,13 @@ pub(crate) fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<Gr | |||
162 | Some(arg.green().clone()) | 188 | Some(arg.green().clone()) |
163 | } | 189 | } |
164 | 190 | ||
165 | pub(crate) fn macro_arg( | 191 | fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> { |
166 | db: &dyn AstDatabase, | ||
167 | id: MacroCallId, | ||
168 | ) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> { | ||
169 | let arg = db.macro_arg_text(id)?; | 192 | let arg = db.macro_arg_text(id)?; |
170 | let (tt, tmap) = mbe::syntax_node_to_token_tree(&SyntaxNode::new_root(arg))?; | 193 | let (tt, tmap) = mbe::syntax_node_to_token_tree(&SyntaxNode::new_root(arg))?; |
171 | Some(Arc::new((tt, tmap))) | 194 | Some(Arc::new((tt, tmap))) |
172 | } | 195 | } |
173 | 196 | ||
174 | pub(crate) fn macro_expand( | 197 | fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> MacroResult<Arc<tt::Subtree>> { |
175 | db: &dyn AstDatabase, | ||
176 | id: MacroCallId, | ||
177 | ) -> (Option<Arc<tt::Subtree>>, Option<String>) { | ||
178 | macro_expand_with_arg(db, id, None) | 198 | macro_expand_with_arg(db, id, None) |
179 | } | 199 | } |
180 | 200 | ||
@@ -195,17 +215,19 @@ fn macro_expand_with_arg( | |||
195 | db: &dyn AstDatabase, | 215 | db: &dyn AstDatabase, |
196 | id: MacroCallId, | 216 | id: MacroCallId, |
197 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, | 217 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, |
198 | ) -> (Option<Arc<tt::Subtree>>, Option<String>) { | 218 | ) -> MacroResult<Arc<tt::Subtree>> { |
199 | let lazy_id = match id { | 219 | let lazy_id = match id { |
200 | MacroCallId::LazyMacro(id) => id, | 220 | MacroCallId::LazyMacro(id) => id, |
201 | MacroCallId::EagerMacro(id) => { | 221 | MacroCallId::EagerMacro(id) => { |
202 | if arg.is_some() { | 222 | if arg.is_some() { |
203 | return ( | 223 | return MacroResult::error( |
204 | None, | 224 | "hypothetical macro expansion not implemented for eager macro".to_owned(), |
205 | Some("hypothetical macro expansion not implemented for eager macro".to_owned()), | ||
206 | ); | 225 | ); |
207 | } else { | 226 | } else { |
208 | return (Some(db.lookup_intern_eager_expansion(id).subtree), None); | 227 | return MacroResult { |
228 | value: Some(db.lookup_intern_eager_expansion(id).subtree), | ||
229 | error: None, | ||
230 | }; | ||
209 | } | 231 | } |
210 | } | 232 | } |
211 | }; | 233 | }; |
@@ -213,23 +235,24 @@ fn macro_expand_with_arg( | |||
213 | let loc = db.lookup_intern_macro(lazy_id); | 235 | let loc = db.lookup_intern_macro(lazy_id); |
214 | let macro_arg = match arg.or_else(|| db.macro_arg(id)) { | 236 | let macro_arg = match arg.or_else(|| db.macro_arg(id)) { |
215 | Some(it) => it, | 237 | Some(it) => it, |
216 | None => return (None, Some("Fail to args in to tt::TokenTree".into())), | 238 | None => return MacroResult::error("Fail to args in to tt::TokenTree".into()), |
217 | }; | 239 | }; |
218 | 240 | ||
219 | let macro_rules = match db.macro_def(loc.def) { | 241 | let macro_rules = match db.macro_def(loc.def) { |
220 | Some(it) => it, | 242 | Some(it) => it, |
221 | None => return (None, Some("Fail to find macro definition".into())), | 243 | None => return MacroResult::error("Fail to find macro definition".into()), |
222 | }; | 244 | }; |
223 | let ExpandResult(tt, err) = macro_rules.0.expand(db, lazy_id, ¯o_arg.0); | 245 | let ExpandResult(tt, err) = macro_rules.0.expand(db, lazy_id, ¯o_arg.0); |
224 | // Set a hard limit for the expanded tt | 246 | // Set a hard limit for the expanded tt |
225 | let count = tt.count(); | 247 | let count = tt.count(); |
226 | if count > 262144 { | 248 | if count > 262144 { |
227 | return (None, Some(format!("Total tokens count exceed limit : count = {}", count))); | 249 | return MacroResult::error(format!("Total tokens count exceed limit : count = {}", count)); |
228 | } | 250 | } |
229 | (Some(Arc::new(tt)), err.map(|e| format!("{:?}", e))) | 251 | |
252 | MacroResult { value: Some(Arc::new(tt)), error: err.map(|e| format!("{:?}", e)) } | ||
230 | } | 253 | } |
231 | 254 | ||
232 | pub(crate) fn expand_proc_macro( | 255 | fn expand_proc_macro( |
233 | db: &dyn AstDatabase, | 256 | db: &dyn AstDatabase, |
234 | id: MacroCallId, | 257 | id: MacroCallId, |
235 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 258 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
@@ -256,36 +279,36 @@ pub(crate) fn expand_proc_macro( | |||
256 | expander.expand(db, lazy_id, ¯o_arg.0) | 279 | expander.expand(db, lazy_id, ¯o_arg.0) |
257 | } | 280 | } |
258 | 281 | ||
259 | pub(crate) fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> { | 282 | fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> { |
260 | match file_id.0 { | 283 | match file_id.0 { |
261 | HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()), | 284 | HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()), |
262 | HirFileIdRepr::MacroFile(macro_file) => { | 285 | HirFileIdRepr::MacroFile(macro_file) => { |
263 | db.parse_macro(macro_file).map(|(it, _)| it.syntax_node()) | 286 | db.parse_macro_expansion(macro_file).map(|(it, _)| it.syntax_node()).value |
264 | } | 287 | } |
265 | } | 288 | } |
266 | } | 289 | } |
267 | 290 | ||
268 | pub(crate) fn parse_macro( | 291 | fn parse_macro_expansion( |
269 | db: &dyn AstDatabase, | 292 | db: &dyn AstDatabase, |
270 | macro_file: MacroFile, | 293 | macro_file: MacroFile, |
271 | ) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { | 294 | ) -> MacroResult<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { |
272 | parse_macro_with_arg(db, macro_file, None) | 295 | parse_macro_with_arg(db, macro_file, None) |
273 | } | 296 | } |
274 | 297 | ||
275 | pub fn parse_macro_with_arg( | 298 | fn parse_macro_with_arg( |
276 | db: &dyn AstDatabase, | 299 | db: &dyn AstDatabase, |
277 | macro_file: MacroFile, | 300 | macro_file: MacroFile, |
278 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, | 301 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, |
279 | ) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { | 302 | ) -> MacroResult<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { |
280 | let _p = profile::span("parse_macro_query"); | 303 | let _p = profile::span("parse_macro_query"); |
281 | 304 | ||
282 | let macro_call_id = macro_file.macro_call_id; | 305 | let macro_call_id = macro_file.macro_call_id; |
283 | let (tt, err) = if let Some(arg) = arg { | 306 | let result = if let Some(arg) = arg { |
284 | macro_expand_with_arg(db, macro_call_id, Some(arg)) | 307 | macro_expand_with_arg(db, macro_call_id, Some(arg)) |
285 | } else { | 308 | } else { |
286 | db.macro_expand(macro_call_id) | 309 | db.macro_expand(macro_call_id) |
287 | }; | 310 | }; |
288 | if let Some(err) = &err { | 311 | if let Some(err) = &result.error { |
289 | // Note: | 312 | // Note: |
290 | // The final goal we would like to make all parse_macro success, | 313 | // The final goal we would like to make all parse_macro success, |
291 | // such that the following log will not call anyway. | 314 | // such that the following log will not call anyway. |
@@ -313,30 +336,40 @@ pub fn parse_macro_with_arg( | |||
313 | log::warn!("fail on macro_parse: (reason: {})", err); | 336 | log::warn!("fail on macro_parse: (reason: {})", err); |
314 | } | 337 | } |
315 | } | 338 | } |
339 | } | ||
340 | let tt = match result.value { | ||
341 | Some(tt) => tt, | ||
342 | None => return result.drop_value(), | ||
316 | }; | 343 | }; |
317 | let tt = tt?; | ||
318 | 344 | ||
319 | let fragment_kind = to_fragment_kind(db, macro_call_id); | 345 | let fragment_kind = to_fragment_kind(db, macro_call_id); |
320 | 346 | ||
321 | let (parse, rev_token_map) = mbe::token_tree_to_syntax_node(&tt, fragment_kind).ok()?; | 347 | let (parse, rev_token_map) = match mbe::token_tree_to_syntax_node(&tt, fragment_kind) { |
348 | Ok(it) => it, | ||
349 | Err(err) => { | ||
350 | return MacroResult::error(format!("{:?}", err)); | ||
351 | } | ||
352 | }; | ||
322 | 353 | ||
323 | if err.is_none() { | 354 | match result.error { |
324 | Some((parse, Arc::new(rev_token_map))) | 355 | Some(error) => { |
325 | } else { | 356 | // Safety check for recursive identity macro. |
326 | // FIXME: | 357 | let node = parse.syntax_node(); |
327 | // In future, we should propagate the actual error with recovery information | 358 | let file: HirFileId = macro_file.into(); |
328 | // instead of ignore the error here. | 359 | let call_node = match file.call_node(db) { |
329 | 360 | Some(it) => it, | |
330 | // Safe check for recurisve identity macro | 361 | None => { |
331 | let node = parse.syntax_node(); | 362 | return MacroResult::error(error); |
332 | let file: HirFileId = macro_file.into(); | 363 | } |
333 | let call_node = file.call_node(db)?; | 364 | }; |
334 | 365 | ||
335 | if !diff(&node, &call_node.value).is_empty() { | 366 | if !diff(&node, &call_node.value).is_empty() { |
336 | Some((parse, Arc::new(rev_token_map))) | 367 | MacroResult { value: Some((parse, Arc::new(rev_token_map))), error: Some(error) } |
337 | } else { | 368 | } else { |
338 | None | 369 | return MacroResult::error(error); |
370 | } | ||
339 | } | 371 | } |
372 | None => MacroResult { value: Some((parse, Arc::new(rev_token_map))), error: None }, | ||
340 | } | 373 | } |
341 | } | 374 | } |
342 | 375 | ||
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index 17f1178ed..83e09738b 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs | |||
@@ -144,7 +144,7 @@ impl HirFileId { | |||
144 | let def_tt = loc.def.ast_id?.to_node(db).token_tree()?; | 144 | let def_tt = loc.def.ast_id?.to_node(db).token_tree()?; |
145 | 145 | ||
146 | let macro_def = db.macro_def(loc.def)?; | 146 | let macro_def = db.macro_def(loc.def)?; |
147 | let (parse, exp_map) = db.parse_macro(macro_file)?; | 147 | let (parse, exp_map) = db.parse_macro_expansion(macro_file).value?; |
148 | let macro_arg = db.macro_arg(macro_file.macro_call_id)?; | 148 | let macro_arg = db.macro_arg(macro_file.macro_call_id)?; |
149 | 149 | ||
150 | Some(ExpansionInfo { | 150 | Some(ExpansionInfo { |