Merge #8973
8973: internal: move diagnostics to hir r=matklad a=matklad Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
commit
5587d0a3e3
@ -3,13 +3,227 @@
|
||||
//!
|
||||
//! This probably isn't the best way to do this -- ideally, diagnistics should
|
||||
//! be expressed in terms of hir types themselves.
|
||||
pub use hir_def::diagnostics::{
|
||||
InactiveCode, UnresolvedMacroCall, UnresolvedModule, UnresolvedProcMacro,
|
||||
};
|
||||
pub use hir_expand::diagnostics::{
|
||||
Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder,
|
||||
};
|
||||
pub use hir_ty::diagnostics::{
|
||||
IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
|
||||
NoSuchField, RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap,
|
||||
use std::any::Any;
|
||||
|
||||
use cfg::{CfgExpr, CfgOptions, DnfExpr};
|
||||
use hir_def::path::ModPath;
|
||||
use hir_expand::{HirFileId, InFile};
|
||||
use stdx::format_to;
|
||||
use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
|
||||
|
||||
pub use hir_ty::{
|
||||
diagnostics::{
|
||||
IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms,
|
||||
MissingOkOrSomeInTailExpr, NoSuchField, RemoveThisSemicolon,
|
||||
ReplaceFilterMapNextWithFindMap,
|
||||
},
|
||||
diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder},
|
||||
};
|
||||
|
||||
// Diagnostic: unresolved-module
|
||||
//
|
||||
// This diagnostic is triggered if rust-analyzer is unable to discover referred module.
|
||||
#[derive(Debug)]
|
||||
pub struct UnresolvedModule {
|
||||
pub file: HirFileId,
|
||||
pub decl: AstPtr<ast::Module>,
|
||||
pub candidate: String,
|
||||
}
|
||||
|
||||
impl Diagnostic for UnresolvedModule {
|
||||
fn code(&self) -> DiagnosticCode {
|
||||
DiagnosticCode("unresolved-module")
|
||||
}
|
||||
fn message(&self) -> String {
|
||||
"unresolved module".to_string()
|
||||
}
|
||||
fn display_source(&self) -> InFile<SyntaxNodePtr> {
|
||||
InFile::new(self.file, self.decl.clone().into())
|
||||
}
|
||||
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// Diagnostic: unresolved-extern-crate
|
||||
//
|
||||
// This diagnostic is triggered if rust-analyzer is unable to discover referred extern crate.
|
||||
#[derive(Debug)]
|
||||
pub struct UnresolvedExternCrate {
|
||||
pub file: HirFileId,
|
||||
pub item: AstPtr<ast::ExternCrate>,
|
||||
}
|
||||
|
||||
impl Diagnostic for UnresolvedExternCrate {
|
||||
fn code(&self) -> DiagnosticCode {
|
||||
DiagnosticCode("unresolved-extern-crate")
|
||||
}
|
||||
fn message(&self) -> String {
|
||||
"unresolved extern crate".to_string()
|
||||
}
|
||||
fn display_source(&self) -> InFile<SyntaxNodePtr> {
|
||||
InFile::new(self.file, self.item.clone().into())
|
||||
}
|
||||
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UnresolvedImport {
|
||||
pub file: HirFileId,
|
||||
pub node: AstPtr<ast::UseTree>,
|
||||
}
|
||||
|
||||
impl Diagnostic for UnresolvedImport {
|
||||
fn code(&self) -> DiagnosticCode {
|
||||
DiagnosticCode("unresolved-import")
|
||||
}
|
||||
fn message(&self) -> String {
|
||||
"unresolved import".to_string()
|
||||
}
|
||||
fn display_source(&self) -> InFile<SyntaxNodePtr> {
|
||||
InFile::new(self.file, self.node.clone().into())
|
||||
}
|
||||
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
||||
self
|
||||
}
|
||||
fn is_experimental(&self) -> bool {
|
||||
// This currently results in false positives in the following cases:
|
||||
// - `cfg_if!`-generated code in libstd (we don't load the sysroot correctly)
|
||||
// - `core::arch` (we don't handle `#[path = "../<path>"]` correctly)
|
||||
// - proc macros and/or proc macro generated code
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
// Diagnostic: unresolved-macro-call
|
||||
//
|
||||
// This diagnostic is triggered if rust-analyzer is unable to resolve the path to a
|
||||
// macro in a macro invocation.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct UnresolvedMacroCall {
|
||||
pub file: HirFileId,
|
||||
pub node: AstPtr<ast::MacroCall>,
|
||||
pub path: ModPath,
|
||||
}
|
||||
|
||||
impl Diagnostic for UnresolvedMacroCall {
|
||||
fn code(&self) -> DiagnosticCode {
|
||||
DiagnosticCode("unresolved-macro-call")
|
||||
}
|
||||
fn message(&self) -> String {
|
||||
format!("unresolved macro `{}!`", self.path)
|
||||
}
|
||||
fn display_source(&self) -> InFile<SyntaxNodePtr> {
|
||||
InFile::new(self.file, self.node.clone().into())
|
||||
}
|
||||
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
||||
self
|
||||
}
|
||||
fn is_experimental(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
// Diagnostic: inactive-code
|
||||
//
|
||||
// This diagnostic is shown for code with inactive `#[cfg]` attributes.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct InactiveCode {
|
||||
pub file: HirFileId,
|
||||
pub node: SyntaxNodePtr,
|
||||
pub cfg: CfgExpr,
|
||||
pub opts: CfgOptions,
|
||||
}
|
||||
|
||||
impl Diagnostic for InactiveCode {
|
||||
fn code(&self) -> DiagnosticCode {
|
||||
DiagnosticCode("inactive-code")
|
||||
}
|
||||
fn message(&self) -> String {
|
||||
let inactive = DnfExpr::new(self.cfg.clone()).why_inactive(&self.opts);
|
||||
let mut buf = "code is inactive due to #[cfg] directives".to_string();
|
||||
|
||||
if let Some(inactive) = inactive {
|
||||
format_to!(buf, ": {}", inactive);
|
||||
}
|
||||
|
||||
buf
|
||||
}
|
||||
fn display_source(&self) -> InFile<SyntaxNodePtr> {
|
||||
InFile::new(self.file, self.node.clone())
|
||||
}
|
||||
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
||||
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.
|
||||
//
|
||||
// If you are seeing a lot of "proc macro not expanded" warnings, you can add this option to the
|
||||
// `rust-analyzer.diagnostics.disabled` list to prevent them from showing. Alternatively you can
|
||||
// enable support for procedural macros (see `rust-analyzer.procMacro.enable`).
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct UnresolvedProcMacro {
|
||||
pub file: HirFileId,
|
||||
pub node: SyntaxNodePtr,
|
||||
/// If the diagnostic can be pinpointed more accurately than via `node`, this is the `TextRange`
|
||||
/// to use instead.
|
||||
pub precise_location: Option<TextRange>,
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -35,12 +35,18 @@ use std::{iter, sync::Arc};
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
use base_db::{CrateDisplayName, CrateId, Edition, FileId};
|
||||
use diagnostics::{
|
||||
InactiveCode, MacroError, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall,
|
||||
UnresolvedModule, UnresolvedProcMacro,
|
||||
};
|
||||
use either::Either;
|
||||
use hir_def::{
|
||||
adt::{ReprKind, VariantData},
|
||||
body::BodyDiagnostic,
|
||||
expr::{BindingAnnotation, LabelId, Pat, PatId},
|
||||
item_tree::ItemTreeNode,
|
||||
lang_item::LangItemTarget,
|
||||
nameres,
|
||||
per_ns::PerNs,
|
||||
resolver::{HasResolver, Resolver},
|
||||
src::HasSource as _,
|
||||
@ -50,11 +56,12 @@ use hir_def::{
|
||||
LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId,
|
||||
TypeParamId, UnionId,
|
||||
};
|
||||
use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind};
|
||||
use hir_expand::{name::name, MacroCallKind, MacroDefKind};
|
||||
use hir_ty::{
|
||||
autoderef,
|
||||
consteval::ConstExt,
|
||||
could_unify,
|
||||
diagnostics_sink::DiagnosticSink,
|
||||
method_resolution::{self, def_crates, TyFingerprint},
|
||||
primitive::UintTy,
|
||||
subst_prefix,
|
||||
@ -65,11 +72,12 @@ use hir_ty::{
|
||||
WhereClause,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use nameres::diagnostics::DefDiagnosticKind;
|
||||
use rustc_hash::FxHashSet;
|
||||
use stdx::{format_to, impl_from};
|
||||
use syntax::{
|
||||
ast::{self, AttrsOwner, NameOwner},
|
||||
AstNode, SmolStr,
|
||||
AstNode, AstPtr, SmolStr, SyntaxKind, SyntaxNodePtr,
|
||||
};
|
||||
use tt::{Ident, Leaf, Literal, TokenTree};
|
||||
|
||||
@ -442,7 +450,137 @@ impl Module {
|
||||
format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string()))
|
||||
});
|
||||
let def_map = self.id.def_map(db.upcast());
|
||||
def_map.add_diagnostics(db.upcast(), self.id.local_id, sink);
|
||||
for diag in def_map.diagnostics() {
|
||||
if diag.in_module != self.id.local_id {
|
||||
// FIXME: This is accidentally quadratic.
|
||||
continue;
|
||||
}
|
||||
match &diag.kind {
|
||||
DefDiagnosticKind::UnresolvedModule { ast: declaration, candidate } => {
|
||||
let decl = declaration.to_node(db.upcast());
|
||||
sink.push(UnresolvedModule {
|
||||
file: declaration.file_id,
|
||||
decl: AstPtr::new(&decl),
|
||||
candidate: candidate.clone(),
|
||||
})
|
||||
}
|
||||
DefDiagnosticKind::UnresolvedExternCrate { ast } => {
|
||||
let item = ast.to_node(db.upcast());
|
||||
sink.push(UnresolvedExternCrate {
|
||||
file: ast.file_id,
|
||||
item: AstPtr::new(&item),
|
||||
});
|
||||
}
|
||||
|
||||
DefDiagnosticKind::UnresolvedImport { ast, index } => {
|
||||
let use_item = ast.to_node(db.upcast());
|
||||
let hygiene = Hygiene::new(db.upcast(), ast.file_id);
|
||||
let mut cur = 0;
|
||||
let mut tree = None;
|
||||
ModPath::expand_use_item(
|
||||
db.upcast(),
|
||||
InFile::new(ast.file_id, use_item),
|
||||
&hygiene,
|
||||
|_mod_path, use_tree, _is_glob, _alias| {
|
||||
if cur == *index {
|
||||
tree = Some(use_tree.clone());
|
||||
}
|
||||
|
||||
cur += 1;
|
||||
},
|
||||
);
|
||||
|
||||
if let Some(tree) = tree {
|
||||
sink.push(UnresolvedImport { file: ast.file_id, node: AstPtr::new(&tree) });
|
||||
}
|
||||
}
|
||||
|
||||
DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } => {
|
||||
let item = ast.to_node(db.upcast());
|
||||
sink.push(InactiveCode {
|
||||
file: ast.file_id,
|
||||
node: AstPtr::new(&item).into(),
|
||||
cfg: cfg.clone(),
|
||||
opts: opts.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
DefDiagnosticKind::UnresolvedProcMacro { ast } => {
|
||||
let mut precise_location = None;
|
||||
let (file, ast, name) = match ast {
|
||||
MacroCallKind::FnLike { ast_id, .. } => {
|
||||
let node = ast_id.to_node(db.upcast());
|
||||
(ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)), None)
|
||||
}
|
||||
MacroCallKind::Derive { ast_id, derive_name, .. } => {
|
||||
let node = ast_id.to_node(db.upcast());
|
||||
|
||||
// Compute the precise location of the macro name's token in the derive
|
||||
// list.
|
||||
// FIXME: This does not handle paths to the macro, but neither does the
|
||||
// rest of r-a.
|
||||
let derive_attrs =
|
||||
node.attrs().filter_map(|attr| match attr.as_simple_call() {
|
||||
Some((name, args)) if name == "derive" => Some(args),
|
||||
_ => None,
|
||||
});
|
||||
'outer: for attr in derive_attrs {
|
||||
let tokens =
|
||||
attr.syntax().children_with_tokens().filter_map(|elem| {
|
||||
match elem {
|
||||
syntax::NodeOrToken::Node(_) => None,
|
||||
syntax::NodeOrToken::Token(tok) => Some(tok),
|
||||
}
|
||||
});
|
||||
for token in tokens {
|
||||
if token.kind() == SyntaxKind::IDENT
|
||||
&& token.text() == derive_name.as_str()
|
||||
{
|
||||
precise_location = Some(token.text_range());
|
||||
break 'outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
ast_id.file_id,
|
||||
SyntaxNodePtr::from(AstPtr::new(&node)),
|
||||
Some(derive_name.clone()),
|
||||
)
|
||||
}
|
||||
};
|
||||
sink.push(UnresolvedProcMacro {
|
||||
file,
|
||||
node: ast,
|
||||
precise_location,
|
||||
macro_name: name,
|
||||
});
|
||||
}
|
||||
|
||||
DefDiagnosticKind::UnresolvedMacroCall { ast, path } => {
|
||||
let node = ast.to_node(db.upcast());
|
||||
sink.push(UnresolvedMacroCall {
|
||||
file: ast.file_id,
|
||||
node: AstPtr::new(&node),
|
||||
path: path.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
DefDiagnosticKind::MacroError { ast, message } => {
|
||||
let (file, ast) = match ast {
|
||||
MacroCallKind::FnLike { ast_id, .. } => {
|
||||
let node = ast_id.to_node(db.upcast());
|
||||
(ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)))
|
||||
}
|
||||
MacroCallKind::Derive { ast_id, .. } => {
|
||||
let node = ast_id.to_node(db.upcast());
|
||||
(ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)))
|
||||
}
|
||||
};
|
||||
sink.push(MacroError { file, node: ast, message: message.clone() });
|
||||
}
|
||||
}
|
||||
}
|
||||
for decl in self.declarations(db) {
|
||||
match decl {
|
||||
crate::ModuleDef::Function(f) => f.diagnostics(db, sink),
|
||||
@ -865,7 +1003,37 @@ impl Function {
|
||||
|
||||
pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
|
||||
let krate = self.module(db).id.krate();
|
||||
hir_def::diagnostics::validate_body(db.upcast(), self.id.into(), sink);
|
||||
|
||||
let source_map = db.body_with_source_map(self.id.into()).1;
|
||||
for diag in source_map.diagnostics() {
|
||||
match diag {
|
||||
BodyDiagnostic::InactiveCode { node, cfg, opts } => sink.push(InactiveCode {
|
||||
file: node.file_id,
|
||||
node: node.value.clone(),
|
||||
cfg: cfg.clone(),
|
||||
opts: opts.clone(),
|
||||
}),
|
||||
BodyDiagnostic::MacroError { node, message } => sink.push(MacroError {
|
||||
file: node.file_id,
|
||||
node: node.value.clone().into(),
|
||||
message: message.to_string(),
|
||||
}),
|
||||
BodyDiagnostic::UnresolvedProcMacro { node } => sink.push(UnresolvedProcMacro {
|
||||
file: node.file_id,
|
||||
node: node.value.clone().into(),
|
||||
precise_location: None,
|
||||
macro_name: None,
|
||||
}),
|
||||
BodyDiagnostic::UnresolvedMacroCall { node, path } => {
|
||||
sink.push(UnresolvedMacroCall {
|
||||
file: node.file_id,
|
||||
node: node.value.clone(),
|
||||
path: path.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink);
|
||||
hir_ty::diagnostics::validate_body(db, self.id.into(), sink);
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
//! Defines `Body`: a lowered representation of bodies of functions, statics and
|
||||
//! consts.
|
||||
mod lower;
|
||||
mod diagnostics;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
pub mod scope;
|
||||
@ -9,17 +8,16 @@ pub mod scope;
|
||||
use std::{mem, ops::Index, sync::Arc};
|
||||
|
||||
use base_db::CrateId;
|
||||
use cfg::CfgOptions;
|
||||
use cfg::{CfgExpr, CfgOptions};
|
||||
use drop_bomb::DropBomb;
|
||||
use either::Either;
|
||||
use hir_expand::{
|
||||
ast_id_map::AstIdMap, diagnostics::DiagnosticSink, hygiene::Hygiene, AstId, ExpandResult,
|
||||
HirFileId, InFile, MacroDefId,
|
||||
ast_id_map::AstIdMap, hygiene::Hygiene, AstId, ExpandResult, HirFileId, InFile, MacroDefId,
|
||||
};
|
||||
use la_arena::{Arena, ArenaMap};
|
||||
use profile::Count;
|
||||
use rustc_hash::FxHashMap;
|
||||
use syntax::{ast, AstNode, AstPtr};
|
||||
use syntax::{ast, AstNode, AstPtr, SyntaxNodePtr};
|
||||
|
||||
use crate::{
|
||||
attr::{Attrs, RawAttrs},
|
||||
@ -273,12 +271,20 @@ pub struct BodySourceMap {
|
||||
|
||||
/// Diagnostics accumulated during body lowering. These contain `AstPtr`s and so are stored in
|
||||
/// the source map (since they're just as volatile).
|
||||
diagnostics: Vec<diagnostics::BodyDiagnostic>,
|
||||
diagnostics: Vec<BodyDiagnostic>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Eq, PartialEq, Clone, Copy)]
|
||||
pub struct SyntheticSyntax;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum BodyDiagnostic {
|
||||
InactiveCode { node: InFile<SyntaxNodePtr>, cfg: CfgExpr, opts: CfgOptions },
|
||||
MacroError { node: InFile<AstPtr<ast::MacroCall>>, message: String },
|
||||
UnresolvedProcMacro { node: InFile<AstPtr<ast::MacroCall>> },
|
||||
UnresolvedMacroCall { node: InFile<AstPtr<ast::MacroCall>>, path: ModPath },
|
||||
}
|
||||
|
||||
impl Body {
|
||||
pub(crate) fn body_with_source_map_query(
|
||||
db: &dyn DefDatabase,
|
||||
@ -416,9 +422,8 @@ impl BodySourceMap {
|
||||
self.field_map.get(&src).cloned()
|
||||
}
|
||||
|
||||
pub(crate) fn add_diagnostics(&self, _db: &dyn DefDatabase, sink: &mut DiagnosticSink<'_>) {
|
||||
for diag in &self.diagnostics {
|
||||
diag.add_to(sink);
|
||||
}
|
||||
/// Get a reference to the body source map's diagnostics.
|
||||
pub fn diagnostics(&self) -> &[BodyDiagnostic] {
|
||||
&self.diagnostics
|
||||
}
|
||||
}
|
||||
|
@ -1,32 +0,0 @@
|
||||
//! Diagnostics emitted during body lowering.
|
||||
|
||||
use hir_expand::diagnostics::DiagnosticSink;
|
||||
|
||||
use crate::diagnostics::{InactiveCode, MacroError, UnresolvedMacroCall, UnresolvedProcMacro};
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub(crate) enum BodyDiagnostic {
|
||||
InactiveCode(InactiveCode),
|
||||
MacroError(MacroError),
|
||||
UnresolvedProcMacro(UnresolvedProcMacro),
|
||||
UnresolvedMacroCall(UnresolvedMacroCall),
|
||||
}
|
||||
|
||||
impl BodyDiagnostic {
|
||||
pub(crate) fn add_to(&self, sink: &mut DiagnosticSink<'_>) {
|
||||
match self {
|
||||
BodyDiagnostic::InactiveCode(diag) => {
|
||||
sink.push(diag.clone());
|
||||
}
|
||||
BodyDiagnostic::MacroError(diag) => {
|
||||
sink.push(diag.clone());
|
||||
}
|
||||
BodyDiagnostic::UnresolvedProcMacro(diag) => {
|
||||
sink.push(diag.clone());
|
||||
}
|
||||
BodyDiagnostic::UnresolvedMacroCall(diag) => {
|
||||
sink.push(diag.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ use hir_expand::{
|
||||
ast_id_map::{AstIdMap, FileAstId},
|
||||
hygiene::Hygiene,
|
||||
name::{name, AsName, Name},
|
||||
ExpandError, HirFileId,
|
||||
ExpandError, HirFileId, InFile,
|
||||
};
|
||||
use la_arena::Arena;
|
||||
use profile::Count;
|
||||
@ -23,9 +23,9 @@ use syntax::{
|
||||
use crate::{
|
||||
adt::StructKind,
|
||||
body::{Body, BodySourceMap, Expander, LabelSource, PatPtr, SyntheticSyntax},
|
||||
body::{BodyDiagnostic, ExprSource, PatSource},
|
||||
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
|
||||
db::DefDatabase,
|
||||
diagnostics::{InactiveCode, MacroError, UnresolvedMacroCall, UnresolvedProcMacro},
|
||||
expr::{
|
||||
dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Label,
|
||||
LabelId, Literal, LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField,
|
||||
@ -38,8 +38,6 @@ use crate::{
|
||||
AdtId, BlockLoc, ModuleDefId, UnresolvedMacro,
|
||||
};
|
||||
|
||||
use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource};
|
||||
|
||||
pub struct LowerCtx<'a> {
|
||||
pub db: &'a dyn DefDatabase,
|
||||
hygiene: Hygiene,
|
||||
@ -592,13 +590,10 @@ impl ExprCollector<'_> {
|
||||
let res = match res {
|
||||
Ok(res) => res,
|
||||
Err(UnresolvedMacro { path }) => {
|
||||
self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedMacroCall(
|
||||
UnresolvedMacroCall {
|
||||
file: outer_file,
|
||||
node: syntax_ptr.cast().unwrap(),
|
||||
path,
|
||||
},
|
||||
));
|
||||
self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedMacroCall {
|
||||
node: InFile::new(outer_file, syntax_ptr),
|
||||
path,
|
||||
});
|
||||
collector(self, None);
|
||||
return;
|
||||
}
|
||||
@ -606,21 +601,15 @@ impl ExprCollector<'_> {
|
||||
|
||||
match &res.err {
|
||||
Some(ExpandError::UnresolvedProcMacro) => {
|
||||
self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedProcMacro(
|
||||
UnresolvedProcMacro {
|
||||
file: outer_file,
|
||||
node: syntax_ptr.into(),
|
||||
precise_location: None,
|
||||
macro_name: None,
|
||||
},
|
||||
));
|
||||
self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedProcMacro {
|
||||
node: InFile::new(outer_file, syntax_ptr),
|
||||
});
|
||||
}
|
||||
Some(err) => {
|
||||
self.source_map.diagnostics.push(BodyDiagnostic::MacroError(MacroError {
|
||||
file: outer_file,
|
||||
node: syntax_ptr.into(),
|
||||
self.source_map.diagnostics.push(BodyDiagnostic::MacroError {
|
||||
node: InFile::new(outer_file, syntax_ptr),
|
||||
message: err.to_string(),
|
||||
}));
|
||||
});
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
@ -945,12 +934,14 @@ impl ExprCollector<'_> {
|
||||
return Some(());
|
||||
}
|
||||
|
||||
self.source_map.diagnostics.push(BodyDiagnostic::InactiveCode(InactiveCode {
|
||||
file: self.expander.current_file_id,
|
||||
node: SyntaxNodePtr::new(owner.syntax()),
|
||||
self.source_map.diagnostics.push(BodyDiagnostic::InactiveCode {
|
||||
node: InFile::new(
|
||||
self.expander.current_file_id,
|
||||
SyntaxNodePtr::new(owner.syntax()),
|
||||
),
|
||||
cfg,
|
||||
opts: self.expander.cfg_options().clone(),
|
||||
}));
|
||||
});
|
||||
|
||||
None
|
||||
}
|
||||
|
@ -96,26 +96,26 @@ fn f() {
|
||||
// The three g̶e̶n̶d̶e̶r̶s̶ statements:
|
||||
|
||||
#[cfg(a)] fn f() {} // Item statement
|
||||
//^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
|
||||
//^^^^^^^^^^^^^^^^^^^ InactiveCode
|
||||
#[cfg(a)] {} // Expression statement
|
||||
//^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
|
||||
//^^^^^^^^^^^^ InactiveCode
|
||||
#[cfg(a)] let x = 0; // let statement
|
||||
//^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
|
||||
//^^^^^^^^^^^^^^^^^^^^ InactiveCode
|
||||
|
||||
abc(#[cfg(a)] 0);
|
||||
//^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
|
||||
//^^^^^^^^^^^ InactiveCode
|
||||
let x = Struct {
|
||||
#[cfg(a)] f: 0,
|
||||
//^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
|
||||
//^^^^^^^^^^^^^^ InactiveCode
|
||||
};
|
||||
match () {
|
||||
() => (),
|
||||
#[cfg(a)] () => (),
|
||||
//^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
|
||||
//^^^^^^^^^^^^^^^^^^ InactiveCode
|
||||
}
|
||||
|
||||
#[cfg(a)] 0 // Trailing expression of block
|
||||
//^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
|
||||
//^^^^^^^^^^^ InactiveCode
|
||||
}
|
||||
",
|
||||
);
|
||||
@ -188,7 +188,7 @@ fn unresolved_macro_diag() {
|
||||
r#"
|
||||
fn f() {
|
||||
m!();
|
||||
//^^^^ unresolved macro `m!`
|
||||
//^^^^ UnresolvedMacroCall
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
@ -1,227 +0,0 @@
|
||||
//! Diagnostics produced by `hir_def`.
|
||||
|
||||
use std::any::Any;
|
||||
use stdx::format_to;
|
||||
|
||||
use cfg::{CfgExpr, CfgOptions, DnfExpr};
|
||||
use hir_expand::diagnostics::{Diagnostic, DiagnosticCode, DiagnosticSink};
|
||||
use hir_expand::{HirFileId, InFile};
|
||||
use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
|
||||
|
||||
use crate::{db::DefDatabase, path::ModPath, DefWithBodyId};
|
||||
|
||||
pub fn validate_body(db: &dyn DefDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) {
|
||||
let source_map = db.body_with_source_map(owner).1;
|
||||
source_map.add_diagnostics(db, sink);
|
||||
}
|
||||
|
||||
// Diagnostic: unresolved-module
|
||||
//
|
||||
// This diagnostic is triggered if rust-analyzer is unable to discover referred module.
|
||||
#[derive(Debug)]
|
||||
pub struct UnresolvedModule {
|
||||
pub file: HirFileId,
|
||||
pub decl: AstPtr<ast::Module>,
|
||||
pub candidate: String,
|
||||
}
|
||||
|
||||
impl Diagnostic for UnresolvedModule {
|
||||
fn code(&self) -> DiagnosticCode {
|
||||
DiagnosticCode("unresolved-module")
|
||||
}
|
||||
fn message(&self) -> String {
|
||||
"unresolved module".to_string()
|
||||
}
|
||||
fn display_source(&self) -> InFile<SyntaxNodePtr> {
|
||||
InFile::new(self.file, self.decl.clone().into())
|
||||
}
|
||||
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// Diagnostic: unresolved-extern-crate
|
||||
//
|
||||
// This diagnostic is triggered if rust-analyzer is unable to discover referred extern crate.
|
||||
#[derive(Debug)]
|
||||
pub struct UnresolvedExternCrate {
|
||||
pub file: HirFileId,
|
||||
pub item: AstPtr<ast::ExternCrate>,
|
||||
}
|
||||
|
||||
impl Diagnostic for UnresolvedExternCrate {
|
||||
fn code(&self) -> DiagnosticCode {
|
||||
DiagnosticCode("unresolved-extern-crate")
|
||||
}
|
||||
fn message(&self) -> String {
|
||||
"unresolved extern crate".to_string()
|
||||
}
|
||||
fn display_source(&self) -> InFile<SyntaxNodePtr> {
|
||||
InFile::new(self.file, self.item.clone().into())
|
||||
}
|
||||
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// Diagnostic: unresolved-import
|
||||
//
|
||||
// This diagnostic is triggered if rust-analyzer is unable to discover imported module.
|
||||
#[derive(Debug)]
|
||||
pub struct UnresolvedImport {
|
||||
pub file: HirFileId,
|
||||
pub node: AstPtr<ast::UseTree>,
|
||||
}
|
||||
|
||||
impl Diagnostic for UnresolvedImport {
|
||||
fn code(&self) -> DiagnosticCode {
|
||||
DiagnosticCode("unresolved-import")
|
||||
}
|
||||
fn message(&self) -> String {
|
||||
"unresolved import".to_string()
|
||||
}
|
||||
fn display_source(&self) -> InFile<SyntaxNodePtr> {
|
||||
InFile::new(self.file, self.node.clone().into())
|
||||
}
|
||||
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
||||
self
|
||||
}
|
||||
fn is_experimental(&self) -> bool {
|
||||
// This currently results in false positives in the following cases:
|
||||
// - `cfg_if!`-generated code in libstd (we don't load the sysroot correctly)
|
||||
// - `core::arch` (we don't handle `#[path = "../<path>"]` correctly)
|
||||
// - proc macros and/or proc macro generated code
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
// Diagnostic: unresolved-macro-call
|
||||
//
|
||||
// This diagnostic is triggered if rust-analyzer is unable to resolve the path to a
|
||||
// macro in a macro invocation.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct UnresolvedMacroCall {
|
||||
pub file: HirFileId,
|
||||
pub node: AstPtr<ast::MacroCall>,
|
||||
pub path: ModPath,
|
||||
}
|
||||
|
||||
impl Diagnostic for UnresolvedMacroCall {
|
||||
fn code(&self) -> DiagnosticCode {
|
||||
DiagnosticCode("unresolved-macro-call")
|
||||
}
|
||||
fn message(&self) -> String {
|
||||
format!("unresolved macro `{}!`", self.path)
|
||||
}
|
||||
fn display_source(&self) -> InFile<SyntaxNodePtr> {
|
||||
InFile::new(self.file, self.node.clone().into())
|
||||
}
|
||||
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
||||
self
|
||||
}
|
||||
fn is_experimental(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
// Diagnostic: inactive-code
|
||||
//
|
||||
// This diagnostic is shown for code with inactive `#[cfg]` attributes.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct InactiveCode {
|
||||
pub file: HirFileId,
|
||||
pub node: SyntaxNodePtr,
|
||||
pub cfg: CfgExpr,
|
||||
pub opts: CfgOptions,
|
||||
}
|
||||
|
||||
impl Diagnostic for InactiveCode {
|
||||
fn code(&self) -> DiagnosticCode {
|
||||
DiagnosticCode("inactive-code")
|
||||
}
|
||||
fn message(&self) -> String {
|
||||
let inactive = DnfExpr::new(self.cfg.clone()).why_inactive(&self.opts);
|
||||
let mut buf = "code is inactive due to #[cfg] directives".to_string();
|
||||
|
||||
if let Some(inactive) = inactive {
|
||||
format_to!(buf, ": {}", inactive);
|
||||
}
|
||||
|
||||
buf
|
||||
}
|
||||
fn display_source(&self) -> InFile<SyntaxNodePtr> {
|
||||
InFile::new(self.file, self.node.clone())
|
||||
}
|
||||
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
||||
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.
|
||||
//
|
||||
// If you are seeing a lot of "proc macro not expanded" warnings, you can add this option to the
|
||||
// `rust-analyzer.diagnostics.disabled` list to prevent them from showing. Alternatively you can
|
||||
// enable support for procedural macros (see `rust-analyzer.procMacro.enable`).
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct UnresolvedProcMacro {
|
||||
pub file: HirFileId,
|
||||
pub node: SyntaxNodePtr,
|
||||
/// If the diagnostic can be pinpointed more accurately than via `node`, this is the `TextRange`
|
||||
/// to use instead.
|
||||
pub precise_location: Option<TextRange>,
|
||||
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
|
||||
}
|
||||
}
|
@ -19,7 +19,6 @@ pub mod path;
|
||||
pub mod type_ref;
|
||||
pub mod builtin_type;
|
||||
pub mod builtin_attr;
|
||||
pub mod diagnostics;
|
||||
pub mod per_ns;
|
||||
pub mod item_scope;
|
||||
|
||||
@ -56,7 +55,6 @@ use std::{
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use adt::VariantData;
|
||||
use base_db::{impl_intern_key, salsa, CrateId};
|
||||
use hir_expand::{
|
||||
ast_id_map::FileAstId,
|
||||
@ -67,15 +65,18 @@ use hir_expand::{
|
||||
use la_arena::Idx;
|
||||
use nameres::DefMap;
|
||||
use path::ModPath;
|
||||
use stdx::impl_from;
|
||||
use syntax::ast;
|
||||
|
||||
use crate::attr::AttrId;
|
||||
use crate::builtin_type::BuiltinType;
|
||||
use item_tree::{
|
||||
Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, ModItem, Static, Struct, Trait,
|
||||
TypeAlias, Union,
|
||||
use crate::{
|
||||
adt::VariantData,
|
||||
attr::AttrId,
|
||||
builtin_type::BuiltinType,
|
||||
item_tree::{
|
||||
Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, ModItem, Static, Struct, Trait,
|
||||
TypeAlias, Union,
|
||||
},
|
||||
};
|
||||
use stdx::impl_from;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct ModuleId {
|
||||
|
@ -47,18 +47,19 @@
|
||||
//! path and, upon success, we run macro expansion and "collect module" phase on
|
||||
//! the result
|
||||
|
||||
pub mod diagnostics;
|
||||
mod collector;
|
||||
mod mod_resolution;
|
||||
mod path_resolution;
|
||||
mod proc_macro;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod proc_macro;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use base_db::{CrateId, Edition, FileId};
|
||||
use hir_expand::{diagnostics::DiagnosticSink, name::Name, InFile, MacroDefId};
|
||||
use hir_expand::{name::Name, InFile, MacroDefId};
|
||||
use la_arena::Arena;
|
||||
use profile::Count;
|
||||
use rustc_hash::FxHashMap;
|
||||
@ -254,15 +255,6 @@ impl DefMap {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_diagnostics(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
module: LocalModuleId,
|
||||
sink: &mut DiagnosticSink,
|
||||
) {
|
||||
self.diagnostics.iter().for_each(|it| it.add_to(db, module, sink))
|
||||
}
|
||||
|
||||
pub fn modules_for_file(&self, file_id: FileId) -> impl Iterator<Item = LocalModuleId> + '_ {
|
||||
self.modules
|
||||
.iter()
|
||||
@ -448,6 +440,11 @@ impl DefMap {
|
||||
module.scope.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a reference to the def map's diagnostics.
|
||||
pub fn diagnostics(&self) -> &[DefDiagnostic] {
|
||||
self.diagnostics.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleData {
|
||||
@ -471,236 +468,3 @@ pub enum ModuleSource {
|
||||
Module(ast::Module),
|
||||
BlockExpr(ast::BlockExpr),
|
||||
}
|
||||
|
||||
mod diagnostics {
|
||||
use cfg::{CfgExpr, CfgOptions};
|
||||
use hir_expand::diagnostics::DiagnosticSink;
|
||||
use hir_expand::hygiene::Hygiene;
|
||||
use hir_expand::{InFile, MacroCallKind};
|
||||
use syntax::ast::AttrsOwner;
|
||||
use syntax::{ast, AstNode, AstPtr, SyntaxKind, SyntaxNodePtr};
|
||||
|
||||
use crate::path::ModPath;
|
||||
use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum DiagnosticKind {
|
||||
UnresolvedModule { declaration: AstId<ast::Module>, candidate: String },
|
||||
|
||||
UnresolvedExternCrate { ast: AstId<ast::ExternCrate> },
|
||||
|
||||
UnresolvedImport { ast: AstId<ast::Use>, index: usize },
|
||||
|
||||
UnconfiguredCode { ast: AstId<ast::Item>, cfg: CfgExpr, opts: CfgOptions },
|
||||
|
||||
UnresolvedProcMacro { ast: MacroCallKind },
|
||||
|
||||
UnresolvedMacroCall { ast: AstId<ast::MacroCall>, path: ModPath },
|
||||
|
||||
MacroError { ast: MacroCallKind, message: String },
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub(super) struct DefDiagnostic {
|
||||
in_module: LocalModuleId,
|
||||
kind: DiagnosticKind,
|
||||
}
|
||||
|
||||
impl DefDiagnostic {
|
||||
pub(super) fn unresolved_module(
|
||||
container: LocalModuleId,
|
||||
declaration: AstId<ast::Module>,
|
||||
candidate: String,
|
||||
) -> Self {
|
||||
Self {
|
||||
in_module: container,
|
||||
kind: DiagnosticKind::UnresolvedModule { declaration, candidate },
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn unresolved_extern_crate(
|
||||
container: LocalModuleId,
|
||||
declaration: AstId<ast::ExternCrate>,
|
||||
) -> Self {
|
||||
Self {
|
||||
in_module: container,
|
||||
kind: DiagnosticKind::UnresolvedExternCrate { ast: declaration },
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn unresolved_import(
|
||||
container: LocalModuleId,
|
||||
ast: AstId<ast::Use>,
|
||||
index: usize,
|
||||
) -> Self {
|
||||
Self { in_module: container, kind: DiagnosticKind::UnresolvedImport { ast, index } }
|
||||
}
|
||||
|
||||
pub(super) fn unconfigured_code(
|
||||
container: LocalModuleId,
|
||||
ast: AstId<ast::Item>,
|
||||
cfg: CfgExpr,
|
||||
opts: CfgOptions,
|
||||
) -> Self {
|
||||
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 unresolved_macro_call(
|
||||
container: LocalModuleId,
|
||||
ast: AstId<ast::MacroCall>,
|
||||
path: ModPath,
|
||||
) -> Self {
|
||||
Self { in_module: container, kind: DiagnosticKind::UnresolvedMacroCall { ast, path } }
|
||||
}
|
||||
|
||||
pub(super) fn add_to(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
target_module: LocalModuleId,
|
||||
sink: &mut DiagnosticSink,
|
||||
) {
|
||||
if self.in_module != target_module {
|
||||
return;
|
||||
}
|
||||
|
||||
match &self.kind {
|
||||
DiagnosticKind::UnresolvedModule { declaration, candidate } => {
|
||||
let decl = declaration.to_node(db.upcast());
|
||||
sink.push(UnresolvedModule {
|
||||
file: declaration.file_id,
|
||||
decl: AstPtr::new(&decl),
|
||||
candidate: candidate.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
DiagnosticKind::UnresolvedExternCrate { ast } => {
|
||||
let item = ast.to_node(db.upcast());
|
||||
sink.push(UnresolvedExternCrate {
|
||||
file: ast.file_id,
|
||||
item: AstPtr::new(&item),
|
||||
});
|
||||
}
|
||||
|
||||
DiagnosticKind::UnresolvedImport { ast, index } => {
|
||||
let use_item = ast.to_node(db.upcast());
|
||||
let hygiene = Hygiene::new(db.upcast(), ast.file_id);
|
||||
let mut cur = 0;
|
||||
let mut tree = None;
|
||||
ModPath::expand_use_item(
|
||||
db,
|
||||
InFile::new(ast.file_id, use_item),
|
||||
&hygiene,
|
||||
|_mod_path, use_tree, _is_glob, _alias| {
|
||||
if cur == *index {
|
||||
tree = Some(use_tree.clone());
|
||||
}
|
||||
|
||||
cur += 1;
|
||||
},
|
||||
);
|
||||
|
||||
if let Some(tree) = tree {
|
||||
sink.push(UnresolvedImport { file: ast.file_id, node: AstPtr::new(&tree) });
|
||||
}
|
||||
}
|
||||
|
||||
DiagnosticKind::UnconfiguredCode { ast, cfg, opts } => {
|
||||
let item = ast.to_node(db.upcast());
|
||||
sink.push(InactiveCode {
|
||||
file: ast.file_id,
|
||||
node: AstPtr::new(&item).into(),
|
||||
cfg: cfg.clone(),
|
||||
opts: opts.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
DiagnosticKind::UnresolvedProcMacro { ast } => {
|
||||
let mut precise_location = None;
|
||||
let (file, ast, name) = match ast {
|
||||
MacroCallKind::FnLike { ast_id, .. } => {
|
||||
let node = ast_id.to_node(db.upcast());
|
||||
(ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)), None)
|
||||
}
|
||||
MacroCallKind::Derive { ast_id, derive_name, .. } => {
|
||||
let node = ast_id.to_node(db.upcast());
|
||||
|
||||
// Compute the precise location of the macro name's token in the derive
|
||||
// list.
|
||||
// FIXME: This does not handle paths to the macro, but neither does the
|
||||
// rest of r-a.
|
||||
let derive_attrs =
|
||||
node.attrs().filter_map(|attr| match attr.as_simple_call() {
|
||||
Some((name, args)) if name == "derive" => Some(args),
|
||||
_ => None,
|
||||
});
|
||||
'outer: for attr in derive_attrs {
|
||||
let tokens =
|
||||
attr.syntax().children_with_tokens().filter_map(|elem| {
|
||||
match elem {
|
||||
syntax::NodeOrToken::Node(_) => None,
|
||||
syntax::NodeOrToken::Token(tok) => Some(tok),
|
||||
}
|
||||
});
|
||||
for token in tokens {
|
||||
if token.kind() == SyntaxKind::IDENT
|
||||
&& token.text() == derive_name.as_str()
|
||||
{
|
||||
precise_location = Some(token.text_range());
|
||||
break 'outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
ast_id.file_id,
|
||||
SyntaxNodePtr::from(AstPtr::new(&node)),
|
||||
Some(derive_name.clone()),
|
||||
)
|
||||
}
|
||||
};
|
||||
sink.push(UnresolvedProcMacro {
|
||||
file,
|
||||
node: ast,
|
||||
precise_location,
|
||||
macro_name: name,
|
||||
});
|
||||
}
|
||||
|
||||
DiagnosticKind::UnresolvedMacroCall { ast, path } => {
|
||||
let node = ast.to_node(db.upcast());
|
||||
sink.push(UnresolvedMacroCall {
|
||||
file: ast.file_id,
|
||||
node: AstPtr::new(&node),
|
||||
path: path.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
DiagnosticKind::MacroError { ast, message } => {
|
||||
let (file, ast) = match ast {
|
||||
MacroCallKind::FnLike { ast_id, .. } => {
|
||||
let node = ast_id.to_node(db.upcast());
|
||||
(ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)))
|
||||
}
|
||||
MacroCallKind::Derive { ast_id, .. } => {
|
||||
let node = ast_id.to_node(db.upcast());
|
||||
(ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)))
|
||||
}
|
||||
};
|
||||
sink.push(MacroError { file, node: ast, message: message.clone() });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,10 @@ use crate::{
|
||||
},
|
||||
macro_call_as_call_id,
|
||||
nameres::{
|
||||
diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint,
|
||||
diagnostics::DefDiagnostic,
|
||||
mod_resolution::ModDir,
|
||||
path_resolution::ReachedFixedPoint,
|
||||
proc_macro::{ProcMacroDef, ProcMacroKind},
|
||||
BuiltinShadowMode, DefMap, ModuleData, ModuleOrigin, ResolveMode,
|
||||
},
|
||||
path::{ImportAlias, ModPath, PathKind},
|
||||
@ -44,8 +47,6 @@ use crate::{
|
||||
UnresolvedMacro,
|
||||
};
|
||||
|
||||
use super::proc_macro::{ProcMacroDef, ProcMacroKind};
|
||||
|
||||
const GLOB_RECURSION_LIMIT: usize = 100;
|
||||
const EXPANSION_DEPTH_LIMIT: usize = 128;
|
||||
const FIXED_POINT_LIMIT: usize = 8192;
|
||||
|
90
crates/hir_def/src/nameres/diagnostics.rs
Normal file
90
crates/hir_def/src/nameres/diagnostics.rs
Normal file
@ -0,0 +1,90 @@
|
||||
//! Diagnostics emitted during DefMap construction.
|
||||
|
||||
use cfg::{CfgExpr, CfgOptions};
|
||||
use hir_expand::MacroCallKind;
|
||||
use syntax::ast;
|
||||
|
||||
use crate::{nameres::LocalModuleId, path::ModPath, AstId};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum DefDiagnosticKind {
|
||||
UnresolvedModule { ast: AstId<ast::Module>, candidate: String },
|
||||
|
||||
UnresolvedExternCrate { ast: AstId<ast::ExternCrate> },
|
||||
|
||||
UnresolvedImport { ast: AstId<ast::Use>, index: usize },
|
||||
|
||||
UnconfiguredCode { ast: AstId<ast::Item>, cfg: CfgExpr, opts: CfgOptions },
|
||||
|
||||
UnresolvedProcMacro { ast: MacroCallKind },
|
||||
|
||||
UnresolvedMacroCall { ast: AstId<ast::MacroCall>, path: ModPath },
|
||||
|
||||
MacroError { ast: MacroCallKind, message: String },
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct DefDiagnostic {
|
||||
pub in_module: LocalModuleId,
|
||||
pub kind: DefDiagnosticKind,
|
||||
}
|
||||
|
||||
impl DefDiagnostic {
|
||||
pub(super) fn unresolved_module(
|
||||
container: LocalModuleId,
|
||||
declaration: AstId<ast::Module>,
|
||||
candidate: String,
|
||||
) -> Self {
|
||||
Self {
|
||||
in_module: container,
|
||||
kind: DefDiagnosticKind::UnresolvedModule { ast: declaration, candidate },
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn unresolved_extern_crate(
|
||||
container: LocalModuleId,
|
||||
declaration: AstId<ast::ExternCrate>,
|
||||
) -> Self {
|
||||
Self {
|
||||
in_module: container,
|
||||
kind: DefDiagnosticKind::UnresolvedExternCrate { ast: declaration },
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn unresolved_import(
|
||||
container: LocalModuleId,
|
||||
ast: AstId<ast::Use>,
|
||||
index: usize,
|
||||
) -> Self {
|
||||
Self { in_module: container, kind: DefDiagnosticKind::UnresolvedImport { ast, index } }
|
||||
}
|
||||
|
||||
pub(super) fn unconfigured_code(
|
||||
container: LocalModuleId,
|
||||
ast: AstId<ast::Item>,
|
||||
cfg: CfgExpr,
|
||||
opts: CfgOptions,
|
||||
) -> Self {
|
||||
Self { in_module: container, kind: DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } }
|
||||
}
|
||||
|
||||
pub(super) fn unresolved_proc_macro(container: LocalModuleId, ast: MacroCallKind) -> Self {
|
||||
Self { in_module: container, kind: DefDiagnosticKind::UnresolvedProcMacro { ast } }
|
||||
}
|
||||
|
||||
pub(super) fn macro_error(
|
||||
container: LocalModuleId,
|
||||
ast: MacroCallKind,
|
||||
message: String,
|
||||
) -> Self {
|
||||
Self { in_module: container, kind: DefDiagnosticKind::MacroError { ast, message } }
|
||||
}
|
||||
|
||||
pub(super) fn unresolved_macro_call(
|
||||
container: LocalModuleId,
|
||||
ast: AstId<ast::MacroCall>,
|
||||
path: ModPath,
|
||||
) -> Self {
|
||||
Self { in_module: container, kind: DefDiagnosticKind::UnresolvedMacroCall { ast, path } }
|
||||
}
|
||||
}
|
@ -18,40 +18,13 @@ fn unresolved_import() {
|
||||
r"
|
||||
use does_exist;
|
||||
use does_not_exist;
|
||||
//^^^^^^^^^^^^^^ unresolved import
|
||||
//^^^^^^^^^^^^^^^^^^^ UnresolvedImport
|
||||
|
||||
mod does_exist {}
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unresolved_import_in_use_tree() {
|
||||
// Only the relevant part of a nested `use` item should be highlighted.
|
||||
check_diagnostics(
|
||||
r"
|
||||
use does_exist::{Exists, DoesntExist};
|
||||
//^^^^^^^^^^^ unresolved import
|
||||
|
||||
use {does_not_exist::*, does_exist};
|
||||
//^^^^^^^^^^^^^^^^^ unresolved import
|
||||
|
||||
use does_not_exist::{
|
||||
a,
|
||||
//^ unresolved import
|
||||
b,
|
||||
//^ unresolved import
|
||||
c,
|
||||
//^ unresolved import
|
||||
};
|
||||
|
||||
mod does_exist {
|
||||
pub struct Exists;
|
||||
}
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unresolved_extern_crate() {
|
||||
check_diagnostics(
|
||||
@ -59,7 +32,7 @@ fn unresolved_extern_crate() {
|
||||
//- /main.rs crate:main deps:core
|
||||
extern crate core;
|
||||
extern crate doesnotexist;
|
||||
//^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate
|
||||
//^^^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedExternCrate
|
||||
//- /lib.rs crate:core
|
||||
",
|
||||
);
|
||||
@ -72,7 +45,7 @@ fn extern_crate_self_as() {
|
||||
r"
|
||||
//- /lib.rs
|
||||
extern crate doesnotexist;
|
||||
//^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate
|
||||
//^^^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedExternCrate
|
||||
// Should not error.
|
||||
extern crate self as foo;
|
||||
struct Foo;
|
||||
@ -88,18 +61,18 @@ fn dedup_unresolved_import_from_unresolved_crate() {
|
||||
//- /main.rs crate:main
|
||||
mod a {
|
||||
extern crate doesnotexist;
|
||||
//^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate
|
||||
//^^^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedExternCrate
|
||||
|
||||
// Should not error, since we already errored for the missing crate.
|
||||
use doesnotexist::{self, bla, *};
|
||||
|
||||
use crate::doesnotexist;
|
||||
//^^^^^^^^^^^^^^^^^^^ unresolved import
|
||||
//^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedImport
|
||||
}
|
||||
|
||||
mod m {
|
||||
use super::doesnotexist;
|
||||
//^^^^^^^^^^^^^^^^^^^ unresolved import
|
||||
//^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedImport
|
||||
}
|
||||
",
|
||||
);
|
||||
@ -112,7 +85,7 @@ fn unresolved_module() {
|
||||
//- /lib.rs
|
||||
mod foo;
|
||||
mod bar;
|
||||
//^^^^^^^^ unresolved module
|
||||
//^^^^^^^^ UnresolvedModule
|
||||
mod baz {}
|
||||
//- /foo.rs
|
||||
",
|
||||
@ -127,16 +100,16 @@ fn inactive_item() {
|
||||
r#"
|
||||
//- /lib.rs
|
||||
#[cfg(no)] pub fn f() {}
|
||||
//^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled
|
||||
//^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
|
||||
|
||||
#[cfg(no)] #[cfg(no2)] mod m;
|
||||
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no and no2 are disabled
|
||||
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
|
||||
|
||||
#[cfg(all(not(a), b))] enum E {}
|
||||
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: b is disabled
|
||||
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
|
||||
|
||||
#[cfg(feature = "std")] use std;
|
||||
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: feature = "std" is disabled
|
||||
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
|
||||
"#,
|
||||
);
|
||||
}
|
||||
@ -149,14 +122,14 @@ fn inactive_via_cfg_attr() {
|
||||
r#"
|
||||
//- /lib.rs
|
||||
#[cfg_attr(not(never), cfg(no))] fn f() {}
|
||||
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled
|
||||
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
|
||||
|
||||
#[cfg_attr(not(never), cfg(not(no)))] fn f() {}
|
||||
|
||||
#[cfg_attr(never, cfg(no))] fn g() {}
|
||||
|
||||
#[cfg_attr(not(never), inline, cfg(no))] fn h() {}
|
||||
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled
|
||||
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
|
||||
"#,
|
||||
);
|
||||
}
|
||||
@ -170,7 +143,7 @@ fn unresolved_legacy_scope_macro() {
|
||||
|
||||
m!();
|
||||
m2!();
|
||||
//^^^^^^ unresolved macro `self::m2!`
|
||||
//^^^^^^ UnresolvedMacroCall
|
||||
"#,
|
||||
);
|
||||
}
|
||||
@ -187,7 +160,7 @@ fn unresolved_module_scope_macro() {
|
||||
|
||||
self::m!();
|
||||
self::m2!();
|
||||
//^^^^^^^^^^^^ unresolved macro `self::m2!`
|
||||
//^^^^^^^^^^^^ UnresolvedMacroCall
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ impl ModPath {
|
||||
}
|
||||
|
||||
/// Calls `cb` with all paths, represented by this use item.
|
||||
pub(crate) fn expand_use_item(
|
||||
pub fn expand_use_item(
|
||||
db: &dyn DefDatabase,
|
||||
item_src: InFile<ast::Use>,
|
||||
hygiene: &Hygiene,
|
||||
|
@ -5,19 +5,20 @@ use std::{
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition, Upcast};
|
||||
use base_db::{
|
||||
salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition, FileRange, Upcast,
|
||||
};
|
||||
use base_db::{AnchoredPath, SourceDatabase};
|
||||
use hir_expand::diagnostics::Diagnostic;
|
||||
use hir_expand::diagnostics::DiagnosticSinkBuilder;
|
||||
use hir_expand::{db::AstDatabase, InFile};
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustc_hash::FxHashSet;
|
||||
use syntax::{algo, ast, AstNode, TextRange, TextSize};
|
||||
use syntax::{algo, ast, AstNode, SyntaxNode, SyntaxNodePtr, TextRange, TextSize};
|
||||
use test_utils::extract_annotations;
|
||||
|
||||
use crate::{
|
||||
body::BodyDiagnostic,
|
||||
db::DefDatabase,
|
||||
nameres::{DefMap, ModuleSource},
|
||||
nameres::{diagnostics::DefDiagnosticKind, DefMap, ModuleSource},
|
||||
src::HasSource,
|
||||
LocalModuleId, Lookup, ModuleDefId, ModuleId,
|
||||
};
|
||||
@ -262,19 +263,70 @@ impl TestDB {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn diagnostics<F: FnMut(&dyn Diagnostic)>(&self, mut cb: F) {
|
||||
pub(crate) fn diagnostics(&self, cb: &mut dyn FnMut(FileRange, String)) {
|
||||
let crate_graph = self.crate_graph();
|
||||
for krate in crate_graph.iter() {
|
||||
let crate_def_map = self.crate_def_map(krate);
|
||||
|
||||
let mut sink = DiagnosticSinkBuilder::new().build(&mut cb);
|
||||
for (module_id, module) in crate_def_map.modules() {
|
||||
crate_def_map.add_diagnostics(self, module_id, &mut sink);
|
||||
for diag in crate_def_map.diagnostics() {
|
||||
let (node, message): (InFile<SyntaxNode>, &str) = match &diag.kind {
|
||||
DefDiagnosticKind::UnresolvedModule { ast, .. } => {
|
||||
let node = ast.to_node(self.upcast());
|
||||
(InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedModule")
|
||||
}
|
||||
DefDiagnosticKind::UnresolvedExternCrate { ast, .. } => {
|
||||
let node = ast.to_node(self.upcast());
|
||||
(InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedExternCrate")
|
||||
}
|
||||
DefDiagnosticKind::UnresolvedImport { ast, .. } => {
|
||||
let node = ast.to_node(self.upcast());
|
||||
(InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedImport")
|
||||
}
|
||||
DefDiagnosticKind::UnconfiguredCode { ast, .. } => {
|
||||
let node = ast.to_node(self.upcast());
|
||||
(InFile::new(ast.file_id, node.syntax().clone()), "UnconfiguredCode")
|
||||
}
|
||||
DefDiagnosticKind::UnresolvedProcMacro { ast, .. } => {
|
||||
(ast.to_node(self.upcast()), "UnresolvedProcMacro")
|
||||
}
|
||||
DefDiagnosticKind::UnresolvedMacroCall { ast, .. } => {
|
||||
let node = ast.to_node(self.upcast());
|
||||
(InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedMacroCall")
|
||||
}
|
||||
DefDiagnosticKind::MacroError { ast, message } => {
|
||||
(ast.to_node(self.upcast()), message.as_str())
|
||||
}
|
||||
};
|
||||
|
||||
let frange = node.as_ref().original_file_range(self);
|
||||
cb(frange, message.to_string())
|
||||
}
|
||||
|
||||
for (_module_id, module) in crate_def_map.modules() {
|
||||
for decl in module.scope.declarations() {
|
||||
if let ModuleDefId::FunctionId(it) = decl {
|
||||
let source_map = self.body_with_source_map(it.into()).1;
|
||||
source_map.add_diagnostics(self, &mut sink);
|
||||
for diag in source_map.diagnostics() {
|
||||
let (ptr, message): (InFile<SyntaxNodePtr>, &str) = match diag {
|
||||
BodyDiagnostic::InactiveCode { node, .. } => {
|
||||
(node.clone().map(|it| it.into()), "InactiveCode")
|
||||
}
|
||||
BodyDiagnostic::MacroError { node, message } => {
|
||||
(node.clone().map(|it| it.into()), message.as_str())
|
||||
}
|
||||
BodyDiagnostic::UnresolvedProcMacro { node } => {
|
||||
(node.clone().map(|it| it.into()), "UnresolvedProcMacro")
|
||||
}
|
||||
BodyDiagnostic::UnresolvedMacroCall { node, .. } => {
|
||||
(node.clone().map(|it| it.into()), "UnresolvedMacroCall")
|
||||
}
|
||||
};
|
||||
|
||||
let root = self.parse_or_expand(ptr.file_id).unwrap();
|
||||
let node = ptr.map(|ptr| ptr.to_node(&root));
|
||||
let frange = node.as_ref().original_file_range(self);
|
||||
cb(frange, message.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -287,14 +339,7 @@ impl TestDB {
|
||||
assert!(!annotations.is_empty());
|
||||
|
||||
let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default();
|
||||
db.diagnostics(|d| {
|
||||
let src = d.display_source();
|
||||
let root = db.parse_or_expand(src.file_id).unwrap();
|
||||
|
||||
let node = src.map(|ptr| ptr.to_node(&root));
|
||||
let frange = node.as_ref().original_file_range(db);
|
||||
|
||||
let message = d.message();
|
||||
db.diagnostics(&mut |frange, message| {
|
||||
actual.entry(frange.file_id).or_default().push((frange.range, message));
|
||||
});
|
||||
|
||||
@ -319,7 +364,7 @@ impl TestDB {
|
||||
assert!(annotations.is_empty());
|
||||
|
||||
let mut has_diagnostics = false;
|
||||
db.diagnostics(|_| {
|
||||
db.diagnostics(&mut |_, _| {
|
||||
has_diagnostics = true;
|
||||
});
|
||||
|
||||
|
@ -186,7 +186,7 @@ fn parse_macro_expansion(
|
||||
// The final goal we would like to make all parse_macro success,
|
||||
// such that the following log will not call anyway.
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id);
|
||||
let node = loc.kind.node(db);
|
||||
let node = loc.kind.to_node(db);
|
||||
|
||||
// collect parent information for warning log
|
||||
let parents =
|
||||
|
@ -8,7 +8,6 @@ pub mod db;
|
||||
pub mod ast_id_map;
|
||||
pub mod name;
|
||||
pub mod hygiene;
|
||||
pub mod diagnostics;
|
||||
pub mod builtin_derive;
|
||||
pub mod builtin_macro;
|
||||
pub mod proc_macro;
|
||||
@ -108,7 +107,7 @@ impl HirFileId {
|
||||
HirFileIdRepr::FileId(_) => None,
|
||||
HirFileIdRepr::MacroFile(macro_file) => {
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id);
|
||||
Some(loc.kind.node(db))
|
||||
Some(loc.kind.to_node(db))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -153,7 +152,7 @@ impl HirFileId {
|
||||
HirFileIdRepr::MacroFile(macro_file) => {
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id);
|
||||
let item = match loc.def.kind {
|
||||
MacroDefKind::BuiltInDerive(..) => loc.kind.node(db),
|
||||
MacroDefKind::BuiltInDerive(..) => loc.kind.to_node(db),
|
||||
_ => return None,
|
||||
};
|
||||
Some(item.with_value(ast::Item::cast(item.value.clone())?))
|
||||
@ -269,7 +268,7 @@ impl MacroCallKind {
|
||||
}
|
||||
}
|
||||
|
||||
fn node(&self, db: &dyn db::AstDatabase) -> InFile<SyntaxNode> {
|
||||
pub fn to_node(&self, db: &dyn db::AstDatabase) -> InFile<SyntaxNode> {
|
||||
match self {
|
||||
MacroCallKind::FnLike { ast_id, .. } => {
|
||||
ast_id.with_value(ast_id.to_node(db).syntax().clone())
|
||||
|
@ -8,12 +8,14 @@ use std::{any::Any, fmt};
|
||||
|
||||
use base_db::CrateId;
|
||||
use hir_def::{DefWithBodyId, ModuleDefId};
|
||||
use hir_expand::diagnostics::{Diagnostic, DiagnosticCode, DiagnosticSink};
|
||||
use hir_expand::{name::Name, HirFileId, InFile};
|
||||
use stdx::format_to;
|
||||
use syntax::{ast, AstPtr, SyntaxNodePtr};
|
||||
|
||||
use crate::db::HirDatabase;
|
||||
use crate::{
|
||||
db::HirDatabase,
|
||||
diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink},
|
||||
};
|
||||
|
||||
pub use crate::diagnostics::expr::{record_literal_missing_fields, record_pattern_missing_fields};
|
||||
|
||||
@ -446,15 +448,13 @@ impl Diagnostic for ReplaceFilterMapNextWithFindMap {
|
||||
mod tests {
|
||||
use base_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt};
|
||||
use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId};
|
||||
use hir_expand::{
|
||||
db::AstDatabase,
|
||||
diagnostics::{Diagnostic, DiagnosticSinkBuilder},
|
||||
};
|
||||
use hir_expand::db::AstDatabase;
|
||||
use rustc_hash::FxHashMap;
|
||||
use syntax::{TextRange, TextSize};
|
||||
|
||||
use crate::{
|
||||
diagnostics::{validate_body, validate_module_item},
|
||||
diagnostics_sink::{Diagnostic, DiagnosticSinkBuilder},
|
||||
test_db::TestDB,
|
||||
};
|
||||
|
||||
|
@ -19,10 +19,7 @@ use hir_def::{
|
||||
src::HasSource,
|
||||
AdtId, AttrDefId, ConstId, EnumId, FunctionId, Lookup, ModuleDefId, StaticId, StructId,
|
||||
};
|
||||
use hir_expand::{
|
||||
diagnostics::DiagnosticSink,
|
||||
name::{AsName, Name},
|
||||
};
|
||||
use hir_expand::name::{AsName, Name};
|
||||
use stdx::{always, never};
|
||||
use syntax::{
|
||||
ast::{self, NameOwner},
|
||||
@ -32,6 +29,7 @@ use syntax::{
|
||||
use crate::{
|
||||
db::HirDatabase,
|
||||
diagnostics::{decl_check::case_conv::*, CaseType, IdentType, IncorrectCase},
|
||||
diagnostics_sink::DiagnosticSink,
|
||||
};
|
||||
|
||||
mod allow {
|
||||
|
@ -5,7 +5,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use hir_def::{expr::Statement, path::path, resolver::HasResolver, AssocItemId, DefWithBodyId};
|
||||
use hir_expand::{diagnostics::DiagnosticSink, name};
|
||||
use hir_expand::name;
|
||||
use rustc_hash::FxHashSet;
|
||||
use syntax::{ast, AstPtr};
|
||||
|
||||
@ -16,6 +16,7 @@ use crate::{
|
||||
MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
|
||||
MissingPatFields, RemoveThisSemicolon,
|
||||
},
|
||||
diagnostics_sink::DiagnosticSink,
|
||||
AdtId, InferenceResult, Interner, TyExt, TyKind,
|
||||
};
|
||||
|
||||
|
@ -9,10 +9,10 @@ use hir_def::{
|
||||
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
|
||||
DefWithBodyId,
|
||||
};
|
||||
use hir_expand::diagnostics::DiagnosticSink;
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase, diagnostics::MissingUnsafe, InferenceResult, Interner, TyExt, TyKind,
|
||||
db::HirDatabase, diagnostics::MissingUnsafe, diagnostics_sink::DiagnosticSink, InferenceResult,
|
||||
Interner, TyExt, TyKind,
|
||||
};
|
||||
|
||||
pub(super) struct UnsafeValidator<'a, 'b: 'a> {
|
||||
|
@ -16,10 +16,9 @@
|
||||
|
||||
use std::{any::Any, fmt};
|
||||
|
||||
use hir_expand::InFile;
|
||||
use syntax::SyntaxNodePtr;
|
||||
|
||||
use crate::InFile;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct DiagnosticCode(pub &'static str);
|
||||
|
@ -28,13 +28,14 @@ use hir_def::{
|
||||
AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule, Lookup,
|
||||
TraitId, TypeAliasId, VariantId,
|
||||
};
|
||||
use hir_expand::{diagnostics::DiagnosticSink, name::name};
|
||||
use hir_expand::name::name;
|
||||
use la_arena::ArenaMap;
|
||||
use rustc_hash::FxHashMap;
|
||||
use stdx::impl_from;
|
||||
use syntax::SmolStr;
|
||||
|
||||
use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty};
|
||||
use crate::diagnostics_sink::DiagnosticSink;
|
||||
use crate::{
|
||||
db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic,
|
||||
lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, Substitution,
|
||||
@ -798,11 +799,11 @@ impl std::ops::BitOrAssign for Diverges {
|
||||
|
||||
mod diagnostics {
|
||||
use hir_def::{expr::ExprId, DefWithBodyId};
|
||||
use hir_expand::diagnostics::DiagnosticSink;
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase,
|
||||
diagnostics::{BreakOutsideOfLoop, NoSuchField},
|
||||
diagnostics_sink::DiagnosticSink,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
|
@ -21,6 +21,7 @@ mod utils;
|
||||
mod walk;
|
||||
pub mod db;
|
||||
pub mod diagnostics;
|
||||
pub mod diagnostics_sink;
|
||||
pub mod display;
|
||||
pub mod method_resolution;
|
||||
pub mod primitive;
|
||||
|
@ -299,10 +299,10 @@ fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use expect_test::{expect, Expect};
|
||||
use expect_test::Expect;
|
||||
use ide_assists::AssistResolveStrategy;
|
||||
use stdx::trim_indent;
|
||||
use test_utils::assert_eq_text;
|
||||
use test_utils::{assert_eq_text, extract_annotations};
|
||||
|
||||
use crate::{fixture, DiagnosticsConfig};
|
||||
|
||||
@ -396,26 +396,51 @@ mod tests {
|
||||
expect.assert_debug_eq(&diagnostics)
|
||||
}
|
||||
|
||||
pub(crate) fn check_diagnostics(ra_fixture: &str) {
|
||||
let (analysis, file_id) = fixture::file(ra_fixture);
|
||||
let diagnostics = analysis
|
||||
.diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id)
|
||||
.unwrap();
|
||||
|
||||
let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
|
||||
let actual = diagnostics.into_iter().map(|d| (d.range, d.message)).collect::<Vec<_>>();
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unresolved_macro_range() {
|
||||
check_expect(
|
||||
r#"foo::bar!(92);"#,
|
||||
expect![[r#"
|
||||
[
|
||||
Diagnostic {
|
||||
message: "unresolved macro `foo::bar!`",
|
||||
range: 5..8,
|
||||
severity: Error,
|
||||
fixes: None,
|
||||
unused: false,
|
||||
code: Some(
|
||||
DiagnosticCode(
|
||||
"unresolved-macro-call",
|
||||
),
|
||||
),
|
||||
},
|
||||
]
|
||||
"#]],
|
||||
check_diagnostics(
|
||||
r#"
|
||||
foo::bar!(92);
|
||||
//^^^ unresolved macro `foo::bar!`
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unresolved_import_in_use_tree() {
|
||||
// Only the relevant part of a nested `use` item should be highlighted.
|
||||
check_diagnostics(
|
||||
r#"
|
||||
use does_exist::{Exists, DoesntExist};
|
||||
//^^^^^^^^^^^ unresolved import
|
||||
|
||||
use {does_not_exist::*, does_exist};
|
||||
//^^^^^^^^^^^^^^^^^ unresolved import
|
||||
|
||||
use does_not_exist::{
|
||||
a,
|
||||
//^ unresolved import
|
||||
b,
|
||||
//^ unresolved import
|
||||
c,
|
||||
//^ unresolved import
|
||||
};
|
||||
|
||||
mod does_exist {
|
||||
pub struct Exists;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user