diff options
author | Jonas Schievink <[email protected]> | 2020-11-26 19:09:54 +0000 |
---|---|---|
committer | Jonas Schievink <[email protected]> | 2020-11-27 12:50:22 +0000 |
commit | 0432aa0ed7be3f41d41928499abc688a956214cf (patch) | |
tree | 64df76e5182412d9a95bc5e63ef3b1db03a5d430 /crates/hir_def | |
parent | 1b2652097183b0a285891c02eea8a7d2af03e4b3 (diff) |
Publish diagnostics for macro expansion errors
Diffstat (limited to 'crates/hir_def')
-rw-r--r-- | crates/hir_def/src/diagnostics.rs | 62 | ||||
-rw-r--r-- | crates/hir_def/src/nameres.rs | 52 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/collector.rs | 27 |
3 files changed, 137 insertions, 4 deletions
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 { | |||
127 | self | 127 | self |
128 | } | 128 | } |
129 | } | 129 | } |
130 | |||
131 | // Diagnostic: unresolved-proc-macro | ||
132 | // | ||
133 | // This diagnostic is shown when a procedural macro can not be found. This usually means that | ||
134 | // procedural macro support is simply disabled (and hence is only a weak hint instead of an error), | ||
135 | // but can also indicate project setup problems. | ||
136 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
137 | pub struct UnresolvedProcMacro { | ||
138 | pub file: HirFileId, | ||
139 | pub node: SyntaxNodePtr, | ||
140 | pub macro_name: Option<String>, | ||
141 | } | ||
142 | |||
143 | impl Diagnostic for UnresolvedProcMacro { | ||
144 | fn code(&self) -> DiagnosticCode { | ||
145 | DiagnosticCode("unresolved-proc-macro") | ||
146 | } | ||
147 | |||
148 | fn message(&self) -> String { | ||
149 | match &self.macro_name { | ||
150 | Some(name) => format!("proc macro `{}` not expanded", name), | ||
151 | None => "proc macro not expanded".to_string(), | ||
152 | } | ||
153 | } | ||
154 | |||
155 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
156 | InFile::new(self.file, self.node.clone()) | ||
157 | } | ||
158 | |||
159 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
160 | self | ||
161 | } | ||
162 | } | ||
163 | |||
164 | // Diagnostic: macro-error | ||
165 | // | ||
166 | // This diagnostic is shown for macro expansion errors. | ||
167 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
168 | pub struct MacroError { | ||
169 | pub file: HirFileId, | ||
170 | pub node: SyntaxNodePtr, | ||
171 | pub message: String, | ||
172 | } | ||
173 | |||
174 | impl Diagnostic for MacroError { | ||
175 | fn code(&self) -> DiagnosticCode { | ||
176 | DiagnosticCode("macro-error") | ||
177 | } | ||
178 | fn message(&self) -> String { | ||
179 | self.message.clone() | ||
180 | } | ||
181 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
182 | InFile::new(self.file, self.node.clone()) | ||
183 | } | ||
184 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
185 | self | ||
186 | } | ||
187 | fn is_experimental(&self) -> bool { | ||
188 | // Newly added and not very well-tested, might contain false positives. | ||
189 | true | ||
190 | } | ||
191 | } | ||
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 { | |||
286 | use cfg::{CfgExpr, CfgOptions}; | 286 | use cfg::{CfgExpr, CfgOptions}; |
287 | use hir_expand::diagnostics::DiagnosticSink; | 287 | use hir_expand::diagnostics::DiagnosticSink; |
288 | use hir_expand::hygiene::Hygiene; | 288 | use hir_expand::hygiene::Hygiene; |
289 | use hir_expand::InFile; | 289 | use hir_expand::{InFile, MacroCallKind}; |
290 | use syntax::{ast, AstPtr}; | 290 | use syntax::{ast, AstPtr, SyntaxNodePtr}; |
291 | 291 | ||
292 | use crate::path::ModPath; | 292 | use crate::path::ModPath; |
293 | use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId}; | 293 | use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId}; |
@@ -301,6 +301,10 @@ mod diagnostics { | |||
301 | UnresolvedImport { ast: AstId<ast::Use>, index: usize }, | 301 | UnresolvedImport { ast: AstId<ast::Use>, index: usize }, |
302 | 302 | ||
303 | UnconfiguredCode { ast: AstId<ast::Item>, cfg: CfgExpr, opts: CfgOptions }, | 303 | UnconfiguredCode { ast: AstId<ast::Item>, cfg: CfgExpr, opts: CfgOptions }, |
304 | |||
305 | UnresolvedProcMacro { ast: MacroCallKind }, | ||
306 | |||
307 | MacroError { ast: MacroCallKind, message: String }, | ||
304 | } | 308 | } |
305 | 309 | ||
306 | #[derive(Debug, PartialEq, Eq)] | 310 | #[derive(Debug, PartialEq, Eq)] |
@@ -348,6 +352,18 @@ mod diagnostics { | |||
348 | Self { in_module: container, kind: DiagnosticKind::UnconfiguredCode { ast, cfg, opts } } | 352 | Self { in_module: container, kind: DiagnosticKind::UnconfiguredCode { ast, cfg, opts } } |
349 | } | 353 | } |
350 | 354 | ||
355 | pub(super) fn unresolved_proc_macro(container: LocalModuleId, ast: MacroCallKind) -> Self { | ||
356 | Self { in_module: container, kind: DiagnosticKind::UnresolvedProcMacro { ast } } | ||
357 | } | ||
358 | |||
359 | pub(super) fn macro_error( | ||
360 | container: LocalModuleId, | ||
361 | ast: MacroCallKind, | ||
362 | message: String, | ||
363 | ) -> Self { | ||
364 | Self { in_module: container, kind: DiagnosticKind::MacroError { ast, message } } | ||
365 | } | ||
366 | |||
351 | pub(super) fn add_to( | 367 | pub(super) fn add_to( |
352 | &self, | 368 | &self, |
353 | db: &dyn DefDatabase, | 369 | db: &dyn DefDatabase, |
@@ -407,6 +423,38 @@ mod diagnostics { | |||
407 | opts: opts.clone(), | 423 | opts: opts.clone(), |
408 | }); | 424 | }); |
409 | } | 425 | } |
426 | |||
427 | DiagnosticKind::UnresolvedProcMacro { ast } => { | ||
428 | let (file, ast, name) = match ast { | ||
429 | MacroCallKind::FnLike(ast) => { | ||
430 | let node = ast.to_node(db.upcast()); | ||
431 | (ast.file_id, SyntaxNodePtr::from(AstPtr::new(&node)), None) | ||
432 | } | ||
433 | MacroCallKind::Attr(ast, name) => { | ||
434 | let node = ast.to_node(db.upcast()); | ||
435 | ( | ||
436 | ast.file_id, | ||
437 | SyntaxNodePtr::from(AstPtr::new(&node)), | ||
438 | Some(name.to_string()), | ||
439 | ) | ||
440 | } | ||
441 | }; | ||
442 | sink.push(UnresolvedProcMacro { file, node: ast, macro_name: name }); | ||
443 | } | ||
444 | |||
445 | DiagnosticKind::MacroError { ast, message } => { | ||
446 | let (file, ast) = match ast { | ||
447 | MacroCallKind::FnLike(ast) => { | ||
448 | let node = ast.to_node(db.upcast()); | ||
449 | (ast.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) | ||
450 | } | ||
451 | MacroCallKind::Attr(ast, _) => { | ||
452 | let node = ast.to_node(db.upcast()); | ||
453 | (ast.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) | ||
454 | } | ||
455 | }; | ||
456 | sink.push(MacroError { file, node: ast, message: message.clone() }); | ||
457 | } | ||
410 | } | 458 | } |
411 | } | 459 | } |
412 | } | 460 | } |
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; | |||
7 | 7 | ||
8 | use base_db::{CrateId, FileId, ProcMacroId}; | 8 | use base_db::{CrateId, FileId, ProcMacroId}; |
9 | use cfg::{CfgExpr, CfgOptions}; | 9 | use cfg::{CfgExpr, CfgOptions}; |
10 | use hir_expand::InFile; | ||
11 | use hir_expand::{ | 10 | use hir_expand::{ |
12 | ast_id_map::FileAstId, | 11 | ast_id_map::FileAstId, |
13 | builtin_derive::find_builtin_derive, | 12 | builtin_derive::find_builtin_derive, |
@@ -16,6 +15,7 @@ use hir_expand::{ | |||
16 | proc_macro::ProcMacroExpander, | 15 | proc_macro::ProcMacroExpander, |
17 | HirFileId, MacroCallId, MacroDefId, MacroDefKind, | 16 | HirFileId, MacroCallId, MacroDefId, MacroDefKind, |
18 | }; | 17 | }; |
18 | use hir_expand::{InFile, MacroCallLoc}; | ||
19 | use rustc_hash::{FxHashMap, FxHashSet}; | 19 | use rustc_hash::{FxHashMap, FxHashSet}; |
20 | use syntax::ast; | 20 | use syntax::ast; |
21 | use test_utils::mark; | 21 | use test_utils::mark; |
@@ -812,7 +812,30 @@ impl DefCollector<'_> { | |||
812 | log::warn!("macro expansion is too deep"); | 812 | log::warn!("macro expansion is too deep"); |
813 | return; | 813 | return; |
814 | } | 814 | } |
815 | let file_id: HirFileId = macro_call_id.as_file(); | 815 | let file_id = macro_call_id.as_file(); |
816 | |||
817 | // First, fetch the raw expansion result for purposes of error reporting. This goes through | ||
818 | // `macro_expand_error` to avoid depending on the full expansion result (to improve | ||
819 | // incrementality). | ||
820 | let err = self.db.macro_expand_error(macro_call_id); | ||
821 | if let Some(err) = err { | ||
822 | if let MacroCallId::LazyMacro(id) = macro_call_id { | ||
823 | let loc: MacroCallLoc = self.db.lookup_intern_macro(id); | ||
824 | |||
825 | let diag = match err { | ||
826 | hir_expand::ExpandError::UnresolvedProcMacro => { | ||
827 | // Missing proc macros are non-fatal, so they are handled specially. | ||
828 | DefDiagnostic::unresolved_proc_macro(module_id, loc.kind) | ||
829 | } | ||
830 | _ => DefDiagnostic::macro_error(module_id, loc.kind, err.to_string()), | ||
831 | }; | ||
832 | |||
833 | self.def_map.diagnostics.push(diag); | ||
834 | } | ||
835 | // FIXME: Handle eager macros. | ||
836 | } | ||
837 | |||
838 | // Then, fetch and process the item tree. This will reuse the expansion result from above. | ||
816 | let item_tree = self.db.item_tree(file_id); | 839 | let item_tree = self.db.item_tree(file_id); |
817 | let mod_dir = self.mod_dirs[&module_id].clone(); | 840 | let mod_dir = self.mod_dirs[&module_id].clone(); |
818 | ModCollector { | 841 | ModCollector { |