From 5c9f31d4c28478b4373e6cf5ec155745c840ee3f Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 23 May 2021 23:31:59 +0300 Subject: 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. --- crates/hir_def/src/body.rs | 25 ++- crates/hir_def/src/body/diagnostics.rs | 32 --- crates/hir_def/src/body/lower.rs | 45 ++--- crates/hir_def/src/body/tests.rs | 16 +- crates/hir_def/src/diagnostics.rs | 227 --------------------- crates/hir_def/src/lib.rs | 17 +- crates/hir_def/src/nameres.rs | 252 +----------------------- crates/hir_def/src/nameres/collector.rs | 7 +- crates/hir_def/src/nameres/diagnostics.rs | 90 +++++++++ crates/hir_def/src/nameres/tests/diagnostics.rs | 57 ++---- crates/hir_def/src/path.rs | 2 +- crates/hir_def/src/test_db.rs | 83 ++++++-- 12 files changed, 232 insertions(+), 621 deletions(-) delete mode 100644 crates/hir_def/src/body/diagnostics.rs delete mode 100644 crates/hir_def/src/diagnostics.rs create mode 100644 crates/hir_def/src/nameres/diagnostics.rs (limited to 'crates/hir_def') diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs index 98b485b60..c521879c8 100644 --- a/crates/hir_def/src/body.rs +++ b/crates/hir_def/src/body.rs @@ -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: Vec, } #[derive(Default, Debug, Eq, PartialEq, Clone, Copy)] pub struct SyntheticSyntax; +#[derive(Debug, Eq, PartialEq)] +pub enum BodyDiagnostic { + InactiveCode { node: InFile, cfg: CfgExpr, opts: CfgOptions }, + MacroError { node: InFile>, message: String }, + UnresolvedProcMacro { node: InFile> }, + UnresolvedMacroCall { node: InFile>, 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 } } diff --git a/crates/hir_def/src/body/diagnostics.rs b/crates/hir_def/src/body/diagnostics.rs deleted file mode 100644 index f6992c9a8..000000000 --- a/crates/hir_def/src/body/diagnostics.rs +++ /dev/null @@ -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()); - } - } - } -} diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index 2a7e0205f..da1fdac33 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs @@ -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 } diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs index 3e8f16306..d4fae05a6 100644 --- a/crates/hir_def/src/body/tests.rs +++ b/crates/hir_def/src/body/tests.rs @@ -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 } "#, ); diff --git a/crates/hir_def/src/diagnostics.rs b/crates/hir_def/src/diagnostics.rs deleted file mode 100644 index a71ae2668..000000000 --- a/crates/hir_def/src/diagnostics.rs +++ /dev/null @@ -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, - 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 { - 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, -} - -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 { - 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, -} - -impl Diagnostic for UnresolvedImport { - fn code(&self) -> DiagnosticCode { - DiagnosticCode("unresolved-import") - } - fn message(&self) -> String { - "unresolved import".to_string() - } - fn display_source(&self) -> InFile { - 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 = "../"]` 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, - 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 { - 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 { - 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, - pub macro_name: Option, -} - -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 { - 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 { - 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 - } -} diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs index 70001cac8..9aa95720a 100644 --- a/crates/hir_def/src/lib.rs +++ b/crates/hir_def/src/lib.rs @@ -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 { diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs index 249af6fc8..ebfcc26c4 100644 --- a/crates/hir_def/src/nameres.rs +++ b/crates/hir_def/src/nameres.rs @@ -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 + '_ { 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, candidate: String }, - - UnresolvedExternCrate { ast: AstId }, - - UnresolvedImport { ast: AstId, index: usize }, - - UnconfiguredCode { ast: AstId, cfg: CfgExpr, opts: CfgOptions }, - - UnresolvedProcMacro { ast: MacroCallKind }, - - UnresolvedMacroCall { ast: AstId, 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, - candidate: String, - ) -> Self { - Self { - in_module: container, - kind: DiagnosticKind::UnresolvedModule { declaration, candidate }, - } - } - - pub(super) fn unresolved_extern_crate( - container: LocalModuleId, - declaration: AstId, - ) -> Self { - Self { - in_module: container, - kind: DiagnosticKind::UnresolvedExternCrate { ast: declaration }, - } - } - - pub(super) fn unresolved_import( - container: LocalModuleId, - ast: AstId, - index: usize, - ) -> Self { - Self { in_module: container, kind: DiagnosticKind::UnresolvedImport { ast, index } } - } - - pub(super) fn unconfigured_code( - container: LocalModuleId, - ast: AstId, - 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, - 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() }); - } - } - } - } -} diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 2ae740d0e..3ea472908 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs @@ -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; diff --git a/crates/hir_def/src/nameres/diagnostics.rs b/crates/hir_def/src/nameres/diagnostics.rs new file mode 100644 index 000000000..8f2f0ff9f --- /dev/null +++ b/crates/hir_def/src/nameres/diagnostics.rs @@ -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, candidate: String }, + + UnresolvedExternCrate { ast: AstId }, + + UnresolvedImport { ast: AstId, index: usize }, + + UnconfiguredCode { ast: AstId, cfg: CfgExpr, opts: CfgOptions }, + + UnresolvedProcMacro { ast: MacroCallKind }, + + UnresolvedMacroCall { ast: AstId, 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, + candidate: String, + ) -> Self { + Self { + in_module: container, + kind: DefDiagnosticKind::UnresolvedModule { ast: declaration, candidate }, + } + } + + pub(super) fn unresolved_extern_crate( + container: LocalModuleId, + declaration: AstId, + ) -> Self { + Self { + in_module: container, + kind: DefDiagnosticKind::UnresolvedExternCrate { ast: declaration }, + } + } + + pub(super) fn unresolved_import( + container: LocalModuleId, + ast: AstId, + index: usize, + ) -> Self { + Self { in_module: container, kind: DefDiagnosticKind::UnresolvedImport { ast, index } } + } + + pub(super) fn unconfigured_code( + container: LocalModuleId, + ast: AstId, + 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, + path: ModPath, + ) -> Self { + Self { in_module: container, kind: DefDiagnosticKind::UnresolvedMacroCall { ast, path } } + } +} diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs index 75147d973..ec6670952 100644 --- a/crates/hir_def/src/nameres/tests/diagnostics.rs +++ b/crates/hir_def/src/nameres/tests/diagnostics.rs @@ -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 "#, ); } diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs index 45ab9d0ff..d9ec03d2d 100644 --- a/crates/hir_def/src/path.rs +++ b/crates/hir_def/src/path.rs @@ -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, hygiene: &Hygiene, diff --git a/crates/hir_def/src/test_db.rs b/crates/hir_def/src/test_db.rs index 8fa703a57..6c357c915 100644 --- a/crates/hir_def/src/test_db.rs +++ b/crates/hir_def/src/test_db.rs @@ -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(&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, &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, &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> = 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; }); -- cgit v1.2.3