Skip to content

Commit a634243

Browse files
Propagate eager expansion errors
1 parent 5a1306a commit a634243

File tree

3 files changed

+153
-33
lines changed

3 files changed

+153
-33
lines changed

crates/hir_def/src/body.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,18 +120,24 @@ impl Expander {
120120
self.resolve_path_as_macro(db, &path)
121121
};
122122

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 {
124129
Some(it) => it,
125130
None => {
126-
// FIXME: this can mean other things too, but `as_call_id` doesn't provide enough
127-
// info.
128-
return ExpandResult::only_err(mbe::ExpandError::Other(
129-
"failed to parse or resolve macro invocation".into(),
130-
));
131+
if err.is_none() {
132+
eprintln!("no error despite `as_call_id_with_errors` returning `None`");
133+
}
134+
return ExpandResult { value: None, err };
131135
}
132136
};
133137

134-
let err = db.macro_expand_error(call_id);
138+
if err.is_none() {
139+
err = db.macro_expand_error(call_id);
140+
}
135141

136142
let file_id = call_id.as_file();
137143

crates/hir_def/src/lib.rs

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -465,21 +465,37 @@ pub trait AsMacroCall {
465465
db: &dyn db::DefDatabase,
466466
krate: CrateId,
467467
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),
468478
) -> Option<MacroCallId>;
469479
}
470480

471481
impl AsMacroCall for InFile<&ast::MacroCall> {
472-
fn as_call_id(
482+
fn as_call_id_with_errors(
473483
&self,
474484
db: &dyn db::DefDatabase,
475485
krate: CrateId,
476486
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
487+
error_sink: &mut dyn FnMut(mbe::ExpandError),
477488
) -> Option<MacroCallId> {
478489
let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
479490
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+
}
481496

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)
483499
}
484500
}
485501

@@ -497,22 +513,32 @@ impl<T: ast::AstNode> AstIdWithPath<T> {
497513
}
498514

499515
impl AsMacroCall for AstIdWithPath<ast::MacroCall> {
500-
fn as_call_id(
516+
fn as_call_id_with_errors(
501517
&self,
502518
db: &dyn db::DefDatabase,
503519
krate: CrateId,
504520
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
521+
error_sink: &mut dyn FnMut(mbe::ExpandError),
505522
) -> 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+
})?;
507527

