diff options
Diffstat (limited to 'crates/ide')
-rw-r--r-- | crates/ide/src/diagnostics.rs | 41 | ||||
-rw-r--r-- | crates/ide/src/diagnostics/fixes.rs | 1 | ||||
-rw-r--r-- | crates/ide/src/diagnostics/unresolved_module.rs (renamed from crates/ide/src/diagnostics/fixes/unresolved_module.rs) | 74 |
3 files changed, 82 insertions, 34 deletions
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 337a904b6..075aae8d5 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -4,6 +4,8 @@ | |||
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 | mod unresolved_module; | ||
8 | |||
7 | mod fixes; | 9 | mod fixes; |
8 | mod field_shorthand; | 10 | mod field_shorthand; |
9 | mod unlinked_file; | 11 | mod unlinked_file; |
@@ -12,7 +14,7 @@ use std::cell::RefCell; | |||
12 | 14 | ||
13 | use hir::{ | 15 | use hir::{ |
14 | db::AstDatabase, | 16 | db::AstDatabase, |
15 | diagnostics::{Diagnostic as _, DiagnosticCode, DiagnosticSinkBuilder}, | 17 | diagnostics::{AnyDiagnostic, Diagnostic as _, DiagnosticCode, DiagnosticSinkBuilder}, |
16 | InFile, Semantics, | 18 | InFile, Semantics, |
17 | }; | 19 | }; |
18 | use ide_assists::AssistResolveStrategy; | 20 | use ide_assists::AssistResolveStrategy; |
@@ -42,6 +44,12 @@ pub struct Diagnostic { | |||
42 | } | 44 | } |
43 | 45 | ||
44 | impl Diagnostic { | 46 | impl Diagnostic { |
47 | fn new(code: &'static str, message: impl Into<String>, range: TextRange) -> Diagnostic { | ||
48 | let message = message.into(); | ||
49 | let code = Some(DiagnosticCode(code)); | ||
50 | Self { message, range, severity: Severity::Error, fixes: None, unused: false, code } | ||
51 | } | ||
52 | |||
45 | fn error(range: TextRange, message: String) -> Self { | 53 | fn error(range: TextRange, message: String) -> Self { |
46 | Self { message, range, severity: Severity::Error, fixes: None, unused: false, code: None } | 54 | Self { message, range, severity: Severity::Error, fixes: None, unused: false, code: None } |
47 | } | 55 | } |
@@ -82,6 +90,13 @@ pub struct DiagnosticsConfig { | |||
82 | pub disabled: FxHashSet<String>, | 90 | pub disabled: FxHashSet<String>, |
83 | } | 91 | } |
84 | 92 | ||
93 | struct DiagnosticsContext<'a> { | ||
94 | config: &'a DiagnosticsConfig, | ||
95 | sema: Semantics<'a, RootDatabase>, | ||
96 | #[allow(unused)] | ||
97 | resolve: &'a AssistResolveStrategy, | ||
98 | } | ||
99 | |||
85 | pub(crate) fn diagnostics( | 100 | pub(crate) fn diagnostics( |
86 | db: &RootDatabase, | 101 | db: &RootDatabase, |
87 | config: &DiagnosticsConfig, | 102 | config: &DiagnosticsConfig, |
@@ -108,9 +123,6 @@ pub(crate) fn diagnostics( | |||
108 | } | 123 | } |
109 | let res = RefCell::new(res); | 124 | let res = RefCell::new(res); |
110 | let sink_builder = DiagnosticSinkBuilder::new() | 125 | let sink_builder = DiagnosticSinkBuilder::new() |
111 | .on::<hir::diagnostics::UnresolvedModule, _>(|d| { | ||
112 | res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve)); | ||
113 | }) | ||
114 | .on::<hir::diagnostics::MissingFields, _>(|d| { | 126 | .on::<hir::diagnostics::MissingFields, _>(|d| { |
115 | res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve)); | 127 | res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve)); |
116 | }) | 128 | }) |
@@ -204,16 +216,33 @@ pub(crate) fn diagnostics( | |||
204 | ); | 216 | ); |
205 | }); | 217 | }); |
206 | 218 | ||
219 | let mut diags = Vec::new(); | ||
207 | let internal_diagnostics = cfg!(test); | 220 | let internal_diagnostics = cfg!(test); |
208 | match sema.to_module_def(file_id) { | 221 | match sema.to_module_def(file_id) { |
209 | Some(m) => m.diagnostics(db, &mut sink, internal_diagnostics), | 222 | Some(m) => diags = m.diagnostics(db, &mut sink, internal_diagnostics), |
210 | None => { | 223 | None => { |
211 | sink.push(UnlinkedFile { file_id, node: SyntaxNodePtr::new(parse.tree().syntax()) }); | 224 | sink.push(UnlinkedFile { file_id, node: SyntaxNodePtr::new(parse.tree().syntax()) }); |
212 | } | 225 | } |
213 | } | 226 | } |
214 | 227 | ||
215 | drop(sink); | 228 | drop(sink); |
216 | res.into_inner() | 229 | |
230 | let mut res = res.into_inner(); | ||
231 | |||
232 | let ctx = DiagnosticsContext { config, sema, resolve }; | ||
233 | for diag in diags { | ||
234 | let d = match diag { | ||
235 | AnyDiagnostic::UnresolvedModule(d) => unresolved_module::render(&ctx, &d), | ||
236 | }; | ||
237 | if let Some(code) = d.code { | ||
238 | if ctx.config.disabled.contains(code.as_str()) { | ||
239 | continue; | ||
240 | } | ||
241 | } | ||
242 | res.push(d) | ||
243 | } | ||
244 | |||
245 | res | ||
217 | } | 246 | } |
218 | 247 | ||
219 | fn diagnostic_with_fix<D: DiagnosticWithFixes>( | 248 | fn diagnostic_with_fix<D: DiagnosticWithFixes>( |
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs index 258ac6974..8640d7231 100644 --- a/crates/ide/src/diagnostics/fixes.rs +++ b/crates/ide/src/diagnostics/fixes.rs | |||
@@ -5,7 +5,6 @@ mod create_field; | |||
5 | mod fill_missing_fields; | 5 | mod fill_missing_fields; |
6 | mod remove_semicolon; | 6 | mod remove_semicolon; |
7 | mod replace_with_find_map; | 7 | mod replace_with_find_map; |
8 | mod unresolved_module; | ||
9 | mod wrap_tail_expr; | 8 | mod wrap_tail_expr; |
10 | 9 | ||
11 | use hir::{diagnostics::Diagnostic, Semantics}; | 10 | use hir::{diagnostics::Diagnostic, Semantics}; |
diff --git a/crates/ide/src/diagnostics/fixes/unresolved_module.rs b/crates/ide/src/diagnostics/unresolved_module.rs index b3d0283bb..abf53a57c 100644 --- a/crates/ide/src/diagnostics/fixes/unresolved_module.rs +++ b/crates/ide/src/diagnostics/unresolved_module.rs | |||
@@ -1,39 +1,59 @@ | |||
1 | use hir::{db::AstDatabase, diagnostics::UnresolvedModule, Semantics}; | 1 | use hir::db::AstDatabase; |
2 | use ide_assists::{Assist, AssistResolveStrategy}; | 2 | use ide_assists::Assist; |
3 | use ide_db::{base_db::AnchoredPathBuf, source_change::FileSystemEdit, RootDatabase}; | 3 | use ide_db::{base_db::AnchoredPathBuf, source_change::FileSystemEdit}; |
4 | use syntax::AstNode; | 4 | use syntax::AstNode; |
5 | 5 | ||
6 | use crate::diagnostics::{fix, DiagnosticWithFixes}; | 6 | use crate::diagnostics::{fix, Diagnostic, DiagnosticsContext}; |
7 | 7 | ||
8 | impl DiagnosticWithFixes for UnresolvedModule { | 8 | // Diagnostic: unresolved-module |
9 | fn fixes( | 9 | // |
10 | &self, | 10 | // This diagnostic is triggered if rust-analyzer is unable to discover referred module. |
11 | sema: &Semantics<RootDatabase>, | 11 | pub(super) fn render(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedModule) -> Diagnostic { |
12 | _resolve: &AssistResolveStrategy, | 12 | Diagnostic::new( |
13 | ) -> Option<Vec<Assist>> { | 13 | "unresolved-module", |
14 | let root = sema.db.parse_or_expand(self.file)?; | 14 | "unresolved module", |
15 | let unresolved_module = self.decl.to_node(&root); | 15 | ctx.sema.diagnostics_display_range(d.decl.clone().map(|it| it.into())).range, |
16 | Some(vec![fix( | 16 | ) |
17 | "create_module", | 17 | .with_fixes(fixes(ctx, d)) |
18 | "Create module", | 18 | } |
19 | FileSystemEdit::CreateFile { | 19 | |
20 | dst: AnchoredPathBuf { | 20 | fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedModule) -> Option<Vec<Assist>> { |
21 | anchor: self.file.original_file(sema.db), | 21 | let root = ctx.sema.db.parse_or_expand(d.decl.file_id)?; |
22 | path: self.candidate.clone(), | 22 | let unresolved_module = d.decl.value.to_node(&root); |
23 | }, | 23 | Some(vec![fix( |
24 | initial_contents: "".to_string(), | 24 | "create_module", |
25 | } | 25 | "Create module", |
26 | .into(), | 26 | FileSystemEdit::CreateFile { |
27 | unresolved_module.syntax().text_range(), | 27 | dst: AnchoredPathBuf { |
28 | )]) | 28 | anchor: d.decl.file_id.original_file(ctx.sema.db), |
29 | } | 29 | path: d.candidate.clone(), |
30 | }, | ||
31 | initial_contents: "".to_string(), | ||
32 | } | ||
33 | .into(), | ||
34 | unresolved_module.syntax().text_range(), | ||
35 | )]) | ||
30 | } | 36 | } |
31 | 37 | ||
32 | #[cfg(test)] | 38 | #[cfg(test)] |
33 | mod tests { | 39 | mod tests { |
34 | use expect_test::expect; | 40 | use expect_test::expect; |
35 | 41 | ||
36 | use crate::diagnostics::tests::check_expect; | 42 | use crate::diagnostics::tests::{check_diagnostics, check_expect}; |
43 | |||
44 | #[test] | ||
45 | fn unresolved_module() { | ||
46 | check_diagnostics( | ||
47 | r#" | ||
48 | //- /lib.rs | ||
49 | mod foo; | ||
50 | mod bar; | ||
51 | //^^^^^^^^ unresolved module | ||
52 | mod baz {} | ||
53 | //- /foo.rs | ||
54 | "#, | ||
55 | ); | ||
56 | } | ||
37 | 57 | ||
38 | #[test] | 58 | #[test] |
39 | fn test_unresolved_module_diagnostic() { | 59 | fn test_unresolved_module_diagnostic() { |