1 //! Re-export diagnostics such that clients of `hir` don't have to depend on
4 //! This probably isn't the best way to do this -- ideally, diagnistics should
5 //! be expressed in terms of hir types themselves.
8 use cfg::{CfgExpr, CfgOptions, DnfExpr};
10 use hir_def::path::ModPath;
11 use hir_expand::{name::Name, HirFileId, InFile};
13 use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
15 pub use crate::diagnostics_sink::{
16 Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder,
19 macro_rules! diagnostics {
20 ($($diag:ident),*) => {
21 pub enum AnyDiagnostic {$(
26 impl From<$diag> for AnyDiagnostic {
27 fn from(d: $diag) -> AnyDiagnostic {
28 AnyDiagnostic::$diag(Box::new(d))
35 diagnostics![UnresolvedModule, MissingFields];
38 pub struct UnresolvedModule {
39 pub decl: InFile<AstPtr<ast::Module>>,
40 pub candidate: String,
43 // Diagnostic: unresolved-extern-crate
45 // This diagnostic is triggered if rust-analyzer is unable to discover referred extern crate.
47 pub struct UnresolvedExternCrate {
49 pub item: AstPtr<ast::ExternCrate>,
52 impl Diagnostic for UnresolvedExternCrate {
53 fn code(&self) -> DiagnosticCode {
54 DiagnosticCode("unresolved-extern-crate")
56 fn message(&self) -> String {
57 "unresolved extern crate".to_string()
59 fn display_source(&self) -> InFile<SyntaxNodePtr> {
60 InFile::new(self.file, self.item.clone().into())
62 fn as_any(&self) -> &(dyn Any + Send + 'static) {
68 pub struct UnresolvedImport {
70 pub node: AstPtr<ast::UseTree>,
73 impl Diagnostic for UnresolvedImport {
74 fn code(&self) -> DiagnosticCode {
75 DiagnosticCode("unresolved-import")
77 fn message(&self) -> String {
78 "unresolved import".to_string()
80 fn display_source(&self) -> InFile<SyntaxNodePtr> {
81 InFile::new(self.file, self.node.clone().into())
83 fn as_any(&self) -> &(dyn Any + Send + 'static) {
86 fn is_experimental(&self) -> bool {
87 // This currently results in false positives in the following cases:
88 // - `cfg_if!`-generated code in libstd (we don't load the sysroot correctly)
89 // - `core::arch` (we don't handle `#[path = "../<path>"]` correctly)
90 // - proc macros and/or proc macro generated code
95 // Diagnostic: unresolved-macro-call
97 // This diagnostic is triggered if rust-analyzer is unable to resolve the path to a
98 // macro in a macro invocation.
99 #[derive(Debug, Clone, Eq, PartialEq)]
100 pub struct UnresolvedMacroCall {
102 pub node: AstPtr<ast::MacroCall>,
106 impl Diagnostic for UnresolvedMacroCall {
107 fn code(&self) -> DiagnosticCode {
108 DiagnosticCode("unresolved-macro-call")
110 fn message(&self) -> String {
111 format!("unresolved macro `{}!`", self.path)
113 fn display_source(&self) -> InFile<SyntaxNodePtr> {
114 InFile::new(self.file, self.node.clone().into())
116 fn as_any(&self) -> &(dyn Any + Send + 'static) {
119 fn is_experimental(&self) -> bool {
124 // Diagnostic: inactive-code
126 // This diagnostic is shown for code with inactive `#[cfg]` attributes.
127 #[derive(Debug, Clone, Eq, PartialEq)]
128 pub struct InactiveCode {
130 pub node: SyntaxNodePtr,
132 pub opts: CfgOptions,
135 impl Diagnostic for InactiveCode {
136 fn code(&self) -> DiagnosticCode {
137 DiagnosticCode("inactive-code")
139 fn message(&self) -> String {
140 let inactive = DnfExpr::new(self.cfg.clone()).why_inactive(&self.opts);
141 let mut buf = "code is inactive due to #[cfg] directives".to_string();
143 if let Some(inactive) = inactive {
144 format_to!(buf, ": {}", inactive);
149 fn display_source(&self) -> InFile<SyntaxNodePtr> {
150 InFile::new(self.file, self.node.clone())
152 fn as_any(&self) -> &(dyn Any + Send + 'static) {
157 // Diagnostic: unresolved-proc-macro
159 // This diagnostic is shown when a procedural macro can not be found. This usually means that
160 // procedural macro support is simply disabled (and hence is only a weak hint instead of an error),
161 // but can also indicate project setup problems.
163 // If you are seeing a lot of "proc macro not expanded" warnings, you can add this option to the
164 // `rust-analyzer.diagnostics.disabled` list to prevent them from showing. Alternatively you can
165 // enable support for procedural macros (see `rust-analyzer.procMacro.enable`).
166 #[derive(Debug, Clone, Eq, PartialEq)]
167 pub struct UnresolvedProcMacro {
169 pub node: SyntaxNodePtr,
170 /// If the diagnostic can be pinpointed more accurately than via `node`, this is the `TextRange`
172 pub precise_location: Option<TextRange>,
173 pub macro_name: Option<String>,
176 impl Diagnostic for UnresolvedProcMacro {
177 fn code(&self) -> DiagnosticCode {
178 DiagnosticCode("unresolved-proc-macro")
181 fn message(&self) -> String {
182 match &self.macro_name {
183 Some(name) => format!("proc macro `{}` not expanded", name),
184 None => "proc macro not expanded".to_string(),
188 fn display_source(&self) -> InFile<SyntaxNodePtr> {
189 InFile::new(self.file, self.node.clone())
192 fn as_any(&self) -> &(dyn Any + Send + 'static) {
197 // Diagnostic: macro-error
199 // This diagnostic is shown for macro expansion errors.
200 #[derive(Debug, Clone, Eq, PartialEq)]
201 pub struct MacroError {
203 pub node: SyntaxNodePtr,
207 impl Diagnostic for MacroError {
208 fn code(&self) -> DiagnosticCode {
209 DiagnosticCode("macro-error")
211 fn message(&self) -> String {
214 fn display_source(&self) -> InFile<SyntaxNodePtr> {
215 InFile::new(self.file, self.node.clone())
217 fn as_any(&self) -> &(dyn Any + Send + 'static) {
220 fn is_experimental(&self) -> bool {
221 // Newly added and not very well-tested, might contain false positives.
227 pub struct UnimplementedBuiltinMacro {
229 pub node: SyntaxNodePtr,
232 impl Diagnostic for UnimplementedBuiltinMacro {
233 fn code(&self) -> DiagnosticCode {
234 DiagnosticCode("unimplemented-builtin-macro")
237 fn message(&self) -> String {
238 "unimplemented built-in macro".to_string()
241 fn display_source(&self) -> InFile<SyntaxNodePtr> {
242 InFile::new(self.file, self.node.clone())
245 fn as_any(&self) -> &(dyn Any + Send + 'static) {
250 // Diagnostic: no-such-field
252 // This diagnostic is triggered if created structure does not have field provided in record.
254 pub struct NoSuchField {
256 pub field: AstPtr<ast::RecordExprField>,
259 impl Diagnostic for NoSuchField {
260 fn code(&self) -> DiagnosticCode {
261 DiagnosticCode("no-such-field")
264 fn message(&self) -> String {
265 "no such field".to_string()
268 fn display_source(&self) -> InFile<SyntaxNodePtr> {
269 InFile::new(self.file, self.field.clone().into())
272 fn as_any(&self) -> &(dyn Any + Send + 'static) {
277 // Diagnostic: break-outside-of-loop
279 // This diagnostic is triggered if the `break` keyword is used outside of a loop.
281 pub struct BreakOutsideOfLoop {
283 pub expr: AstPtr<ast::Expr>,
286 impl Diagnostic for BreakOutsideOfLoop {
287 fn code(&self) -> DiagnosticCode {
288 DiagnosticCode("break-outside-of-loop")
290 fn message(&self) -> String {
291 "break outside of loop".to_string()
293 fn display_source(&self) -> InFile<SyntaxNodePtr> {
294 InFile { file_id: self.file, value: self.expr.clone().into() }
296 fn as_any(&self) -> &(dyn Any + Send + 'static) {
301 // Diagnostic: missing-unsafe
303 // This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block.
305 pub struct MissingUnsafe {
307 pub expr: AstPtr<ast::Expr>,
310 impl Diagnostic for MissingUnsafe {
311 fn code(&self) -> DiagnosticCode {
312 DiagnosticCode("missing-unsafe")
314 fn message(&self) -> String {
315 format!("This operation is unsafe and requires an unsafe function or block")
317 fn display_source(&self) -> InFile<SyntaxNodePtr> {
318 InFile { file_id: self.file, value: self.expr.clone().into() }
320 fn as_any(&self) -> &(dyn Any + Send + 'static) {
326 pub struct MissingFields {
328 pub field_list_parent: Either<AstPtr<ast::RecordExpr>, AstPtr<ast::RecordPat>>,
329 pub field_list_parent_path: Option<AstPtr<ast::Path>>,
330 pub missed_fields: Vec<Name>,
333 // Diagnostic: replace-filter-map-next-with-find-map
335 // This diagnostic is triggered when `.filter_map(..).next()` is used, rather than the more concise `.find_map(..)`.
337 pub struct ReplaceFilterMapNextWithFindMap {
339 /// This expression is the whole method chain up to and including `.filter_map(..).next()`.
340 pub next_expr: AstPtr<ast::Expr>,
343 impl Diagnostic for ReplaceFilterMapNextWithFindMap {
344 fn code(&self) -> DiagnosticCode {
345 DiagnosticCode("replace-filter-map-next-with-find-map")
347 fn message(&self) -> String {
348 "replace filter_map(..).next() with find_map(..)".to_string()
350 fn display_source(&self) -> InFile<SyntaxNodePtr> {
351 InFile { file_id: self.file, value: self.next_expr.clone().into() }
353 fn as_any(&self) -> &(dyn Any + Send + 'static) {
358 // Diagnostic: mismatched-arg-count
360 // This diagnostic is triggered if a function is invoked with an incorrect amount of arguments.
362 pub struct MismatchedArgCount {
364 pub call_expr: AstPtr<ast::Expr>,
369 impl Diagnostic for MismatchedArgCount {
370 fn code(&self) -> DiagnosticCode {
371 DiagnosticCode("mismatched-arg-count")
373 fn message(&self) -> String {
374 let s = if self.expected == 1 { "" } else { "s" };
375 format!("Expected {} argument{}, found {}", self.expected, s, self.found)
377 fn display_source(&self) -> InFile<SyntaxNodePtr> {
378 InFile { file_id: self.file, value: self.call_expr.clone().into() }
380 fn as_any(&self) -> &(dyn Any + Send + 'static) {
383 fn is_experimental(&self) -> bool {
389 pub struct RemoveThisSemicolon {
391 pub expr: AstPtr<ast::Expr>,
394 impl Diagnostic for RemoveThisSemicolon {
395 fn code(&self) -> DiagnosticCode {
396 DiagnosticCode("remove-this-semicolon")
399 fn message(&self) -> String {
400 "Remove this semicolon".to_string()
403 fn display_source(&self) -> InFile<SyntaxNodePtr> {
404 InFile { file_id: self.file, value: self.expr.clone().into() }
407 fn as_any(&self) -> &(dyn Any + Send + 'static) {
412 // Diagnostic: missing-ok-or-some-in-tail-expr
414 // This diagnostic is triggered if a block that should return `Result` returns a value not wrapped in `Ok`,
415 // or if a block that should return `Option` returns a value not wrapped in `Some`.
420 // fn foo() -> Result<u8, ()> {
425 pub struct MissingOkOrSomeInTailExpr {
427 pub expr: AstPtr<ast::Expr>,
428 // `Some` or `Ok` depending on whether the return type is Result or Option
429 pub required: String,
432 impl Diagnostic for MissingOkOrSomeInTailExpr {
433 fn code(&self) -> DiagnosticCode {
434 DiagnosticCode("missing-ok-or-some-in-tail-expr")
436 fn message(&self) -> String {
437 format!("wrap return expression in {}", self.required)
439 fn display_source(&self) -> InFile<SyntaxNodePtr> {
440 InFile { file_id: self.file, value: self.expr.clone().into() }
442 fn as_any(&self) -> &(dyn Any + Send + 'static) {
447 // Diagnostic: missing-match-arm
449 // This diagnostic is triggered if `match` block is missing one or more match arms.
451 pub struct MissingMatchArms {
453 pub match_expr: AstPtr<ast::Expr>,
454 pub arms: AstPtr<ast::MatchArmList>,
457 impl Diagnostic for MissingMatchArms {
458 fn code(&self) -> DiagnosticCode {
459 DiagnosticCode("missing-match-arm")
461 fn message(&self) -> String {
462 String::from("Missing match arm")
464 fn display_source(&self) -> InFile<SyntaxNodePtr> {
465 InFile { file_id: self.file, value: self.match_expr.clone().into() }
467 fn as_any(&self) -> &(dyn Any + Send + 'static) {
473 pub struct InternalBailedOut {
475 pub pat_syntax_ptr: SyntaxNodePtr,
478 impl Diagnostic for InternalBailedOut {
479 fn code(&self) -> DiagnosticCode {
480 DiagnosticCode("internal:match-check-bailed-out")
482 fn message(&self) -> String {
483 format!("Internal: match check bailed out")
485 fn display_source(&self) -> InFile<SyntaxNodePtr> {
486 InFile { file_id: self.file, value: self.pat_syntax_ptr.clone() }
488 fn as_any(&self) -> &(dyn Any + Send + 'static) {
493 pub use hir_ty::diagnostics::IncorrectCase;
495 impl Diagnostic for IncorrectCase {
496 fn code(&self) -> DiagnosticCode {
497 DiagnosticCode("incorrect-ident-case")
500 fn message(&self) -> String {
502 "{} `{}` should have {} name, e.g. `{}`",
505 self.expected_case.to_string(),
510 fn display_source(&self) -> InFile<SyntaxNodePtr> {
511 InFile::new(self.file, self.ident.clone().into())
514 fn as_any(&self) -> &(dyn Any + Send + 'static) {
518 fn is_experimental(&self) -> bool {