aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/hir/src/diagnostics.rs2
-rw-r--r--crates/hir_def/src/diagnostics.rs67
-rw-r--r--crates/hir_def/src/nameres.rs87
-rw-r--r--crates/hir_def/src/nameres/collector.rs27
-rw-r--r--crates/hir_expand/src/db.rs9
-rw-r--r--crates/hir_expand/src/lib.rs2
-rw-r--r--crates/hir_expand/src/proc_macro.rs2
-rw-r--r--crates/ide/src/diagnostics.rs9
-rw-r--r--crates/mbe/src/lib.rs2
-rw-r--r--docs/user/generated_diagnostic.adoc14
10 files changed, 212 insertions, 9 deletions
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index d9ad8db6f..eaf1a14ec 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -1,5 +1,5 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2pub use hir_def::diagnostics::{InactiveCode, UnresolvedModule}; 2pub use hir_def::diagnostics::{InactiveCode, UnresolvedModule, UnresolvedProcMacro};
3pub use hir_expand::diagnostics::{ 3pub use hir_expand::diagnostics::{
4 Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder, 4 Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder,
5}; 5};
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;
6use cfg::{CfgExpr, CfgOptions, DnfExpr}; 6use cfg::{CfgExpr, CfgOptions, DnfExpr};
7use hir_expand::diagnostics::{Diagnostic, DiagnosticCode, DiagnosticSink}; 7use hir_expand::diagnostics::{Diagnostic, DiagnosticCode, DiagnosticSink};
8use hir_expand::{HirFileId, InFile}; 8use hir_expand::{HirFileId, InFile};
9use syntax::{ast, AstPtr, SyntaxNodePtr}; 9use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
10 10
11use crate::{db::DefDatabase, DefWithBodyId}; 11use 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)]
137pub 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
146impl 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)]
171pub struct MacroError {
172 pub file: HirFileId,
173 pub node: SyntaxNodePtr,
174 pub message: String,
175}
176
177impl 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
8use base_db::{CrateId, FileId, ProcMacroId}; 8use base_db::{CrateId, FileId, ProcMacroId};
9use cfg::{CfgExpr, CfgOptions}; 9use cfg::{CfgExpr, CfgOptions};
10use hir_expand::InFile;
11use hir_expand::{ 10use 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};
18use hir_expand::{InFile, MacroCallLoc};
19use rustc_hash::{FxHashMap, FxHashSet}; 19use rustc_hash::{FxHashMap, FxHashSet};
20use syntax::ast; 20use syntax::ast;
21use test_utils::mark; 21use 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 {
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index bf16b56b1..ff50bfd82 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -3,7 +3,7 @@
3use std::sync::Arc; 3use std::sync::Arc;
4 4
5use base_db::{salsa, SourceDatabase}; 5use base_db::{salsa, SourceDatabase};
6use mbe::{ExpandResult, MacroRules}; 6use mbe::{ExpandError, ExpandResult, MacroRules};
7use parser::FragmentKind; 7use parser::FragmentKind;
8use syntax::{algo::diff, AstNode, GreenNode, Parse, SyntaxKind::*, SyntaxNode}; 8use syntax::{algo::diff, AstNode, GreenNode, Parse, SyntaxKind::*, SyntaxNode};
9 9
@@ -81,6 +81,9 @@ pub trait AstDatabase: SourceDatabase {
81 ) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>>; 81 ) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>>;
82 fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>>; 82 fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>>;
83 83
84 /// Firewall query that returns the error from the `macro_expand` query.
85 fn macro_expand_error(&self, macro_call: MacroCallId) -> Option<ExpandError>;
86
84 #[salsa::interned] 87 #[salsa::interned]
85 fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; 88 fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId;
86 89
@@ -171,6 +174,10 @@ fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult<Option<Ar
171 macro_expand_with_arg(db, id, None) 174 macro_expand_with_arg(db, id, None)
172} 175}
173 176
177fn macro_expand_error(db: &dyn AstDatabase, macro_call: MacroCallId) -> Option<ExpandError> {
178 db.macro_expand(macro_call).err
179}
180
174fn expander(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> { 181fn expander(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> {
175 let lazy_id = match id { 182 let lazy_id = match id {
176 MacroCallId::LazyMacro(id) => id, 183 MacroCallId::LazyMacro(id) => id,
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index d5ba691b7..6dad2507b 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -255,7 +255,7 @@ pub enum MacroDefKind {
255pub struct MacroCallLoc { 255pub struct MacroCallLoc {
256 pub(crate) def: MacroDefId, 256 pub(crate) def: MacroDefId,
257 pub(crate) krate: CrateId, 257 pub(crate) krate: CrateId,
258 pub(crate) kind: MacroCallKind, 258 pub kind: MacroCallKind,
259} 259}
260 260
261#[derive(Debug, Clone, PartialEq, Eq, Hash)] 261#[derive(Debug, Clone, PartialEq, Eq, Hash)]
diff --git a/crates/hir_expand/src/proc_macro.rs b/crates/hir_expand/src/proc_macro.rs
index 7505cb061..97edf0fb6 100644
--- a/crates/hir_expand/src/proc_macro.rs
+++ b/crates/hir_expand/src/proc_macro.rs
@@ -50,7 +50,7 @@ impl ProcMacroExpander {
50 50
51 proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from) 51 proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from)
52 } 52 }
53 None => Err(err!("Unresolved proc macro")), 53 None => Err(mbe::ExpandError::UnresolvedProcMacro),
54 } 54 }
55 } 55 }
56} 56}
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 3df73ed4f..9d3d88289 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -142,6 +142,15 @@ pub(crate) fn diagnostics(
142 .with_code(Some(d.code())), 142 .with_code(Some(d.code())),
143 ); 143 );
144 }) 144 })
145 .on::<hir::diagnostics::UnresolvedProcMacro, _>(|d| {
146 // Use more accurate position if available.
147 let display_range =
148 d.precise_location.unwrap_or_else(|| sema.diagnostics_display_range(d).range);
149
150 // FIXME: it would be nice to tell the user whether proc macros are currently disabled
151 res.borrow_mut()
152 .push(Diagnostic::hint(display_range, d.message()).with_code(Some(d.code())));
153 })
145 // Only collect experimental diagnostics when they're enabled. 154 // Only collect experimental diagnostics when they're enabled.
146 .filter(|diag| !(diag.is_experimental() && config.disable_experimental)) 155 .filter(|diag| !(diag.is_experimental() && config.disable_experimental))
147 .filter(|diag| !config.disabled.contains(diag.code().as_str())); 156 .filter(|diag| !config.disabled.contains(diag.code().as_str()));
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs
index 2d0763c47..3ad609a00 100644
--- a/crates/mbe/src/lib.rs
+++ b/crates/mbe/src/lib.rs
@@ -35,6 +35,7 @@ pub enum ExpandError {
35 ConversionError, 35 ConversionError,
36 InvalidRepeat, 36 InvalidRepeat,
37 ProcMacroError(tt::ExpansionError), 37 ProcMacroError(tt::ExpansionError),
38 UnresolvedProcMacro,
38 Other(String), 39 Other(String),
39} 40}
40 41
@@ -53,6 +54,7 @@ impl fmt::Display for ExpandError {
53 ExpandError::ConversionError => f.write_str("could not convert tokens"), 54 ExpandError::ConversionError => f.write_str("could not convert tokens"),
54 ExpandError::InvalidRepeat => f.write_str("invalid repeat expression"), 55 ExpandError::InvalidRepeat => f.write_str("invalid repeat expression"),
55 ExpandError::ProcMacroError(e) => e.fmt(f), 56 ExpandError::ProcMacroError(e) => e.fmt(f),
57 ExpandError::UnresolvedProcMacro => f.write_str("unresolved proc macro"),
56 ExpandError::Other(e) => f.write_str(e), 58 ExpandError::Other(e) => f.write_str(e),
57 } 59 }
58 } 60 }
diff --git a/docs/user/generated_diagnostic.adoc b/docs/user/generated_diagnostic.adoc
index 34c4f98a3..ec8581a03 100644
--- a/docs/user/generated_diagnostic.adoc
+++ b/docs/user/generated_diagnostic.adoc
@@ -17,6 +17,12 @@ This diagnostic is shown for code with inactive `#[cfg]` attributes.
17This diagnostic is triggered if item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention]. 17This diagnostic is triggered if item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention].
18 18
19 19
20=== macro-error
21**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/hir_def/src/diagnostics.rs#L167[diagnostics.rs]
22
23This diagnostic is shown for macro expansion errors.
24
25
20=== mismatched-arg-count 26=== mismatched-arg-count
21**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/hir_ty/src/diagnostics.rs#L267[diagnostics.rs] 27**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/hir_ty/src/diagnostics.rs#L267[diagnostics.rs]
22 28
@@ -103,3 +109,11 @@ This diagnostic is triggered if rust-analyzer is unable to discover imported mod
103**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/hir_def/src/diagnostics.rs#L18[diagnostics.rs] 109**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/hir_def/src/diagnostics.rs#L18[diagnostics.rs]
104 110
105This diagnostic is triggered if rust-analyzer is unable to discover referred module. 111This diagnostic is triggered if rust-analyzer is unable to discover referred module.
112
113
114=== unresolved-proc-macro
115**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/hir_def/src/diagnostics.rs#L131[diagnostics.rs]
116
117This diagnostic is shown when a procedural macro can not be found. This usually means that
118procedural macro support is simply disabled (and hence is only a weak hint instead of an error),
119but can also indicate project setup problems.