mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-02 15:32:06 +00:00
internal: move diagnostics to hir
The idea here is to eventually get rid of `dyn Diagnostic` and `DiagnosticSink` infrastructure altogether, and just have a `enum hir::Diagnostic` instead. The problem with `dyn Diagnostic` is that it is defined in the lowest level of the stack (hir_expand), but is used by the highest level (ide). As a first step, we free hir_expand and hir_def from `dyn Diagnostic` and kick the can up to `hir_ty`, as an intermediate state. The plan is then to move DiagnosticSink similarly to the hir crate, and, as final third step, remove its usage from the ide. One currently unsolved problem is testing. You can notice that the test which checks precise diagnostic ranges, unresolved_import_in_use_tree, was moved to the ide layer. Logically, only IDE should have the infra to render a specific range. At the same time, the range is determined with the data produced in hir_def and hir crates, so this layering is rather unfortunate. Working on hir_def shouldn't require compiling `ide` for testing.
This commit is contained in:
parent
b7414fa14a
commit
5c9f31d4c2
@ -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,
|
||||
@ -793,11 +794,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…
Reference in New Issue
Block a user