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