From be50908a5031fee45fcbba97bc0c96187324ea54 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Mon, 30 Nov 2020 20:26:35 +0100 Subject: Emit macro diagnostics when lowering bodies --- crates/hir_def/src/body.rs | 73 +++++++++++++++++++++++----------- crates/hir_def/src/body/diagnostics.rs | 6 ++- crates/hir_def/src/body/lower.rs | 14 ++++++- crates/hir_def/src/data.rs | 2 +- 4 files changed, 68 insertions(+), 27 deletions(-) (limited to 'crates') diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs index d10b1af01..33eb5e78c 100644 --- a/crates/hir_def/src/body.rs +++ b/crates/hir_def/src/body.rs @@ -14,8 +14,8 @@ use cfg::CfgOptions; use drop_bomb::DropBomb; use either::Either; use hir_expand::{ - ast_id_map::AstIdMap, diagnostics::DiagnosticSink, hygiene::Hygiene, AstId, HirFileId, InFile, - MacroDefId, + ast_id_map::AstIdMap, diagnostics::DiagnosticSink, hygiene::Hygiene, AstId, ExpandResult, + HirFileId, InFile, MacroDefId, }; use rustc_hash::FxHashMap; use syntax::{ast, AstNode, AstPtr}; @@ -102,11 +102,11 @@ impl Expander { db: &dyn DefDatabase, local_scope: Option<&ItemScope>, macro_call: ast::MacroCall, - ) -> Option<(Mark, T)> { + ) -> ExpandResult> { self.recursion_limit += 1; if self.recursion_limit > EXPANSION_RECURSION_LIMIT { mark::hit!(your_stack_belongs_to_me); - return None; + return ExpandResult::str_err("reached recursion limit during macro expansion".into()); } let macro_call = InFile::new(self.current_file_id, ¯o_call); @@ -120,28 +120,55 @@ impl Expander { self.resolve_path_as_macro(db, &path) }; - if let Some(call_id) = macro_call.as_call_id(db, self.crate_def_map.krate, resolver) { - let file_id = call_id.as_file(); - if let Some(node) = db.parse_or_expand(file_id) { - if let Some(expr) = T::cast(node) { - log::debug!("macro expansion {:#?}", expr.syntax()); - - let mark = Mark { - file_id: self.current_file_id, - ast_id_map: mem::take(&mut self.ast_id_map), - bomb: DropBomb::new("expansion mark dropped"), - }; - self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id); - self.current_file_id = file_id; - self.ast_id_map = db.ast_id_map(file_id); - return Some((mark, expr)); + let call_id = match macro_call.as_call_id(db, self.crate_def_map.krate, resolver) { + Some(it) => it, + None => { + // FIXME: this can mean other things too, but `as_call_id` doesn't provide enough + // info. + return ExpandResult::only_err(mbe::ExpandError::Other( + "failed to parse or resolve macro invocation".into(), + )); + } + }; + + let err = db.macro_expand_error(call_id); + + let file_id = call_id.as_file(); + + let raw_node = match db.parse_or_expand(file_id) { + Some(it) => it, + None => { + // Only `None` if the macro expansion produced no usable AST. + if err.is_none() { + log::warn!("no error despite `parse_or_expand` failing"); } + + return ExpandResult::only_err(err.unwrap_or_else(|| { + mbe::ExpandError::Other("failed to parse macro invocation".into()) + })); } - } + }; + + let node = match T::cast(raw_node) { + Some(it) => it, + None => { + // This can happen without being an error, so only forward previous errors. + return ExpandResult { value: None, err }; + } + }; + + log::debug!("macro expansion {:#?}", node.syntax()); + + let mark = Mark { + file_id: self.current_file_id, + ast_id_map: mem::take(&mut self.ast_id_map), + bomb: DropBomb::new("expansion mark dropped"), + }; + self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id); + self.current_file_id = file_id; + self.ast_id_map = db.ast_id_map(file_id); - // FIXME: Instead of just dropping the error from expansion - // report it - None + ExpandResult { value: Some((mark, node)), err } } pub(crate) fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) { diff --git a/crates/hir_def/src/body/diagnostics.rs b/crates/hir_def/src/body/diagnostics.rs index e57bdc133..144c61b17 100644 --- a/crates/hir_def/src/body/diagnostics.rs +++ b/crates/hir_def/src/body/diagnostics.rs @@ -2,11 +2,12 @@ use hir_expand::diagnostics::DiagnosticSink; -use crate::diagnostics::InactiveCode; +use crate::diagnostics::{InactiveCode, MacroError}; #[derive(Debug, Eq, PartialEq)] pub(crate) enum BodyDiagnostic { InactiveCode(InactiveCode), + MacroError(MacroError), } impl BodyDiagnostic { @@ -15,6 +16,9 @@ impl BodyDiagnostic { BodyDiagnostic::InactiveCode(diag) => { sink.push(diag.clone()); } + BodyDiagnostic::MacroError(diag) => { + sink.push(diag.clone()); + } } } } diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index cd7958746..c0617c1a1 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs @@ -25,7 +25,7 @@ use crate::{ body::{Body, BodySourceMap, Expander, PatPtr, SyntheticSyntax}, builtin_type::{BuiltinFloat, BuiltinInt}, db::DefDatabase, - diagnostics::InactiveCode, + diagnostics::{InactiveCode, MacroError}, expr::{ dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal, LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, @@ -561,7 +561,17 @@ impl ExprCollector<'_> { self.alloc_expr(Expr::Missing, syntax_ptr) } else { let macro_call = self.expander.to_source(AstPtr::new(&e)); - match self.expander.enter_expand(self.db, Some(&self.body.item_scope), e) { + let res = self.expander.enter_expand(self.db, Some(&self.body.item_scope), e); + + if let Some(err) = res.err { + self.source_map.diagnostics.push(BodyDiagnostic::MacroError(MacroError { + file: self.expander.current_file_id, + node: syntax_ptr.clone().into(), + message: err.to_string(), + })); + } + + match res.value { Some((mark, expansion)) => { self.source_map .expansions diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs index ff1ef0df6..146045938 100644 --- a/crates/hir_def/src/data.rs +++ b/crates/hir_def/src/data.rs @@ -257,7 +257,7 @@ fn collect_items( let root = db.parse_or_expand(file_id).unwrap(); let call = ast_id_map.get(call.ast_id).to_node(&root); - if let Some((mark, mac)) = expander.enter_expand(db, None, call) { + if let Some((mark, mac)) = expander.enter_expand(db, None, call).value { let src: InFile = expander.to_source(mac); let item_tree = db.item_tree(src.file_id); let iter = -- cgit v1.2.3