diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/assists/src/assist_context.rs | 16 | ||||
-rw-r--r-- | crates/assists/src/lib.rs | 13 | ||||
-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/hir_ty/src/tests.rs | 17 | ||||
-rw-r--r-- | crates/ide/src/diagnostics.rs | 146 | ||||
-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.rs | 43 | ||||
-rw-r--r-- | crates/ide_db/src/label.rs | 49 | ||||
-rw-r--r-- | crates/ide_db/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/rust-analyzer/src/cli/analysis_bench.rs | 7 | ||||
-rw-r--r-- | crates/rust-analyzer/src/cli/diagnostics.rs | 5 | ||||
-rw-r--r-- | crates/rust-analyzer/src/config.rs | 39 | ||||
-rw-r--r-- | crates/rust-analyzer/src/diagnostics.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/diagnostics/to_proto.rs | 18 | ||||
-rw-r--r-- | crates/rust-analyzer/src/handlers.rs | 48 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/to_proto.rs | 2 | ||||
-rw-r--r-- | crates/stdx/src/lib.rs | 30 |
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; | |||
6 | use base_db::{FileId, FileRange}; | 6 | use base_db::{FileId, FileRange}; |
7 | use hir::Semantics; | 7 | use hir::Semantics; |
8 | use ide_db::{ | 8 | use 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 | ||
20 | use base_db::FileRange; | 20 | use base_db::FileRange; |
21 | use hir::Semantics; | 21 | use hir::Semantics; |
22 | use ide_db::{source_change::SourceChange, RootDatabase}; | 22 | use ide_db::{label::Label, source_change::SourceChange, RootDatabase}; |
23 | use syntax::TextRange; | 23 | use syntax::TextRange; |
24 | 24 | ||
25 | pub(crate) use crate::assist_context::{AssistContext, Assists}; | 25 | pub(crate) use crate::assist_context::{AssistContext, Assists}; |
@@ -68,7 +68,7 @@ pub struct GroupLabel(pub String); | |||
68 | pub struct Assist { | 68 | pub 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 | ||
84 | impl Assist { | 84 | impl 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 | ||
127 | mod handlers { | 118 | mod 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 | ||
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/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; | |||
8 | mod macros; | 8 | mod macros; |
9 | mod display_source_code; | 9 | mod display_source_code; |
10 | 10 | ||
11 | use std::sync::Arc; | 11 | use std::{env, sync::Arc}; |
12 | 12 | ||
13 | use base_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt}; | 13 | use base_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt}; |
14 | use expect::Expect; | 14 | use expect::Expect; |
@@ -22,12 +22,14 @@ use hir_def::{ | |||
22 | AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, | 22 | AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, |
23 | }; | 23 | }; |
24 | use hir_expand::{db::AstDatabase, InFile}; | 24 | use hir_expand::{db::AstDatabase, InFile}; |
25 | use stdx::format_to; | 25 | use stdx::{format_to, RacyFlag}; |
26 | use syntax::{ | 26 | use syntax::{ |
27 | algo, | 27 | algo, |
28 | ast::{self, AstNode}, | 28 | ast::{self, AstNode}, |
29 | SyntaxNode, | 29 | SyntaxNode, |
30 | }; | 30 | }; |
31 | use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Registry}; | ||
32 | use tracing_tree::HierarchicalLayer; | ||
31 | 33 | ||
32 | use crate::{ | 34 | use 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 | ||
40 | fn setup_tracing() -> tracing::subscriber::DefaultGuard { | 42 | fn 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 | ||
53 | fn check_types(ra_fixture: &str) { | 58 | fn 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 | ||
7 | use std::{cell::RefCell, collections::HashSet}; | 7 | mod fixes; |
8 | |||
9 | use std::cell::RefCell; | ||
8 | 10 | ||
9 | use base_db::SourceDatabase; | 11 | use base_db::SourceDatabase; |
10 | use hir::{diagnostics::DiagnosticSinkBuilder, Semantics}; | 12 | use hir::{diagnostics::DiagnosticSinkBuilder, Semantics}; |
11 | use ide_db::RootDatabase; | 13 | use ide_db::RootDatabase; |
12 | use itertools::Itertools; | 14 | use itertools::Itertools; |
15 | use rustc_hash::FxHashSet; | ||
13 | use syntax::{ | 16 | use syntax::{ |
14 | ast::{self, AstNode}, | 17 | ast::{self, AstNode}, |
15 | SyntaxNode, TextRange, T, | 18 | SyntaxNode, TextRange, T, |
16 | }; | 19 | }; |
17 | use text_edit::TextEdit; | 20 | use text_edit::TextEdit; |
18 | 21 | ||
19 | use crate::{Diagnostic, FileId, Fix, SourceFileEdit}; | 22 | use crate::{FileId, Label, SourceChange, SourceFileEdit}; |
23 | |||
24 | use self::fixes::DiagnosticWithFix; | ||
25 | |||
26 | #[derive(Debug)] | ||
27 | pub 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 | ||
21 | mod diagnostics_with_fix; | 35 | #[derive(Debug)] |
22 | use diagnostics_with_fix::DiagnosticWithFix; | 36 | pub 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 | |||
43 | impl 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)] |
25 | pub enum Severity { | 51 | pub enum Severity { |
@@ -27,11 +53,16 @@ pub enum Severity { | |||
27 | WeakWarning, | 53 | WeakWarning, |
28 | } | 54 | } |
29 | 55 | ||
56 | #[derive(Default, Debug, Clone)] | ||
57 | pub struct DiagnosticsConfig { | ||
58 | pub disable_experimental: bool, | ||
59 | pub disabled: FxHashSet<String>, | ||
60 | } | ||
61 | |||
30 | pub(crate) fn diagnostics( | 62 | pub(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 | ||
96 | 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 { |
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)] |
189 | mod tests { | 216 | mod 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. |
3 | use crate::Fix; | ||
4 | use ast::{edit::IndentLevel, make}; | ||
5 | use base_db::FileId; | 3 | use base_db::FileId; |
6 | use hir::{ | 4 | use 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 | }; |
15 | use syntax::{algo, ast, AstNode}; | 13 | use syntax::{ |
14 | algo, | ||
15 | ast::{self, edit::IndentLevel, make}, | ||
16 | AstNode, | ||
17 | }; | ||
16 | use text_edit::TextEdit; | 18 | use text_edit::TextEdit; |
17 | 19 | ||
20 | use 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; | |||
44 | mod syntax_tree; | 44 | mod syntax_tree; |
45 | mod typing; | 45 | mod typing; |
46 | 46 | ||
47 | use std::{collections::HashSet, sync::Arc}; | 47 | use std::sync::Arc; |
48 | 48 | ||
49 | use base_db::{ | 49 | use 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::{ | |||
88 | pub use hir::{Documentation, Semantics}; | 88 | pub use hir::{Documentation, Semantics}; |
89 | pub use ide_db::{ | 89 | pub 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 | ||
100 | pub type Cancelable<T> = Result<T, Canceled>; | 101 | pub type Cancelable<T> = Result<T, Canceled>; |
101 | 102 | ||
102 | #[derive(Debug)] | ||
103 | pub 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)] | ||
112 | pub 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 | |||
119 | impl 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)] |
133 | pub struct RangeInfo<T> { | 105 | pub struct RangeInfo<T> { |
@@ -148,7 +120,7 @@ pub struct AnalysisHost { | |||
148 | } | 120 | } |
149 | 121 | ||
150 | impl AnalysisHost { | 122 | impl 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` | ||
2 | use 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)] | ||
11 | pub struct Label(String); | ||
12 | |||
13 | impl PartialEq<str> for Label { | ||
14 | fn eq(&self, other: &str) -> bool { | ||
15 | self.0 == other | ||
16 | } | ||
17 | } | ||
18 | |||
19 | impl PartialEq<&'_ str> for Label { | ||
20 | fn eq(&self, other: &&str) -> bool { | ||
21 | self == *other | ||
22 | } | ||
23 | } | ||
24 | |||
25 | impl From<Label> for String { | ||
26 | fn from(label: Label) -> String { | ||
27 | label.0 | ||
28 | } | ||
29 | } | ||
30 | |||
31 | impl 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 | |||
39 | impl fmt::Display for Label { | ||
40 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
41 | fmt::Display::fmt(&self.0, f) | ||
42 | } | ||
43 | } | ||
44 | |||
45 | impl 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 | ||
5 | pub mod label; | ||
5 | pub mod line_index; | 6 | pub mod line_index; |
6 | pub mod symbol_index; | 7 | pub mod symbol_index; |
7 | pub mod change; | 8 | pub 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 | }; |
10 | use ide::{Analysis, AnalysisChange, AnalysisHost, CompletionConfig, FilePosition, LineCol}; | 10 | use ide::{ |
11 | Analysis, AnalysisChange, AnalysisHost, CompletionConfig, DiagnosticsConfig, FilePosition, | ||
12 | LineCol, | ||
13 | }; | ||
11 | use vfs::AbsPathBuf; | 14 | use vfs::AbsPathBuf; |
12 | 15 | ||
13 | use crate::{ | 16 | use 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 | ||
9 | use base_db::SourceDatabaseExt; | 9 | use base_db::SourceDatabaseExt; |
10 | use hir::Crate; | 10 | use hir::Crate; |
11 | use ide::Severity; | 11 | use ide::{DiagnosticsConfig, Severity}; |
12 | 12 | ||
13 | use crate::cli::{load_cargo::load_cargo, Result}; | 13 | use 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 | ||
10 | use std::{collections::HashSet, ffi::OsString, path::PathBuf}; | 10 | use std::{ffi::OsString, path::PathBuf}; |
11 | 11 | ||
12 | use flycheck::FlycheckConfig; | 12 | use flycheck::FlycheckConfig; |
13 | use ide::{AssistConfig, CompletionConfig, HoverConfig, InlayHintsConfig}; | 13 | use ide::{AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig}; |
14 | use lsp_types::ClientCapabilities; | 14 | use lsp_types::ClientCapabilities; |
15 | use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest}; | 15 | use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest}; |
16 | use rustc_hash::FxHashSet; | ||
16 | use serde::Deserialize; | 17 | use serde::Deserialize; |
17 | use vfs::AbsPathBuf; | 18 | use vfs::AbsPathBuf; |
18 | 19 | ||
19 | use crate::diagnostics::DiagnosticsConfig; | 20 | use crate::diagnostics::DiagnosticsMapConfig; |
20 | 21 | ||
21 | #[derive(Debug, Clone)] | 22 | #[derive(Debug, Clone)] |
22 | pub struct Config { | 23 | pub 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)] | ||
54 | pub 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; | |||
11 | pub(crate) type CheckFixes = Arc<FxHashMap<FileId, Vec<Fix>>>; | 11 | pub(crate) type CheckFixes = Arc<FxHashMap<FileId, Vec<Fix>>>; |
12 | 12 | ||
13 | #[derive(Debug, Default, Clone)] | 13 | #[derive(Debug, Default, Clone)] |
14 | pub struct DiagnosticsConfig { | 14 | pub 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 | ||
8 | use crate::{lsp_ext, to_proto::url_from_abs_path}; | 8 | use crate::{lsp_ext, to_proto::url_from_abs_path}; |
9 | 9 | ||
10 | use super::DiagnosticsConfig; | 10 | use super::DiagnosticsMapConfig; |
11 | 11 | ||
12 | /// Determines the LSP severity from a diagnostic | 12 | /// Determines the LSP severity from a diagnostic |
13 | fn diagnostic_severity( | 13 | fn 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` |
143 | pub(crate) fn map_rust_diagnostic_to_lsp( | 143 | pub(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. |
2 | use std::time::Instant; | 2 | use std::{ |
3 | sync::atomic::{AtomicUsize, Ordering}, | ||
4 | time::Instant, | ||
5 | }; | ||
3 | 6 | ||
4 | mod macros; | 7 | mod macros; |
5 | 8 | ||
@@ -134,6 +137,31 @@ where | |||
134 | left | 137 | left |
135 | } | 138 | } |
136 | 139 | ||
140 | pub struct RacyFlag(AtomicUsize); | ||
141 | |||
142 | impl 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)] |
138 | mod tests { | 166 | mod tests { |
139 | use super::*; | 167 | use super::*; |