aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide')
-rw-r--r--crates/ra_ide/src/diagnostics.rs80
-rw-r--r--crates/ra_ide/src/lib.rs28
2 files changed, 99 insertions, 9 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}
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index 89fcb6f17..2cbd7e4f0 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -45,7 +45,7 @@ mod syntax_highlighting;
45mod syntax_tree; 45mod syntax_tree;
46mod typing; 46mod typing;
47 47
48use std::sync::Arc; 48use std::{collections::HashSet, sync::Arc};
49 49
50use ra_cfg::CfgOptions; 50use ra_cfg::CfgOptions;
51use ra_db::{ 51use ra_db::{
@@ -100,8 +100,15 @@ pub use ra_text_edit::{Indel, TextEdit};
100 100
101pub type Cancelable<T> = Result<T, Canceled>; 101pub type Cancelable<T> = Result<T, Canceled>;
102 102
103/// Configuration parameters for the analysis run.
104#[derive(Debug, Default, Clone)]
105pub struct AnalysisConfig {
106 pub disabled_diagnostics: HashSet<String>,
107}
108
103#[derive(Debug)] 109#[derive(Debug)]
104pub struct Diagnostic { 110pub struct Diagnostic {
111 pub name: Option<String>,
105 pub message: String, 112 pub message: String,
106 pub range: TextRange, 113 pub range: TextRange,
107 pub severity: Severity, 114 pub severity: Severity,
@@ -145,11 +152,16 @@ impl<T> RangeInfo<T> {
145#[derive(Debug)] 152#[derive(Debug)]
146pub struct AnalysisHost { 153pub struct AnalysisHost {
147 db: RootDatabase, 154 db: RootDatabase,
155 config: AnalysisConfig,
148} 156}
149 157
150impl AnalysisHost { 158impl AnalysisHost {
151 pub fn new(lru_capacity: Option<usize>) -> AnalysisHost { 159 pub fn new(lru_capacity: Option<usize>) -> Self {
152 AnalysisHost { db: RootDatabase::new(lru_capacity) } 160 Self::with_config(lru_capacity, AnalysisConfig::default())
161 }
162
163 pub fn with_config(lru_capacity: Option<usize>, config: AnalysisConfig) -> Self {
164 AnalysisHost { db: RootDatabase::new(lru_capacity), config }
153 } 165 }
154 166
155 pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) { 167 pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) {
@@ -159,7 +171,7 @@ impl AnalysisHost {
159 /// Returns a snapshot of the current state, which you can query for 171 /// Returns a snapshot of the current state, which you can query for
160 /// semantic information. 172 /// semantic information.
161 pub fn analysis(&self) -> Analysis { 173 pub fn analysis(&self) -> Analysis {
162 Analysis { db: self.db.snapshot() } 174 Analysis { db: self.db.snapshot(), config: self.config.clone() }
163 } 175 }
164 176
165 /// Applies changes to the current state of the world. If there are 177 /// Applies changes to the current state of the world. If there are
@@ -203,6 +215,7 @@ impl Default for AnalysisHost {
203#[derive(Debug)] 215#[derive(Debug)]
204pub struct Analysis { 216pub struct Analysis {
205 db: salsa::Snapshot<RootDatabase>, 217 db: salsa::Snapshot<RootDatabase>,
218 config: AnalysisConfig,
206} 219}
207 220
208// As a general design guideline, `Analysis` API are intended to be independent 221// As a general design guideline, `Analysis` API are intended to be independent
@@ -498,7 +511,7 @@ impl Analysis {
498 file_id: FileId, 511 file_id: FileId,
499 enable_experimental: bool, 512 enable_experimental: bool,
500 ) -> Cancelable<Vec<Diagnostic>> { 513 ) -> Cancelable<Vec<Diagnostic>> {
501 self.with_db(|db| diagnostics::diagnostics(db, file_id, enable_experimental)) 514 self.with_db(|db| diagnostics::diagnostics(db, file_id, enable_experimental, &self.config))
502 } 515 }
503 516
504 /// Returns the edit required to rename reference at the position to the new 517 /// Returns the edit required to rename reference at the position to the new
@@ -524,6 +537,11 @@ impl Analysis {
524 }) 537 })
525 } 538 }
526 539
540 /// Sets the provided config.
541 pub fn set_config(&mut self, config: AnalysisConfig) {
542 self.config = config;
543 }
544
527 /// Performs an operation on that may be Canceled. 545 /// Performs an operation on that may be Canceled.
528 fn with_db<F: FnOnce(&RootDatabase) -> T + std::panic::UnwindSafe, T>( 546 fn with_db<F: FnOnce(&RootDatabase) -> T + std::panic::UnwindSafe, T>(
529 &self, 547 &self,