]> git.lizzy.rs Git - rust.git/blob - crates/hir_def/src/diagnostics.rs
Merge #6924
[rust.git] / crates / hir_def / src / diagnostics.rs
1 //! Diagnostics produced by `hir_def`.
2
3 use std::any::Any;
4 use stdx::format_to;
5
6 use cfg::{CfgExpr, CfgOptions, DnfExpr};
7 use hir_expand::diagnostics::{Diagnostic, DiagnosticCode, DiagnosticSink};
8 use hir_expand::{HirFileId, InFile};
9 use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
10
11 use crate::{db::DefDatabase, DefWithBodyId};
12
13 pub 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)]
22 pub struct UnresolvedModule {
23     pub file: HirFileId,
24     pub decl: AstPtr<ast::Module>,
25     pub candidate: String,
26 }
27
28 impl 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)]
47 pub struct UnresolvedExternCrate {
48     pub file: HirFileId,
49     pub item: AstPtr<ast::ExternCrate>,
50 }
51
52 impl 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)]
71 pub struct UnresolvedImport {
72     pub file: HirFileId,
73     pub node: AstPtr<ast::UseTree>,
74 }
75
76 impl 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: inactive-code
99 //
100 // This diagnostic is shown for code with inactive `#[cfg]` attributes.
101 #[derive(Debug, Clone, Eq, PartialEq)]
102 pub struct InactiveCode {
103     pub file: HirFileId,
104     pub node: SyntaxNodePtr,
105     pub cfg: CfgExpr,
106     pub opts: CfgOptions,
107 }
108
109 impl Diagnostic for InactiveCode {
110     fn code(&self) -> DiagnosticCode {
111         DiagnosticCode("inactive-code")
112     }
113     fn message(&self) -> String {
114         let inactive = DnfExpr::new(self.cfg.clone()).why_inactive(&self.opts);
115         let mut buf = "code is inactive due to #[cfg] directives".to_string();
116
117         if let Some(inactive) = inactive {
118             format_to!(buf, ": {}", inactive);
119         }
120
121         buf
122     }
123     fn display_source(&self) -> InFile<SyntaxNodePtr> {
124         InFile::new(self.file, self.node.clone())
125     }
126     fn as_any(&self) -> &(dyn Any + Send + 'static) {
127         self
128     }
129 }
130
131 // Diagnostic: unresolved-proc-macro
132 //
133 // This diagnostic is shown when a procedural macro can not be found. This usually means that
134 // procedural macro support is simply disabled (and hence is only a weak hint instead of an error),
135 // but can also indicate project setup problems.
136 #[derive(Debug, Clone, Eq, PartialEq)]
137 pub struct UnresolvedProcMacro {
138     pub file: HirFileId,
139     pub node: SyntaxNodePtr,
140     /// If the diagnostic can be pinpointed more accurately than via `node`, this is the `TextRange`
141     /// to use instead.
142     pub precise_location: Option<TextRange>,
143     pub macro_name: Option<String>,
144 }
145
146 impl Diagnostic for UnresolvedProcMacro {
147     fn code(&self) -> DiagnosticCode {
148         DiagnosticCode("unresolved-proc-macro")
149     }
150
151     fn message(&self) -> String {
152         match &self.macro_name {
153             Some(name) => format!("proc macro `{}` not expanded", name),
154             None => "proc macro not expanded".to_string(),
155         }
156     }
157
158     fn display_source(&self) -> InFile<SyntaxNodePtr> {
159         InFile::new(self.file, self.node.clone())
160     }
161
162     fn as_any(&self) -> &(dyn Any + Send + 'static) {
163         self
164     }
165 }
166
167 // Diagnostic: macro-error
168 //
169 // This diagnostic is shown for macro expansion errors.
170 #[derive(Debug, Clone, Eq, PartialEq)]
171 pub struct MacroError {
172     pub file: HirFileId,
173     pub node: SyntaxNodePtr,
174     pub message: String,
175 }
176
177 impl Diagnostic for MacroError {
178     fn code(&self) -> DiagnosticCode {
179         DiagnosticCode("macro-error")
180     }
181     fn message(&self) -> String {
182         self.message.clone()
183     }
184     fn display_source(&self) -> InFile<SyntaxNodePtr> {
185         InFile::new(self.file, self.node.clone())
186     }
187     fn as_any(&self) -> &(dyn Any + Send + 'static) {
188         self
189     }
190     fn is_experimental(&self) -> bool {
191         // Newly added and not very well-tested, might contain false positives.
192         true
193     }
194 }