diff options
Diffstat (limited to 'crates/hir_def')
-rw-r--r-- | crates/hir_def/src/diagnostics.rs | 67 | ||||
-rw-r--r-- | crates/hir_def/src/nameres.rs | 87 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/collector.rs | 27 |
3 files changed, 176 insertions, 5 deletions
diff --git a/crates/hir_def/src/diagnostics.rs b/crates/hir_def/src/diagnostics.rs index b221b290c..c71266dc0 100644 --- a/crates/hir_def/src/diagnostics.rs +++ b/crates/hir_def/src/diagnostics.rs | |||
@@ -6,7 +6,7 @@ use stdx::format_to; | |||
6 | use cfg::{CfgExpr, CfgOptions, DnfExpr}; | 6 | use cfg::{CfgExpr, CfgOptions, DnfExpr}; |
7 | use hir_expand::diagnostics::{Diagnostic, DiagnosticCode, DiagnosticSink}; | 7 | use hir_expand::diagnostics::{Diagnostic, DiagnosticCode, DiagnosticSink}; |
8 | use hir_expand::{HirFileId, InFile}; | 8 | use hir_expand::{HirFileId, InFile}; |
9 | use syntax::{ast, AstPtr, SyntaxNodePtr}; | 9 | use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange}; |
10 | 10 | ||
11 | use crate::{db::DefDatabase, DefWithBodyId}; | 11 | use crate::{db::DefDatabase, DefWithBodyId}; |
12 | 12 | ||
@@ -127,3 +127,68 @@ 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 | /// If the diagnostic can be pinpointed more accurately than via `node`, this is the `TextRange` | ||
141 | /// to use instead. | ||
142 | pub precise_location: Option<TextRange>, | ||
143 | pub macro_name: Option<String>, | ||
144 | } | ||
145 | |||
146 | impl Diagnostic for UnresolvedProcMacro { | ||
147 | fn code(&self) -> DiagnosticCode { | ||
148 | DiagnosticCode("unresolved-proc-macro") | ||
149 | } | ||
150 | |||
151 | fn message(&self) -> String { | ||
152 | match &self.macro_name { | ||
153 | Some(name) => format!("proc macro `{}` not expanded", name), | ||
154 | None => "proc macro not expanded".to_string(), | ||
155 | } | ||
156 | } | ||
157 | |||
158 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
159 | InFile::new(self.file, self.node.clone()) | ||
160 | } | ||
161 | |||
162 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
163 | self | ||
164 | } | ||
165 | } | ||
166 | |||
167 | // Diagnostic: macro-error | ||
168 | // | ||
169 | // This diagnostic is shown for macro expansion errors. | ||
170 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
171 | pub struct MacroError { | ||
172 | pub file: HirFileId, | ||
173 | pub node: SyntaxNodePtr, | ||
174 | pub message: String, | ||
175 | } | ||
176 | |||
177 | impl Diagnostic for MacroError { | ||
178 | fn code(&self) -> DiagnosticCode { | ||
179 | DiagnosticCode("macro-error") | ||
180 | } | ||
181 | fn message(&self) -> String { | ||
182 | self.message.clone() | ||
183 | } | ||
184 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
185 | InFile::new(self.file, self.node.clone()) | ||
186 | } | ||
187 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
188 | self | ||
189 | } | ||
190 | fn is_experimental(&self) -> bool { | ||
191 | // Newly added and not very well-tested, might contain false positives. | ||
192 | true | ||
193 | } | ||
194 | } | ||
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs index 202a7dcb6..ffd0381d4 100644 --- a/crates/hir_def/src/nameres.rs +++ b/crates/hir_def/src/nameres.rs | |||
@@ -286,8 +286,9 @@ 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::AttrsOwner; |
291 | use syntax::{ast, AstNode, AstPtr, SyntaxKind, SyntaxNodePtr}; | ||
291 | 292 | ||
292 | use crate::path::ModPath; | 293 | use crate::path::ModPath; |
293 | use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId}; | 294 | use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId}; |
@@ -301,6 +302,10 @@ mod diagnostics { | |||
301 | UnresolvedImport { ast: AstId<ast::Use>, index: usize }, | 302 | UnresolvedImport { ast: AstId<ast::Use>, index: usize }, |
302 | 303 | ||
303 | UnconfiguredCode { ast: AstId<ast::Item>, cfg: CfgExpr, opts: CfgOptions }, | 304 | UnconfiguredCode { ast: AstId<ast::Item>, cfg: CfgExpr, opts: CfgOptions }, |
305 | |||
306 | UnresolvedProcMacro { ast: MacroCallKind }, | ||
307 | |||
308 | MacroError { ast: MacroCallKind, message: String }, | ||
304 | } | 309 | } |
305 | 310 | ||
306 | #[derive(Debug, PartialEq, Eq)] | 311 | #[derive(Debug, PartialEq, Eq)] |
@@ -348,6 +353,18 @@ mod diagnostics { | |||
348 | Self { in_module: container, kind: DiagnosticKind::UnconfiguredCode { ast, cfg, opts } } | 353 | Self { in_module: container, kind: DiagnosticKind::UnconfiguredCode { ast, cfg, opts } } |
349 | } | 354 | } |
350 | 355 | ||
356 | pub(super) fn unresolved_proc_macro(container: LocalModuleId, ast: MacroCallKind) -> Self { | ||
357 | Self { in_module: container, kind: DiagnosticKind::UnresolvedProcMacro { ast } } | ||
358 | } | ||
359 | |||
360 | pub(super) fn macro_error( | ||
361 | container: LocalModuleId, | ||
362 | ast: MacroCallKind, | ||
363 | message: String, | ||
364 | ) -> Self { | ||
365 | Self { in_module: container, kind: DiagnosticKind::MacroError { ast, message } } | ||
366 | } | ||
367 | |||
351 | pub(super) fn add_to( | 368 | pub(super) fn add_to( |
352 | &self, | 369 | &self, |
353 | db: &dyn DefDatabase, | 370 | db: &dyn DefDatabase, |
@@ -407,6 +424,72 @@ mod diagnostics { | |||
407 | opts: opts.clone(), | 424 | opts: opts.clone(), |
408 | }); | 425 | }); |
409 | } | 426 | } |
427 | |||
428 | DiagnosticKind::UnresolvedProcMacro { ast } => { | ||
429 | let mut precise_location = None; | ||
430 | let (file, ast, name) = match ast { | ||
431 | MacroCallKind::FnLike(ast) => { | ||
432 | let node = ast.to_node(db.upcast()); | ||
433 | (ast.file_id, SyntaxNodePtr::from(AstPtr::new(&node)), None) | ||
434 | } | ||
435 | MacroCallKind::Attr(ast, name) => { | ||
436 | let node = ast.to_node(db.upcast()); | ||
437 | |||
438 | // Compute the precise location of the macro name's token in the derive | ||
439 | // list. | ||
440 | // FIXME: This does not handle paths to the macro, but neither does the | ||
441 | // rest of r-a. | ||
442 | let derive_attrs = | ||
443 | node.attrs().filter_map(|attr| match attr.as_simple_call() { | ||
444 | Some((name, args)) if name == "derive" => Some(args), | ||
445 | _ => None, | ||
446 | }); | ||
447 | 'outer: for attr in derive_attrs { | ||
448 | let tokens = | ||
449 | attr.syntax().children_with_tokens().filter_map(|elem| { | ||
450 | match elem { | ||
451 | syntax::NodeOrToken::Node(_) => None, | ||
452 | syntax::NodeOrToken::Token(tok) => Some(tok), | ||
453 | } | ||
454 | }); | ||
455 | for token in tokens { | ||
456 | if token.kind() == SyntaxKind::IDENT | ||
457 | && token.to_string() == *name | ||
458 | { | ||
459 | precise_location = Some(token.text_range()); | ||
460 | break 'outer; | ||
461 | } | ||
462 | } | ||
463 | } | ||
464 | |||
465 | ( | ||
466 | ast.file_id, | ||
467 | SyntaxNodePtr::from(AstPtr::new(&node)), | ||
468 | Some(name.clone()), | ||
469 | ) | ||
470 | } | ||
471 | }; | ||
472 | sink.push(UnresolvedProcMacro { | ||
473 | file, | ||
474 | node: ast, | ||
475 | precise_location, | ||
476 | macro_name: name, | ||
477 | }); | ||
478 | } | ||
479 | |||
480 | DiagnosticKind::MacroError { ast, message } => { | ||
481 | let (file, ast) = match ast { | ||
482 | MacroCallKind::FnLike(ast) => { | ||
483 | let node = ast.to_node(db.upcast()); | ||
484 | (ast.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) | ||
485 | } | ||
486 | MacroCallKind::Attr(ast, _) => { | ||
487 | let node = ast.to_node(db.upcast()); | ||
488 | (ast.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) | ||
489 | } | ||
490 | }; | ||
491 | sink.push(MacroError { file, node: ast, message: message.clone() }); | ||
492 | } | ||
410 | } | 493 | } |
411 | } | 494 | } |
412 | } | 495 | } |
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 { |