diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-08-18 17:40:12 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2020-08-18 17:40:12 +0100 |
commit | aa2def023e6b0aa8a68f4992423f566d435e55c6 (patch) | |
tree | 769fbd88c0845367795e1273333245d2cadfc475 /crates | |
parent | f7a8ce35256e6a79dbe641787007e18a74e52b16 (diff) | |
parent | 8146669542dfc887956901b54a453c9a97fee7e3 (diff) |
Merge #5804
5804: Add type safety to diagnostic codes
r=matklad a=matklad
bors r+
🤖
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r-- | crates/hir_def/src/diagnostics.rs | 6 | ||||
-rw-r--r-- | crates/hir_expand/src/diagnostics.rs | 11 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics.rs | 34 | ||||
-rw-r--r-- | crates/ide/src/diagnostics.rs | 76 |
4 files changed, 47 insertions, 80 deletions
diff --git a/crates/hir_def/src/diagnostics.rs b/crates/hir_def/src/diagnostics.rs index c7723de00..3e19d9117 100644 --- a/crates/hir_def/src/diagnostics.rs +++ b/crates/hir_def/src/diagnostics.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | use std::any::Any; | 3 | use std::any::Any; |
4 | 4 | ||
5 | use hir_expand::diagnostics::Diagnostic; | 5 | use hir_expand::diagnostics::{Diagnostic, DiagnosticCode}; |
6 | use syntax::{ast, AstPtr, SyntaxNodePtr}; | 6 | use syntax::{ast, AstPtr, SyntaxNodePtr}; |
7 | 7 | ||
8 | use hir_expand::{HirFileId, InFile}; | 8 | use hir_expand::{HirFileId, InFile}; |
@@ -15,8 +15,8 @@ pub struct UnresolvedModule { | |||
15 | } | 15 | } |
16 | 16 | ||
17 | impl Diagnostic for UnresolvedModule { | 17 | impl Diagnostic for UnresolvedModule { |
18 | fn name(&self) -> &'static str { | 18 | fn code(&self) -> DiagnosticCode { |
19 | "unresolved-module" | 19 | DiagnosticCode("unresolved-module") |
20 | } | 20 | } |
21 | fn message(&self) -> String { | 21 | fn message(&self) -> String { |
22 | "unresolved module".to_string() | 22 | "unresolved module".to_string() |
diff --git a/crates/hir_expand/src/diagnostics.rs b/crates/hir_expand/src/diagnostics.rs index 6c81b2501..78ccc212c 100644 --- a/crates/hir_expand/src/diagnostics.rs +++ b/crates/hir_expand/src/diagnostics.rs | |||
@@ -20,8 +20,17 @@ use syntax::SyntaxNodePtr; | |||
20 | 20 | ||
21 | use crate::InFile; | 21 | use crate::InFile; |
22 | 22 | ||
23 | #[derive(Copy, Clone, PartialEq)] | ||
24 | pub struct DiagnosticCode(pub &'static str); | ||
25 | |||
26 | impl DiagnosticCode { | ||
27 | pub fn as_str(&self) -> &str { | ||
28 | self.0 | ||
29 | } | ||
30 | } | ||
31 | |||
23 | pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { | 32 | pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { |
24 | fn name(&self) -> &'static str; | 33 | fn code(&self) -> DiagnosticCode; |
25 | fn message(&self) -> String; | 34 | fn message(&self) -> String; |
26 | /// Used in highlighting and related purposes | 35 | /// Used in highlighting and related purposes |
27 | fn display_source(&self) -> InFile<SyntaxNodePtr>; | 36 | fn display_source(&self) -> InFile<SyntaxNodePtr>; |
diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs index 38fa24ee0..9ba005fab 100644 --- a/crates/hir_ty/src/diagnostics.rs +++ b/crates/hir_ty/src/diagnostics.rs | |||
@@ -6,7 +6,7 @@ mod unsafe_check; | |||
6 | use std::any::Any; | 6 | use std::any::Any; |
7 | 7 | ||
8 | use hir_def::DefWithBodyId; | 8 | use hir_def::DefWithBodyId; |
9 | use hir_expand::diagnostics::{Diagnostic, DiagnosticSink}; | 9 | use hir_expand::diagnostics::{Diagnostic, DiagnosticCode, DiagnosticSink}; |
10 | use hir_expand::{name::Name, HirFileId, InFile}; | 10 | use hir_expand::{name::Name, HirFileId, InFile}; |
11 | use stdx::format_to; | 11 | use stdx::format_to; |
12 | use syntax::{ast, AstPtr, SyntaxNodePtr}; | 12 | use syntax::{ast, AstPtr, SyntaxNodePtr}; |
@@ -32,8 +32,8 @@ pub struct NoSuchField { | |||
32 | } | 32 | } |
33 | 33 | ||
34 | impl Diagnostic for NoSuchField { | 34 | impl Diagnostic for NoSuchField { |
35 | fn name(&self) -> &'static str { | 35 | fn code(&self) -> DiagnosticCode { |
36 | "no-such-field" | 36 | DiagnosticCode("no-such-field") |
37 | } | 37 | } |
38 | 38 | ||
39 | fn message(&self) -> String { | 39 | fn message(&self) -> String { |
@@ -58,8 +58,8 @@ pub struct MissingFields { | |||
58 | } | 58 | } |
59 | 59 | ||
60 | impl Diagnostic for MissingFields { | 60 | impl Diagnostic for MissingFields { |
61 | fn name(&self) -> &'static str { | 61 | fn code(&self) -> DiagnosticCode { |
62 | "missing-structure-fields" | 62 | DiagnosticCode("missing-structure-fields") |
63 | } | 63 | } |
64 | fn message(&self) -> String { | 64 | fn message(&self) -> String { |
65 | let mut buf = String::from("Missing structure fields:\n"); | 65 | let mut buf = String::from("Missing structure fields:\n"); |
@@ -94,8 +94,8 @@ pub struct MissingPatFields { | |||
94 | } | 94 | } |
95 | 95 | ||
96 | impl Diagnostic for MissingPatFields { | 96 | impl Diagnostic for MissingPatFields { |
97 | fn name(&self) -> &'static str { | 97 | fn code(&self) -> DiagnosticCode { |
98 | "missing-pat-fields" | 98 | DiagnosticCode("missing-pat-fields") |
99 | } | 99 | } |
100 | fn message(&self) -> String { | 100 | fn message(&self) -> String { |
101 | let mut buf = String::from("Missing structure fields:\n"); | 101 | let mut buf = String::from("Missing structure fields:\n"); |
@@ -127,8 +127,8 @@ pub struct MissingMatchArms { | |||
127 | } | 127 | } |
128 | 128 | ||
129 | impl Diagnostic for MissingMatchArms { | 129 | impl Diagnostic for MissingMatchArms { |
130 | fn name(&self) -> &'static str { | 130 | fn code(&self) -> DiagnosticCode { |
131 | "missing-match-arm" | 131 | DiagnosticCode("missing-match-arm") |
132 | } | 132 | } |
133 | fn message(&self) -> String { | 133 | fn message(&self) -> String { |
134 | String::from("Missing match arm") | 134 | String::from("Missing match arm") |
@@ -148,8 +148,8 @@ pub struct MissingOkInTailExpr { | |||
148 | } | 148 | } |
149 | 149 | ||
150 | impl Diagnostic for MissingOkInTailExpr { | 150 | impl Diagnostic for MissingOkInTailExpr { |
151 | fn name(&self) -> &'static str { | 151 | fn code(&self) -> DiagnosticCode { |
152 | "missing-ok-in-tail-expr" | 152 | DiagnosticCode("missing-ok-in-tail-expr") |
153 | } | 153 | } |
154 | fn message(&self) -> String { | 154 | fn message(&self) -> String { |
155 | "wrap return expression in Ok".to_string() | 155 | "wrap return expression in Ok".to_string() |
@@ -169,8 +169,8 @@ pub struct BreakOutsideOfLoop { | |||
169 | } | 169 | } |
170 | 170 | ||
171 | impl Diagnostic for BreakOutsideOfLoop { | 171 | impl Diagnostic for BreakOutsideOfLoop { |
172 | fn name(&self) -> &'static str { | 172 | fn code(&self) -> DiagnosticCode { |
173 | "break-outside-of-loop" | 173 | DiagnosticCode("break-outside-of-loop") |
174 | } | 174 | } |
175 | fn message(&self) -> String { | 175 | fn message(&self) -> String { |
176 | "break outside of loop".to_string() | 176 | "break outside of loop".to_string() |
@@ -190,8 +190,8 @@ pub struct MissingUnsafe { | |||
190 | } | 190 | } |
191 | 191 | ||
192 | impl Diagnostic for MissingUnsafe { | 192 | impl Diagnostic for MissingUnsafe { |
193 | fn name(&self) -> &'static str { | 193 | fn code(&self) -> DiagnosticCode { |
194 | "missing-unsafe" | 194 | DiagnosticCode("missing-unsafe") |
195 | } | 195 | } |
196 | fn message(&self) -> String { | 196 | fn message(&self) -> String { |
197 | format!("This operation is unsafe and requires an unsafe function or block") | 197 | format!("This operation is unsafe and requires an unsafe function or block") |
@@ -213,8 +213,8 @@ pub struct MismatchedArgCount { | |||
213 | } | 213 | } |
214 | 214 | ||
215 | impl Diagnostic for MismatchedArgCount { | 215 | impl Diagnostic for MismatchedArgCount { |
216 | fn name(&self) -> &'static str { | 216 | fn code(&self) -> DiagnosticCode { |
217 | "mismatched-arg-count" | 217 | DiagnosticCode("mismatched-arg-count") |
218 | } | 218 | } |
219 | fn message(&self) -> String { | 219 | fn message(&self) -> String { |
220 | let s = if self.expected == 1 { "" } else { "s" }; | 220 | let s = if self.expected == 1 { "" } else { "s" }; |
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 3c36c0e15..92b5adaa2 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -25,7 +25,7 @@ use self::fixes::DiagnosticWithFix; | |||
25 | 25 | ||
26 | #[derive(Debug)] | 26 | #[derive(Debug)] |
27 | pub struct Diagnostic { | 27 | pub struct Diagnostic { |
28 | pub name: Option<String>, | 28 | // pub name: Option<String>, |
29 | pub message: String, | 29 | pub message: String, |
30 | pub range: TextRange, | 30 | pub range: TextRange, |
31 | pub severity: Severity, | 31 | pub severity: Severity, |
@@ -71,7 +71,7 @@ pub(crate) fn diagnostics( | |||
71 | 71 | ||
72 | // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily. | 72 | // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily. |
73 | res.extend(parse.errors().iter().take(128).map(|err| Diagnostic { | 73 | res.extend(parse.errors().iter().take(128).map(|err| Diagnostic { |
74 | name: None, | 74 | // name: None, |
75 | range: err.range(), | 75 | range: err.range(), |
76 | message: format!("Syntax Error: {}", err), | 76 | message: format!("Syntax Error: {}", err), |
77 | severity: Severity::Error, | 77 | severity: Severity::Error, |
@@ -98,14 +98,14 @@ pub(crate) fn diagnostics( | |||
98 | }) | 98 | }) |
99 | // Only collect experimental diagnostics when they're enabled. | 99 | // Only collect experimental diagnostics when they're enabled. |
100 | .filter(|diag| !(diag.is_experimental() && config.disable_experimental)) | 100 | .filter(|diag| !(diag.is_experimental() && config.disable_experimental)) |
101 | .filter(|diag| !config.disabled.contains(diag.name())); | 101 | .filter(|diag| !config.disabled.contains(diag.code().as_str())); |
102 | 102 | ||
103 | // Finalize the `DiagnosticSink` building process. | 103 | // Finalize the `DiagnosticSink` building process. |
104 | let mut sink = sink_builder | 104 | let mut sink = sink_builder |
105 | // Diagnostics not handled above get no fix and default treatment. | 105 | // Diagnostics not handled above get no fix and default treatment. |
106 | .build(|d| { | 106 | .build(|d| { |
107 | res.borrow_mut().push(Diagnostic { | 107 | res.borrow_mut().push(Diagnostic { |
108 | name: Some(d.name().into()), | 108 | // name: Some(d.name().into()), |
109 | message: d.message(), | 109 | message: d.message(), |
110 | range: sema.diagnostics_display_range(d).range, | 110 | range: sema.diagnostics_display_range(d).range, |
111 | severity: Severity::Error, | 111 | severity: Severity::Error, |
@@ -122,7 +122,7 @@ pub(crate) fn diagnostics( | |||
122 | 122 | ||
123 | fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic { | 123 | fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic { |
124 | Diagnostic { | 124 | Diagnostic { |
125 | name: Some(d.name().into()), | 125 | // name: Some(d.name().into()), |
126 | range: sema.diagnostics_display_range(d).range, | 126 | range: sema.diagnostics_display_range(d).range, |
127 | message: d.message(), | 127 | message: d.message(), |
128 | severity: Severity::Error, | 128 | severity: Severity::Error, |
@@ -149,7 +149,7 @@ fn check_unnecessary_braces_in_use_statement( | |||
149 | }); | 149 | }); |
150 | 150 | ||
151 | acc.push(Diagnostic { | 151 | acc.push(Diagnostic { |
152 | name: None, | 152 | // name: None, |
153 | range: use_range, | 153 | range: use_range, |
154 | message: "Unnecessary braces in use statement".to_string(), | 154 | message: "Unnecessary braces in use statement".to_string(), |
155 | severity: Severity::WeakWarning, | 155 | severity: Severity::WeakWarning, |
@@ -196,7 +196,7 @@ fn check_struct_shorthand_initialization( | |||
196 | 196 | ||
197 | let field_range = record_field.syntax().text_range(); | 197 | let field_range = record_field.syntax().text_range(); |
198 | acc.push(Diagnostic { | 198 | acc.push(Diagnostic { |
199 | name: None, | 199 | // name: None, |
200 | range: field_range, | 200 | range: field_range, |
201 | message: "Shorthand struct initialization".to_string(), | 201 | message: "Shorthand struct initialization".to_string(), |
202 | severity: Severity::WeakWarning, | 202 | severity: Severity::WeakWarning, |
@@ -294,54 +294,6 @@ mod tests { | |||
294 | assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics); | 294 | assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics); |
295 | } | 295 | } |
296 | 296 | ||
297 | /// Takes a multi-file input fixture with annotated cursor position and the list of disabled diagnostics, | ||
298 | /// and checks that provided diagnostics aren't spawned during analysis. | ||
299 | fn check_disabled_diagnostics(ra_fixture: &str, disabled_diagnostics: &[&'static str]) { | ||
300 | let mut config = DiagnosticsConfig::default(); | ||
301 | config.disabled = disabled_diagnostics.into_iter().map(|diag| diag.to_string()).collect(); | ||
302 | |||
303 | let mock = MockAnalysis::with_files(ra_fixture); | ||
304 | let files = mock.files().map(|(it, _)| it).collect::<Vec<_>>(); | ||
305 | let analysis = mock.analysis(); | ||
306 | |||
307 | let diagnostics = files | ||
308 | .clone() | ||
309 | .into_iter() | ||
310 | .flat_map(|file_id| analysis.diagnostics(&config, file_id).unwrap()) | ||
311 | .collect::<Vec<_>>(); | ||
312 | |||
313 | // First, we have to check that diagnostic is not emitted when it's added to the disabled diagnostics list. | ||
314 | for diagnostic in diagnostics { | ||
315 | if let Some(name) = diagnostic.name { | ||
316 | assert!( | ||
317 | !disabled_diagnostics.contains(&name.as_str()), | ||
318 | "Diagnostic {} is disabled", | ||
319 | name | ||
320 | ); | ||
321 | } | ||
322 | } | ||
323 | |||
324 | // Then, we must reset the config and repeat the check, so that we'll be sure that without | ||
325 | // config these diagnostics are emitted. | ||
326 | // This is required for tests to not become outdated if e.g. diagnostics name changes: | ||
327 | // without this additional run the test will pass simply because a diagnostic with an old name | ||
328 | // will no longer exist. | ||
329 | let diagnostics = files | ||
330 | .into_iter() | ||
331 | .flat_map(|file_id| { | ||
332 | analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap() | ||
333 | }) | ||
334 | .collect::<Vec<_>>(); | ||
335 | |||
336 | assert!( | ||
337 | diagnostics | ||
338 | .into_iter() | ||
339 | .filter_map(|diag| diag.name) | ||
340 | .any(|name| disabled_diagnostics.contains(&name.as_str())), | ||
341 | "At least one of the diagnostics was not emitted even without config; are the diagnostics names correct?" | ||
342 | ); | ||
343 | } | ||
344 | |||
345 | fn check_expect(ra_fixture: &str, expect: Expect) { | 297 | fn check_expect(ra_fixture: &str, expect: Expect) { |
346 | let (analysis, file_id) = single_file(ra_fixture); | 298 | let (analysis, file_id) = single_file(ra_fixture); |
347 | let diagnostics = analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap(); | 299 | let diagnostics = analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap(); |
@@ -604,9 +556,6 @@ fn test_fn() { | |||
604 | expect![[r#" | 556 | expect![[r#" |
605 | [ | 557 | [ |
606 | Diagnostic { | 558 | Diagnostic { |
607 | name: Some( | ||
608 | "unresolved-module", | ||
609 | ), | ||
610 | message: "unresolved module", | 559 | message: "unresolved module", |
611 | range: 0..8, | 560 | range: 0..8, |
612 | severity: Error, | 561 | severity: Error, |
@@ -783,6 +732,15 @@ struct Foo { | |||
783 | 732 | ||
784 | #[test] | 733 | #[test] |
785 | fn test_disabled_diagnostics() { | 734 | fn test_disabled_diagnostics() { |
786 | check_disabled_diagnostics(r#"mod foo;"#, &["unresolved-module"]); | 735 | let mut config = DiagnosticsConfig::default(); |
736 | config.disabled.insert("unresolved-module".into()); | ||
737 | |||
738 | let (analysis, file_id) = single_file(r#"mod foo;"#); | ||
739 | |||
740 | let diagnostics = analysis.diagnostics(&config, file_id).unwrap(); | ||
741 | assert!(diagnostics.is_empty()); | ||
742 | |||
743 | let diagnostics = analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap(); | ||
744 | assert!(!diagnostics.is_empty()); | ||
787 | } | 745 | } |
788 | } | 746 | } |