From 0432aa0ed7be3f41d41928499abc688a956214cf Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Thu, 26 Nov 2020 20:09:54 +0100 Subject: Publish diagnostics for macro expansion errors --- crates/hir_def/src/diagnostics.rs | 62 +++++++++++++++++++++++++++++++++ crates/hir_def/src/nameres.rs | 52 +++++++++++++++++++++++++-- crates/hir_def/src/nameres/collector.rs | 27 ++++++++++++-- 3 files changed, 137 insertions(+), 4 deletions(-) (limited to 'crates/hir_def') diff --git a/crates/hir_def/src/diagnostics.rs b/crates/hir_def/src/diagnostics.rs index b221b290c..dd06e3f20 100644 --- a/crates/hir_def/src/diagnostics.rs +++ b/crates/hir_def/src/diagnostics.rs @@ -127,3 +127,65 @@ impl Diagnostic for InactiveCode { self } } + +// Diagnostic: unresolved-proc-macro +// +// This diagnostic is shown when a procedural macro can not be found. This usually means that +// procedural macro support is simply disabled (and hence is only a weak hint instead of an error), +// but can also indicate project setup problems. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct UnresolvedProcMacro { + pub file: HirFileId, + pub node: SyntaxNodePtr, + pub macro_name: Option, +} + +impl Diagnostic for UnresolvedProcMacro { + fn code(&self) -> DiagnosticCode { + DiagnosticCode("unresolved-proc-macro") + } + + fn message(&self) -> String { + match &self.macro_name { + Some(name) => format!("proc macro `{}` not expanded", name), + None => "proc macro not expanded".to_string(), + } + } + + fn display_source(&self) -> InFile { + InFile::new(self.file, self.node.clone()) + } + + fn as_any(&self) -> &(dyn Any + Send + 'static) { + self + } +} + +// Diagnostic: macro-error +// +// This diagnostic is shown for macro expansion errors. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct MacroError { + pub file: HirFileId, + pub node: SyntaxNodePtr, + pub message: String, +} + +impl Diagnostic for MacroError { + fn code(&self) -> DiagnosticCode { + DiagnosticCode("macro-error") + } + fn message(&self) -> String { + self.message.clone() + } + fn display_source(&self) -> InFile { + InFile::new(self.file, self.node.clone()) + } + fn as_any(&self) -> &(dyn Any + Send + 'static) { + self + } + fn is_experimental(&self) -> bool { + // Newly added and not very well-tested, might contain false positives. + true + } +} diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs index 202a7dcb6..3d65a46bf 100644 --- a/crates/hir_def/src/nameres.rs +++ b/crates/hir_def/src/nameres.rs @@ -286,8 +286,8 @@ mod diagnostics { use cfg::{CfgExpr, CfgOptions}; use hir_expand::diagnostics::DiagnosticSink; use hir_expand::hygiene::Hygiene; - use hir_expand::InFile; - use syntax::{ast, AstPtr}; + use hir_expand::{InFile, MacroCallKind}; + use syntax::{ast, AstPtr, SyntaxNodePtr}; use crate::path::ModPath; use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId}; @@ -301,6 +301,10 @@ mod diagnostics { UnresolvedImport { ast: AstId, index: usize }, UnconfiguredCode { ast: AstId, cfg: CfgExpr, opts: CfgOptions }, + + UnresolvedProcMacro { ast: MacroCallKind }, + + MacroError { ast: MacroCallKind, message: String }, } #[derive(Debug, PartialEq, Eq)] @@ -348,6 +352,18 @@ mod diagnostics { Self { in_module: container, kind: DiagnosticKind::UnconfiguredCode { ast, cfg, opts } } } + pub(super) fn unresolved_proc_macro(container: LocalModuleId, ast: MacroCallKind) -> Self { + Self { in_module: container, kind: DiagnosticKind::UnresolvedProcMacro { ast } } + } + + pub(super) fn macro_error( + container: LocalModuleId, + ast: MacroCallKind, + message: String, + ) -> Self { + Self { in_module: container, kind: DiagnosticKind::MacroError { ast, message } } + } + pub(super) fn add_to( &self, db: &dyn DefDatabase, @@ -407,6 +423,38 @@ mod diagnostics { opts: opts.clone(), }); } + + DiagnosticKind::UnresolvedProcMacro { ast } => { + let (file, ast, name) = match ast { + MacroCallKind::FnLike(ast) => { + let node = ast.to_node(db.upcast()); + (ast.file_id, SyntaxNodePtr::from(AstPtr::new(&node)), None) + } + MacroCallKind::Attr(ast, name) => { + let node = ast.to_node(db.upcast()); + ( + ast.file_id, + SyntaxNodePtr::from(AstPtr::new(&node)), + Some(name.to_string()), + ) + } + }; + sink.push(UnresolvedProcMacro { file, node: ast, macro_name: name }); + } + + DiagnosticKind::MacroError { ast, message } => { + let (file, ast) = match ast { + MacroCallKind::FnLike(ast) => { + let node = ast.to_node(db.upcast()); + (ast.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) + } + MacroCallKind::Attr(ast, _) => { + let node = ast.to_node(db.upcast()); + (ast.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) + } + }; + sink.push(MacroError { file, node: ast, message: message.clone() }); + } } } } diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 5ed9073e0..19cd713ba 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs @@ -7,7 +7,6 @@ use std::iter; use base_db::{CrateId, FileId, ProcMacroId}; use cfg::{CfgExpr, CfgOptions}; -use hir_expand::InFile; use hir_expand::{ ast_id_map::FileAstId, builtin_derive::find_builtin_derive, @@ -16,6 +15,7 @@ use hir_expand::{ proc_macro::ProcMacroExpander, HirFileId, MacroCallId, MacroDefId, MacroDefKind, }; +use hir_expand::{InFile, MacroCallLoc}; use rustc_hash::{FxHashMap, FxHashSet}; use syntax::ast; use test_utils::mark; @@ -812,7 +812,30 @@ impl DefCollector<'_> { log::warn!("macro expansion is too deep"); return; } - let file_id: HirFileId = macro_call_id.as_file(); + let file_id = macro_call_id.as_file(); + + // First, fetch the raw expansion result for purposes of error reporting. This goes through + // `macro_expand_error` to avoid depending on the full expansion result (to improve + // incrementality). + let err = self.db.macro_expand_error(macro_call_id); + if let Some(err) = err { + if let MacroCallId::LazyMacro(id) = macro_call_id { + let loc: MacroCallLoc = self.db.lookup_intern_macro(id); + + let diag = match err { + hir_expand::ExpandError::UnresolvedProcMacro => { + // Missing proc macros are non-fatal, so they are handled specially. + DefDiagnostic::unresolved_proc_macro(module_id, loc.kind) + } + _ => DefDiagnostic::macro_error(module_id, loc.kind, err.to_string()), + }; + + self.def_map.diagnostics.push(diag); + } + // FIXME: Handle eager macros. + } + + // Then, fetch and process the item tree. This will reuse the expansion result from above. let item_tree = self.db.item_tree(file_id); let mod_dir = self.mod_dirs[&module_id].clone(); ModCollector { -- cgit v1.2.3