aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/assists/src/assist_context.rs16
-rw-r--r--crates/assists/src/lib.rs13
-rw-r--r--crates/hir_def/src/diagnostics.rs6
-rw-r--r--crates/hir_expand/src/diagnostics.rs11
-rw-r--r--crates/hir_ty/src/diagnostics.rs34
-rw-r--r--crates/hir_ty/src/tests.rs17
-rw-r--r--crates/ide/src/diagnostics.rs146
-rw-r--r--crates/ide/src/diagnostics/fixes.rs (renamed from crates/ide/src/diagnostics/diagnostics_with_fix.rs)10
-rw-r--r--crates/ide/src/lib.rs43
-rw-r--r--crates/ide_db/src/label.rs49
-rw-r--r--crates/ide_db/src/lib.rs1
-rw-r--r--crates/rust-analyzer/src/cli/analysis_bench.rs7
-rw-r--r--crates/rust-analyzer/src/cli/diagnostics.rs5
-rw-r--r--crates/rust-analyzer/src/config.rs39
-rw-r--r--crates/rust-analyzer/src/diagnostics.rs2
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs18
-rw-r--r--crates/rust-analyzer/src/handlers.rs48
-rw-r--r--crates/rust-analyzer/src/main_loop.rs2
-rw-r--r--crates/rust-analyzer/src/to_proto.rs2
-rw-r--r--crates/stdx/src/lib.rs30
20 files changed, 268 insertions, 231 deletions
diff --git a/crates/assists/src/assist_context.rs b/crates/assists/src/assist_context.rs
index 79574b9ac..11c171fc2 100644
--- a/crates/assists/src/assist_context.rs
+++ b/crates/assists/src/assist_context.rs
@@ -6,6 +6,7 @@ use algo::find_covering_element;
6use base_db::{FileId, FileRange}; 6use base_db::{FileId, FileRange};
7use hir::Semantics; 7use hir::Semantics;
8use ide_db::{ 8use ide_db::{
9 label::Label,
9 source_change::{SourceChange, SourceFileEdit}, 10 source_change::{SourceChange, SourceFileEdit},
10 RootDatabase, 11 RootDatabase,
11}; 12};
@@ -157,8 +158,9 @@ impl Assists {
157 if !self.is_allowed(&id) { 158 if !self.is_allowed(&id) {
158 return None; 159 return None;
159 } 160 }
160 let label = Assist::new(id, label.into(), None, target); 161 let label = Label::new(label.into());
161 self.add_impl(label, f) 162 let assist = Assist { id, label, group: None, target };
163 self.add_impl(assist, f)
162 } 164 }
163 165
164 pub(crate) fn add_group( 166 pub(crate) fn add_group(
@@ -172,12 +174,12 @@ impl Assists {
172 if !self.is_allowed(&id) { 174 if !self.is_allowed(&id) {
173 return None; 175 return None;
174 } 176 }
175 177 let label = Label::new(label.into());
176 let label = Assist::new(id, label.into(), Some(group.clone()), target); 178 let assist = Assist { id, label, group: Some(group.clone()), target };
177 self.add_impl(label, f) 179 self.add_impl(assist, f)
178 } 180 }
179 181
180 fn add_impl(&mut self, label: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { 182 fn add_impl(&mut self, assist: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> {
181 let source_change = if self.resolve { 183 let source_change = if self.resolve {
182 let mut builder = AssistBuilder::new(self.file); 184 let mut builder = AssistBuilder::new(self.file);
183 f(&mut builder); 185 f(&mut builder);
@@ -186,7 +188,7 @@ impl Assists {
186 None 188 None
187 }; 189 };
188 190
189 self.buf.push((label, source_change)); 191 self.buf.push((assist, source_change));
190 Some(()) 192 Some(())
191 } 193 }
192 194
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index c589b08dc..14834480a 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -19,7 +19,7 @@ pub mod ast_transform;
19 19
20use base_db::FileRange; 20use base_db::FileRange;
21use hir::Semantics; 21use hir::Semantics;
22use ide_db::{source_change::SourceChange, RootDatabase}; 22use ide_db::{label::Label, source_change::SourceChange, RootDatabase};
23use syntax::TextRange; 23use syntax::TextRange;
24 24
25pub(crate) use crate::assist_context::{AssistContext, Assists}; 25pub(crate) use crate::assist_context::{AssistContext, Assists};
@@ -68,7 +68,7 @@ pub struct GroupLabel(pub String);
68pub struct Assist { 68pub struct Assist {
69 pub id: AssistId, 69 pub id: AssistId,
70 /// Short description of the assist, as shown in the UI. 70 /// Short description of the assist, as shown in the UI.
71 label: String, 71 pub label: Label,
72 pub group: Option<GroupLabel>, 72 pub group: Option<GroupLabel>,
73 /// Target ranges are used to sort assists: the smaller the target range, 73 /// Target ranges are used to sort assists: the smaller the target range,
74 /// the more specific assist is, and so it should be sorted first. 74 /// the more specific assist is, and so it should be sorted first.
@@ -82,11 +82,6 @@ pub struct ResolvedAssist {
82} 82}
83 83
84impl Assist { 84impl Assist {
85 fn new(id: AssistId, label: String, group: Option<GroupLabel>, target: TextRange) -> Assist {
86 assert!(label.starts_with(char::is_uppercase));
87 Assist { id, label, group, target }
88 }
89
90 /// Return all the assists applicable at the given position. 85 /// Return all the assists applicable at the given position.
91 /// 86 ///
92 /// Assists are returned in the "unresolved" state, that is only labels are 87 /// Assists are returned in the "unresolved" state, that is only labels are
@@ -118,10 +113,6 @@ impl Assist {
118 }); 113 });
119 acc.finish_resolved() 114 acc.finish_resolved()
120 } 115 }
121
122 pub fn label(&self) -> &str {
123 self.label.as_str()
124 }
125} 116}
126 117
127mod handlers { 118mod handlers {
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
3use std::any::Any; 3use std::any::Any;
4 4
5use hir_expand::diagnostics::Diagnostic; 5use hir_expand::diagnostics::{Diagnostic, DiagnosticCode};
6use syntax::{ast, AstPtr, SyntaxNodePtr}; 6use syntax::{ast, AstPtr, SyntaxNodePtr};
7 7
8use hir_expand::{HirFileId, InFile}; 8use hir_expand::{HirFileId, InFile};
@@ -15,8 +15,8 @@ pub struct UnresolvedModule {
15} 15}
16 16
17impl Diagnostic for UnresolvedModule { 17impl 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
21use crate::InFile; 21use crate::InFile;
22 22
23#[derive(Copy, Clone, PartialEq)]
24pub struct DiagnosticCode(pub &'static str);
25
26impl DiagnosticCode {
27 pub fn as_str(&self) -> &str {
28 self.0
29 }
30}
31
23pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { 32pub 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;
6use std::any::Any; 6use std::any::Any;
7 7
8use hir_def::DefWithBodyId; 8use hir_def::DefWithBodyId;
9use hir_expand::diagnostics::{Diagnostic, DiagnosticSink}; 9use hir_expand::diagnostics::{Diagnostic, DiagnosticCode, DiagnosticSink};
10use hir_expand::{name::Name, HirFileId, InFile}; 10use hir_expand::{name::Name, HirFileId, InFile};
11use stdx::format_to; 11use stdx::format_to;
12use syntax::{ast, AstPtr, SyntaxNodePtr}; 12use syntax::{ast, AstPtr, SyntaxNodePtr};
@@ -32,8 +32,8 @@ pub struct NoSuchField {
32} 32}
33 33
34impl Diagnostic for NoSuchField { 34impl 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
60impl Diagnostic for MissingFields { 60impl 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
96impl Diagnostic for MissingPatFields { 96impl 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
129impl Diagnostic for MissingMatchArms { 129impl 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
150impl Diagnostic for MissingOkInTailExpr { 150impl 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
171impl Diagnostic for BreakOutsideOfLoop { 171impl 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
192impl Diagnostic for MissingUnsafe { 192impl 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
215impl Diagnostic for MismatchedArgCount { 215impl 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/hir_ty/src/tests.rs b/crates/hir_ty/src/tests.rs
index c953925ec..91c9d38c5 100644
--- a/crates/hir_ty/src/tests.rs
+++ b/crates/hir_ty/src/tests.rs
@@ -8,7 +8,7 @@ mod method_resolution;
8mod macros; 8mod macros;
9mod display_source_code; 9mod display_source_code;
10 10
11use std::sync::Arc; 11use std::{env, sync::Arc};
12 12
13use base_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt}; 13use base_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt};
14use expect::Expect; 14use expect::Expect;
@@ -22,12 +22,14 @@ use hir_def::{
22 AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, 22 AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId,
23}; 23};
24use hir_expand::{db::AstDatabase, InFile}; 24use hir_expand::{db::AstDatabase, InFile};
25use stdx::format_to; 25use stdx::{format_to, RacyFlag};
26use syntax::{ 26use syntax::{
27 algo, 27 algo,
28 ast::{self, AstNode}, 28 ast::{self, AstNode},
29 SyntaxNode, 29 SyntaxNode,
30}; 30};
31use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Registry};
32use tracing_tree::HierarchicalLayer;
31 33
32use crate::{ 34use crate::{
33 db::HirDatabase, display::HirDisplay, infer::TypeMismatch, test_db::TestDB, InferenceResult, Ty, 35 db::HirDatabase, display::HirDisplay, infer::TypeMismatch, test_db::TestDB, InferenceResult, Ty,
@@ -37,9 +39,12 @@ use crate::{
37// against snapshots of the expected results using expect. Use 39// against snapshots of the expected results using expect. Use
38// `env UPDATE_EXPECT=1 cargo test -p hir_ty` to update the snapshots. 40// `env UPDATE_EXPECT=1 cargo test -p hir_ty` to update the snapshots.
39 41
40fn setup_tracing() -> tracing::subscriber::DefaultGuard { 42fn setup_tracing() -> Option<tracing::subscriber::DefaultGuard> {
41 use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Registry}; 43 static ENABLE: RacyFlag = RacyFlag::new();
42 use tracing_tree::HierarchicalLayer; 44 if !ENABLE.get(|| env::var("CHALK_DEBUG").is_ok()) {
45 return None;
46 }
47
43 let filter = EnvFilter::from_env("CHALK_DEBUG"); 48 let filter = EnvFilter::from_env("CHALK_DEBUG");
44 let layer = HierarchicalLayer::default() 49 let layer = HierarchicalLayer::default()
45 .with_indent_lines(true) 50 .with_indent_lines(true)
@@ -47,7 +52,7 @@ fn setup_tracing() -> tracing::subscriber::DefaultGuard {
47 .with_indent_amount(2) 52 .with_indent_amount(2)
48 .with_writer(std::io::stderr); 53 .with_writer(std::io::stderr);
49 let subscriber = Registry::default().with(filter).with(layer); 54 let subscriber = Registry::default().with(filter).with(layer);
50 tracing::subscriber::set_default(subscriber) 55 Some(tracing::subscriber::set_default(subscriber))
51} 56}
52 57
53fn check_types(ra_fixture: &str) { 58fn check_types(ra_fixture: &str) {
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 606a6064b..92b5adaa2 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -4,22 +4,48 @@
4//! macro-expanded files, but we need to present them to the users in terms of 4//! macro-expanded files, but we need to present them to the users in terms of
5//! original files. So we need to map the ranges. 5//! original files. So we need to map the ranges.
6 6
7use std::{cell::RefCell, collections::HashSet}; 7mod fixes;
8
9use std::cell::RefCell;
8 10
9use base_db::SourceDatabase; 11use base_db::SourceDatabase;
10use hir::{diagnostics::DiagnosticSinkBuilder, Semantics}; 12use hir::{diagnostics::DiagnosticSinkBuilder, Semantics};
11use ide_db::RootDatabase; 13use ide_db::RootDatabase;
12use itertools::Itertools; 14use itertools::Itertools;
15use rustc_hash::FxHashSet;
13use syntax::{ 16use syntax::{
14 ast::{self, AstNode}, 17 ast::{self, AstNode},
15 SyntaxNode, TextRange, T, 18 SyntaxNode, TextRange, T,
16}; 19};
17use text_edit::TextEdit; 20use text_edit::TextEdit;
18 21
19use crate::{Diagnostic, FileId, Fix, SourceFileEdit}; 22use crate::{FileId, Label, SourceChange, SourceFileEdit};
23
24use self::fixes::DiagnosticWithFix;
25
26#[derive(Debug)]
27pub struct Diagnostic {
28 // pub name: Option<String>,
29 pub message: String,
30 pub range: TextRange,
31 pub severity: Severity,
32 pub fix: Option<Fix>,
33}
20 34
21mod diagnostics_with_fix; 35#[derive(Debug)]
22use diagnostics_with_fix::DiagnosticWithFix; 36pub struct Fix {
37 pub label: Label,
38 pub source_change: SourceChange,
39 /// Allows to trigger the fix only when the caret is in the range given
40 pub fix_trigger_range: TextRange,
41}
42
43impl Fix {
44 fn new(label: &str, source_change: SourceChange, fix_trigger_range: TextRange) -> Self {
45 let label = Label::new(label);
46 Self { label, source_change, fix_trigger_range }
47 }
48}
23 49
24#[derive(Debug, Copy, Clone)] 50#[derive(Debug, Copy, Clone)]
25pub enum Severity { 51pub enum Severity {
@@ -27,11 +53,16 @@ pub enum Severity {
27 WeakWarning, 53 WeakWarning,
28} 54}
29 55
56#[derive(Default, Debug, Clone)]
57pub struct DiagnosticsConfig {
58 pub disable_experimental: bool,
59 pub disabled: FxHashSet<String>,
60}
61
30pub(crate) fn diagnostics( 62pub(crate) fn diagnostics(
31 db: &RootDatabase, 63 db: &RootDatabase,
64 config: &DiagnosticsConfig,
32 file_id: FileId, 65 file_id: FileId,
33 enable_experimental: bool,
34 disabled_diagnostics: Option<HashSet<String>>,
35) -> Vec<Diagnostic> { 66) -> Vec<Diagnostic> {
36 let _p = profile::span("diagnostics"); 67 let _p = profile::span("diagnostics");
37 let sema = Semantics::new(db); 68 let sema = Semantics::new(db);
@@ -40,7 +71,7 @@ pub(crate) fn diagnostics(
40 71
41 // [#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.
42 res.extend(parse.errors().iter().take(128).map(|err| Diagnostic { 73 res.extend(parse.errors().iter().take(128).map(|err| Diagnostic {
43 name: None, 74 // name: None,
44 range: err.range(), 75 range: err.range(),
45 message: format!("Syntax Error: {}", err), 76 message: format!("Syntax Error: {}", err),
46 severity: Severity::Error, 77 severity: Severity::Error,
@@ -52,7 +83,7 @@ pub(crate) fn diagnostics(
52 check_struct_shorthand_initialization(&mut res, file_id, &node); 83 check_struct_shorthand_initialization(&mut res, file_id, &node);
53 } 84 }
54 let res = RefCell::new(res); 85 let res = RefCell::new(res);
55 let mut sink_builder = DiagnosticSinkBuilder::new() 86 let sink_builder = DiagnosticSinkBuilder::new()
56 .on::<hir::diagnostics::UnresolvedModule, _>(|d| { 87 .on::<hir::diagnostics::UnresolvedModule, _>(|d| {
57 res.borrow_mut().push(diagnostic_with_fix(d, &sema)); 88 res.borrow_mut().push(diagnostic_with_fix(d, &sema));
58 }) 89 })
@@ -66,19 +97,15 @@ pub(crate) fn diagnostics(
66 res.borrow_mut().push(diagnostic_with_fix(d, &sema)); 97 res.borrow_mut().push(diagnostic_with_fix(d, &sema));
67 }) 98 })
68 // Only collect experimental diagnostics when they're enabled. 99 // Only collect experimental diagnostics when they're enabled.
69 .filter(|diag| !diag.is_experimental() || enable_experimental); 100 .filter(|diag| !(diag.is_experimental() && config.disable_experimental))
70 101 .filter(|diag| !config.disabled.contains(diag.code().as_str()));
71 if let Some(disabled_diagnostics) = disabled_diagnostics {
72 // Do not collect disabled diagnostics.
73 sink_builder = sink_builder.filter(move |diag| !disabled_diagnostics.contains(diag.name()));
74 }
75 102
76 // Finalize the `DiagnosticSink` building process. 103 // Finalize the `DiagnosticSink` building process.
77 let mut sink = sink_builder 104 let mut sink = sink_builder
78 // Diagnostics not handled above get no fix and default treatment. 105 // Diagnostics not handled above get no fix and default treatment.
79 .build(|d| { 106 .build(|d| {
80 res.borrow_mut().push(Diagnostic { 107 res.borrow_mut().push(Diagnostic {
81 name: Some(d.name().into()), 108 // name: Some(d.name().into()),
82 message: d.message(), 109 message: d.message(),
83 range: sema.diagnostics_display_range(d).range, 110 range: sema.diagnostics_display_range(d).range,
84 severity: Severity::Error, 111 severity: Severity::Error,
@@ -95,7 +122,7 @@ pub(crate) fn diagnostics(
95 122
96fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic { 123fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic {
97 Diagnostic { 124 Diagnostic {
98 name: Some(d.name().into()), 125 // name: Some(d.name().into()),
99 range: sema.diagnostics_display_range(d).range, 126 range: sema.diagnostics_display_range(d).range,
100 message: d.message(), 127 message: d.message(),
101 severity: Severity::Error, 128 severity: Severity::Error,
@@ -122,7 +149,7 @@ fn check_unnecessary_braces_in_use_statement(
122 }); 149 });
123 150
124 acc.push(Diagnostic { 151 acc.push(Diagnostic {
125 name: None, 152 // name: None,
126 range: use_range, 153 range: use_range,
127 message: "Unnecessary braces in use statement".to_string(), 154 message: "Unnecessary braces in use statement".to_string(),
128 severity: Severity::WeakWarning, 155 severity: Severity::WeakWarning,
@@ -169,7 +196,7 @@ fn check_struct_shorthand_initialization(
169 196
170 let field_range = record_field.syntax().text_range(); 197 let field_range = record_field.syntax().text_range();
171 acc.push(Diagnostic { 198 acc.push(Diagnostic {
172 name: None, 199 // name: None,
173 range: field_range, 200 range: field_range,
174 message: "Shorthand struct initialization".to_string(), 201 message: "Shorthand struct initialization".to_string(),
175 severity: Severity::WeakWarning, 202 severity: Severity::WeakWarning,
@@ -187,12 +214,14 @@ fn check_struct_shorthand_initialization(
187 214
188#[cfg(test)] 215#[cfg(test)]
189mod tests { 216mod tests {
190 use std::collections::HashSet; 217 use expect::{expect, Expect};
191 use stdx::trim_indent; 218 use stdx::trim_indent;
192 use test_utils::assert_eq_text; 219 use test_utils::assert_eq_text;
193 220
194 use crate::mock_analysis::{analysis_and_position, single_file, MockAnalysis}; 221 use crate::{
195 use expect::{expect, Expect}; 222 mock_analysis::{analysis_and_position, single_file, MockAnalysis},
223 DiagnosticsConfig,
224 };
196 225
197 /// Takes a multi-file input fixture with annotated cursor positions, 226 /// Takes a multi-file input fixture with annotated cursor positions,
198 /// and checks that: 227 /// and checks that:
@@ -203,8 +232,11 @@ mod tests {
203 let after = trim_indent(ra_fixture_after); 232 let after = trim_indent(ra_fixture_after);
204 233
205 let (analysis, file_position) = analysis_and_position(ra_fixture_before); 234 let (analysis, file_position) = analysis_and_position(ra_fixture_before);
206 let diagnostic = 235 let diagnostic = analysis
207 analysis.diagnostics(file_position.file_id, true, None).unwrap().pop().unwrap(); 236 .diagnostics(&DiagnosticsConfig::default(), file_position.file_id)
237 .unwrap()
238 .pop()
239 .unwrap();
208 let mut fix = diagnostic.fix.unwrap(); 240 let mut fix = diagnostic.fix.unwrap();
209 let edit = fix.source_change.source_file_edits.pop().unwrap().edit; 241 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
210 let target_file_contents = analysis.file_text(file_position.file_id).unwrap(); 242 let target_file_contents = analysis.file_text(file_position.file_id).unwrap();
@@ -230,7 +262,11 @@ mod tests {
230 let ra_fixture_after = &trim_indent(ra_fixture_after); 262 let ra_fixture_after = &trim_indent(ra_fixture_after);
231 let (analysis, file_pos) = analysis_and_position(ra_fixture_before); 263 let (analysis, file_pos) = analysis_and_position(ra_fixture_before);
232 let current_file_id = file_pos.file_id; 264 let current_file_id = file_pos.file_id;
233 let diagnostic = analysis.diagnostics(current_file_id, true, None).unwrap().pop().unwrap(); 265 let diagnostic = analysis
266 .diagnostics(&DiagnosticsConfig::default(), current_file_id)
267 .unwrap()
268 .pop()
269 .unwrap();
234 let mut fix = diagnostic.fix.unwrap(); 270 let mut fix = diagnostic.fix.unwrap();
235 let edit = fix.source_change.source_file_edits.pop().unwrap(); 271 let edit = fix.source_change.source_file_edits.pop().unwrap();
236 let changed_file_id = edit.file_id; 272 let changed_file_id = edit.file_id;
@@ -251,58 +287,16 @@ mod tests {
251 let analysis = mock.analysis(); 287 let analysis = mock.analysis();
252 let diagnostics = files 288 let diagnostics = files
253 .into_iter() 289 .into_iter()
254 .flat_map(|file_id| analysis.diagnostics(file_id, true, None).unwrap())
255 .collect::<Vec<_>>();
256 assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics);
257 }
258
259 /// Takes a multi-file input fixture with annotated cursor position and the list of disabled diagnostics,
260 /// and checks that provided diagnostics aren't spawned during analysis.
261 fn check_disabled_diagnostics(ra_fixture: &str, disabled_diagnostics: &[&'static str]) {
262 let disabled_diagnostics: HashSet<_> =
263 disabled_diagnostics.into_iter().map(|diag| diag.to_string()).collect();
264
265 let mock = MockAnalysis::with_files(ra_fixture);
266 let files = mock.files().map(|(it, _)| it).collect::<Vec<_>>();
267 let analysis = mock.analysis();
268
269 let diagnostics = files
270 .clone()
271 .into_iter()
272 .flat_map(|file_id| { 290 .flat_map(|file_id| {
273 analysis.diagnostics(file_id, true, Some(disabled_diagnostics.clone())).unwrap() 291 analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap()
274 }) 292 })
275 .collect::<Vec<_>>(); 293 .collect::<Vec<_>>();
276 294 assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics);
277 // First, we have to check that diagnostic is not emitted when it's added to the disabled diagnostics list.
278 for diagnostic in diagnostics {
279 if let Some(name) = diagnostic.name {
280 assert!(!disabled_diagnostics.contains(&name), "Diagnostic {} is disabled", name);
281 }
282 }
283
284 // Then, we must reset the config and repeat the check, so that we'll be sure that without
285 // config these diagnostics are emitted.
286 // This is required for tests to not become outdated if e.g. diagnostics name changes:
287 // without this additional run the test will pass simply because a diagnostic with an old name
288 // will no longer exist.
289 let diagnostics = files
290 .into_iter()
291 .flat_map(|file_id| analysis.diagnostics(file_id, true, None).unwrap())
292 .collect::<Vec<_>>();
293
294 assert!(
295 diagnostics
296 .into_iter()
297 .filter_map(|diag| diag.name)
298 .any(|name| disabled_diagnostics.contains(&name)),
299 "At least one of the diagnostics was not emitted even without config; are the diagnostics names correct?"
300 );
301 } 295 }
302 296
303 fn check_expect(ra_fixture: &str, expect: Expect) { 297 fn check_expect(ra_fixture: &str, expect: Expect) {
304 let (analysis, file_id) = single_file(ra_fixture); 298 let (analysis, file_id) = single_file(ra_fixture);
305 let diagnostics = analysis.diagnostics(file_id, true, None).unwrap(); 299 let diagnostics = analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap();
306 expect.assert_debug_eq(&diagnostics) 300 expect.assert_debug_eq(&diagnostics)
307 } 301 }
308 302
@@ -562,9 +556,6 @@ fn test_fn() {
562 expect![[r#" 556 expect![[r#"
563 [ 557 [
564 Diagnostic { 558 Diagnostic {
565 name: Some(
566 "unresolved-module",
567 ),
568 message: "unresolved module", 559 message: "unresolved module",
569 range: 0..8, 560 range: 0..8,
570 severity: Error, 561 severity: Error,
@@ -741,6 +732,15 @@ struct Foo {
741 732
742 #[test] 733 #[test]
743 fn test_disabled_diagnostics() { 734 fn test_disabled_diagnostics() {
744 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());
745 } 745 }
746} 746}
diff --git a/crates/ide/src/diagnostics/diagnostics_with_fix.rs b/crates/ide/src/diagnostics/fixes.rs
index 85b46c995..68ae1c239 100644
--- a/crates/ide/src/diagnostics/diagnostics_with_fix.rs
+++ b/crates/ide/src/diagnostics/fixes.rs
@@ -1,7 +1,5 @@
1//! Provides a way to attach fixes to the diagnostics. 1//! Provides a way to attach fixes to the diagnostics.
2//! The same module also has all curret custom fixes for the diagnostics implemented. 2//! The same module also has all curret custom fixes for the diagnostics implemented.
3use crate::Fix;
4use ast::{edit::IndentLevel, make};
5use base_db::FileId; 3use base_db::FileId;
6use hir::{ 4use hir::{
7 db::AstDatabase, 5 db::AstDatabase,
@@ -12,9 +10,15 @@ use ide_db::{
12 source_change::{FileSystemEdit, SourceFileEdit}, 10 source_change::{FileSystemEdit, SourceFileEdit},
13 RootDatabase, 11 RootDatabase,
14}; 12};
15use syntax::{algo, ast, AstNode}; 13use syntax::{
14 algo,
15 ast::{self, edit::IndentLevel, make},
16 AstNode,
17};
16use text_edit::TextEdit; 18use text_edit::TextEdit;
17 19
20use crate::diagnostics::Fix;
21
18/// A [Diagnostic] that potentially has a fix available. 22/// A [Diagnostic] that potentially has a fix available.
19/// 23///
20/// [Diagnostic]: hir::diagnostics::Diagnostic 24/// [Diagnostic]: hir::diagnostics::Diagnostic
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 4b797f374..e3af6d5bc 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -44,7 +44,7 @@ mod syntax_highlighting;
44mod syntax_tree; 44mod syntax_tree;
45mod typing; 45mod typing;
46 46
47use std::{collections::HashSet, sync::Arc}; 47use std::sync::Arc;
48 48
49use base_db::{ 49use base_db::{
50 salsa::{self, ParallelDatabase}, 50 salsa::{self, ParallelDatabase},
@@ -65,7 +65,7 @@ pub use crate::{
65 completion::{ 65 completion::{
66 CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat, 66 CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat,
67 }, 67 },
68 diagnostics::Severity, 68 diagnostics::{Diagnostic, DiagnosticsConfig, Fix, Severity},
69 display::NavigationTarget, 69 display::NavigationTarget,
70 expand_macro::ExpandedMacro, 70 expand_macro::ExpandedMacro,
71 file_structure::StructureNode, 71 file_structure::StructureNode,
@@ -88,6 +88,7 @@ pub use base_db::{
88pub use hir::{Documentation, Semantics}; 88pub use hir::{Documentation, Semantics};
89pub use ide_db::{ 89pub use ide_db::{
90 change::AnalysisChange, 90 change::AnalysisChange,
91 label::Label,
91 line_index::{LineCol, LineIndex}, 92 line_index::{LineCol, LineIndex},
92 search::SearchScope, 93 search::SearchScope,
93 source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, 94 source_change::{FileSystemEdit, SourceChange, SourceFileEdit},
@@ -99,35 +100,6 @@ pub use text_edit::{Indel, TextEdit};
99 100
100pub type Cancelable<T> = Result<T, Canceled>; 101pub type Cancelable<T> = Result<T, Canceled>;
101 102
102#[derive(Debug)]
103pub struct Diagnostic {
104 pub name: Option<String>,
105 pub message: String,
106 pub range: TextRange,
107 pub severity: Severity,
108 pub fix: Option<Fix>,
109}
110
111#[derive(Debug)]
112pub struct Fix {
113 pub label: String,
114 pub source_change: SourceChange,
115 /// Allows to trigger the fix only when the caret is in the range given
116 pub fix_trigger_range: TextRange,
117}
118
119impl Fix {
120 pub fn new(
121 label: impl Into<String>,
122 source_change: SourceChange,
123 fix_trigger_range: TextRange,
124 ) -> Self {
125 let label = label.into();
126 assert!(label.starts_with(char::is_uppercase) && !label.ends_with('.'));
127 Self { label, source_change, fix_trigger_range }
128 }
129}
130
131/// Info associated with a text range. 103/// Info associated with a text range.
132#[derive(Debug)] 104#[derive(Debug)]
133pub struct RangeInfo<T> { 105pub struct RangeInfo<T> {
@@ -148,7 +120,7 @@ pub struct AnalysisHost {
148} 120}
149 121
150impl AnalysisHost { 122impl AnalysisHost {
151 pub fn new(lru_capacity: Option<usize>) -> Self { 123 pub fn new(lru_capacity: Option<usize>) -> AnalysisHost {
152 AnalysisHost { db: RootDatabase::new(lru_capacity) } 124 AnalysisHost { db: RootDatabase::new(lru_capacity) }
153 } 125 }
154 126
@@ -495,13 +467,10 @@ impl Analysis {
495 /// Computes the set of diagnostics for the given file. 467 /// Computes the set of diagnostics for the given file.
496 pub fn diagnostics( 468 pub fn diagnostics(
497 &self, 469 &self,
470 config: &DiagnosticsConfig,
498 file_id: FileId, 471 file_id: FileId,
499 enable_experimental: bool,
500 disabled_diagnostics: Option<HashSet<String>>,
501 ) -> Cancelable<Vec<Diagnostic>> { 472 ) -> Cancelable<Vec<Diagnostic>> {
502 self.with_db(|db| { 473 self.with_db(|db| diagnostics::diagnostics(db, config, file_id))
503 diagnostics::diagnostics(db, file_id, enable_experimental, disabled_diagnostics)
504 })
505 } 474 }
506 475
507 /// Returns the edit required to rename reference at the position to the new 476 /// Returns the edit required to rename reference at the position to the new
diff --git a/crates/ide_db/src/label.rs b/crates/ide_db/src/label.rs
new file mode 100644
index 000000000..c0e89e72f
--- /dev/null
+++ b/crates/ide_db/src/label.rs
@@ -0,0 +1,49 @@
1//! See `Label`
2use std::fmt;
3
4/// A type to specify UI label, like an entry in the list of assists. Enforces
5/// proper casing:
6///
7/// Frobnicate bar
8///
9/// Note the upper-case first letter and the absence of `.` at the end.
10#[derive(Clone)]
11pub struct Label(String);
12
13impl PartialEq<str> for Label {
14 fn eq(&self, other: &str) -> bool {
15 self.0 == other
16 }
17}
18
19impl PartialEq<&'_ str> for Label {
20 fn eq(&self, other: &&str) -> bool {
21 self == *other
22 }
23}
24
25impl From<Label> for String {
26 fn from(label: Label) -> String {
27 label.0
28 }
29}
30
31impl Label {
32 pub fn new(label: impl Into<String>) -> Label {
33 let label = label.into();
34 assert!(label.starts_with(char::is_uppercase) && !label.ends_with('.'));
35 Label(label)
36 }
37}
38
39impl fmt::Display for Label {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 fmt::Display::fmt(&self.0, f)
42 }
43}
44
45impl fmt::Debug for Label {
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 fmt::Debug::fmt(&self.0, f)
48 }
49}
diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs
index fd474cd0f..70ada02f3 100644
--- a/crates/ide_db/src/lib.rs
+++ b/crates/ide_db/src/lib.rs
@@ -2,6 +2,7 @@
2//! 2//!
3//! It is mainly a `HirDatabase` for semantic analysis, plus a `SymbolsDatabase`, for fuzzy search. 3//! It is mainly a `HirDatabase` for semantic analysis, plus a `SymbolsDatabase`, for fuzzy search.
4 4
5pub mod label;
5pub mod line_index; 6pub mod line_index;
6pub mod symbol_index; 7pub mod symbol_index;
7pub mod change; 8pub mod change;
diff --git a/crates/rust-analyzer/src/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs
index 43f0196af..c312e0a2e 100644
--- a/crates/rust-analyzer/src/cli/analysis_bench.rs
+++ b/crates/rust-analyzer/src/cli/analysis_bench.rs
@@ -7,7 +7,10 @@ use base_db::{
7 salsa::{Database, Durability}, 7 salsa::{Database, Durability},
8 FileId, 8 FileId,
9}; 9};
10use ide::{Analysis, AnalysisChange, AnalysisHost, CompletionConfig, FilePosition, LineCol}; 10use ide::{
11 Analysis, AnalysisChange, AnalysisHost, CompletionConfig, DiagnosticsConfig, FilePosition,
12 LineCol,
13};
11use vfs::AbsPathBuf; 14use vfs::AbsPathBuf;
12 15
13use crate::{ 16use crate::{
@@ -71,7 +74,7 @@ impl BenchCmd {
71 match &self.what { 74 match &self.what {
72 BenchWhat::Highlight { .. } => { 75 BenchWhat::Highlight { .. } => {
73 let res = do_work(&mut host, file_id, |analysis| { 76 let res = do_work(&mut host, file_id, |analysis| {
74 analysis.diagnostics(file_id, true, None).unwrap(); 77 analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap();
75 analysis.highlight_as_html(file_id, false).unwrap() 78 analysis.highlight_as_html(file_id, false).unwrap()
76 }); 79 });
77 if verbosity.is_verbose() { 80 if verbosity.is_verbose() {
diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs
index 31eb7ff3f..c424aa6e2 100644
--- a/crates/rust-analyzer/src/cli/diagnostics.rs
+++ b/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -8,7 +8,7 @@ use rustc_hash::FxHashSet;
8 8
9use base_db::SourceDatabaseExt; 9use base_db::SourceDatabaseExt;
10use hir::Crate; 10use hir::Crate;
11use ide::Severity; 11use ide::{DiagnosticsConfig, Severity};
12 12
13use crate::cli::{load_cargo::load_cargo, Result}; 13use crate::cli::{load_cargo::load_cargo, Result};
14 14
@@ -47,7 +47,8 @@ pub fn diagnostics(
47 String::from("unknown") 47 String::from("unknown")
48 }; 48 };
49 println!("processing crate: {}, module: {}", crate_name, _vfs.file_path(file_id)); 49 println!("processing crate: {}, module: {}", crate_name, _vfs.file_path(file_id));
50 for diagnostic in analysis.diagnostics(file_id, true, None).unwrap() { 50 for diagnostic in analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap()
51 {
51 if matches!(diagnostic.severity, Severity::Error) { 52 if matches!(diagnostic.severity, Severity::Error) {
52 found_error = true; 53 found_error = true;
53 } 54 }
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 44fd7c286..99f7751ac 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -7,24 +7,25 @@
7//! configure the server itself, feature flags are passed into analysis, and 7//! configure the server itself, feature flags are passed into analysis, and
8//! tweak things like automatic insertion of `()` in completions. 8//! tweak things like automatic insertion of `()` in completions.
9 9
10use std::{collections::HashSet, ffi::OsString, path::PathBuf}; 10use std::{ffi::OsString, path::PathBuf};
11 11
12use flycheck::FlycheckConfig; 12use flycheck::FlycheckConfig;
13use ide::{AssistConfig, CompletionConfig, HoverConfig, InlayHintsConfig}; 13use ide::{AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig};
14use lsp_types::ClientCapabilities; 14use lsp_types::ClientCapabilities;
15use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest}; 15use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest};
16use rustc_hash::FxHashSet;
16use serde::Deserialize; 17use serde::Deserialize;
17use vfs::AbsPathBuf; 18use vfs::AbsPathBuf;
18 19
19use crate::diagnostics::DiagnosticsConfig; 20use crate::diagnostics::DiagnosticsMapConfig;
20 21
21#[derive(Debug, Clone)] 22#[derive(Debug, Clone)]
22pub struct Config { 23pub struct Config {
23 pub client_caps: ClientCapsConfig, 24 pub client_caps: ClientCapsConfig,
24 25
25 pub publish_diagnostics: bool, 26 pub publish_diagnostics: bool,
26 pub experimental_diagnostics: bool,
27 pub diagnostics: DiagnosticsConfig, 27 pub diagnostics: DiagnosticsConfig,
28 pub diagnostics_map: DiagnosticsMapConfig,
28 pub lru_capacity: Option<usize>, 29 pub lru_capacity: Option<usize>,
29 pub proc_macro_srv: Option<(PathBuf, Vec<OsString>)>, 30 pub proc_macro_srv: Option<(PathBuf, Vec<OsString>)>,
30 pub files: FilesConfig, 31 pub files: FilesConfig,
@@ -45,14 +46,6 @@ pub struct Config {
45 pub with_sysroot: bool, 46 pub with_sysroot: bool,
46 pub linked_projects: Vec<LinkedProject>, 47 pub linked_projects: Vec<LinkedProject>,
47 pub root_path: AbsPathBuf, 48 pub root_path: AbsPathBuf,
48
49 pub analysis: AnalysisConfig,
50}
51
52/// Configuration parameters for the analysis run.
53#[derive(Debug, Default, Clone)]
54pub struct AnalysisConfig {
55 pub disabled_diagnostics: HashSet<String>,
56} 49}
57 50
58#[derive(Debug, Clone, Eq, PartialEq)] 51#[derive(Debug, Clone, Eq, PartialEq)]
@@ -146,8 +139,8 @@ impl Config {
146 139
147 with_sysroot: true, 140 with_sysroot: true,
148 publish_diagnostics: true, 141 publish_diagnostics: true,
149 experimental_diagnostics: true,
150 diagnostics: DiagnosticsConfig::default(), 142 diagnostics: DiagnosticsConfig::default(),
143 diagnostics_map: DiagnosticsMapConfig::default(),
151 lru_capacity: None, 144 lru_capacity: None,
152 proc_macro_srv: None, 145 proc_macro_srv: None,
153 files: FilesConfig { watcher: FilesWatcher::Notify, exclude: Vec::new() }, 146 files: FilesConfig { watcher: FilesWatcher::Notify, exclude: Vec::new() },
@@ -184,8 +177,6 @@ impl Config {
184 hover: HoverConfig::default(), 177 hover: HoverConfig::default(),
185 linked_projects: Vec::new(), 178 linked_projects: Vec::new(),
186 root_path, 179 root_path,
187
188 analysis: AnalysisConfig::default(),
189 } 180 }
190 } 181 }
191 182
@@ -200,8 +191,11 @@ impl Config {
200 191
201 self.with_sysroot = data.withSysroot; 192 self.with_sysroot = data.withSysroot;
202 self.publish_diagnostics = data.diagnostics_enable; 193 self.publish_diagnostics = data.diagnostics_enable;
203 self.experimental_diagnostics = data.diagnostics_enableExperimental;
204 self.diagnostics = DiagnosticsConfig { 194 self.diagnostics = DiagnosticsConfig {
195 disable_experimental: !data.diagnostics_enableExperimental,
196 disabled: data.diagnostics_disabled,
197 };
198 self.diagnostics_map = DiagnosticsMapConfig {
205 warnings_as_info: data.diagnostics_warningsAsInfo, 199 warnings_as_info: data.diagnostics_warningsAsInfo,
206 warnings_as_hint: data.diagnostics_warningsAsHint, 200 warnings_as_hint: data.diagnostics_warningsAsHint,
207 }; 201 };
@@ -303,8 +297,6 @@ impl Config {
303 goto_type_def: data.hoverActions_enable && data.hoverActions_gotoTypeDef, 297 goto_type_def: data.hoverActions_enable && data.hoverActions_gotoTypeDef,
304 }; 298 };
305 299
306 self.analysis = AnalysisConfig { disabled_diagnostics: data.analysis_disabledDiagnostics };
307
308 log::info!("Config::update() = {:#?}", self); 300 log::info!("Config::update() = {:#?}", self);
309 } 301 }
310 302
@@ -369,14 +361,6 @@ impl Config {
369 self.client_caps.status_notification = get_bool("statusNotification"); 361 self.client_caps.status_notification = get_bool("statusNotification");
370 } 362 }
371 } 363 }
372
373 pub fn disabled_diagnostics(&self) -> Option<HashSet<String>> {
374 if self.analysis.disabled_diagnostics.is_empty() {
375 None
376 } else {
377 Some(self.analysis.disabled_diagnostics.clone())
378 }
379 }
380} 364}
381 365
382#[derive(Deserialize)] 366#[derive(Deserialize)]
@@ -434,6 +418,7 @@ config_data! {
434 418
435 diagnostics_enable: bool = true, 419 diagnostics_enable: bool = true,
436 diagnostics_enableExperimental: bool = true, 420 diagnostics_enableExperimental: bool = true,
421 diagnostics_disabled: FxHashSet<String> = FxHashSet::default(),
437 diagnostics_warningsAsHint: Vec<String> = Vec::new(), 422 diagnostics_warningsAsHint: Vec<String> = Vec::new(),
438 diagnostics_warningsAsInfo: Vec<String> = Vec::new(), 423 diagnostics_warningsAsInfo: Vec<String> = Vec::new(),
439 424
@@ -464,7 +449,5 @@ config_data! {
464 rustfmt_overrideCommand: Option<Vec<String>> = None, 449 rustfmt_overrideCommand: Option<Vec<String>> = None,
465 450
466 withSysroot: bool = true, 451 withSysroot: bool = true,
467
468 analysis_disabledDiagnostics: HashSet<String> = HashSet::new(),
469 } 452 }
470} 453}
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs
index 108df3eb0..ee6f2a867 100644
--- a/crates/rust-analyzer/src/diagnostics.rs
+++ b/crates/rust-analyzer/src/diagnostics.rs
@@ -11,7 +11,7 @@ use crate::lsp_ext;
11pub(crate) type CheckFixes = Arc<FxHashMap<FileId, Vec<Fix>>>; 11pub(crate) type CheckFixes = Arc<FxHashMap<FileId, Vec<Fix>>>;
12 12
13#[derive(Debug, Default, Clone)] 13#[derive(Debug, Default, Clone)]
14pub struct DiagnosticsConfig { 14pub struct DiagnosticsMapConfig {
15 pub warnings_as_info: Vec<String>, 15 pub warnings_as_info: Vec<String>,
16 pub warnings_as_hint: Vec<String>, 16 pub warnings_as_hint: Vec<String>,
17} 17}
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 6d5408156..df5583897 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -7,11 +7,11 @@ use stdx::format_to;
7 7
8use crate::{lsp_ext, to_proto::url_from_abs_path}; 8use crate::{lsp_ext, to_proto::url_from_abs_path};
9 9
10use super::DiagnosticsConfig; 10use super::DiagnosticsMapConfig;
11 11
12/// Determines the LSP severity from a diagnostic 12/// Determines the LSP severity from a diagnostic
13fn diagnostic_severity( 13fn diagnostic_severity(
14 config: &DiagnosticsConfig, 14 config: &DiagnosticsMapConfig,
15 level: flycheck::DiagnosticLevel, 15 level: flycheck::DiagnosticLevel,
16 code: Option<flycheck::DiagnosticCode>, 16 code: Option<flycheck::DiagnosticCode>,
17) -> Option<lsp_types::DiagnosticSeverity> { 17) -> Option<lsp_types::DiagnosticSeverity> {
@@ -141,7 +141,7 @@ pub(crate) struct MappedRustDiagnostic {
141/// 141///
142/// If the diagnostic has no primary span this will return `None` 142/// If the diagnostic has no primary span this will return `None`
143pub(crate) fn map_rust_diagnostic_to_lsp( 143pub(crate) fn map_rust_diagnostic_to_lsp(
144 config: &DiagnosticsConfig, 144 config: &DiagnosticsMapConfig,
145 rd: &flycheck::Diagnostic, 145 rd: &flycheck::Diagnostic,
146 workspace_root: &Path, 146 workspace_root: &Path,
147) -> Vec<MappedRustDiagnostic> { 147) -> Vec<MappedRustDiagnostic> {
@@ -259,10 +259,10 @@ mod tests {
259 use expect::{expect_file, ExpectFile}; 259 use expect::{expect_file, ExpectFile};
260 260
261 fn check(diagnostics_json: &str, expect: ExpectFile) { 261 fn check(diagnostics_json: &str, expect: ExpectFile) {
262 check_with_config(DiagnosticsConfig::default(), diagnostics_json, expect) 262 check_with_config(DiagnosticsMapConfig::default(), diagnostics_json, expect)
263 } 263 }
264 264
265 fn check_with_config(config: DiagnosticsConfig, diagnostics_json: &str, expect: ExpectFile) { 265 fn check_with_config(config: DiagnosticsMapConfig, diagnostics_json: &str, expect: ExpectFile) {
266 let diagnostic: flycheck::Diagnostic = serde_json::from_str(diagnostics_json).unwrap(); 266 let diagnostic: flycheck::Diagnostic = serde_json::from_str(diagnostics_json).unwrap();
267 let workspace_root = Path::new("/test/"); 267 let workspace_root = Path::new("/test/");
268 let actual = map_rust_diagnostic_to_lsp(&config, &diagnostic, workspace_root); 268 let actual = map_rust_diagnostic_to_lsp(&config, &diagnostic, workspace_root);
@@ -402,9 +402,9 @@ mod tests {
402 #[cfg(not(windows))] 402 #[cfg(not(windows))]
403 fn rustc_unused_variable_as_info() { 403 fn rustc_unused_variable_as_info() {
404 check_with_config( 404 check_with_config(
405 DiagnosticsConfig { 405 DiagnosticsMapConfig {
406 warnings_as_info: vec!["unused_variables".to_string()], 406 warnings_as_info: vec!["unused_variables".to_string()],
407 ..DiagnosticsConfig::default() 407 ..DiagnosticsMapConfig::default()
408 }, 408 },
409 r##"{ 409 r##"{
410 "message": "unused variable: `foo`", 410 "message": "unused variable: `foo`",
@@ -486,9 +486,9 @@ mod tests {
486 #[cfg(not(windows))] 486 #[cfg(not(windows))]
487 fn rustc_unused_variable_as_hint() { 487 fn rustc_unused_variable_as_hint() {
488 check_with_config( 488 check_with_config(
489 DiagnosticsConfig { 489 DiagnosticsMapConfig {
490 warnings_as_hint: vec!["unused_variables".to_string()], 490 warnings_as_hint: vec!["unused_variables".to_string()],
491 ..DiagnosticsConfig::default() 491 ..DiagnosticsMapConfig::default()
492 }, 492 },
493 r##"{ 493 r##"{
494 "message": "unused variable: `foo`", 494 "message": "unused variable: `foo`",
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 4f77b1b4d..33e60b500 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -271,24 +271,24 @@ pub(crate) fn handle_document_symbol(
271 }; 271 };
272 parents.push((doc_symbol, symbol.parent)); 272 parents.push((doc_symbol, symbol.parent));
273 } 273 }
274 let mut document_symbols = Vec::new();
275 // Constructs `document_symbols` from `parents`, in order from the end.
276 while let Some((node, parent)) = parents.pop() {
277 match parent {
278 None => document_symbols.push(node),
279 Some(i) => {
280 parents[i].0.children.get_or_insert_with(Vec::new).push(node);
281 }
282 }
283 }
284 274
285 fn reverse(symbols: &mut Vec<DocumentSymbol>) { 275 // Builds hierarchy from a flat list, in reverse order (so that indices
286 for sym in symbols.iter_mut() { 276 // makes sense)
287 sym.children.as_mut().map(|c| reverse(c)); 277 let document_symbols = {
278 let mut acc = Vec::new();
279 while let Some((mut node, parent_idx)) = parents.pop() {
280 if let Some(children) = &mut node.children {
281 children.reverse();
282 }
283 let parent = match parent_idx {
284 None => &mut acc,
285 Some(i) => parents[i].0.children.get_or_insert_with(Vec::new),
286 };
287 parent.push(node);
288 } 288 }
289 symbols.reverse(); 289 acc.reverse();
290 } 290 acc
291 reverse(&mut document_symbols); 291 };
292 292
293 let res = if snap.config.client_caps.hierarchical_symbols { 293 let res = if snap.config.client_caps.hierarchical_symbols {
294 document_symbols.into() 294 document_symbols.into()
@@ -775,21 +775,16 @@ fn handle_fixes(
775 None => {} 775 None => {}
776 }; 776 };
777 777
778 let diagnostics = snap.analysis.diagnostics( 778 let diagnostics = snap.analysis.diagnostics(&snap.config.diagnostics, file_id)?;
779 file_id,
780 snap.config.experimental_diagnostics,
781 snap.config.disabled_diagnostics(),
782 )?;
783 779
784 for fix in diagnostics 780 for fix in diagnostics
785 .into_iter() 781 .into_iter()
786 .filter_map(|d| d.fix) 782 .filter_map(|d| d.fix)
787 .filter(|fix| fix.fix_trigger_range.intersect(range).is_some()) 783 .filter(|fix| fix.fix_trigger_range.intersect(range).is_some())
788 { 784 {
789 let title = fix.label;
790 let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?; 785 let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?;
791 let action = lsp_ext::CodeAction { 786 let action = lsp_ext::CodeAction {
792 title, 787 title: fix.label.to_string(),
793 id: None, 788 id: None,
794 group: None, 789 group: None,
795 kind: Some(CodeActionKind::QUICKFIX), 790 kind: Some(CodeActionKind::QUICKFIX),
@@ -1051,13 +1046,10 @@ pub(crate) fn publish_diagnostics(
1051) -> Result<Vec<Diagnostic>> { 1046) -> Result<Vec<Diagnostic>> {
1052 let _p = profile::span("publish_diagnostics"); 1047 let _p = profile::span("publish_diagnostics");
1053 let line_index = snap.analysis.file_line_index(file_id)?; 1048 let line_index = snap.analysis.file_line_index(file_id)?;
1049
1054 let diagnostics: Vec<Diagnostic> = snap 1050 let diagnostics: Vec<Diagnostic> = snap
1055 .analysis 1051 .analysis
1056 .diagnostics( 1052 .diagnostics(&snap.config.diagnostics, file_id)?
1057 file_id,
1058 snap.config.experimental_diagnostics,
1059 snap.config.disabled_diagnostics(),
1060 )?
1061 .into_iter() 1053 .into_iter()
1062 .map(|d| Diagnostic { 1054 .map(|d| Diagnostic {
1063 range: to_proto::range(&line_index, d.range), 1055 range: to_proto::range(&line_index, d.range),
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 66e04653a..f039cdc31 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -248,7 +248,7 @@ impl GlobalState {
248 Event::Flycheck(task) => match task { 248 Event::Flycheck(task) => match task {
249 flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => { 249 flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => {
250 let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( 250 let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp(
251 &self.config.diagnostics, 251 &self.config.diagnostics_map,
252 &diagnostic, 252 &diagnostic,
253 &workspace_root, 253 &workspace_root,
254 ); 254 );
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 535de2f71..643dcb4fc 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -704,7 +704,7 @@ pub(crate) fn unresolved_code_action(
704 index: usize, 704 index: usize,
705) -> Result<lsp_ext::CodeAction> { 705) -> Result<lsp_ext::CodeAction> {
706 let res = lsp_ext::CodeAction { 706 let res = lsp_ext::CodeAction {
707 title: assist.label().to_string(), 707 title: assist.label.to_string(),
708 id: Some(format!("{}:{}", assist.id.0, index.to_string())), 708 id: Some(format!("{}:{}", assist.id.0, index.to_string())),
709 group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0), 709 group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0),
710 kind: Some(code_action_kind(assist.id.1)), 710 kind: Some(code_action_kind(assist.id.1)),
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index 265d19288..5d60f0219 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -1,5 +1,8 @@
1//! Missing batteries for standard libraries. 1//! Missing batteries for standard libraries.
2use std::time::Instant; 2use std::{
3 sync::atomic::{AtomicUsize, Ordering},
4 time::Instant,
5};
3 6
4mod macros; 7mod macros;
5 8
@@ -134,6 +137,31 @@ where
134 left 137 left
135} 138}
136 139
140pub struct RacyFlag(AtomicUsize);
141
142impl RacyFlag {
143 pub const fn new() -> RacyFlag {
144 RacyFlag(AtomicUsize::new(0))
145 }
146
147 pub fn get(&self, init: impl FnMut() -> bool) -> bool {
148 let mut init = Some(init);
149 self.get_impl(&mut || init.take().map_or(false, |mut f| f()))
150 }
151
152 fn get_impl(&self, init: &mut dyn FnMut() -> bool) -> bool {
153 match self.0.load(Ordering::Relaxed) {
154 0 => false,
155 1 => true,
156 _ => {
157 let res = init();
158 self.0.store(if res { 1 } else { 0 }, Ordering::Relaxed);
159 res
160 }
161 }
162 }
163}
164
137#[cfg(test)] 165#[cfg(test)]
138mod tests { 166mod tests {
139 use super::*; 167 use super::*;