aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/diagnostics.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/diagnostics.rs')
-rw-r--r--crates/ra_ide/src/diagnostics.rs80
1 files changed, 76 insertions, 4 deletions
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index 1046d7ab3..79dbb0865 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -17,7 +17,7 @@ use ra_syntax::{
17}; 17};
18use ra_text_edit::{TextEdit, TextEditBuilder}; 18use ra_text_edit::{TextEdit, TextEditBuilder};
19 19
20use crate::{Diagnostic, FileId, Fix, SourceFileEdit}; 20use crate::{AnalysisConfig, Diagnostic, FileId, Fix, SourceFileEdit};
21 21
22mod diagnostics_with_fix; 22mod diagnostics_with_fix;
23use diagnostics_with_fix::DiagnosticWithFix; 23use diagnostics_with_fix::DiagnosticWithFix;
@@ -32,6 +32,7 @@ pub(crate) fn diagnostics(
32 db: &RootDatabase, 32 db: &RootDatabase,
33 file_id: FileId, 33 file_id: FileId,
34 enable_experimental: bool, 34 enable_experimental: bool,
35 analysis_config: &AnalysisConfig,
35) -> Vec<Diagnostic> { 36) -> Vec<Diagnostic> {
36 let _p = profile("diagnostics"); 37 let _p = profile("diagnostics");
37 let sema = Semantics::new(db); 38 let sema = Semantics::new(db);
@@ -40,6 +41,7 @@ pub(crate) fn diagnostics(
40 41
41 // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily. 42 // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily.
42 res.extend(parse.errors().iter().take(128).map(|err| Diagnostic { 43 res.extend(parse.errors().iter().take(128).map(|err| Diagnostic {
44 name: None,
43 range: err.range(), 45 range: err.range(),
44 message: format!("Syntax Error: {}", err), 46 message: format!("Syntax Error: {}", err),
45 severity: Severity::Error, 47 severity: Severity::Error,
@@ -51,7 +53,7 @@ pub(crate) fn diagnostics(
51 check_struct_shorthand_initialization(&mut res, file_id, &node); 53 check_struct_shorthand_initialization(&mut res, file_id, &node);
52 } 54 }
53 let res = RefCell::new(res); 55 let res = RefCell::new(res);
54 let mut sink = DiagnosticSinkBuilder::new() 56 let mut sink_builder = DiagnosticSinkBuilder::new()
55 .on::<hir::diagnostics::UnresolvedModule, _>(|d| { 57 .on::<hir::diagnostics::UnresolvedModule, _>(|d| {
56 res.borrow_mut().push(diagnostic_with_fix(d, &sema)); 58 res.borrow_mut().push(diagnostic_with_fix(d, &sema));
57 }) 59 })
@@ -65,10 +67,20 @@ pub(crate) fn diagnostics(
65 res.borrow_mut().push(diagnostic_with_fix(d, &sema)); 67 res.borrow_mut().push(diagnostic_with_fix(d, &sema));
66 }) 68 })
67 // Only collect experimental diagnostics when they're enabled. 69 // Only collect experimental diagnostics when they're enabled.
68 .filter(|diag| !diag.is_experimental() || enable_experimental) 70 .filter(|diag| !diag.is_experimental() || enable_experimental);
71
72 if !analysis_config.disabled_diagnostics.is_empty() {
73 // Do not collect disabled diagnostics.
74 sink_builder =
75 sink_builder.filter(|diag| !analysis_config.disabled_diagnostics.contains(diag.name()));
76 }
77
78 // Finalize the `DiagnosticSink` building process.
79 let mut sink = sink_builder
69 // Diagnostics not handled above get no fix and default treatment. 80 // Diagnostics not handled above get no fix and default treatment.
70 .build(|d| { 81 .build(|d| {
71 res.borrow_mut().push(Diagnostic { 82 res.borrow_mut().push(Diagnostic {
83 name: Some(d.name().into()),
72 message: d.message(), 84 message: d.message(),
73 range: sema.diagnostics_display_range(d).range, 85 range: sema.diagnostics_display_range(d).range,
74 severity: Severity::Error, 86 severity: Severity::Error,
@@ -85,6 +97,7 @@ pub(crate) fn diagnostics(
85 97
86fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic { 98fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic {
87 Diagnostic { 99 Diagnostic {
100 name: Some(d.name().into()),
88 range: sema.diagnostics_display_range(d).range, 101 range: sema.diagnostics_display_range(d).range,
89 message: d.message(), 102 message: d.message(),
90 severity: Severity::Error, 103 severity: Severity::Error,
@@ -111,6 +124,7 @@ fn check_unnecessary_braces_in_use_statement(
111 }); 124 });
112 125
113 acc.push(Diagnostic { 126 acc.push(Diagnostic {
127 name: None,
114 range: use_range, 128 range: use_range,
115 message: "Unnecessary braces in use statement".to_string(), 129 message: "Unnecessary braces in use statement".to_string(),
116 severity: Severity::WeakWarning, 130 severity: Severity::WeakWarning,
@@ -157,6 +171,7 @@ fn check_struct_shorthand_initialization(
157 171
158 let field_range = record_field.syntax().text_range(); 172 let field_range = record_field.syntax().text_range();
159 acc.push(Diagnostic { 173 acc.push(Diagnostic {
174 name: None,
160 range: field_range, 175 range: field_range,
161 message: "Shorthand struct initialization".to_string(), 176 message: "Shorthand struct initialization".to_string(),
162 severity: Severity::WeakWarning, 177 severity: Severity::WeakWarning,
@@ -174,10 +189,14 @@ fn check_struct_shorthand_initialization(
174 189
175#[cfg(test)] 190#[cfg(test)]
176mod tests { 191mod tests {
192 use std::collections::HashSet;
177 use stdx::trim_indent; 193 use stdx::trim_indent;
178 use test_utils::assert_eq_text; 194 use test_utils::assert_eq_text;
179 195
180 use crate::mock_analysis::{analysis_and_position, single_file, MockAnalysis}; 196 use crate::{
197 mock_analysis::{analysis_and_position, single_file, MockAnalysis},
198 AnalysisConfig,
199 };
181 use expect::{expect, Expect}; 200 use expect::{expect, Expect};
182 201
183 /// Takes a multi-file input fixture with annotated cursor positions, 202 /// Takes a multi-file input fixture with annotated cursor positions,
@@ -241,6 +260,51 @@ mod tests {
241 assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics); 260 assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics);
242 } 261 }
243 262
263 /// Takes a multi-file input fixture with annotated cursor position and the list of disabled diagnostics,
264 /// and checks that provided diagnostics aren't spawned during analysis.
265 fn check_disabled_diagnostics(ra_fixture: &str, disabled_diagnostics: &[&'static str]) {
266 let disabled_diagnostics: HashSet<_> =
267 disabled_diagnostics.into_iter().map(|diag| diag.to_string()).collect();
268
269 let mock = MockAnalysis::with_files(ra_fixture);
270 let files = mock.files().map(|(it, _)| it).collect::<Vec<_>>();
271 let mut analysis = mock.analysis();
272 analysis.set_config(AnalysisConfig { disabled_diagnostics: disabled_diagnostics.clone() });
273
274 let diagnostics = files
275 .clone()
276 .into_iter()
277 .flat_map(|file_id| analysis.diagnostics(file_id, true).unwrap())
278 .collect::<Vec<_>>();
279
280 // First, we have to check that diagnostic is not emitted when it's added to the disabled diagnostics list.
281 for diagnostic in diagnostics {
282 if let Some(name) = diagnostic.name {
283 assert!(!disabled_diagnostics.contains(&name), "Diagnostic {} is disabled", name);
284 }
285 }
286
287 // Then, we must reset the config and repeat the check, so that we'll be sure that without
288 // config these diagnostics are emitted.
289 // This is required for tests to not become outdated if e.g. diagnostics name changes:
290 // without this additional run the test will pass simply because a diagnostic with an old name
291 // will no longer exist.
292 analysis.set_config(AnalysisConfig { disabled_diagnostics: Default::default() });
293
294 let diagnostics = files
295 .into_iter()
296 .flat_map(|file_id| analysis.diagnostics(file_id, true).unwrap())
297 .collect::<Vec<_>>();
298
299 assert!(
300 diagnostics
301 .into_iter()
302 .filter_map(|diag| diag.name)
303 .any(|name| disabled_diagnostics.contains(&name)),
304 "At least one of the diagnostics was not emitted even without config; are the diagnostics names correct?"
305 );
306 }
307
244 fn check_expect(ra_fixture: &str, expect: Expect) { 308 fn check_expect(ra_fixture: &str, expect: Expect) {
245 let (analysis, file_id) = single_file(ra_fixture); 309 let (analysis, file_id) = single_file(ra_fixture);
246 let diagnostics = analysis.diagnostics(file_id, true).unwrap(); 310 let diagnostics = analysis.diagnostics(file_id, true).unwrap();
@@ -503,6 +567,9 @@ fn test_fn() {
503 expect![[r#" 567 expect![[r#"
504 [ 568 [
505 Diagnostic { 569 Diagnostic {
570 name: Some(
571 "unresolved-module",
572 ),
506 message: "unresolved module", 573 message: "unresolved module",
507 range: 0..8, 574 range: 0..8,
508 severity: Error, 575 severity: Error,
@@ -676,4 +743,9 @@ struct Foo {
676 ", 743 ",
677 ) 744 )
678 } 745 }
746
747 #[test]
748 fn test_disabled_diagnostics() {
749 check_disabled_diagnostics(r#"mod foo;"#, &["unresolved-module"]);
750 }
679} 751}