aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2021-05-23 21:31:59 +0100
committerAleksey Kladov <[email protected]>2021-05-25 15:49:59 +0100
commit5c9f31d4c28478b4373e6cf5ec155745c840ee3f (patch)
tree6d105121d271c7532170875feafaadcd7ad500ba /crates
parentb7414fa14a85f4acd37b5bdfdc2a4ab97a072bd2 (diff)
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.
Diffstat (limited to 'crates')
-rw-r--r--crates/hir/src/diagnostics.rs232
-rw-r--r--crates/hir/src/lib.rs176
-rw-r--r--crates/hir_def/src/body.rs25
-rw-r--r--crates/hir_def/src/body/diagnostics.rs32
-rw-r--r--crates/hir_def/src/body/lower.rs45
-rw-r--r--crates/hir_def/src/body/tests.rs16
-rw-r--r--crates/hir_def/src/diagnostics.rs227
-rw-r--r--crates/hir_def/src/lib.rs17
-rw-r--r--crates/hir_def/src/nameres.rs252
-rw-r--r--crates/hir_def/src/nameres/collector.rs7
-rw-r--r--crates/hir_def/src/nameres/diagnostics.rs90
-rw-r--r--crates/hir_def/src/nameres/tests/diagnostics.rs57
-rw-r--r--crates/hir_def/src/path.rs2
-rw-r--r--crates/hir_def/src/test_db.rs83
-rw-r--r--crates/hir_expand/src/db.rs2
-rw-r--r--crates/hir_expand/src/lib.rs7
-rw-r--r--crates/hir_ty/src/diagnostics.rs12
-rw-r--r--crates/hir_ty/src/diagnostics/decl_check.rs6
-rw-r--r--crates/hir_ty/src/diagnostics/expr.rs3
-rw-r--r--crates/hir_ty/src/diagnostics/unsafe_check.rs4
-rw-r--r--crates/hir_ty/src/diagnostics_sink.rs (renamed from crates/hir_expand/src/diagnostics.rs)3
-rw-r--r--crates/hir_ty/src/infer.rs5
-rw-r--r--crates/hir_ty/src/lib.rs1
-rw-r--r--crates/ide/src/diagnostics.rs65
24 files changed, 693 insertions, 676 deletions
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 414c3f35e..22ec7c6ac 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -3,13 +3,227 @@
3//! 3//!
4//! This probably isn't the best way to do this -- ideally, diagnistics should 4//! This probably isn't the best way to do this -- ideally, diagnistics should
5//! be expressed in terms of hir types themselves. 5//! be expressed in terms of hir types themselves.
6pub use hir_def::diagnostics::{ 6use std::any::Any;
7 InactiveCode, UnresolvedMacroCall, UnresolvedModule, UnresolvedProcMacro, 7
8}; 8use cfg::{CfgExpr, CfgOptions, DnfExpr};
9pub use hir_expand::diagnostics::{ 9use hir_def::path::ModPath;
10 Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder, 10use hir_expand::{HirFileId, InFile};
11}; 11use stdx::format_to;
12pub use hir_ty::diagnostics::{ 12use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
13 IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr, 13
14 NoSuchField, RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap, 14pub use hir_ty::{
15 diagnostics::{
16 IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms,
17 MissingOkOrSomeInTailExpr, NoSuchField, RemoveThisSemicolon,
18 ReplaceFilterMapNextWithFindMap,
19 },
20 diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder},
15}; 21};
22
23// Diagnostic: unresolved-module
24//
25// This diagnostic is triggered if rust-analyzer is unable to discover referred module.
26#[derive(Debug)]
27pub struct UnresolvedModule {
28 pub file: HirFileId,
29 pub decl: AstPtr<ast::Module>,
30 pub candidate: String,
31}
32
33impl Diagnostic for UnresolvedModule {
34 fn code(&self) -> DiagnosticCode {
35 DiagnosticCode("unresolved-module")
36 }
37 fn message(&self) -> String {
38 "unresolved module".to_string()
39 }
40 fn display_source(&self) -> InFile<SyntaxNodePtr> {
41 InFile::new(self.file, self.decl.clone().into())
42 }
43 fn as_any(&self) -> &(dyn Any + Send + 'static) {
44 self
45 }
46}
47
48// Diagnostic: unresolved-extern-crate
49//
50// This diagnostic is triggered if rust-analyzer is unable to discover referred extern crate.
51#[derive(Debug)]
52pub struct UnresolvedExternCrate {
53 pub file: HirFileId,
54 pub item: AstPtr<ast::ExternCrate>,
55}
56
57impl Diagnostic for UnresolvedExternCrate {
58 fn code(&self) -> DiagnosticCode {
59 DiagnosticCode("unresolved-extern-crate")
60 }
61 fn message(&self) -> String {
62 "unresolved extern crate".to_string()
63 }
64 fn display_source(&self) -> InFile<SyntaxNodePtr> {
65 InFile::new(self.file, self.item.clone().into())
66 }
67 fn as_any(&self) -> &(dyn Any + Send + 'static) {
68 self
69 }
70}
71
72#[derive(Debug)]
73pub struct UnresolvedImport {
74 pub file: HirFileId,
75 pub node: AstPtr<ast::UseTree>,
76}
77
78impl Diagnostic for UnresolvedImport {
79 fn code(&self) -> DiagnosticCode {
80 DiagnosticCode("unresolved-import")
81 }
82 fn message(&self) -> String {
83 "unresolved import".to_string()
84 }
85 fn display_source(&self) -> InFile<SyntaxNodePtr> {
86 InFile::new(self.file, self.node.clone().into())
87 }
88 fn as_any(&self) -> &(dyn Any + Send + 'static) {
89 self
90 }
91 fn is_experimental(&self) -> bool {
92 // This currently results in false positives in the following cases:
93 // - `cfg_if!`-generated code in libstd (we don't load the sysroot correctly)
94 // - `core::arch` (we don't handle `#[path = "../<path>"]` correctly)
95 // - proc macros and/or proc macro generated code
96 true
97 }
98}
99
100// Diagnostic: unresolved-macro-call
101//
102// This diagnostic is triggered if rust-analyzer is unable to resolve the path to a
103// macro in a macro invocation.
104#[derive(Debug, Clone, Eq, PartialEq)]
105pub struct UnresolvedMacroCall {
106 pub file: HirFileId,
107 pub node: AstPtr<ast::MacroCall>,
108 pub path: ModPath,
109}
110
111impl Diagnostic for UnresolvedMacroCall {
112 fn code(&self) -> DiagnosticCode {
113 DiagnosticCode("unresolved-macro-call")
114 }
115 fn message(&self) -> String {
116 format!("unresolved macro `{}!`", self.path)
117 }
118 fn display_source(&self) -> InFile<SyntaxNodePtr> {
119 InFile::new(self.file, self.node.clone().into())
120 }
121 fn as_any(&self) -> &(dyn Any + Send + 'static) {
122 self
123 }
124 fn is_experimental(&self) -> bool {
125 true
126 }
127}
128
129// Diagnostic: inactive-code
130//
131// This diagnostic is shown for code with inactive `#[cfg]` attributes.
132#[derive(Debug, Clone, Eq, PartialEq)]
133pub struct InactiveCode {
134 pub file: HirFileId,
135 pub node: SyntaxNodePtr,
136 pub cfg: CfgExpr,
137 pub opts: CfgOptions,
138}
139
140impl Diagnostic for InactiveCode {
141 fn code(&self) -> DiagnosticCode {
142 DiagnosticCode("inactive-code")
143 }
144 fn message(&self) -> String {
145 let inactive = DnfExpr::new(self.cfg.clone()).why_inactive(&self.opts);
146 let mut buf = "code is inactive due to #[cfg] directives".to_string();
147
148 if let Some(inactive) = inactive {
149 format_to!(buf, ": {}", inactive);
150 }
151
152 buf
153 }
154 fn display_source(&self) -> InFile<SyntaxNodePtr> {
155 InFile::new(self.file, self.node.clone())
156 }
157 fn as_any(&self) -> &(dyn Any + Send + 'static) {
158 self
159 }
160}
161
162// Diagnostic: unresolved-proc-macro
163//
164// This diagnostic is shown when a procedural macro can not be found. This usually means that
165// procedural macro support is simply disabled (and hence is only a weak hint instead of an error),
166// but can also indicate project setup problems.
167//
168// If you are seeing a lot of "proc macro not expanded" warnings, you can add this option to the
169// `rust-analyzer.diagnostics.disabled` list to prevent them from showing. Alternatively you can
170// enable support for procedural macros (see `rust-analyzer.procMacro.enable`).
171#[derive(Debug, Clone, Eq, PartialEq)]
172pub struct UnresolvedProcMacro {
173 pub file: HirFileId,
174 pub node: SyntaxNodePtr,
175 /// If the diagnostic can be pinpointed more accurately than via `node`, this is the `TextRange`
176 /// to use instead.
177 pub precise_location: Option<TextRange>,
178 pub macro_name: Option<String>,
179}
180
181impl Diagnostic for UnresolvedProcMacro {
182 fn code(&self) -> DiagnosticCode {
183 DiagnosticCode("unresolved-proc-macro")
184 }
185
186 fn message(&self) -> String {
187 match &self.macro_name {
188 Some(name) => format!("proc macro `{}` not expanded", name),
189 None => "proc macro not expanded".to_string(),
190 }
191 }
192
193 fn display_source(&self) -> InFile<SyntaxNodePtr> {
194 InFile::new(self.file, self.node.clone())
195 }
196
197 fn as_any(&self) -> &(dyn Any + Send + 'static) {
198 self
199 }
200}
201
202// Diagnostic: macro-error
203//
204// This diagnostic is shown for macro expansion errors.
205#[derive(Debug, Clone, Eq, PartialEq)]
206pub struct MacroError {
207 pub file: HirFileId,
208 pub node: SyntaxNodePtr,
209 pub message: String,
210}
211
212impl Diagnostic for MacroError {
213 fn code(&self) -> DiagnosticCode {
214 DiagnosticCode("macro-error")
215 }
216 fn message(&self) -> String {
217 self.message.clone()
218 }
219 fn display_source(&self) -> InFile<SyntaxNodePtr> {
220 InFile::new(self.file, self.node.clone())
221 }
222 fn as_any(&self) -> &(dyn Any + Send + 'static) {
223 self
224 }
225 fn is_experimental(&self) -> bool {
226 // Newly added and not very well-tested, might contain false positives.
227 true
228 }
229}
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index cdf65a044..1ecd2391b 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -35,12 +35,18 @@ use std::{iter, sync::Arc};
35 35
36use arrayvec::ArrayVec; 36use arrayvec::ArrayVec;
37use base_db::{CrateDisplayName, CrateId, Edition, FileId}; 37use base_db::{CrateDisplayName, CrateId, Edition, FileId};
38use diagnostics::{
39 InactiveCode, MacroError, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall,
40 UnresolvedModule, UnresolvedProcMacro,
41};
38use either::Either; 42use either::Either;
39use hir_def::{ 43use hir_def::{
40 adt::{ReprKind, VariantData}, 44 adt::{ReprKind, VariantData},
45 body::BodyDiagnostic,
41 expr::{BindingAnnotation, LabelId, Pat, PatId}, 46 expr::{BindingAnnotation, LabelId, Pat, PatId},
42 item_tree::ItemTreeNode, 47 item_tree::ItemTreeNode,
43 lang_item::LangItemTarget, 48 lang_item::LangItemTarget,
49 nameres,
44 per_ns::PerNs, 50 per_ns::PerNs,
45 resolver::{HasResolver, Resolver}, 51 resolver::{HasResolver, Resolver},
46 src::HasSource as _, 52 src::HasSource as _,
@@ -50,11 +56,12 @@ use hir_def::{
50 LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId, 56 LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId,
51 TypeParamId, UnionId, 57 TypeParamId, UnionId,
52}; 58};
53use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind}; 59use hir_expand::{name::name, MacroCallKind, MacroDefKind};
54use hir_ty::{ 60use hir_ty::{
55 autoderef, 61 autoderef,
56 consteval::ConstExt, 62 consteval::ConstExt,
57 could_unify, 63 could_unify,
64 diagnostics_sink::DiagnosticSink,
58 method_resolution::{self, def_crates, TyFingerprint}, 65 method_resolution::{self, def_crates, TyFingerprint},
59 primitive::UintTy, 66 primitive::UintTy,
60 subst_prefix, 67 subst_prefix,
@@ -65,11 +72,12 @@ use hir_ty::{
65 WhereClause, 72 WhereClause,
66}; 73};
67use itertools::Itertools; 74use itertools::Itertools;
75use nameres::diagnostics::DefDiagnosticKind;
68use rustc_hash::FxHashSet; 76use rustc_hash::FxHashSet;
69use stdx::{format_to, impl_from}; 77use stdx::{format_to, impl_from};
70use syntax::{ 78use syntax::{
71 ast::{self, AttrsOwner, NameOwner}, 79 ast::{self, AttrsOwner, NameOwner},
72 AstNode, SmolStr, 80 AstNode, AstPtr, SmolStr, SyntaxKind, SyntaxNodePtr,
73}; 81};
74use tt::{Ident, Leaf, Literal, TokenTree}; 82use tt::{Ident, Leaf, Literal, TokenTree};
75 83
@@ -442,7 +450,137 @@ impl Module {
442 format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string())) 450 format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string()))
443 }); 451 });
444 let def_map = self.id.def_map(db.upcast()); 452 let def_map = self.id.def_map(db.upcast());
445 def_map.add_diagnostics(db.upcast(), self.id.local_id, sink); 453 for diag in def_map.diagnostics() {
454 if diag.in_module != self.id.local_id {
455 // FIXME: This is accidentally quadratic.
456 continue;
457 }
458 match &diag.kind {
459 DefDiagnosticKind::UnresolvedModule { ast: declaration, candidate } => {
460 let decl = declaration.to_node(db.upcast());
461 sink.push(UnresolvedModule {
462 file: declaration.file_id,
463 decl: AstPtr::new(&decl),
464 candidate: candidate.clone(),
465 })
466 }
467 DefDiagnosticKind::UnresolvedExternCrate { ast } => {
468 let item = ast.to_node(db.upcast());
469 sink.push(UnresolvedExternCrate {
470 file: ast.file_id,
471 item: AstPtr::new(&item),
472 });
473 }
474
475 DefDiagnosticKind::UnresolvedImport { ast, index } => {
476 let use_item = ast.to_node(db.upcast());
477 let hygiene = Hygiene::new(db.upcast(), ast.file_id);
478 let mut cur = 0;
479 let mut tree = None;
480 ModPath::expand_use_item(
481 db.upcast(),
482 InFile::new(ast.file_id, use_item),
483 &hygiene,
484 |_mod_path, use_tree, _is_glob, _alias| {
485 if cur == *index {
486 tree = Some(use_tree.clone());
487 }
488
489 cur += 1;
490 },
491 );
492
493 if let Some(tree) = tree {
494 sink.push(UnresolvedImport { file: ast.file_id, node: AstPtr::new(&tree) });
495 }
496 }
497
498 DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } => {
499 let item = ast.to_node(db.upcast());
500 sink.push(InactiveCode {
501 file: ast.file_id,
502 node: AstPtr::new(&item).into(),
503 cfg: cfg.clone(),
504 opts: opts.clone(),
505 });
506 }
507
508 DefDiagnosticKind::UnresolvedProcMacro { ast } => {
509 let mut precise_location = None;
510 let (file, ast, name) = match ast {
511 MacroCallKind::FnLike { ast_id, .. } => {
512 let node = ast_id.to_node(db.upcast());
513 (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)), None)
514 }
515 MacroCallKind::Derive { ast_id, derive_name, .. } => {
516 let node = ast_id.to_node(db.upcast());
517
518 // Compute the precise location of the macro name's token in the derive
519 // list.
520 // FIXME: This does not handle paths to the macro, but neither does the
521 // rest of r-a.
522 let derive_attrs =
523 node.attrs().filter_map(|attr| match attr.as_simple_call() {
524 Some((name, args)) if name == "derive" => Some(args),
525 _ => None,
526 });
527 'outer: for attr in derive_attrs {
528 let tokens =
529 attr.syntax().children_with_tokens().filter_map(|elem| {
530 match elem {
531 syntax::NodeOrToken::Node(_) => None,
532 syntax::NodeOrToken::Token(tok) => Some(tok),
533 }
534 });
535 for token in tokens {
536 if token.kind() == SyntaxKind::IDENT
537 && token.text() == derive_name.as_str()
538 {
539 precise_location = Some(token.text_range());
540 break 'outer;
541 }
542 }
543 }
544
545 (
546 ast_id.file_id,
547 SyntaxNodePtr::from(AstPtr::new(&node)),
548 Some(derive_name.clone()),
549 )
550 }
551 };
552 sink.push(UnresolvedProcMacro {
553 file,
554 node: ast,
555 precise_location,
556 macro_name: name,
557 });
558 }
559
560 DefDiagnosticKind::UnresolvedMacroCall { ast, path } => {
561 let node = ast.to_node(db.upcast());
562 sink.push(UnresolvedMacroCall {
563 file: ast.file_id,
564 node: AstPtr::new(&node),
565 path: path.clone(),
566 });
567 }
568
569 DefDiagnosticKind::MacroError { ast, message } => {
570 let (file, ast) = match ast {
571 MacroCallKind::FnLike { ast_id, .. } => {
572 let node = ast_id.to_node(db.upcast());
573 (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)))
574 }
575 MacroCallKind::Derive { ast_id, .. } => {
576 let node = ast_id.to_node(db.upcast());
577 (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)))
578 }
579 };
580 sink.push(MacroError { file, node: ast, message: message.clone() });
581 }
582 }
583 }
446 for decl in self.declarations(db) { 584 for decl in self.declarations(db) {
447 match decl { 585 match decl {
448 crate::ModuleDef::Function(f) => f.diagnostics(db, sink), 586 crate::ModuleDef::Function(f) => f.diagnostics(db, sink),
@@ -865,7 +1003,37 @@ impl Function {
865 1003
866 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { 1004 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
867 let krate = self.module(db).id.krate(); 1005 let krate = self.module(db).id.krate();
868 hir_def::diagnostics::validate_body(db.upcast(), self.id.into(), sink); 1006
1007 let source_map = db.body_with_source_map(self.id.into()).1;
1008 for diag in source_map.diagnostics() {
1009 match diag {
1010 BodyDiagnostic::InactiveCode { node, cfg, opts } => sink.push(InactiveCode {
1011 file: node.file_id,
1012 node: node.value.clone(),
1013 cfg: cfg.clone(),
1014 opts: opts.clone(),
1015 }),
1016 BodyDiagnostic::MacroError { node, message } => sink.push(MacroError {
1017 file: node.file_id,
1018 node: node.value.clone().into(),
1019 message: message.to_string(),
1020 }),
1021 BodyDiagnostic::UnresolvedProcMacro { node } => sink.push(UnresolvedProcMacro {
1022 file: node.file_id,
1023 node: node.value.clone().into(),
1024 precise_location: None,
1025 macro_name: None,
1026 }),
1027 BodyDiagnostic::UnresolvedMacroCall { node, path } => {
1028 sink.push(UnresolvedMacroCall {
1029 file: node.file_id,
1030 node: node.value.clone(),
1031 path: path.clone(),
1032 })
1033 }
1034 }
1035 }
1036
869 hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink); 1037 hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink);
870 hir_ty::diagnostics::validate_body(db, self.id.into(), sink); 1038 hir_ty::diagnostics::validate_body(db, self.id.into(), sink);
871 } 1039 }
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 @@
1//! Defines `Body`: a lowered representation of bodies of functions, statics and 1//! Defines `Body`: a lowered representation of bodies of functions, statics and
2//! consts. 2//! consts.
3mod lower; 3mod lower;
4mod diagnostics;
5#[cfg(test)] 4#[cfg(test)]
6mod tests; 5mod tests;
7pub mod scope; 6pub mod scope;
@@ -9,17 +8,16 @@ pub mod scope;
9use std::{mem, ops::Index, sync::Arc}; 8use std::{mem, ops::Index, sync::Arc};
10 9
11use base_db::CrateId; 10use base_db::CrateId;
12use cfg::CfgOptions; 11use cfg::{CfgExpr, CfgOptions};
13use drop_bomb::DropBomb; 12use drop_bomb::DropBomb;
14use either::Either; 13use either::Either;
15use hir_expand::{ 14use hir_expand::{
16 ast_id_map::AstIdMap, diagnostics::DiagnosticSink, hygiene::Hygiene, AstId, ExpandResult, 15 ast_id_map::AstIdMap, hygiene::Hygiene, AstId, ExpandResult, HirFileId, InFile, MacroDefId,
17 HirFileId, InFile, MacroDefId,
18}; 16};
19use la_arena::{Arena, ArenaMap}; 17use la_arena::{Arena, ArenaMap};
20use profile::Count; 18use profile::Count;
21use rustc_hash::FxHashMap; 19use rustc_hash::FxHashMap;
22use syntax::{ast, AstNode, AstPtr}; 20use syntax::{ast, AstNode, AstPtr, SyntaxNodePtr};
23 21
24use crate::{ 22use crate::{
25 attr::{Attrs, RawAttrs}, 23 attr::{Attrs, RawAttrs},
@@ -273,12 +271,20 @@ pub struct BodySourceMap {
273 271
274 /// Diagnostics accumulated during body lowering. These contain `AstPtr`s and so are stored in 272 /// Diagnostics accumulated during body lowering. These contain `AstPtr`s and so are stored in
275 /// the source map (since they're just as volatile). 273 /// the source map (since they're just as volatile).
276 diagnostics: Vec<diagnostics::BodyDiagnostic>, 274 diagnostics: Vec<BodyDiagnostic>,
277} 275}
278 276
279#[derive(Default, Debug, Eq, PartialEq, Clone, Copy)] 277#[derive(Default, Debug, Eq, PartialEq, Clone, Copy)]
280pub struct SyntheticSyntax; 278pub struct SyntheticSyntax;
281 279
280#[derive(Debug, Eq, PartialEq)]
281pub enum BodyDiagnostic {
282 InactiveCode { node: InFile<SyntaxNodePtr>, cfg: CfgExpr, opts: CfgOptions },
283 MacroError { node: InFile<AstPtr<ast::MacroCall>>, message: String },
284 UnresolvedProcMacro { node: InFile<AstPtr<ast::MacroCall>> },
285 UnresolvedMacroCall { node: InFile<AstPtr<ast::MacroCall>>, path: ModPath },
286}
287
282impl Body { 288impl Body {
283 pub(crate) fn body_with_source_map_query( 289 pub(crate) fn body_with_source_map_query(
284 db: &dyn DefDatabase, 290 db: &dyn DefDatabase,
@@ -416,9 +422,8 @@ impl BodySourceMap {
416 self.field_map.get(&src).cloned() 422 self.field_map.get(&src).cloned()
417 } 423 }
418 424
419 pub(crate) fn add_diagnostics(&self, _db: &dyn DefDatabase, sink: &mut DiagnosticSink<'_>) { 425 /// Get a reference to the body source map's diagnostics.
420 for diag in &self.diagnostics { 426 pub fn diagnostics(&self) -> &[BodyDiagnostic] {
421 diag.add_to(sink); 427 &self.diagnostics
422 }
423 } 428 }
424} 429}
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 @@
1//! Diagnostics emitted during body lowering.
2
3use hir_expand::diagnostics::DiagnosticSink;
4
5use crate::diagnostics::{InactiveCode, MacroError, UnresolvedMacroCall, UnresolvedProcMacro};
6
7#[derive(Debug, Eq, PartialEq)]
8pub(crate) enum BodyDiagnostic {
9 InactiveCode(InactiveCode),
10 MacroError(MacroError),
11 UnresolvedProcMacro(UnresolvedProcMacro),
12 UnresolvedMacroCall(UnresolvedMacroCall),
13}
14
15impl BodyDiagnostic {
16 pub(crate) fn add_to(&self, sink: &mut DiagnosticSink<'_>) {
17 match self {
18 BodyDiagnostic::InactiveCode(diag) => {
19 sink.push(diag.clone());
20 }
21 BodyDiagnostic::MacroError(diag) => {
22 sink.push(diag.clone());
23 }
24 BodyDiagnostic::UnresolvedProcMacro(diag) => {
25 sink.push(diag.clone());
26 }
27 BodyDiagnostic::UnresolvedMacroCall(diag) => {
28 sink.push(diag.clone());
29 }
30 }
31 }
32}
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::{
8 ast_id_map::{AstIdMap, FileAstId}, 8 ast_id_map::{AstIdMap, FileAstId},
9 hygiene::Hygiene, 9 hygiene::Hygiene,
10 name::{name, AsName, Name}, 10 name::{name, AsName, Name},
11 ExpandError, HirFileId, 11 ExpandError, HirFileId, InFile,
12}; 12};
13use la_arena::Arena; 13use la_arena::Arena;
14use profile::Count; 14use profile::Count;
@@ -23,9 +23,9 @@ use syntax::{
23use crate::{ 23use crate::{
24 adt::StructKind, 24 adt::StructKind,
25 body::{Body, BodySourceMap, Expander, LabelSource, PatPtr, SyntheticSyntax}, 25 body::{Body, BodySourceMap, Expander, LabelSource, PatPtr, SyntheticSyntax},
26 body::{BodyDiagnostic, ExprSource, PatSource},
26 builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint}, 27 builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
27 db::DefDatabase, 28 db::DefDatabase,
28 diagnostics::{InactiveCode, MacroError, UnresolvedMacroCall, UnresolvedProcMacro},
29 expr::{ 29 expr::{
30 dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Label, 30 dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Label,
31 LabelId, Literal, LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, 31 LabelId, Literal, LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField,
@@ -38,8 +38,6 @@ use crate::{
38 AdtId, BlockLoc, ModuleDefId, UnresolvedMacro, 38 AdtId, BlockLoc, ModuleDefId, UnresolvedMacro,
39}; 39};
40 40
41use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource};
42
43pub struct LowerCtx<'a> { 41pub struct LowerCtx<'a> {
44 pub db: &'a dyn DefDatabase, 42 pub db: &'a dyn DefDatabase,
45 hygiene: Hygiene, 43 hygiene: Hygiene,
@@ -592,13 +590,10 @@ impl ExprCollector<'_> {
592 let res = match res { 590 let res = match res {
593 Ok(res) => res, 591 Ok(res) => res,
594 Err(UnresolvedMacro { path }) => { 592 Err(UnresolvedMacro { path }) => {
595 self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedMacroCall( 593 self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedMacroCall {
596 UnresolvedMacroCall { 594 node: InFile::new(outer_file, syntax_ptr),
597 file: outer_file, 595 path,
598 node: syntax_ptr.cast().unwrap(), 596 });
599 path,
600 },
601 ));
602 collector(self, None); 597 collector(self, None);
603 return; 598 return;
604 } 599 }
@@ -606,21 +601,15 @@ impl ExprCollector<'_> {
606 601
607 match &res.err { 602 match &res.err {
608 Some(ExpandError::UnresolvedProcMacro) => { 603 Some(ExpandError::UnresolvedProcMacro) => {
609 self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedProcMacro( 604 self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedProcMacro {
610 UnresolvedProcMacro { 605 node: InFile::new(outer_file, syntax_ptr),
611 file: outer_file, 606 });
612 node: syntax_ptr.into(),
613 precise_location: None,
614 macro_name: None,
615 },
616 ));
617 } 607 }
618 Some(err) => { 608 Some(err) => {
619 self.source_map.diagnostics.push(BodyDiagnostic::MacroError(MacroError { 609 self.source_map.diagnostics.push(BodyDiagnostic::MacroError {
620 file: outer_file, 610 node: InFile::new(outer_file, syntax_ptr),
621 node: syntax_ptr.into(),
622 message: err.to_string(), 611 message: err.to_string(),
623 })); 612 });
624 } 613 }
625 None => {} 614 None => {}
626 } 615 }
@@ -945,12 +934,14 @@ impl ExprCollector<'_> {
945 return Some(()); 934 return Some(());
946 } 935 }
947 936
948 self.source_map.diagnostics.push(BodyDiagnostic::InactiveCode(InactiveCode { 937 self.source_map.diagnostics.push(BodyDiagnostic::InactiveCode {
949 file: self.expander.current_file_id, 938 node: InFile::new(
950 node: SyntaxNodePtr::new(owner.syntax()), 939 self.expander.current_file_id,
940 SyntaxNodePtr::new(owner.syntax()),
941 ),
951 cfg, 942 cfg,
952 opts: self.expander.cfg_options().clone(), 943 opts: self.expander.cfg_options().clone(),
953 })); 944 });
954 945
955 None 946 None
956 } 947 }
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() {
96 // The three g̶e̶n̶d̶e̶r̶s̶ statements: 96 // The three g̶e̶n̶d̶e̶r̶s̶ statements:
97 97
98 #[cfg(a)] fn f() {} // Item statement 98 #[cfg(a)] fn f() {} // Item statement
99 //^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled 99 //^^^^^^^^^^^^^^^^^^^ InactiveCode
100 #[cfg(a)] {} // Expression statement 100 #[cfg(a)] {} // Expression statement
101 //^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled 101 //^^^^^^^^^^^^ InactiveCode
102 #[cfg(a)] let x = 0; // let statement 102 #[cfg(a)] let x = 0; // let statement
103 //^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled 103 //^^^^^^^^^^^^^^^^^^^^ InactiveCode
104 104
105 abc(#[cfg(a)] 0); 105 abc(#[cfg(a)] 0);
106 //^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled 106 //^^^^^^^^^^^ InactiveCode
107 let x = Struct { 107 let x = Struct {
108 #[cfg(a)] f: 0, 108 #[cfg(a)] f: 0,
109 //^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled 109 //^^^^^^^^^^^^^^ InactiveCode
110 }; 110 };
111 match () { 111 match () {
112 () => (), 112 () => (),
113 #[cfg(a)] () => (), 113 #[cfg(a)] () => (),
114 //^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled 114 //^^^^^^^^^^^^^^^^^^ InactiveCode
115 } 115 }
116 116
117 #[cfg(a)] 0 // Trailing expression of block 117 #[cfg(a)] 0 // Trailing expression of block
118 //^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled 118 //^^^^^^^^^^^ InactiveCode
119} 119}
120 ", 120 ",
121 ); 121 );
@@ -188,7 +188,7 @@ fn unresolved_macro_diag() {
188 r#" 188 r#"
189fn f() { 189fn f() {
190 m!(); 190 m!();
191 //^^^^ unresolved macro `m!` 191 //^^^^ UnresolvedMacroCall
192} 192}
193 "#, 193 "#,
194 ); 194 );
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 @@
1//! Diagnostics produced by `hir_def`.
2
3use std::any::Any;
4use stdx::format_to;
5
6use cfg::{CfgExpr, CfgOptions, DnfExpr};
7use hir_expand::diagnostics::{Diagnostic, DiagnosticCode, DiagnosticSink};
8use hir_expand::{HirFileId, InFile};
9use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
10
11use crate::{db::DefDatabase, path::ModPath, DefWithBodyId};
12
13pub fn validate_body(db: &dyn DefDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) {
14 let source_map = db.body_with_source_map(owner).1;
15 source_map.add_diagnostics(db, sink);
16}
17
18// Diagnostic: unresolved-module
19//
20// This diagnostic is triggered if rust-analyzer is unable to discover referred module.
21#[derive(Debug)]
22pub struct UnresolvedModule {
23 pub file: HirFileId,
24 pub decl: AstPtr<ast::Module>,
25 pub candidate: String,
26}
27
28impl Diagnostic for UnresolvedModule {
29 fn code(&self) -> DiagnosticCode {
30 DiagnosticCode("unresolved-module")
31 }
32 fn message(&self) -> String {
33 "unresolved module".to_string()
34 }
35 fn display_source(&self) -> InFile<SyntaxNodePtr> {
36 InFile::new(self.file, self.decl.clone().into())
37 }
38 fn as_any(&self) -> &(dyn Any + Send + 'static) {
39 self
40 }
41}
42
43// Diagnostic: unresolved-extern-crate
44//
45// This diagnostic is triggered if rust-analyzer is unable to discover referred extern crate.
46#[derive(Debug)]
47pub struct UnresolvedExternCrate {
48 pub file: HirFileId,
49 pub item: AstPtr<ast::ExternCrate>,
50}
51
52impl Diagnostic for UnresolvedExternCrate {
53 fn code(&self) -> DiagnosticCode {
54 DiagnosticCode("unresolved-extern-crate")
55 }
56 fn message(&self) -> String {
57 "unresolved extern crate".to_string()
58 }
59 fn display_source(&self) -> InFile<SyntaxNodePtr> {
60 InFile::new(self.file, self.item.clone().into())
61 }
62 fn as_any(&self) -> &(dyn Any + Send + 'static) {
63 self
64 }
65}
66
67// Diagnostic: unresolved-import
68//
69// This diagnostic is triggered if rust-analyzer is unable to discover imported module.
70#[derive(Debug)]
71pub struct UnresolvedImport {
72 pub file: HirFileId,
73 pub node: AstPtr<ast::UseTree>,
74}
75
76impl Diagnostic for UnresolvedImport {
77 fn code(&self) -> DiagnosticCode {
78 DiagnosticCode("unresolved-import")
79 }
80 fn message(&self) -> String {
81 "unresolved import".to_string()
82 }
83 fn display_source(&self) -> InFile<SyntaxNodePtr> {
84 InFile::new(self.file, self.node.clone().into())
85 }
86 fn as_any(&self) -> &(dyn Any + Send + 'static) {
87 self
88 }
89 fn is_experimental(&self) -> bool {
90 // This currently results in false positives in the following cases:
91 // - `cfg_if!`-generated code in libstd (we don't load the sysroot correctly)
92 // - `core::arch` (we don't handle `#[path = "../<path>"]` correctly)
93 // - proc macros and/or proc macro generated code
94 true
95 }
96}
97
98// Diagnostic: unresolved-macro-call
99//
100// This diagnostic is triggered if rust-analyzer is unable to resolve the path to a
101// macro in a macro invocation.
102#[derive(Debug, Clone, Eq, PartialEq)]
103pub struct UnresolvedMacroCall {
104 pub file: HirFileId,
105 pub node: AstPtr<ast::MacroCall>,
106 pub path: ModPath,
107}
108
109impl Diagnostic for UnresolvedMacroCall {
110 fn code(&self) -> DiagnosticCode {
111 DiagnosticCode("unresolved-macro-call")
112 }
113 fn message(&self) -> String {
114 format!("unresolved macro `{}!`", self.path)
115 }
116 fn display_source(&self) -> InFile<SyntaxNodePtr> {
117 InFile::new(self.file, self.node.clone().into())
118 }
119 fn as_any(&self) -> &(dyn Any + Send + 'static) {
120 self
121 }
122 fn is_experimental(&self) -> bool {
123 true
124 }
125}
126
127// Diagnostic: inactive-code
128//
129// This diagnostic is shown for code with inactive `#[cfg]` attributes.
130#[derive(Debug, Clone, Eq, PartialEq)]
131pub struct InactiveCode {
132 pub file: HirFileId,
133 pub node: SyntaxNodePtr,
134 pub cfg: CfgExpr,
135 pub opts: CfgOptions,
136}
137
138impl Diagnostic for InactiveCode {
139 fn code(&self) -> DiagnosticCode {
140 DiagnosticCode("inactive-code")
141 }
142 fn message(&self) -> String {
143 let inactive = DnfExpr::new(self.cfg.clone()).why_inactive(&self.opts);
144 let mut buf = "code is inactive due to #[cfg] directives".to_string();
145
146 if let Some(inactive) = inactive {
147 format_to!(buf, ": {}", inactive);
148 }
149
150 buf
151 }
152 fn display_source(&self) -> InFile<SyntaxNodePtr> {
153 InFile::new(self.file, self.node.clone())
154 }
155 fn as_any(&self) -> &(dyn Any + Send + 'static) {
156 self
157 }
158}
159
160// Diagnostic: unresolved-proc-macro
161//
162// This diagnostic is shown when a procedural macro can not be found. This usually means that
163// procedural macro support is simply disabled (and hence is only a weak hint instead of an error),
164// but can also indicate project setup problems.
165//
166// If you are seeing a lot of "proc macro not expanded" warnings, you can add this option to the
167// `rust-analyzer.diagnostics.disabled` list to prevent them from showing. Alternatively you can
168// enable support for procedural macros (see `rust-analyzer.procMacro.enable`).
169#[derive(Debug, Clone, Eq, PartialEq)]
170pub struct UnresolvedProcMacro {
171 pub file: HirFileId,
172 pub node: SyntaxNodePtr,
173 /// If the diagnostic can be pinpointed more accurately than via `node`, this is the `TextRange`
174 /// to use instead.
175 pub precise_location: Option<TextRange>,
176 pub macro_name: Option<String>,
177}
178
179impl Diagnostic for UnresolvedProcMacro {
180 fn code(&self) -> DiagnosticCode {
181 DiagnosticCode("unresolved-proc-macro")
182 }
183
184 fn message(&self) -> String {
185 match &self.macro_name {
186 Some(name) => format!("proc macro `{}` not expanded", name),
187 None => "proc macro not expanded".to_string(),
188 }
189 }
190
191 fn display_source(&self) -> InFile<SyntaxNodePtr> {
192 InFile::new(self.file, self.node.clone())
193 }
194
195 fn as_any(&self) -> &(dyn Any + Send + 'static) {
196 self
197 }
198}
199
200// Diagnostic: macro-error
201//
202// This diagnostic is shown for macro expansion errors.
203#[derive(Debug, Clone, Eq, PartialEq)]
204pub struct MacroError {
205 pub file: HirFileId,
206 pub node: SyntaxNodePtr,
207 pub message: String,
208}
209
210impl Diagnostic for MacroError {
211 fn code(&self) -> DiagnosticCode {
212 DiagnosticCode("macro-error")
213 }
214 fn message(&self) -> String {
215 self.message.clone()
216 }
217 fn display_source(&self) -> InFile<SyntaxNodePtr> {
218 InFile::new(self.file, self.node.clone())
219 }
220 fn as_any(&self) -> &(dyn Any + Send + 'static) {
221 self
222 }
223 fn is_experimental(&self) -> bool {
224 // Newly added and not very well-tested, might contain false positives.
225 true
226 }
227}
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;
19pub mod type_ref; 19pub mod type_ref;
20pub mod builtin_type; 20pub mod builtin_type;
21pub mod builtin_attr; 21pub mod builtin_attr;
22pub mod diagnostics;
23pub mod per_ns; 22pub mod per_ns;
24pub mod item_scope; 23pub mod item_scope;
25 24
@@ -56,7 +55,6 @@ use std::{
56 sync::Arc, 55 sync::Arc,
57}; 56};
58 57
59use adt::VariantData;
60use base_db::{impl_intern_key, salsa, CrateId}; 58use base_db::{impl_intern_key, salsa, CrateId};
61use hir_expand::{ 59use hir_expand::{
62 ast_id_map::FileAstId, 60 ast_id_map::FileAstId,
@@ -67,15 +65,18 @@ use hir_expand::{
67use la_arena::Idx; 65use la_arena::Idx;
68use nameres::DefMap; 66use nameres::DefMap;
69use path::ModPath; 67use path::ModPath;
68use stdx::impl_from;
70use syntax::ast; 69use syntax::ast;
71 70
72use crate::attr::AttrId; 71use crate::{
73use crate::builtin_type::BuiltinType; 72 adt::VariantData,
74use item_tree::{ 73 attr::AttrId,
75 Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, ModItem, Static, Struct, Trait, 74 builtin_type::BuiltinType,
76 TypeAlias, Union, 75 item_tree::{
76 Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, ModItem, Static, Struct, Trait,
77 TypeAlias, Union,
78 },
77}; 79};
78use stdx::impl_from;
79 80
80#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 81#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
81pub struct ModuleId { 82pub 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 @@
47//! path and, upon success, we run macro expansion and "collect module" phase on 47//! path and, upon success, we run macro expansion and "collect module" phase on
48//! the result 48//! the result
49 49
50pub mod diagnostics;
50mod collector; 51mod collector;
51mod mod_resolution; 52mod mod_resolution;
52mod path_resolution; 53mod path_resolution;
54mod proc_macro;
53 55
54#[cfg(test)] 56#[cfg(test)]
55mod tests; 57mod tests;
56mod proc_macro;
57 58
58use std::sync::Arc; 59use std::sync::Arc;
59 60
60use base_db::{CrateId, Edition, FileId}; 61use base_db::{CrateId, Edition, FileId};
61use hir_expand::{diagnostics::DiagnosticSink, name::Name, InFile, MacroDefId}; 62use hir_expand::{name::Name, InFile, MacroDefId};
62use la_arena::Arena; 63use la_arena::Arena;
63use profile::Count; 64use profile::Count;
64use rustc_hash::FxHashMap; 65use rustc_hash::FxHashMap;
@@ -254,15 +255,6 @@ impl DefMap {
254 } 255 }
255 } 256 }
256 257
257 pub fn add_diagnostics(
258 &self,
259 db: &dyn DefDatabase,
260 module: LocalModuleId,
261 sink: &mut DiagnosticSink,
262 ) {
263 self.diagnostics.iter().for_each(|it| it.add_to(db, module, sink))
264 }
265
266 pub fn modules_for_file(&self, file_id: FileId) -> impl Iterator<Item = LocalModuleId> + '_ { 258 pub fn modules_for_file(&self, file_id: FileId) -> impl Iterator<Item = LocalModuleId> + '_ {
267 self.modules 259 self.modules
268 .iter() 260 .iter()
@@ -448,6 +440,11 @@ impl DefMap {
448 module.scope.shrink_to_fit(); 440 module.scope.shrink_to_fit();
449 } 441 }
450 } 442 }
443
444 /// Get a reference to the def map's diagnostics.
445 pub fn diagnostics(&self) -> &[DefDiagnostic] {
446 self.diagnostics.as_slice()
447 }
451} 448}
452 449
453impl ModuleData { 450impl ModuleData {
@@ -471,236 +468,3 @@ pub enum ModuleSource {
471 Module(ast::Module), 468 Module(ast::Module),
472 BlockExpr(ast::BlockExpr), 469 BlockExpr(ast::BlockExpr),
473} 470}
474
475mod diagnostics {
476 use cfg::{CfgExpr, CfgOptions};
477 use hir_expand::diagnostics::DiagnosticSink;
478 use hir_expand::hygiene::Hygiene;
479 use hir_expand::{InFile, MacroCallKind};
480 use syntax::ast::AttrsOwner;
481 use syntax::{ast, AstNode, AstPtr, SyntaxKind, SyntaxNodePtr};
482
483 use crate::path::ModPath;
484 use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId};
485
486 #[derive(Debug, PartialEq, Eq)]
487 enum DiagnosticKind {
488 UnresolvedModule { declaration: AstId<ast::Module>, candidate: String },
489
490 UnresolvedExternCrate { ast: AstId<ast::ExternCrate> },
491
492 UnresolvedImport { ast: AstId<ast::Use>, index: usize },
493
494 UnconfiguredCode { ast: AstId<ast::Item>, cfg: CfgExpr, opts: CfgOptions },
495
496 UnresolvedProcMacro { ast: MacroCallKind },
497
498 UnresolvedMacroCall { ast: AstId<ast::MacroCall>, path: ModPath },
499
500 MacroError { ast: MacroCallKind, message: String },
501 }
502
503 #[derive(Debug, PartialEq, Eq)]
504 pub(super) struct DefDiagnostic {
505 in_module: LocalModuleId,
506 kind: DiagnosticKind,
507 }
508
509 impl DefDiagnostic {
510 pub(super) fn unresolved_module(
511 container: LocalModuleId,
512 declaration: AstId<ast::Module>,
513 candidate: String,
514 ) -> Self {
515 Self {
516 in_module: container,
517 kind: DiagnosticKind::UnresolvedModule { declaration, candidate },
518 }
519 }
520
521 pub(super) fn unresolved_extern_crate(
522 container: LocalModuleId,
523 declaration: AstId<ast::ExternCrate>,
524 ) -> Self {
525 Self {
526 in_module: container,
527 kind: DiagnosticKind::UnresolvedExternCrate { ast: declaration },
528 }
529 }
530
531 pub(super) fn unresolved_import(
532 container: LocalModuleId,
533 ast: AstId<ast::Use>,
534 index: usize,
535 ) -> Self {
536 Self { in_module: container, kind: DiagnosticKind::UnresolvedImport { ast, index } }
537 }
538
539 pub(super) fn unconfigured_code(
540 container: LocalModuleId,
541 ast: AstId<ast::Item>,
542 cfg: CfgExpr,
543 opts: CfgOptions,
544 ) -> Self {
545 Self { in_module: container, kind: DiagnosticKind::UnconfiguredCode { ast, cfg, opts } }
546 }
547
548 pub(super) fn unresolved_proc_macro(container: LocalModuleId, ast: MacroCallKind) -> Self {
549 Self { in_module: container, kind: DiagnosticKind::UnresolvedProcMacro { ast } }
550 }
551
552 pub(super) fn macro_error(
553 container: LocalModuleId,
554 ast: MacroCallKind,
555 message: String,
556 ) -> Self {
557 Self { in_module: container, kind: DiagnosticKind::MacroError { ast, message } }
558 }
559
560 pub(super) fn unresolved_macro_call(
561 container: LocalModuleId,
562 ast: AstId<ast::MacroCall>,
563 path: ModPath,
564 ) -> Self {
565 Self { in_module: container, kind: DiagnosticKind::UnresolvedMacroCall { ast, path } }
566 }
567
568 pub(super) fn add_to(
569 &self,
570 db: &dyn DefDatabase,
571 target_module: LocalModuleId,
572 sink: &mut DiagnosticSink,
573 ) {
574 if self.in_module != target_module {
575 return;
576 }
577
578 match &self.kind {
579 DiagnosticKind::UnresolvedModule { declaration, candidate } => {
580 let decl = declaration.to_node(db.upcast());
581 sink.push(UnresolvedModule {
582 file: declaration.file_id,
583 decl: AstPtr::new(&decl),
584 candidate: candidate.clone(),
585 })
586 }
587
588 DiagnosticKind::UnresolvedExternCrate { ast } => {
589 let item = ast.to_node(db.upcast());
590 sink.push(UnresolvedExternCrate {
591 file: ast.file_id,
592 item: AstPtr::new(&item),
593 });
594 }
595
596 DiagnosticKind::UnresolvedImport { ast, index } => {
597 let use_item = ast.to_node(db.upcast());
598 let hygiene = Hygiene::new(db.upcast(), ast.file_id);
599 let mut cur = 0;
600 let mut tree = None;
601 ModPath::expand_use_item(
602 db,
603 InFile::new(ast.file_id, use_item),
604 &hygiene,
605 |_mod_path, use_tree, _is_glob, _alias| {
606 if cur == *index {
607 tree = Some(use_tree.clone());
608 }
609
610 cur += 1;
611 },
612 );
613
614 if let Some(tree) = tree {
615 sink.push(UnresolvedImport { file: ast.file_id, node: AstPtr::new(&tree) });
616 }
617 }
618
619 DiagnosticKind::UnconfiguredCode { ast, cfg, opts } => {
620 let item = ast.to_node(db.upcast());
621 sink.push(InactiveCode {
622 file: ast.file_id,
623 node: AstPtr::new(&item).into(),
624 cfg: cfg.clone(),
625 opts: opts.clone(),
626 });
627 }
628
629 DiagnosticKind::UnresolvedProcMacro { ast } => {
630 let mut precise_location = None;
631 let (file, ast, name) = match ast {
632 MacroCallKind::FnLike { ast_id, .. } => {
633 let node = ast_id.to_node(db.upcast());
634 (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)), None)
635 }
636 MacroCallKind::Derive { ast_id, derive_name, .. } => {
637 let node = ast_id.to_node(db.upcast());
638
639 // Compute the precise location of the macro name's token in the derive
640 // list.
641 // FIXME: This does not handle paths to the macro, but neither does the
642 // rest of r-a.
643 let derive_attrs =
644 node.attrs().filter_map(|attr| match attr.as_simple_call() {
645 Some((name, args)) if name == "derive" => Some(args),
646 _ => None,
647 });
648 'outer: for attr in derive_attrs {
649 let tokens =
650 attr.syntax().children_with_tokens().filter_map(|elem| {
651 match elem {
652 syntax::NodeOrToken::Node(_) => None,
653 syntax::NodeOrToken::Token(tok) => Some(tok),
654 }
655 });
656 for token in tokens {
657 if token.kind() == SyntaxKind::IDENT
658 && token.text() == derive_name.as_str()
659 {
660 precise_location = Some(token.text_range());
661 break 'outer;
662 }
663 }
664 }
665
666 (
667 ast_id.file_id,
668 SyntaxNodePtr::from(AstPtr::new(&node)),
669 Some(derive_name.clone()),
670 )
671 }
672 };
673 sink.push(UnresolvedProcMacro {
674 file,
675 node: ast,
676 precise_location,
677 macro_name: name,
678 });
679 }
680
681 DiagnosticKind::UnresolvedMacroCall { ast, path } => {
682 let node = ast.to_node(db.upcast());
683 sink.push(UnresolvedMacroCall {
684 file: ast.file_id,
685 node: AstPtr::new(&node),
686 path: path.clone(),
687 });
688 }
689
690 DiagnosticKind::MacroError { ast, message } => {
691 let (file, ast) = match ast {
692 MacroCallKind::FnLike { ast_id, .. } => {
693 let node = ast_id.to_node(db.upcast());
694 (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)))
695 }
696 MacroCallKind::Derive { ast_id, .. } => {
697 let node = ast_id.to_node(db.upcast());
698 (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)))
699 }
700 };
701 sink.push(MacroError { file, node: ast, message: message.clone() });
702 }
703 }
704 }
705 }
706}
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::{
33 }, 33 },
34 macro_call_as_call_id, 34 macro_call_as_call_id,
35 nameres::{ 35 nameres::{
36 diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint, 36 diagnostics::DefDiagnostic,
37 mod_resolution::ModDir,
38 path_resolution::ReachedFixedPoint,
39 proc_macro::{ProcMacroDef, ProcMacroKind},
37 BuiltinShadowMode, DefMap, ModuleData, ModuleOrigin, ResolveMode, 40 BuiltinShadowMode, DefMap, ModuleData, ModuleOrigin, ResolveMode,
38 }, 41 },
39 path::{ImportAlias, ModPath, PathKind}, 42 path::{ImportAlias, ModPath, PathKind},
@@ -44,8 +47,6 @@ use crate::{
44 UnresolvedMacro, 47 UnresolvedMacro,
45}; 48};
46 49
47use super::proc_macro::{ProcMacroDef, ProcMacroKind};
48
49const GLOB_RECURSION_LIMIT: usize = 100; 50const GLOB_RECURSION_LIMIT: usize = 100;
50const EXPANSION_DEPTH_LIMIT: usize = 128; 51const EXPANSION_DEPTH_LIMIT: usize = 128;
51const FIXED_POINT_LIMIT: usize = 8192; 52const 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 @@
1//! Diagnostics emitted during DefMap construction.
2
3use cfg::{CfgExpr, CfgOptions};
4use hir_expand::MacroCallKind;
5use syntax::ast;
6
7use crate::{nameres::LocalModuleId, path::ModPath, AstId};
8
9#[derive(Debug, PartialEq, Eq)]
10pub enum DefDiagnosticKind {
11 UnresolvedModule { ast: AstId<ast::Module>, candidate: String },
12
13 UnresolvedExternCrate { ast: AstId<ast::ExternCrate> },
14
15 UnresolvedImport { ast: AstId<ast::Use>, index: usize },
16
17 UnconfiguredCode { ast: AstId<ast::Item>, cfg: CfgExpr, opts: CfgOptions },
18
19 UnresolvedProcMacro { ast: MacroCallKind },
20
21 UnresolvedMacroCall { ast: AstId<ast::MacroCall>, path: ModPath },
22
23 MacroError { ast: MacroCallKind, message: String },
24}
25
26#[derive(Debug, PartialEq, Eq)]
27pub struct DefDiagnostic {
28 pub in_module: LocalModuleId,
29 pub kind: DefDiagnosticKind,
30}
31
32impl DefDiagnostic {
33 pub(super) fn unresolved_module(
34 container: LocalModuleId,
35 declaration: AstId<ast::Module>,
36 candidate: String,
37 ) -> Self {
38 Self {
39 in_module: container,
40 kind: DefDiagnosticKind::UnresolvedModule { ast: declaration, candidate },
41 }
42 }
43
44 pub(super) fn unresolved_extern_crate(
45 container: LocalModuleId,
46 declaration: AstId<ast::ExternCrate>,
47 ) -> Self {
48 Self {
49 in_module: container,
50 kind: DefDiagnosticKind::UnresolvedExternCrate { ast: declaration },
51 }
52 }
53
54 pub(super) fn unresolved_import(
55 container: LocalModuleId,
56 ast: AstId<ast::Use>,
57 index: usize,
58 ) -> Self {
59 Self { in_module: container, kind: DefDiagnosticKind::UnresolvedImport { ast, index } }
60 }
61
62 pub(super) fn unconfigured_code(
63 container: LocalModuleId,
64 ast: AstId<ast::Item>,
65 cfg: CfgExpr,
66 opts: CfgOptions,
67 ) -> Self {
68 Self { in_module: container, kind: DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } }
69 }
70
71 pub(super) fn unresolved_proc_macro(container: LocalModuleId, ast: MacroCallKind) -> Self {
72 Self { in_module: container, kind: DefDiagnosticKind::UnresolvedProcMacro { ast } }
73 }
74
75 pub(super) fn macro_error(
76 container: LocalModuleId,
77 ast: MacroCallKind,
78 message: String,
79 ) -> Self {
80 Self { in_module: container, kind: DefDiagnosticKind::MacroError { ast, message } }
81 }
82
83 pub(super) fn unresolved_macro_call(
84 container: LocalModuleId,
85 ast: AstId<ast::MacroCall>,
86 path: ModPath,
87 ) -> Self {
88 Self { in_module: container, kind: DefDiagnosticKind::UnresolvedMacroCall { ast, path } }
89 }
90}
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,7 +18,7 @@ fn unresolved_import() {
18 r" 18 r"
19 use does_exist; 19 use does_exist;
20 use does_not_exist; 20 use does_not_exist;
21 //^^^^^^^^^^^^^^ unresolved import 21 //^^^^^^^^^^^^^^^^^^^ UnresolvedImport
22 22
23 mod does_exist {} 23 mod does_exist {}
24 ", 24 ",
@@ -26,40 +26,13 @@ fn unresolved_import() {
26} 26}
27 27
28#[test] 28#[test]
29fn unresolved_import_in_use_tree() {
30 // Only the relevant part of a nested `use` item should be highlighted.
31 check_diagnostics(
32 r"
33 use does_exist::{Exists, DoesntExist};
34 //^^^^^^^^^^^ unresolved import
35
36 use {does_not_exist::*, does_exist};
37 //^^^^^^^^^^^^^^^^^ unresolved import
38
39 use does_not_exist::{
40 a,
41 //^ unresolved import
42 b,
43 //^ unresolved import
44 c,
45 //^ unresolved import
46 };
47
48 mod does_exist {
49 pub struct Exists;
50 }
51 ",
52 );
53}
54
55#[test]
56fn unresolved_extern_crate() { 29fn unresolved_extern_crate() {
57 check_diagnostics( 30 check_diagnostics(
58 r" 31 r"
59 //- /main.rs crate:main deps:core 32 //- /main.rs crate:main deps:core
60 extern crate core; 33 extern crate core;
61 extern crate doesnotexist; 34 extern crate doesnotexist;
62 //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate 35 //^^^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedExternCrate
63 //- /lib.rs crate:core 36 //- /lib.rs crate:core
64 ", 37 ",
65 ); 38 );
@@ -72,7 +45,7 @@ fn extern_crate_self_as() {
72 r" 45 r"
73 //- /lib.rs 46 //- /lib.rs
74 extern crate doesnotexist; 47 extern crate doesnotexist;
75 //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate 48 //^^^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedExternCrate
76 // Should not error. 49 // Should not error.
77 extern crate self as foo; 50 extern crate self as foo;
78 struct Foo; 51 struct Foo;
@@ -88,18 +61,18 @@ fn dedup_unresolved_import_from_unresolved_crate() {
88 //- /main.rs crate:main 61 //- /main.rs crate:main
89 mod a { 62 mod a {
90 extern crate doesnotexist; 63 extern crate doesnotexist;
91 //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate 64 //^^^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedExternCrate
92 65
93 // Should not error, since we already errored for the missing crate. 66 // Should not error, since we already errored for the missing crate.
94 use doesnotexist::{self, bla, *}; 67 use doesnotexist::{self, bla, *};
95 68
96 use crate::doesnotexist; 69 use crate::doesnotexist;
97 //^^^^^^^^^^^^^^^^^^^ unresolved import 70 //^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedImport
98 } 71 }
99 72
100 mod m { 73 mod m {
101 use super::doesnotexist; 74 use super::doesnotexist;
102 //^^^^^^^^^^^^^^^^^^^ unresolved import 75 //^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedImport
103 } 76 }
104 ", 77 ",
105 ); 78 );
@@ -112,7 +85,7 @@ fn unresolved_module() {
112 //- /lib.rs 85 //- /lib.rs
113 mod foo; 86 mod foo;
114 mod bar; 87 mod bar;
115 //^^^^^^^^ unresolved module 88 //^^^^^^^^ UnresolvedModule
116 mod baz {} 89 mod baz {}
117 //- /foo.rs 90 //- /foo.rs
118 ", 91 ",
@@ -127,16 +100,16 @@ fn inactive_item() {
127 r#" 100 r#"
128 //- /lib.rs 101 //- /lib.rs
129 #[cfg(no)] pub fn f() {} 102 #[cfg(no)] pub fn f() {}
130 //^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled 103 //^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
131 104
132 #[cfg(no)] #[cfg(no2)] mod m; 105 #[cfg(no)] #[cfg(no2)] mod m;
133 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no and no2 are disabled 106 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
134 107
135 #[cfg(all(not(a), b))] enum E {} 108 #[cfg(all(not(a), b))] enum E {}
136 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: b is disabled 109 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
137 110
138 #[cfg(feature = "std")] use std; 111 #[cfg(feature = "std")] use std;
139 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: feature = "std" is disabled 112 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
140 "#, 113 "#,
141 ); 114 );
142} 115}
@@ -149,14 +122,14 @@ fn inactive_via_cfg_attr() {
149 r#" 122 r#"
150 //- /lib.rs 123 //- /lib.rs
151 #[cfg_attr(not(never), cfg(no))] fn f() {} 124 #[cfg_attr(not(never), cfg(no))] fn f() {}
152 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled 125 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
153 126
154 #[cfg_attr(not(never), cfg(not(no)))] fn f() {} 127 #[cfg_attr(not(never), cfg(not(no)))] fn f() {}
155 128
156 #[cfg_attr(never, cfg(no))] fn g() {} 129 #[cfg_attr(never, cfg(no))] fn g() {}
157 130
158 #[cfg_attr(not(never), inline, cfg(no))] fn h() {} 131 #[cfg_attr(not(never), inline, cfg(no))] fn h() {}
159 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled 132 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
160 "#, 133 "#,
161 ); 134 );
162} 135}
@@ -170,7 +143,7 @@ fn unresolved_legacy_scope_macro() {
170 143
171 m!(); 144 m!();
172 m2!(); 145 m2!();
173 //^^^^^^ unresolved macro `self::m2!` 146 //^^^^^^ UnresolvedMacroCall
174 "#, 147 "#,
175 ); 148 );
176} 149}
@@ -187,7 +160,7 @@ fn unresolved_module_scope_macro() {
187 160
188 self::m!(); 161 self::m!();
189 self::m2!(); 162 self::m2!();
190 //^^^^^^^^^^^^ unresolved macro `self::m2!` 163 //^^^^^^^^^^^^ UnresolvedMacroCall
191 "#, 164 "#,
192 ); 165 );
193} 166}
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 {
71 } 71 }
72 72
73 /// Calls `cb` with all paths, represented by this use item. 73 /// Calls `cb` with all paths, represented by this use item.
74 pub(crate) fn expand_use_item( 74 pub fn expand_use_item(
75 db: &dyn DefDatabase, 75 db: &dyn DefDatabase,
76 item_src: InFile<ast::Use>, 76 item_src: InFile<ast::Use>,
77 hygiene: &Hygiene, 77 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::{
5 sync::{Arc, Mutex}, 5 sync::{Arc, Mutex},
6}; 6};
7 7
8use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition, Upcast}; 8use base_db::{
9 salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition, FileRange, Upcast,
10};
9use base_db::{AnchoredPath, SourceDatabase}; 11use base_db::{AnchoredPath, SourceDatabase};
10use hir_expand::diagnostics::Diagnostic;
11use hir_expand::diagnostics::DiagnosticSinkBuilder;
12use hir_expand::{db::AstDatabase, InFile}; 12use hir_expand::{db::AstDatabase, InFile};
13use rustc_hash::FxHashMap; 13use rustc_hash::FxHashMap;
14use rustc_hash::FxHashSet; 14use rustc_hash::FxHashSet;
15use syntax::{algo, ast, AstNode, TextRange, TextSize}; 15use syntax::{algo, ast, AstNode, SyntaxNode, SyntaxNodePtr, TextRange, TextSize};
16use test_utils::extract_annotations; 16use test_utils::extract_annotations;
17 17
18use crate::{ 18use crate::{
19 body::BodyDiagnostic,
19 db::DefDatabase, 20 db::DefDatabase,
20 nameres::{DefMap, ModuleSource}, 21 nameres::{diagnostics::DefDiagnosticKind, DefMap, ModuleSource},
21 src::HasSource, 22 src::HasSource,
22 LocalModuleId, Lookup, ModuleDefId, ModuleId, 23 LocalModuleId, Lookup, ModuleDefId, ModuleId,
23}; 24};
@@ -262,19 +263,70 @@ impl TestDB {
262 .collect() 263 .collect()
263 } 264 }
264 265
265 pub(crate) fn diagnostics<F: FnMut(&dyn Diagnostic)>(&self, mut cb: F) { 266 pub(crate) fn diagnostics(&self, cb: &mut dyn FnMut(FileRange, String)) {
266 let crate_graph = self.crate_graph(); 267 let crate_graph = self.crate_graph();
267 for krate in crate_graph.iter() { 268 for krate in crate_graph.iter() {
268 let crate_def_map = self.crate_def_map(krate); 269 let crate_def_map = self.crate_def_map(krate);
269 270
270 let mut sink = DiagnosticSinkBuilder::new().build(&mut cb); 271 for diag in crate_def_map.diagnostics() {
271 for (module_id, module) in crate_def_map.modules() { 272 let (node, message): (InFile<SyntaxNode>, &str) = match &diag.kind {
272 crate_def_map.add_diagnostics(self, module_id, &mut sink); 273 DefDiagnosticKind::UnresolvedModule { ast, .. } => {
274 let node = ast.to_node(self.upcast());
275 (InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedModule")
276 }
277 DefDiagnosticKind::UnresolvedExternCrate { ast, .. } => {
278 let node = ast.to_node(self.upcast());
279 (InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedExternCrate")
280 }
281 DefDiagnosticKind::UnresolvedImport { ast, .. } => {
282 let node = ast.to_node(self.upcast());
283 (InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedImport")
284 }
285 DefDiagnosticKind::UnconfiguredCode { ast, .. } => {
286 let node = ast.to_node(self.upcast());
287 (InFile::new(ast.file_id, node.syntax().clone()), "UnconfiguredCode")
288 }
289 DefDiagnosticKind::UnresolvedProcMacro { ast, .. } => {
290 (ast.to_node(self.upcast()), "UnresolvedProcMacro")
291 }
292 DefDiagnosticKind::UnresolvedMacroCall { ast, .. } => {
293 let node = ast.to_node(self.upcast());
294 (InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedMacroCall")
295 }
296 DefDiagnosticKind::MacroError { ast, message } => {
297 (ast.to_node(self.upcast()), message.as_str())
298 }
299 };
300
301 let frange = node.as_ref().original_file_range(self);
302 cb(frange, message.to_string())
303 }
273 304
305 for (_module_id, module) in crate_def_map.modules() {
274 for decl in module.scope.declarations() { 306 for decl in module.scope.declarations() {
275 if let ModuleDefId::FunctionId(it) = decl { 307 if let ModuleDefId::FunctionId(it) = decl {
276 let source_map = self.body_with_source_map(it.into()).1; 308 let source_map = self.body_with_source_map(it.into()).1;
277 source_map.add_diagnostics(self, &mut sink); 309 for diag in source_map.diagnostics() {
310 let (ptr, message): (InFile<SyntaxNodePtr>, &str) = match diag {
311 BodyDiagnostic::InactiveCode { node, .. } => {
312 (node.clone().map(|it| it.into()), "InactiveCode")
313 }
314 BodyDiagnostic::MacroError { node, message } => {
315 (node.clone().map(|it| it.into()), message.as_str())
316 }
317 BodyDiagnostic::UnresolvedProcMacro { node } => {
318 (node.clone().map(|it| it.into()), "UnresolvedProcMacro")
319 }
320 BodyDiagnostic::UnresolvedMacroCall { node, .. } => {
321 (node.clone().map(|it| it.into()), "UnresolvedMacroCall")
322 }
323 };
324
325 let root = self.parse_or_expand(ptr.file_id).unwrap();
326 let node = ptr.map(|ptr| ptr.to_node(&root));
327 let frange = node.as_ref().original_file_range(self);
328 cb(frange, message.to_string())
329 }
278 } 330 }
279 } 331 }
280 } 332 }
@@ -287,14 +339,7 @@ impl TestDB {
287 assert!(!annotations.is_empty()); 339 assert!(!annotations.is_empty());
288 340
289 let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default(); 341 let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default();
290 db.diagnostics(|d| { 342 db.diagnostics(&mut |frange, message| {
291 let src = d.display_source();
292 let root = db.parse_or_expand(src.file_id).unwrap();
293
294 let node = src.map(|ptr| ptr.to_node(&root));
295 let frange = node.as_ref().original_file_range(db);
296
297 let message = d.message();
298 actual.entry(frange.file_id).or_default().push((frange.range, message)); 343 actual.entry(frange.file_id).or_default().push((frange.range, message));
299 }); 344 });
300 345
@@ -319,7 +364,7 @@ impl TestDB {
319 assert!(annotations.is_empty()); 364 assert!(annotations.is_empty());
320 365
321 let mut has_diagnostics = false; 366 let mut has_diagnostics = false;
322 db.diagnostics(|_| { 367 db.diagnostics(&mut |_, _| {
323 has_diagnostics = true; 368 has_diagnostics = true;
324 }); 369 });
325 370
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index 625c26f0a..e8f4af309 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -186,7 +186,7 @@ fn parse_macro_expansion(
186 // The final goal we would like to make all parse_macro success, 186 // The final goal we would like to make all parse_macro success,
187 // such that the following log will not call anyway. 187 // such that the following log will not call anyway.
188 let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id); 188 let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id);
189 let node = loc.kind.node(db); 189 let node = loc.kind.to_node(db);
190 190
191 // collect parent information for warning log 191 // collect parent information for warning log
192 let parents = 192 let parents =
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index 6be4516a3..10d37234e 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -8,7 +8,6 @@ pub mod db;
8pub mod ast_id_map; 8pub mod ast_id_map;
9pub mod name; 9pub mod name;
10pub mod hygiene; 10pub mod hygiene;
11pub mod diagnostics;
12pub mod builtin_derive; 11pub mod builtin_derive;
13pub mod builtin_macro; 12pub mod builtin_macro;
14pub mod proc_macro; 13pub mod proc_macro;
@@ -108,7 +107,7 @@ impl HirFileId {
108 HirFileIdRepr::FileId(_) => None, 107 HirFileIdRepr::FileId(_) => None,
109 HirFileIdRepr::MacroFile(macro_file) => { 108 HirFileIdRepr::MacroFile(macro_file) => {
110 let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id); 109 let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id);
111 Some(loc.kind.node(db)) 110 Some(loc.kind.to_node(db))
112 } 111 }
113 } 112 }
114 } 113 }
@@ -153,7 +152,7 @@ impl HirFileId {
153 HirFileIdRepr::MacroFile(macro_file) => { 152 HirFileIdRepr::MacroFile(macro_file) => {
154 let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id); 153 let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id);
155 let item = match loc.def.kind { 154 let item = match loc.def.kind {
156 MacroDefKind::BuiltInDerive(..) => loc.kind.node(db), 155 MacroDefKind::BuiltInDerive(..) => loc.kind.to_node(db),
157 _ => return None, 156 _ => return None,
158 }; 157 };
159 Some(item.with_value(ast::Item::cast(item.value.clone())?)) 158 Some(item.with_value(ast::Item::cast(item.value.clone())?))
@@ -269,7 +268,7 @@ impl MacroCallKind {
269 } 268 }
270 } 269 }
271 270
272 fn node(&self, db: &dyn db::AstDatabase) -> InFile<SyntaxNode> { 271 pub fn to_node(&self, db: &dyn db::AstDatabase) -> InFile<SyntaxNode> {
273 match self { 272 match self {
274 MacroCallKind::FnLike { ast_id, .. } => { 273 MacroCallKind::FnLike { ast_id, .. } => {
275 ast_id.with_value(ast_id.to_node(db).syntax().clone()) 274 ast_id.with_value(ast_id.to_node(db).syntax().clone())
diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs
index 84fc8ce14..7598e2193 100644
--- a/crates/hir_ty/src/diagnostics.rs
+++ b/crates/hir_ty/src/diagnostics.rs
@@ -8,12 +8,14 @@ use std::{any::Any, fmt};
8 8
9use base_db::CrateId; 9use base_db::CrateId;
10use hir_def::{DefWithBodyId, ModuleDefId}; 10use hir_def::{DefWithBodyId, ModuleDefId};
11use hir_expand::diagnostics::{Diagnostic, DiagnosticCode, DiagnosticSink};
12use hir_expand::{name::Name, HirFileId, InFile}; 11use hir_expand::{name::Name, HirFileId, InFile};
13use stdx::format_to; 12use stdx::format_to;
14use syntax::{ast, AstPtr, SyntaxNodePtr}; 13use syntax::{ast, AstPtr, SyntaxNodePtr};
15 14
16use crate::db::HirDatabase; 15use crate::{
16 db::HirDatabase,
17 diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink},
18};
17 19
18pub use crate::diagnostics::expr::{record_literal_missing_fields, record_pattern_missing_fields}; 20pub use crate::diagnostics::expr::{record_literal_missing_fields, record_pattern_missing_fields};
19 21
@@ -446,15 +448,13 @@ impl Diagnostic for ReplaceFilterMapNextWithFindMap {
446mod tests { 448mod tests {
447 use base_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt}; 449 use base_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt};
448 use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId}; 450 use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId};
449 use hir_expand::{ 451 use hir_expand::db::AstDatabase;
450 db::AstDatabase,
451 diagnostics::{Diagnostic, DiagnosticSinkBuilder},
452 };
453 use rustc_hash::FxHashMap; 452 use rustc_hash::FxHashMap;
454 use syntax::{TextRange, TextSize}; 453 use syntax::{TextRange, TextSize};
455 454
456 use crate::{ 455 use crate::{
457 diagnostics::{validate_body, validate_module_item}, 456 diagnostics::{validate_body, validate_module_item},
457 diagnostics_sink::{Diagnostic, DiagnosticSinkBuilder},
458 test_db::TestDB, 458 test_db::TestDB,
459 }; 459 };
460 460
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs
index 075dc4131..ef982cbcd 100644
--- a/crates/hir_ty/src/diagnostics/decl_check.rs
+++ b/crates/hir_ty/src/diagnostics/decl_check.rs
@@ -19,10 +19,7 @@ use hir_def::{
19 src::HasSource, 19 src::HasSource,
20 AdtId, AttrDefId, ConstId, EnumId, FunctionId, Lookup, ModuleDefId, StaticId, StructId, 20 AdtId, AttrDefId, ConstId, EnumId, FunctionId, Lookup, ModuleDefId, StaticId, StructId,
21}; 21};
22use hir_expand::{ 22use hir_expand::name::{AsName, Name};
23 diagnostics::DiagnosticSink,
24 name::{AsName, Name},
25};
26use stdx::{always, never}; 23use stdx::{always, never};
27use syntax::{ 24use syntax::{
28 ast::{self, NameOwner}, 25 ast::{self, NameOwner},
@@ -32,6 +29,7 @@ use syntax::{
32use crate::{ 29use crate::{
33 db::HirDatabase, 30 db::HirDatabase,
34 diagnostics::{decl_check::case_conv::*, CaseType, IdentType, IncorrectCase}, 31 diagnostics::{decl_check::case_conv::*, CaseType, IdentType, IncorrectCase},
32 diagnostics_sink::DiagnosticSink,
35}; 33};
36 34
37mod allow { 35mod allow {
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs
index d1f113e7f..86f82e3fa 100644
--- a/crates/hir_ty/src/diagnostics/expr.rs
+++ b/crates/hir_ty/src/diagnostics/expr.rs
@@ -5,7 +5,7 @@
5use std::sync::Arc; 5use std::sync::Arc;
6 6
7use hir_def::{expr::Statement, path::path, resolver::HasResolver, AssocItemId, DefWithBodyId}; 7use hir_def::{expr::Statement, path::path, resolver::HasResolver, AssocItemId, DefWithBodyId};
8use hir_expand::{diagnostics::DiagnosticSink, name}; 8use hir_expand::name;
9use rustc_hash::FxHashSet; 9use rustc_hash::FxHashSet;
10use syntax::{ast, AstPtr}; 10use syntax::{ast, AstPtr};
11 11
@@ -16,6 +16,7 @@ use crate::{
16 MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr, 16 MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
17 MissingPatFields, RemoveThisSemicolon, 17 MissingPatFields, RemoveThisSemicolon,
18 }, 18 },
19 diagnostics_sink::DiagnosticSink,
19 AdtId, InferenceResult, Interner, TyExt, TyKind, 20 AdtId, InferenceResult, Interner, TyExt, TyKind,
20}; 21};
21 22
diff --git a/crates/hir_ty/src/diagnostics/unsafe_check.rs b/crates/hir_ty/src/diagnostics/unsafe_check.rs
index 5d13bddea..c3c483425 100644
--- a/crates/hir_ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir_ty/src/diagnostics/unsafe_check.rs
@@ -9,10 +9,10 @@ use hir_def::{
9 resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, 9 resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
10 DefWithBodyId, 10 DefWithBodyId,
11}; 11};
12use hir_expand::diagnostics::DiagnosticSink;
13 12
14use crate::{ 13use crate::{
15 db::HirDatabase, diagnostics::MissingUnsafe, InferenceResult, Interner, TyExt, TyKind, 14 db::HirDatabase, diagnostics::MissingUnsafe, diagnostics_sink::DiagnosticSink, InferenceResult,
15 Interner, TyExt, TyKind,
16}; 16};
17 17
18pub(super) struct UnsafeValidator<'a, 'b: 'a> { 18pub(super) struct UnsafeValidator<'a, 'b: 'a> {
diff --git a/crates/hir_expand/src/diagnostics.rs b/crates/hir_ty/src/diagnostics_sink.rs
index bf0b85ce9..084fa8b06 100644
--- a/crates/hir_expand/src/diagnostics.rs
+++ b/crates/hir_ty/src/diagnostics_sink.rs
@@ -16,10 +16,9 @@
16 16
17use std::{any::Any, fmt}; 17use std::{any::Any, fmt};
18 18
19use hir_expand::InFile;
19use syntax::SyntaxNodePtr; 20use syntax::SyntaxNodePtr;
20 21
21use crate::InFile;
22
23#[derive(Copy, Clone, Debug, PartialEq)] 22#[derive(Copy, Clone, Debug, PartialEq)]
24pub struct DiagnosticCode(pub &'static str); 23pub struct DiagnosticCode(pub &'static str);
25 24
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index db3c937ff..174b7471e 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -28,13 +28,14 @@ use hir_def::{
28 AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule, Lookup, 28 AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule, Lookup,
29 TraitId, TypeAliasId, VariantId, 29 TraitId, TypeAliasId, VariantId,
30}; 30};
31use hir_expand::{diagnostics::DiagnosticSink, name::name}; 31use hir_expand::name::name;
32use la_arena::ArenaMap; 32use la_arena::ArenaMap;
33use rustc_hash::FxHashMap; 33use rustc_hash::FxHashMap;
34use stdx::impl_from; 34use stdx::impl_from;
35use syntax::SmolStr; 35use syntax::SmolStr;
36 36
37use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty}; 37use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty};
38use crate::diagnostics_sink::DiagnosticSink;
38use crate::{ 39use crate::{
39 db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic, 40 db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic,
40 lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, Substitution, 41 lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, Substitution,
@@ -793,11 +794,11 @@ impl std::ops::BitOrAssign for Diverges {
793 794
794mod diagnostics { 795mod diagnostics {
795 use hir_def::{expr::ExprId, DefWithBodyId}; 796 use hir_def::{expr::ExprId, DefWithBodyId};
796 use hir_expand::diagnostics::DiagnosticSink;
797 797
798 use crate::{ 798 use crate::{
799 db::HirDatabase, 799 db::HirDatabase,
800 diagnostics::{BreakOutsideOfLoop, NoSuchField}, 800 diagnostics::{BreakOutsideOfLoop, NoSuchField},
801 diagnostics_sink::DiagnosticSink,
801 }; 802 };
802 803
803 #[derive(Debug, PartialEq, Eq, Clone)] 804 #[derive(Debug, PartialEq, Eq, Clone)]
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs
index ef021978a..50e0d6333 100644
--- a/crates/hir_ty/src/lib.rs
+++ b/crates/hir_ty/src/lib.rs
@@ -21,6 +21,7 @@ mod utils;
21mod walk; 21mod walk;
22pub mod db; 22pub mod db;
23pub mod diagnostics; 23pub mod diagnostics;
24pub mod diagnostics_sink;
24pub mod display; 25pub mod display;
25pub mod method_resolution; 26pub mod method_resolution;
26pub mod primitive; 27pub mod primitive;
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 27d347dbd..dcac7c76d 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -299,10 +299,10 @@ fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist {
299 299
300#[cfg(test)] 300#[cfg(test)]
301mod tests { 301mod tests {
302 use expect_test::{expect, Expect}; 302 use expect_test::Expect;
303 use ide_assists::AssistResolveStrategy; 303 use ide_assists::AssistResolveStrategy;
304 use stdx::trim_indent; 304 use stdx::trim_indent;
305 use test_utils::assert_eq_text; 305 use test_utils::{assert_eq_text, extract_annotations};
306 306
307 use crate::{fixture, DiagnosticsConfig}; 307 use crate::{fixture, DiagnosticsConfig};
308 308
@@ -396,26 +396,51 @@ mod tests {
396 expect.assert_debug_eq(&diagnostics) 396 expect.assert_debug_eq(&diagnostics)
397 } 397 }
398 398
399 pub(crate) fn check_diagnostics(ra_fixture: &str) {
400 let (analysis, file_id) = fixture::file(ra_fixture);
401 let diagnostics = analysis
402 .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id)
403 .unwrap();
404
405 let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
406 let actual = diagnostics.into_iter().map(|d| (d.range, d.message)).collect::<Vec<_>>();
407 assert_eq!(expected, actual);
408 }
409
399 #[test] 410 #[test]
400 fn test_unresolved_macro_range() { 411 fn test_unresolved_macro_range() {
401 check_expect( 412 check_diagnostics(
402 r#"foo::bar!(92);"#, 413 r#"
403 expect![[r#" 414foo::bar!(92);
404 [ 415 //^^^ unresolved macro `foo::bar!`
405 Diagnostic { 416"#,
406 message: "unresolved macro `foo::bar!`", 417 );
407 range: 5..8, 418 }
408 severity: Error, 419
409 fixes: None, 420 #[test]
410 unused: false, 421 fn unresolved_import_in_use_tree() {
411 code: Some( 422 // Only the relevant part of a nested `use` item should be highlighted.
412 DiagnosticCode( 423 check_diagnostics(
413 "unresolved-macro-call", 424 r#"
414 ), 425use does_exist::{Exists, DoesntExist};
415 ), 426 //^^^^^^^^^^^ unresolved import
416 }, 427
417 ] 428use {does_not_exist::*, does_exist};
418 "#]], 429 //^^^^^^^^^^^^^^^^^ unresolved import
430
431use does_not_exist::{
432 a,
433 //^ unresolved import
434 b,
435 //^ unresolved import
436 c,
437 //^ unresolved import
438};
439
440mod does_exist {
441 pub struct Exists;
442}
443"#,
419 ); 444 );
420 } 445 }
421 446