mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-09-21 15:02:03 +00:00
Publish diagnostics for macro expansion errors
This commit is contained in:
parent
1b26520971
commit
0432aa0ed7
8 changed files with 168 additions and 7 deletions
|
@ -1,5 +1,5 @@
|
||||||
//! FIXME: write short doc here
|
//! FIXME: write short doc here
|
||||||
pub use hir_def::diagnostics::{InactiveCode, UnresolvedModule};
|
pub use hir_def::diagnostics::{InactiveCode, UnresolvedModule, UnresolvedProcMacro};
|
||||||
pub use hir_expand::diagnostics::{
|
pub use hir_expand::diagnostics::{
|
||||||
Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder,
|
Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder,
|
||||||
};
|
};
|
||||||
|
|
|
@ -127,3 +127,65 @@ impl Diagnostic for InactiveCode {
|
||||||
self
|
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<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<SyntaxNodePtr> {
|
||||||
|
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<SyntaxNodePtr> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -286,8 +286,8 @@ mod diagnostics {
|
||||||
use cfg::{CfgExpr, CfgOptions};
|
use cfg::{CfgExpr, CfgOptions};
|
||||||
use hir_expand::diagnostics::DiagnosticSink;
|
use hir_expand::diagnostics::DiagnosticSink;
|
||||||
use hir_expand::hygiene::Hygiene;
|
use hir_expand::hygiene::Hygiene;
|
||||||
use hir_expand::InFile;
|
use hir_expand::{InFile, MacroCallKind};
|
||||||
use syntax::{ast, AstPtr};
|
use syntax::{ast, AstPtr, SyntaxNodePtr};
|
||||||
|
|
||||||
use crate::path::ModPath;
|
use crate::path::ModPath;
|
||||||
use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId};
|
use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId};
|
||||||
|
@ -301,6 +301,10 @@ mod diagnostics {
|
||||||
UnresolvedImport { ast: AstId<ast::Use>, index: usize },
|
UnresolvedImport { ast: AstId<ast::Use>, index: usize },
|
||||||
|
|
||||||
UnconfiguredCode { ast: AstId<ast::Item>, cfg: CfgExpr, opts: CfgOptions },
|
UnconfiguredCode { ast: AstId<ast::Item>, cfg: CfgExpr, opts: CfgOptions },
|
||||||
|
|
||||||
|
UnresolvedProcMacro { ast: MacroCallKind },
|
||||||
|
|
||||||
|
MacroError { ast: MacroCallKind, message: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
@ -348,6 +352,18 @@ mod diagnostics {
|
||||||
Self { in_module: container, kind: DiagnosticKind::UnconfiguredCode { ast, cfg, opts } }
|
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(
|
pub(super) fn add_to(
|
||||||
&self,
|
&self,
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
|
@ -407,6 +423,38 @@ mod diagnostics {
|
||||||
opts: opts.clone(),
|
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() });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ use std::iter;
|
||||||
|
|
||||||
use base_db::{CrateId, FileId, ProcMacroId};
|
use base_db::{CrateId, FileId, ProcMacroId};
|
||||||
use cfg::{CfgExpr, CfgOptions};
|
use cfg::{CfgExpr, CfgOptions};
|
||||||
use hir_expand::InFile;
|
|
||||||
use hir_expand::{
|
use hir_expand::{
|
||||||
ast_id_map::FileAstId,
|
ast_id_map::FileAstId,
|
||||||
builtin_derive::find_builtin_derive,
|
builtin_derive::find_builtin_derive,
|
||||||
|
@ -16,6 +15,7 @@ use hir_expand::{
|
||||||
proc_macro::ProcMacroExpander,
|
proc_macro::ProcMacroExpander,
|
||||||
HirFileId, MacroCallId, MacroDefId, MacroDefKind,
|
HirFileId, MacroCallId, MacroDefId, MacroDefKind,
|
||||||
};
|
};
|
||||||
|
use hir_expand::{InFile, MacroCallLoc};
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
use test_utils::mark;
|
use test_utils::mark;
|
||||||
|
@ -812,7 +812,30 @@ impl DefCollector<'_> {
|
||||||
log::warn!("macro expansion is too deep");
|
log::warn!("macro expansion is too deep");
|
||||||
return;
|
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 item_tree = self.db.item_tree(file_id);
|
||||||
let mod_dir = self.mod_dirs[&module_id].clone();
|
let mod_dir = self.mod_dirs[&module_id].clone();
|
||||||
ModCollector {
|
ModCollector {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use base_db::{salsa, SourceDatabase};
|
use base_db::{salsa, SourceDatabase};
|
||||||
use mbe::{ExpandResult, MacroRules};
|
use mbe::{ExpandError, ExpandResult, MacroRules};
|
||||||
use parser::FragmentKind;
|
use parser::FragmentKind;
|
||||||
use syntax::{algo::diff, AstNode, GreenNode, Parse, SyntaxKind::*, SyntaxNode};
|
use syntax::{algo::diff, AstNode, GreenNode, Parse, SyntaxKind::*, SyntaxNode};
|
||||||
|
|
||||||
|
@ -81,6 +81,9 @@ pub trait AstDatabase: SourceDatabase {
|
||||||
) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>>;
|
) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>>;
|
||||||
fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>>;
|
fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>>;
|
||||||
|
|
||||||
|
/// Firewall query that returns the error from the `macro_expand` query.
|
||||||
|
fn macro_expand_error(&self, macro_call: MacroCallId) -> Option<ExpandError>;
|
||||||
|
|
||||||
#[salsa::interned]
|
#[salsa::interned]
|
||||||
fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId;
|
fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId;
|
||||||
|
|
||||||
|
@ -171,6 +174,10 @@ fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult<Option<Ar
|
||||||
macro_expand_with_arg(db, id, None)
|
macro_expand_with_arg(db, id, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn macro_expand_error(db: &dyn AstDatabase, macro_call: MacroCallId) -> Option<ExpandError> {
|
||||||
|
db.macro_expand(macro_call).err
|
||||||
|
}
|
||||||
|
|
||||||
fn expander(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> {
|
fn expander(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> {
|
||||||
let lazy_id = match id {
|
let lazy_id = match id {
|
||||||
MacroCallId::LazyMacro(id) => id,
|
MacroCallId::LazyMacro(id) => id,
|
||||||
|
|
|
@ -255,7 +255,7 @@ pub enum MacroDefKind {
|
||||||
pub struct MacroCallLoc {
|
pub struct MacroCallLoc {
|
||||||
pub(crate) def: MacroDefId,
|
pub(crate) def: MacroDefId,
|
||||||
pub(crate) krate: CrateId,
|
pub(crate) krate: CrateId,
|
||||||
pub(crate) kind: MacroCallKind,
|
pub kind: MacroCallKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
|
|
@ -142,6 +142,13 @@ pub(crate) fn diagnostics(
|
||||||
.with_code(Some(d.code())),
|
.with_code(Some(d.code())),
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
.on::<hir::diagnostics::UnresolvedProcMacro, _>(|d| {
|
||||||
|
// FIXME: it would be nice to tell the user whether proc macros are currently disabled
|
||||||
|
res.borrow_mut().push(
|
||||||
|
Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message())
|
||||||
|
.with_code(Some(d.code())),
|
||||||
|
);
|
||||||
|
})
|
||||||
// Only collect experimental diagnostics when they're enabled.
|
// Only collect experimental diagnostics when they're enabled.
|
||||||
.filter(|diag| !(diag.is_experimental() && config.disable_experimental))
|
.filter(|diag| !(diag.is_experimental() && config.disable_experimental))
|
||||||
.filter(|diag| !config.disabled.contains(diag.code().as_str()));
|
.filter(|diag| !config.disabled.contains(diag.code().as_str()));
|
||||||
|
|
|
@ -17,6 +17,12 @@ This diagnostic is shown for code with inactive `#[cfg]` attributes.
|
||||||
This 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].
|
This 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].
|
||||||
|
|
||||||
|
|
||||||
|
=== macro-error
|
||||||
|
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/hir_def/src/diagnostics.rs#L164[diagnostics.rs]
|
||||||
|
|
||||||
|
This diagnostic is shown for macro expansion errors.
|
||||||
|
|
||||||
|
|
||||||
=== mismatched-arg-count
|
=== mismatched-arg-count
|
||||||
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/hir_ty/src/diagnostics.rs#L267[diagnostics.rs]
|
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/hir_ty/src/diagnostics.rs#L267[diagnostics.rs]
|
||||||
|
|
||||||
|
@ -103,3 +109,11 @@ This diagnostic is triggered if rust-analyzer is unable to discover imported mod
|
||||||
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/hir_def/src/diagnostics.rs#L18[diagnostics.rs]
|
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/hir_def/src/diagnostics.rs#L18[diagnostics.rs]
|
||||||
|
|
||||||
This diagnostic is triggered if rust-analyzer is unable to discover referred module.
|
This diagnostic is triggered if rust-analyzer is unable to discover referred module.
|
||||||
|
|
||||||
|
|
||||||
|
=== unresolved-proc-macro
|
||||||
|
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/hir_def/src/diagnostics.rs#L131[diagnostics.rs]
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
Loading…
Reference in a new issue