508528
if let MacroDefKind::BuiltInEager(_) = def.kind {
509529
let macro_call = InFile::new(self.ast_id.file_id, self.ast_id.to_node(db.upcast()));
510530
let hygiene = Hygiene::new(db.upcast(), self.ast_id.file_id);
511531

512532
Some(
513-
expand_eager_macro(db.upcast(), krate, macro_call, def, &|path: ast::Path| {
514-
resolver(path::ModPath::from_src(path, &hygiene)?)
515-
})?
533+
expand_eager_macro(
534+
db.upcast(),
535+
krate,
536+
macro_call,
537+
def,
538+
&|path: ast::Path| resolver(path::ModPath::from_src(path, &hygiene)?),
539+
error_sink,
540+
)
541+
.ok()?
516542
.into(),
517543
)
518544
} else {
@@ -522,13 +548,18 @@ impl AsMacroCall for AstIdWithPath<ast::MacroCall> {
522548
}
523549

524550
impl AsMacroCall for AstIdWithPath<ast::Item> {
525-
fn as_call_id(
551+
fn as_call_id_with_errors(
526552
&self,
527553
db: &dyn db::DefDatabase,
528554
krate: CrateId,
529555
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
556+
error_sink: &mut dyn FnMut(mbe::ExpandError),
530557
) -> 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+
532563
Some(
533564
def.as_lazy_macro(
534565
db.upcast(),

crates/hir_expand/src/eager.rs

Lines changed: 99 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,89 @@ use crate::{
2626
};
2727

2828
use base_db::CrateId;
29+
use mbe::ExpandResult;
2930
use parser::FragmentKind;
3031
use std::sync::Arc;
3132
use syntax::{algo::SyntaxRewriter, SyntaxNode};
3233

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+
33100
pub fn expand_eager_macro(
34101
db: &dyn AstDatabase,
35102
krate: CrateId,
36103
macro_call: InFile<ast::MacroCall>,
37104
def: MacroDefId,
38105
resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
39-
) -> Option<EagerMacroId> {
40-
let args = macro_call.value.token_tree()?;
41-
let parsed_args = mbe::ast_to_token_tree(&args)?.0;
106+
mut error_sink: &mut dyn FnMut(mbe::ExpandError),
107+
) -> Result<EagerMacroId, ErrorEmitted> {
108+
let parsed_args = error_sink.option_with(
109+
|| Some(mbe::ast_to_token_tree(&macro_call.value.token_tree()?)?.0),
110+
|| err("malformed macro invocation"),
111+
)?;
42112

43113
// Note:
44114
// When `lazy_expand` is called, its *parent* file must be already exists.
@@ -55,17 +125,21 @@ pub fn expand_eager_macro(
55125
});
56126
let arg_file_id: MacroCallId = arg_id.into();
57127

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;
59130
let result = eager_macro_recur(
60131
db,
61132
InFile::new(arg_file_id.as_file(), parsed_args.syntax_node()),
62133
krate,
63134
resolver,
135+
error_sink,
64136
)?;
65-
let subtree = to_subtree(&result)?;
137+
let subtree = error_sink.option(to_subtree(&result), || err("failed to parse macro result"))?;
66138

67139
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)?;
69143
let eager = EagerCallLoc {
70144
def,
71145
fragment,
@@ -74,9 +148,9 @@ pub fn expand_eager_macro(
74148
file_id: macro_call.file_id,
75149
};
76150

77-
Some(db.intern_eager_expansion(eager))
151+
Ok(db.intern_eager_expansion(eager))
78152
} else {
79-
None
153+
panic!("called `expand_eager_macro` on non-eager macro def {:?}", def);
80154
}
81155
}
82156

@@ -91,29 +165,34 @@ fn lazy_expand(
91165
def: &MacroDefId,
92166
macro_call: InFile<ast::MacroCall>,
93167
krate: CrateId,
94-
) -> Option<InFile<SyntaxNode>> {
168+
) -> ExpandResult<Option<InFile<SyntaxNode>>> {
95169
let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value);
96170

97171
let id: MacroCallId =
98172
def.as_lazy_macro(db, krate, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into();
99173

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 }
101178
}
102179

103180
fn eager_macro_recur(
104181
db: &dyn AstDatabase,
105182
curr: InFile<SyntaxNode>,
106183
krate: CrateId,
107184
macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
108-
) -> Option<SyntaxNode> {
185+
mut error_sink: &mut dyn FnMut(mbe::ExpandError),
186+
) -> Result<SyntaxNode, ErrorEmitted> {
109187
let original = curr.value.clone();
110188

111189
let children = curr.value.descendants().filter_map(ast::MacroCall::cast);
112190
let mut rewriter = SyntaxRewriter::default();
113191

114192
// Collect replacement
115193
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"))?;
117196
let insert = match def.kind {
118197
MacroDefKind::BuiltInEager(_) => {
119198
let id: MacroCallId = expand_eager_macro(
@@ -122,23 +201,27 @@ fn eager_macro_recur(
122201
curr.with_value(child.clone()),
123202
def,
124203
macro_resolver,
204+
error_sink,
125205
)?
126206
.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")
128209
}
129210
MacroDefKind::Declarative
130211
| MacroDefKind::BuiltIn(_)
131212
| MacroDefKind::BuiltInDerive(_)
132213
| 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+
134217
// replace macro inside
135-
eager_macro_recur(db, expanded, krate, macro_resolver)?
218+
eager_macro_recur(db, val, krate, macro_resolver, error_sink)?
136219
}
137220
};
138221

139222
rewriter.replace(child.syntax(), &insert);
140223
}
141224

142225
let res = rewriter.rewrite(&original);
143-
Some(res)
226+
Ok(res)
144227
}

0 commit comments

Comments
 (0)