diff options
author | Jonas Schievink <[email protected]> | 2020-12-02 15:52:14 +0000 |
---|---|---|
committer | Jonas Schievink <[email protected]> | 2020-12-03 14:48:29 +0000 |
commit | a6342436343e01b32d4482a48994b8c22bcbe659 (patch) | |
tree | 1fd4c71c6b203417c64b9ffb31221242d6f1a270 /crates | |
parent | 5a1306a43652d914035b2cf0b703f4bfd3451a33 (diff) |
Propagate eager expansion errors
Diffstat (limited to 'crates')
-rw-r--r-- | crates/hir_def/src/body.rs | 20 | ||||
-rw-r--r-- | crates/hir_def/src/lib.rs | 51 | ||||
-rw-r--r-- | crates/hir_expand/src/eager.rs | 115 |
3 files changed, 153 insertions, 33 deletions
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs index 33eb5e78c..92bcc1705 100644 --- a/crates/hir_def/src/body.rs +++ b/crates/hir_def/src/body.rs | |||
@@ -120,18 +120,24 @@ impl Expander { | |||
120 | self.resolve_path_as_macro(db, &path) | 120 | self.resolve_path_as_macro(db, &path) |
121 | }; | 121 | }; |
122 | 122 | ||
123 | let call_id = match macro_call.as_call_id(db, self.crate_def_map.krate, resolver) { | 123 | let mut err = None; |
124 | let call_id = | ||
125 | macro_call.as_call_id_with_errors(db, self.crate_def_map.krate, resolver, &mut |e| { | ||
126 | err.get_or_insert(e); | ||
127 | }); | ||
128 | let call_id = match call_id { | ||
124 | Some(it) => it, | 129 | Some(it) => it, |
125 | None => { | 130 | None => { |
126 | // FIXME: this can mean other things too, but `as_call_id` doesn't provide enough | 131 | if err.is_none() { |
127 | // info. | 132 | eprintln!("no error despite `as_call_id_with_errors` returning `None`"); |
128 | return ExpandResult::only_err(mbe::ExpandError::Other( | 133 | } |
129 | "failed to parse or resolve macro invocation".into(), | 134 | return ExpandResult { value: None, err }; |
130 | )); | ||
131 | } | 135 | } |
132 | }; | 136 | }; |
133 | 137 | ||
134 | let err = db.macro_expand_error(call_id); | 138 | if err.is_none() { |
139 | err = db.macro_expand_error(call_id); | ||
140 | } | ||
135 | 141 | ||
136 | let file_id = call_id.as_file(); | 142 | let file_id = call_id.as_file(); |
137 | 143 | ||
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs index 1b22d1eec..ce2be8e2b 100644 --- a/crates/hir_def/src/lib.rs +++ b/crates/hir_def/src/lib.rs | |||
@@ -465,21 +465,37 @@ pub trait AsMacroCall { | |||
465 | db: &dyn db::DefDatabase, | 465 | db: &dyn db::DefDatabase, |
466 | krate: CrateId, | 466 | krate: CrateId, |
467 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | 467 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, |
468 | ) -> Option<MacroCallId> { | ||
469 | self.as_call_id_with_errors(db, krate, resolver, &mut |_| ()) | ||
470 | } | ||
471 | |||
472 | fn as_call_id_with_errors( | ||
473 | &self, | ||
474 | db: &dyn db::DefDatabase, | ||
475 | krate: CrateId, | ||
476 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | ||
477 | error_sink: &mut dyn FnMut(mbe::ExpandError), | ||
468 | ) -> Option<MacroCallId>; | 478 | ) -> Option<MacroCallId>; |
469 | } | 479 | } |
470 | 480 | ||
471 | impl AsMacroCall for InFile<&ast::MacroCall> { | 481 | impl AsMacroCall for InFile<&ast::MacroCall> { |
472 | fn as_call_id( | 482 | fn as_call_id_with_errors( |
473 | &self, | 483 | &self, |
474 | db: &dyn db::DefDatabase, | 484 | db: &dyn db::DefDatabase, |
475 | krate: CrateId, | 485 | krate: CrateId, |
476 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | 486 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, |
487 | error_sink: &mut dyn FnMut(mbe::ExpandError), | ||
477 | ) -> Option<MacroCallId> { | 488 | ) -> Option<MacroCallId> { |
478 | let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value)); | 489 | let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value)); |
479 | let h = Hygiene::new(db.upcast(), self.file_id); | 490 | let h = Hygiene::new(db.upcast(), self.file_id); |
480 | let path = path::ModPath::from_src(self.value.path()?, &h)?; | 491 | let path = self.value.path().and_then(|path| path::ModPath::from_src(path, &h)); |
492 | |||
493 | if path.is_none() { | ||
494 | error_sink(mbe::ExpandError::Other("malformed macro invocation".into())); | ||
495 | } | ||
481 | 496 | ||
482 | AstIdWithPath::new(ast_id.file_id, ast_id.value, path).as_call_id(db, krate, resolver) | 497 | AstIdWithPath::new(ast_id.file_id, ast_id.value, path?) |
498 | .as_call_id_with_errors(db, krate, resolver, error_sink) | ||
483 | } | 499 | } |
484 | } | 500 | } |
485 | 501 | ||
@@ -497,22 +513,32 @@ impl<T: ast::AstNode> AstIdWithPath<T> { | |||
497 | } | 513 | } |
498 | 514 | ||
499 | impl AsMacroCall for AstIdWithPath<ast::MacroCall> { | 515 | impl AsMacroCall for AstIdWithPath<ast::MacroCall> { |
500 | fn as_call_id( | 516 | fn as_call_id_with_errors( |
501 | &self, | 517 | &self, |
502 | db: &dyn db::DefDatabase, | 518 | db: &dyn db::DefDatabase, |
503 | krate: CrateId, | 519 | krate: CrateId, |
504 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | 520 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, |
521 | error_sink: &mut dyn FnMut(mbe::ExpandError), | ||
505 | ) -> Option<MacroCallId> { | 522 | ) -> Option<MacroCallId> { |
506 | let def: MacroDefId = resolver(self.path.clone())?; | 523 | let def: MacroDefId = resolver(self.path.clone()).or_else(|| { |
524 | error_sink(mbe::ExpandError::Other("could not resolve macro".into())); | ||
525 | None | ||
526 | })?; | ||
507 | 527 | ||
508 | if let MacroDefKind::BuiltInEager(_) = def.kind { | 528 | if let MacroDefKind::BuiltInEager(_) = def.kind { |
509 | let macro_call = InFile::new(self.ast_id.file_id, self.ast_id.to_node(db.upcast())); | 529 | let macro_call = InFile::new(self.ast_id.file_id, self.ast_id.to_node(db.upcast())); |
510 | let hygiene = Hygiene::new(db.upcast(), self.ast_id.file_id); | 530 | let hygiene = Hygiene::new(db.upcast(), self.ast_id.file_id); |
511 | 531 | ||
512 | Some( | 532 | Some( |
513 | expand_eager_macro(db.upcast(), krate, macro_call, def, &|path: ast::Path| { | 533 | expand_eager_macro( |
514 | resolver(path::ModPath::from_src(path, &hygiene)?) | 534 | db.upcast(), |
515 | })? | 535 | krate, |
536 | macro_call, | ||
537 | def, | ||
538 | &|path: ast::Path| resolver(path::ModPath::from_src(path, &hygiene)?), | ||
539 | error_sink, | ||
540 | ) | ||
541 | .ok()? | ||
516 | .into(), | 542 | .into(), |
517 | ) | 543 | ) |
518 | } else { | 544 | } else { |
@@ -522,13 +548,18 @@ impl AsMacroCall for AstIdWithPath<ast::MacroCall> { | |||
522 | } | 548 | } |
523 | 549 | ||
524 | impl AsMacroCall for AstIdWithPath<ast::Item> { | 550 | impl AsMacroCall for AstIdWithPath<ast::Item> { |
525 | fn as_call_id( | 551 | fn as_call_id_with_errors( |
526 | &self, | 552 | &self, |
527 | db: &dyn db::DefDatabase, | 553 | db: &dyn db::DefDatabase, |
528 | krate: CrateId, | 554 | krate: CrateId, |
529 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | 555 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, |
556 | error_sink: &mut dyn FnMut(mbe::ExpandError), | ||
530 | ) -> Option<MacroCallId> { | 557 | ) -> Option<MacroCallId> { |
531 | let def = resolver(self.path.clone())?; | 558 | let def: MacroDefId = resolver(self.path.clone()).or_else(|| { |
559 | error_sink(mbe::ExpandError::Other("could not resolve macro".into())); | ||
560 | None | ||
561 | })?; | ||
562 | |||
532 | Some( | 563 | Some( |
533 | def.as_lazy_macro( | 564 | def.as_lazy_macro( |
534 | db.upcast(), | 565 | db.upcast(), |
diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs index ab6b4477c..a48d08f3b 100644 --- a/crates/hir_expand/src/eager.rs +++ b/crates/hir_expand/src/eager.rs | |||
@@ -26,19 +26,89 @@ use crate::{ | |||
26 | }; | 26 | }; |
27 | 27 | ||
28 | use base_db::CrateId; | 28 | use base_db::CrateId; |
29 | use mbe::ExpandResult; | ||
29 | use parser::FragmentKind; | 30 | use parser::FragmentKind; |
30 | use std::sync::Arc; | 31 | use std::sync::Arc; |
31 | use syntax::{algo::SyntaxRewriter, SyntaxNode}; | 32 | use syntax::{algo::SyntaxRewriter, SyntaxNode}; |
32 | 33 | ||
34 | pub struct ErrorEmitted { | ||
35 | _private: (), | ||
36 | } | ||
37 | |||
38 | trait ErrorSink { | ||
39 | fn emit(&mut self, err: mbe::ExpandError); | ||
40 | |||
41 | fn option<T>( | ||
42 | &mut self, | ||
43 | opt: Option<T>, | ||
44 | error: impl FnOnce() -> mbe::ExpandError, | ||
45 | ) -> Result<T, ErrorEmitted> { | ||
46 | match opt { | ||
47 | Some(it) => Ok(it), | ||
48 | None => { | ||
49 | self.emit(error()); | ||
50 | Err(ErrorEmitted { _private: () }) | ||
51 | } | ||
52 | } | ||
53 | } | ||
54 | |||
55 | fn option_with<T>( | ||
56 | &mut self, | ||
57 | opt: impl FnOnce() -> Option<T>, | ||
58 | error: impl FnOnce() -> mbe::ExpandError, | ||
59 | ) -> Result<T, ErrorEmitted> { | ||
60 | self.option(opt(), error) | ||
61 | } | ||
62 | |||
63 | fn result<T>(&mut self, res: Result<T, mbe::ExpandError>) -> Result<T, ErrorEmitted> { | ||
64 | match res { | ||
65 | Ok(it) => Ok(it), | ||
66 | Err(e) => { | ||
67 | self.emit(e); | ||
68 | Err(ErrorEmitted { _private: () }) | ||
69 | } | ||
70 | } | ||
71 | } | ||
72 | |||
73 | fn expand_result_option<T>(&mut self, res: ExpandResult<Option<T>>) -> Result<T, ErrorEmitted> { | ||
74 | match (res.value, res.err) { | ||
75 | (None, Some(err)) => { | ||
76 | self.emit(err); | ||
77 | Err(ErrorEmitted { _private: () }) | ||
78 | } | ||
79 | (Some(value), opt_err) => { | ||
80 | if let Some(err) = opt_err { | ||
81 | self.emit(err); | ||
82 | } | ||
83 | Ok(value) | ||
84 | } | ||
85 | (None, None) => unreachable!("`ExpandResult` without value or error"), | ||
86 | } | ||
87 | } | ||
88 | } | ||
89 | |||
90 | impl ErrorSink for &'_ mut dyn FnMut(mbe::ExpandError) { | ||
91 | fn emit(&mut self, err: mbe::ExpandError) { | ||
92 | self(err); | ||
93 | } | ||
94 | } | ||
95 | |||
96 | fn err(msg: impl Into<String>) -> mbe::ExpandError { | ||
97 | mbe::ExpandError::Other(msg.into()) | ||
98 | } | ||
99 | |||
33 | pub fn expand_eager_macro( | 100 | pub fn expand_eager_macro( |
34 | db: &dyn AstDatabase, | 101 | db: &dyn AstDatabase, |
35 | krate: CrateId, | 102 | krate: CrateId, |
36 | macro_call: InFile<ast::MacroCall>, | 103 | macro_call: InFile<ast::MacroCall>, |
37 | def: MacroDefId, | 104 | def: MacroDefId, |
38 | resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>, | 105 | resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>, |
39 | ) -> Option<EagerMacroId> { | 106 | mut error_sink: &mut dyn FnMut(mbe::ExpandError), |
40 | let args = macro_call.value.token_tree()?; | 107 | ) -> Result<EagerMacroId, ErrorEmitted> { |
41 | let parsed_args = mbe::ast_to_token_tree(&args)?.0; | 108 | let parsed_args = error_sink.option_with( |
109 | || Some(mbe::ast_to_token_tree(¯o_call.value.token_tree()?)?.0), | ||
110 | || err("malformed macro invocation"), | ||
111 | )?; | ||
42 | 112 | ||
43 | // Note: | 113 | // Note: |
44 | // When `lazy_expand` is called, its *parent* file must be already exists. | 114 | // When `lazy_expand` is called, its *parent* file must be already exists. |
@@ -55,17 +125,21 @@ pub fn expand_eager_macro( | |||
55 | }); | 125 | }); |
56 | let arg_file_id: MacroCallId = arg_id.into(); | 126 | let arg_file_id: MacroCallId = arg_id.into(); |
57 | 127 | ||
58 | let parsed_args = mbe::token_tree_to_syntax_node(&parsed_args, FragmentKind::Expr).ok()?.0; | 128 | let parsed_args = |
129 | error_sink.result(mbe::token_tree_to_syntax_node(&parsed_args, FragmentKind::Expr))?.0; | ||
59 | let result = eager_macro_recur( | 130 | let result = eager_macro_recur( |
60 | db, | 131 | db, |
61 | InFile::new(arg_file_id.as_file(), parsed_args.syntax_node()), | 132 | InFile::new(arg_file_id.as_file(), parsed_args.syntax_node()), |
62 | krate, | 133 | krate, |
63 | resolver, | 134 | resolver, |
135 | error_sink, | ||
64 | )?; | 136 | )?; |
65 | let subtree = to_subtree(&result)?; | 137 | let subtree = error_sink.option(to_subtree(&result), || err("failed to parse macro result"))?; |
66 | 138 | ||
67 | if let MacroDefKind::BuiltInEager(eager) = def.kind { | 139 | if let MacroDefKind::BuiltInEager(eager) = def.kind { |
68 | let (subtree, fragment) = eager.expand(db, arg_id, &subtree).value?; | 140 | let res = eager.expand(db, arg_id, &subtree); |
141 | |||
142 | let (subtree, fragment) = error_sink.expand_result_option(res)?; | ||
69 | let eager = EagerCallLoc { | 143 | let eager = EagerCallLoc { |
70 | def, | 144 | def, |
71 | fragment, | 145 | fragment, |
@@ -74,9 +148,9 @@ pub fn expand_eager_macro( | |||
74 | file_id: macro_call.file_id, | 148 | file_id: macro_call.file_id, |
75 | }; | 149 | }; |
76 | 150 | ||
77 | Some(db.intern_eager_expansion(eager)) | 151 | Ok(db.intern_eager_expansion(eager)) |
78 | } else { | 152 | } else { |
79 | None | 153 | panic!("called `expand_eager_macro` on non-eager macro def {:?}", def); |
80 | } | 154 | } |
81 | } | 155 | } |
82 | 156 | ||
@@ -91,13 +165,16 @@ fn lazy_expand( | |||
91 | def: &MacroDefId, | 165 | def: &MacroDefId, |
92 | macro_call: InFile<ast::MacroCall>, | 166 | macro_call: InFile<ast::MacroCall>, |
93 | krate: CrateId, | 167 | krate: CrateId, |
94 | ) -> Option<InFile<SyntaxNode>> { | 168 | ) -> ExpandResult<Option<InFile<SyntaxNode>>> { |
95 | let ast_id = db.ast_id_map(macro_call.file_id).ast_id(¯o_call.value); | 169 | let ast_id = db.ast_id_map(macro_call.file_id).ast_id(¯o_call.value); |
96 | 170 | ||
97 | let id: MacroCallId = | 171 | let id: MacroCallId = |
98 | def.as_lazy_macro(db, krate, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into(); | 172 | def.as_lazy_macro(db, krate, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into(); |
99 | 173 | ||
100 | db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node)) | 174 | let err = db.macro_expand_error(id); |
175 | let value = db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node)); | ||
176 | |||
177 | ExpandResult { value, err } | ||
101 | } | 178 | } |
102 | 179 | ||
103 | fn eager_macro_recur( | 180 | fn eager_macro_recur( |
@@ -105,7 +182,8 @@ fn eager_macro_recur( | |||
105 | curr: InFile<SyntaxNode>, | 182 | curr: InFile<SyntaxNode>, |
106 | krate: CrateId, | 183 | krate: CrateId, |
107 | macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>, | 184 | macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>, |
108 | ) -> Option<SyntaxNode> { | 185 | mut error_sink: &mut dyn FnMut(mbe::ExpandError), |
186 | ) -> Result<SyntaxNode, ErrorEmitted> { | ||
109 | let original = curr.value.clone(); | 187 | let original = curr.value.clone(); |
110 | 188 | ||
111 | let children = curr.value.descendants().filter_map(ast::MacroCall::cast); | 189 | let children = curr.value.descendants().filter_map(ast::MacroCall::cast); |
@@ -113,7 +191,8 @@ fn eager_macro_recur( | |||
113 | 191 | ||
114 | // Collect replacement | 192 | // Collect replacement |
115 | for child in children { | 193 | for child in children { |
116 | let def: MacroDefId = macro_resolver(child.path()?)?; | 194 | let def = error_sink |
195 | .option_with(|| macro_resolver(child.path()?), || err("failed to resolve macro"))?; | ||
117 | let insert = match def.kind { | 196 | let insert = match def.kind { |
118 | MacroDefKind::BuiltInEager(_) => { | 197 | MacroDefKind::BuiltInEager(_) => { |
119 | let id: MacroCallId = expand_eager_macro( | 198 | let id: MacroCallId = expand_eager_macro( |
@@ -122,17 +201,21 @@ fn eager_macro_recur( | |||
122 | curr.with_value(child.clone()), | 201 | curr.with_value(child.clone()), |
123 | def, | 202 | def, |
124 | macro_resolver, | 203 | macro_resolver, |
204 | error_sink, | ||
125 | )? | 205 | )? |
126 | .into(); | 206 | .into(); |
127 | db.parse_or_expand(id.as_file())? | 207 | db.parse_or_expand(id.as_file()) |
208 | .expect("successful macro expansion should be parseable") | ||
128 | } | 209 | } |
129 | MacroDefKind::Declarative | 210 | MacroDefKind::Declarative |
130 | | MacroDefKind::BuiltIn(_) | 211 | | MacroDefKind::BuiltIn(_) |
131 | | MacroDefKind::BuiltInDerive(_) | 212 | | MacroDefKind::BuiltInDerive(_) |
132 | | MacroDefKind::ProcMacro(_) => { | 213 | | MacroDefKind::ProcMacro(_) => { |
133 | let expanded = lazy_expand(db, &def, curr.with_value(child.clone()), krate)?; | 214 | let res = lazy_expand(db, &def, curr.with_value(child.clone()), krate); |
215 | let val = error_sink.expand_result_option(res)?; | ||
216 | |||
134 | // replace macro inside | 217 | // replace macro inside |
135 | eager_macro_recur(db, expanded, krate, macro_resolver)? | 218 | eager_macro_recur(db, val, krate, macro_resolver, error_sink)? |
136 | } | 219 | } |
137 | }; | 220 | }; |
138 | 221 | ||
@@ -140,5 +223,5 @@ fn eager_macro_recur( | |||
140 | } | 223 | } |
141 | 224 | ||
142 | let res = rewriter.rewrite(&original); | 225 | let res = rewriter.rewrite(&original); |
143 | Some(res) | 226 | Ok(res) |
144 | } | 227 | } |