diff options
author | Jonas Schievink <[email protected]> | 2020-11-24 18:00:23 +0000 |
---|---|---|
committer | Jonas Schievink <[email protected]> | 2020-11-24 18:00:23 +0000 |
commit | d5e9bf80f9e68cabf694226e2bad896c1ee00742 (patch) | |
tree | 12fc2464ad36452481fd29b9a174840be3765dd2 | |
parent | d2f398cd76659425c9d48185a1be92ee9a9332c2 (diff) |
hir_expand: propagate expansion errors
-rw-r--r-- | crates/hir/src/lib.rs | 4 | ||||
-rw-r--r-- | crates/hir_expand/src/db.rs | 123 | ||||
-rw-r--r-- | crates/hir_expand/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ide/src/status.rs | 8 |
4 files changed, 91 insertions, 46 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 5fea25ef1..ed110329d 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -57,8 +57,8 @@ pub use hir_def::{ | |||
57 | visibility::Visibility, | 57 | visibility::Visibility, |
58 | }; | 58 | }; |
59 | pub use hir_expand::{ | 59 | pub use hir_expand::{ |
60 | name::known, name::AsName, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, | 60 | db::MacroResult, name::known, name::AsName, name::Name, HirFileId, InFile, MacroCallId, |
61 | /* FIXME */ MacroDefId, MacroFile, Origin, | 61 | MacroCallLoc, /* FIXME */ MacroDefId, MacroFile, Origin, |
62 | }; | 62 | }; |
63 | pub use hir_ty::display::HirDisplay; | 63 | pub use hir_ty::display::HirDisplay; |
64 | 64 | ||
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index ade57ac1b..fc512517c 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( |
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,7 +131,7 @@ 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()?; |
@@ -171,10 +200,7 @@ pub(crate) fn macro_arg( | |||
171 | Some(Arc::new((tt, tmap))) | 200 | Some(Arc::new((tt, tmap))) |
172 | } | 201 | } |
173 | 202 | ||
174 | pub(crate) fn macro_expand( | 203 | pub(crate) 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) | 204 | macro_expand_with_arg(db, id, None) |
179 | } | 205 | } |
180 | 206 | ||
@@ -195,17 +221,19 @@ fn macro_expand_with_arg( | |||
195 | db: &dyn AstDatabase, | 221 | db: &dyn AstDatabase, |
196 | id: MacroCallId, | 222 | id: MacroCallId, |
197 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, | 223 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, |
198 | ) -> (Option<Arc<tt::Subtree>>, Option<String>) { | 224 | ) -> MacroResult<Arc<tt::Subtree>> { |
199 | let lazy_id = match id { | 225 | let lazy_id = match id { |
200 | MacroCallId::LazyMacro(id) => id, | 226 | MacroCallId::LazyMacro(id) => id, |
201 | MacroCallId::EagerMacro(id) => { | 227 | MacroCallId::EagerMacro(id) => { |
202 | if arg.is_some() { | 228 | if arg.is_some() { |
203 | return ( | 229 | return MacroResult::error( |
204 | None, | 230 | "hypothetical macro expansion not implemented for eager macro".to_owned(), |
205 | Some("hypothetical macro expansion not implemented for eager macro".to_owned()), | ||
206 | ); | 231 | ); |
207 | } else { | 232 | } else { |
208 | return (Some(db.lookup_intern_eager_expansion(id).subtree), None); | 233 | return MacroResult { |
234 | value: Some(db.lookup_intern_eager_expansion(id).subtree), | ||
235 | error: None, | ||
236 | }; | ||
209 | } | 237 | } |
210 | } | 238 | } |
211 | }; | 239 | }; |
@@ -213,20 +241,21 @@ fn macro_expand_with_arg( | |||
213 | let loc = db.lookup_intern_macro(lazy_id); | 241 | let loc = db.lookup_intern_macro(lazy_id); |
214 | let macro_arg = match arg.or_else(|| db.macro_arg(id)) { | 242 | let macro_arg = match arg.or_else(|| db.macro_arg(id)) { |
215 | Some(it) => it, | 243 | Some(it) => it, |
216 | None => return (None, Some("Fail to args in to tt::TokenTree".into())), | 244 | None => return MacroResult::error("Fail to args in to tt::TokenTree".into()), |
217 | }; | 245 | }; |
218 | 246 | ||
219 | let macro_rules = match db.macro_def(loc.def) { | 247 | let macro_rules = match db.macro_def(loc.def) { |
220 | Some(it) => it, | 248 | Some(it) => it, |
221 | None => return (None, Some("Fail to find macro definition".into())), | 249 | None => return MacroResult::error("Fail to find macro definition".into()), |
222 | }; | 250 | }; |
223 | let ExpandResult(tt, err) = macro_rules.0.expand(db, lazy_id, ¯o_arg.0); | 251 | let ExpandResult(tt, err) = macro_rules.0.expand(db, lazy_id, ¯o_arg.0); |
224 | // Set a hard limit for the expanded tt | 252 | // Set a hard limit for the expanded tt |
225 | let count = tt.count(); | 253 | let count = tt.count(); |
226 | if count > 262144 { | 254 | if count > 262144 { |
227 | return (None, Some(format!("Total tokens count exceed limit : count = {}", count))); | 255 | return MacroResult::error(format!("Total tokens count exceed limit : count = {}", count)); |
228 | } | 256 | } |
229 | (Some(Arc::new(tt)), err.map(|e| format!("{:?}", e))) | 257 | |
258 | MacroResult { value: Some(Arc::new(tt)), error: err.map(|e| format!("{:?}", e)) } | ||
230 | } | 259 | } |
231 | 260 | ||
232 | pub(crate) fn expand_proc_macro( | 261 | pub(crate) fn expand_proc_macro( |
@@ -260,7 +289,7 @@ pub(crate) fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Optio | |||
260 | match file_id.0 { | 289 | match file_id.0 { |
261 | HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()), | 290 | HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()), |
262 | HirFileIdRepr::MacroFile(macro_file) => { | 291 | HirFileIdRepr::MacroFile(macro_file) => { |
263 | db.parse_macro(macro_file).map(|(it, _)| it.syntax_node()) | 292 | db.parse_macro(macro_file).map(|(it, _)| it.syntax_node()).value |
264 | } | 293 | } |
265 | } | 294 | } |
266 | } | 295 | } |
@@ -268,7 +297,7 @@ pub(crate) fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Optio | |||
268 | pub(crate) fn parse_macro( | 297 | pub(crate) fn parse_macro( |
269 | db: &dyn AstDatabase, | 298 | db: &dyn AstDatabase, |
270 | macro_file: MacroFile, | 299 | macro_file: MacroFile, |
271 | ) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { | 300 | ) -> MacroResult<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { |
272 | parse_macro_with_arg(db, macro_file, None) | 301 | parse_macro_with_arg(db, macro_file, None) |
273 | } | 302 | } |
274 | 303 | ||
@@ -276,16 +305,16 @@ pub fn parse_macro_with_arg( | |||
276 | db: &dyn AstDatabase, | 305 | db: &dyn AstDatabase, |
277 | macro_file: MacroFile, | 306 | macro_file: MacroFile, |
278 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, | 307 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, |
279 | ) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { | 308 | ) -> MacroResult<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { |
280 | let _p = profile::span("parse_macro_query"); | 309 | let _p = profile::span("parse_macro_query"); |
281 | 310 | ||
282 | let macro_call_id = macro_file.macro_call_id; | 311 | let macro_call_id = macro_file.macro_call_id; |
283 | let (tt, err) = if let Some(arg) = arg { | 312 | let result = if let Some(arg) = arg { |
284 | macro_expand_with_arg(db, macro_call_id, Some(arg)) | 313 | macro_expand_with_arg(db, macro_call_id, Some(arg)) |
285 | } else { | 314 | } else { |
286 | db.macro_expand(macro_call_id) | 315 | db.macro_expand(macro_call_id) |
287 | }; | 316 | }; |
288 | if let Some(err) = &err { | 317 | if let Some(err) = &result.error { |
289 | // Note: | 318 | // Note: |
290 | // The final goal we would like to make all parse_macro success, | 319 | // The final goal we would like to make all parse_macro success, |
291 | // such that the following log will not call anyway. | 320 | // such that the following log will not call anyway. |
@@ -313,30 +342,44 @@ pub fn parse_macro_with_arg( | |||
313 | log::warn!("fail on macro_parse: (reason: {})", err); | 342 | log::warn!("fail on macro_parse: (reason: {})", err); |
314 | } | 343 | } |
315 | } | 344 | } |
345 | } | ||
346 | let tt = match result.value { | ||
347 | Some(tt) => tt, | ||
348 | None => return result.drop_value(), | ||
316 | }; | 349 | }; |
317 | let tt = tt?; | ||
318 | 350 | ||
319 | let fragment_kind = to_fragment_kind(db, macro_call_id); | 351 | let fragment_kind = to_fragment_kind(db, macro_call_id); |
320 | 352 | ||
321 | let (parse, rev_token_map) = mbe::token_tree_to_syntax_node(&tt, fragment_kind).ok()?; | 353 | let (parse, rev_token_map) = match mbe::token_tree_to_syntax_node(&tt, fragment_kind) { |
354 | Ok(it) => it, | ||
355 | Err(err) => { | ||
356 | return MacroResult::error(format!("{:?}", err)); | ||
357 | } | ||
358 | }; | ||
322 | 359 | ||
323 | if err.is_none() { | 360 | match result.error { |
324 | Some((parse, Arc::new(rev_token_map))) | 361 | Some(error) => { |
325 | } else { | 362 | // FIXME: |
326 | // FIXME: | 363 | // In future, we should propagate the actual error with recovery information |
327 | // In future, we should propagate the actual error with recovery information | 364 | // instead of ignore the error here. |
328 | // instead of ignore the error here. | 365 | |
329 | 366 | // Safe check for recurisve identity macro | |
330 | // Safe check for recurisve identity macro | 367 | let node = parse.syntax_node(); |
331 | let node = parse.syntax_node(); | 368 | let file: HirFileId = macro_file.into(); |
332 | let file: HirFileId = macro_file.into(); | 369 | let call_node = match file.call_node(db) { |
333 | let call_node = file.call_node(db)?; | 370 | Some(it) => it, |
334 | 371 | None => { | |
335 | if !diff(&node, &call_node.value).is_empty() { | 372 | return MacroResult::error(error); |
336 | Some((parse, Arc::new(rev_token_map))) | 373 | } |
337 | } else { | 374 | }; |
338 | None | 375 | |
376 | if !diff(&node, &call_node.value).is_empty() { | ||
377 | MacroResult { value: Some((parse, Arc::new(rev_token_map))), error: None } | ||
378 | } else { | ||
379 | return MacroResult::error(error); | ||
380 | } | ||
339 | } | 381 | } |
382 | None => MacroResult { value: Some((parse, Arc::new(rev_token_map))), error: None }, | ||
340 | } | 383 | } |
341 | } | 384 | } |
342 | 385 | ||
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index 17f1178ed..9fc697d6f 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(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 { |
diff --git a/crates/ide/src/status.rs b/crates/ide/src/status.rs index 8e91c99d7..8b4a1652e 100644 --- a/crates/ide/src/status.rs +++ b/crates/ide/src/status.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use std::{fmt, iter::FromIterator, sync::Arc}; | 1 | use std::{fmt, iter::FromIterator, sync::Arc}; |
2 | 2 | ||
3 | use hir::MacroFile; | 3 | use hir::{MacroFile, MacroResult}; |
4 | use ide_db::base_db::{ | 4 | use ide_db::base_db::{ |
5 | salsa::debug::{DebugQueryTable, TableEntry}, | 5 | salsa::debug::{DebugQueryTable, TableEntry}, |
6 | CrateId, FileId, FileTextQuery, SourceDatabase, SourceRootId, | 6 | CrateId, FileId, FileTextQuery, SourceDatabase, SourceRootId, |
@@ -115,10 +115,12 @@ impl FromIterator<TableEntry<FileId, Parse<ast::SourceFile>>> for SyntaxTreeStat | |||
115 | } | 115 | } |
116 | } | 116 | } |
117 | 117 | ||
118 | impl<M> FromIterator<TableEntry<MacroFile, Option<(Parse<SyntaxNode>, M)>>> for SyntaxTreeStats { | 118 | impl<M> FromIterator<TableEntry<MacroFile, MacroResult<(Parse<SyntaxNode>, M)>>> |
119 | for SyntaxTreeStats | ||
120 | { | ||
119 | fn from_iter<T>(iter: T) -> SyntaxTreeStats | 121 | fn from_iter<T>(iter: T) -> SyntaxTreeStats |
120 | where | 122 | where |
121 | T: IntoIterator<Item = TableEntry<MacroFile, Option<(Parse<SyntaxNode>, M)>>>, | 123 | T: IntoIterator<Item = TableEntry<MacroFile, MacroResult<(Parse<SyntaxNode>, M)>>>, |
122 | { | 124 | { |
123 | let mut res = SyntaxTreeStats::default(); | 125 | let mut res = SyntaxTreeStats::default(); |
124 | for entry in iter { | 126 | for entry in iter { |