1 //! Diagnostics rendering and fixits.
3 //! Most of the diagnostics originate from the dark depth of the compiler, and
4 //! are originally expressed in term of IR. When we emit the diagnostic, we are
5 //! usually not in the position to decide how to best "render" it in terms of
6 //! user-authored source code. We are especially not in the position to offer
7 //! fixits, as the compiler completely lacks the infrastructure to edit the
10 //! Instead, we "bubble up" raw, structured diagnostics until the `hir` crate,
11 //! where we "cook" them so that each diagnostic is formulated in terms of `hir`
12 //! types. Well, at least that's the aspiration, the "cooking" is somewhat
13 //! ad-hoc at the moment. Anyways, we get a bunch of ide-friendly diagnostic
14 //! structs from hir, and we want to render them to unified serializable
15 //! representation (span, level, message) here. If we can, we also provide
16 //! fixits. By the way, that's why we want to keep diagnostics structured
17 //! internally -- so that we have all the info to make fixes.
19 //! We have one "handler" module per diagnostic code. Such a module contains
20 //! rendering, optional fixes and tests. It's OK if some low-level compiler
21 //! functionality ends up being tested via a diagnostic.
23 //! There are also a couple of ad-hoc diagnostics implemented directly here, we
24 //! don't yet have a great pattern for how to do them properly.
27 pub(crate) mod break_outside_of_loop;
28 pub(crate) mod inactive_code;
29 pub(crate) mod incorrect_case;
30 pub(crate) mod invalid_derive_target;
31 pub(crate) mod macro_error;
32 pub(crate) mod malformed_derive;
33 pub(crate) mod mismatched_arg_count;
34 pub(crate) mod missing_fields;
35 pub(crate) mod missing_match_arms;
36 pub(crate) mod missing_unsafe;
37 pub(crate) mod no_such_field;
38 pub(crate) mod replace_filter_map_next_with_find_map;
39 pub(crate) mod type_mismatch;
40 pub(crate) mod unimplemented_builtin_macro;
41 pub(crate) mod unresolved_extern_crate;
42 pub(crate) mod unresolved_import;
43 pub(crate) mod unresolved_macro_call;
44 pub(crate) mod unresolved_module;
45 pub(crate) mod unresolved_proc_macro;
47 // The handlers below are unusual, the implement the diagnostics as well.
48 pub(crate) mod field_shorthand;
49 pub(crate) mod useless_braces;
50 pub(crate) mod unlinked_file;
56 use hir::{diagnostics::AnyDiagnostic, Semantics};
58 assists::{Assist, AssistId, AssistKind, AssistResolveStrategy},
59 base_db::{FileId, SourceDatabase},
61 source_change::SourceChange,
62 FxHashSet, RootDatabase,
64 use syntax::{ast::AstNode, TextRange};
66 #[derive(Copy, Clone, Debug, PartialEq)]
67 pub struct DiagnosticCode(pub &'static str);
70 pub fn as_str(&self) -> &str {
76 pub struct Diagnostic {
77 pub code: DiagnosticCode,
80 pub severity: Severity,
82 pub experimental: bool,
83 pub fixes: Option<Vec<Assist>>,
87 fn new(code: &'static str, message: impl Into<String>, range: TextRange) -> Diagnostic {
88 let message = message.into();
90 code: DiagnosticCode(code),
93 severity: Severity::Error,
100 fn experimental(mut self) -> Diagnostic {
101 self.experimental = true;
105 fn severity(mut self, severity: Severity) -> Diagnostic {
106 self.severity = severity;
110 fn with_fixes(mut self, fixes: Option<Vec<Assist>>) -> Diagnostic {
115 fn with_unused(mut self, unused: bool) -> Diagnostic {
116 self.unused = unused;
121 #[derive(Debug, Copy, Clone)]
124 // We don't actually emit this one yet, but we should at some point.
129 #[derive(Clone, Debug, PartialEq, Eq)]
130 pub enum ExprFillDefaultMode {
134 impl Default for ExprFillDefaultMode {
135 fn default() -> Self {
140 #[derive(Default, Debug, Clone)]
141 pub struct DiagnosticsConfig {
142 pub attr_proc_macros_enabled: bool,
143 pub disable_experimental: bool,
144 pub disabled: FxHashSet<String>,
145 pub expr_fill_default: ExprFillDefaultMode,
148 struct DiagnosticsContext<'a> {
149 config: &'a DiagnosticsConfig,
150 sema: Semantics<'a, RootDatabase>,
151 resolve: &'a AssistResolveStrategy,
156 config: &DiagnosticsConfig,
157 resolve: &AssistResolveStrategy,
159 ) -> Vec<Diagnostic> {
160 let _p = profile::span("diagnostics");
161 let sema = Semantics::new(db);
162 let parse = db.parse(file_id);
163 let mut res = Vec::new();
165 // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily.
167 parse.errors().iter().take(128).map(|err| {
168 Diagnostic::new("syntax-error", format!("Syntax Error: {}", err), err.range())
172 for node in parse.tree().syntax().descendants() {
173 handlers::useless_braces::useless_braces(&mut res, file_id, &node);
174 handlers::field_shorthand::field_shorthand(&mut res, file_id, &node);
177 let module = sema.to_module_def(file_id);
179 let ctx = DiagnosticsContext { config, sema, resolve };
180 if module.is_none() {
181 handlers::unlinked_file::unlinked_file(&ctx, &mut res, file_id);
184 let mut diags = Vec::new();
185 if let Some(m) = module {
186 m.diagnostics(db, &mut diags)
192 AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
193 AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d),
194 AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d),
195 AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d),
196 AnyDiagnostic::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d),
197 AnyDiagnostic::MissingFields(d) => handlers::missing_fields::missing_fields(&ctx, &d),
198 AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d),
199 AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d),
200 AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d),
201 AnyDiagnostic::ReplaceFilterMapNextWithFindMap(d) => handlers::replace_filter_map_next_with_find_map::replace_filter_map_next_with_find_map(&ctx, &d),
202 AnyDiagnostic::TypeMismatch(d) => handlers::type_mismatch::type_mismatch(&ctx, &d),
203 AnyDiagnostic::UnimplementedBuiltinMacro(d) => handlers::unimplemented_builtin_macro::unimplemented_builtin_macro(&ctx, &d),
204 AnyDiagnostic::UnresolvedExternCrate(d) => handlers::unresolved_extern_crate::unresolved_extern_crate(&ctx, &d),
205 AnyDiagnostic::UnresolvedImport(d) => handlers::unresolved_import::unresolved_import(&ctx, &d),
206 AnyDiagnostic::UnresolvedMacroCall(d) => handlers::unresolved_macro_call::unresolved_macro_call(&ctx, &d),
207 AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&ctx, &d),
208 AnyDiagnostic::UnresolvedProcMacro(d) => handlers::unresolved_proc_macro::unresolved_proc_macro(&ctx, &d, config.attr_proc_macros_enabled),
209 AnyDiagnostic::InvalidDeriveTarget(d) => handlers::invalid_derive_target::invalid_derive_target(&ctx, &d),
211 AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) {
220 !ctx.config.disabled.contains(d.code.as_str())
221 && !(ctx.config.disable_experimental && d.experimental)
227 fn fix(id: &'static str, label: &str, source_change: SourceChange, target: TextRange) -> Assist {
228 let mut res = unresolved_fix(id, label, target);
229 res.source_change = Some(source_change);
233 fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist {
234 assert!(!id.contains(' '));
236 id: AssistId(id, AssistKind::QuickFix),
237 label: Label::new(label.to_string()),
241 trigger_signature_help: false,