aboutsummaryrefslogtreecommitdiff
path: root/crates/ide
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide')
-rw-r--r--crates/ide/src/diagnostics.rs41
-rw-r--r--crates/ide/src/diagnostics/fixes.rs1
-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
7mod unresolved_module;
8
7mod fixes; 9mod fixes;
8mod field_shorthand; 10mod field_shorthand;
9mod unlinked_file; 11mod unlinked_file;
@@ -12,7 +14,7 @@ use std::cell::RefCell;
12 14
13use hir::{ 15use 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};
18use ide_assists::AssistResolveStrategy; 20use ide_assists::AssistResolveStrategy;
@@ -42,6 +44,12 @@ pub struct Diagnostic {
42} 44}
43 45
44impl Diagnostic { 46impl 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
93struct DiagnosticsContext<'a> {
94 config: &'a DiagnosticsConfig,
95 sema: Semantics<'a, RootDatabase>,
96 #[allow(unused)]
97 resolve: &'a AssistResolveStrategy,
98}
99
85pub(crate) fn diagnostics( 100pub(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
219fn diagnostic_with_fix<D: DiagnosticWithFixes>( 248fn 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;
5mod fill_missing_fields; 5mod fill_missing_fields;
6mod remove_semicolon; 6mod remove_semicolon;
7mod replace_with_find_map; 7mod replace_with_find_map;
8mod unresolved_module;
9mod wrap_tail_expr; 8mod wrap_tail_expr;
10 9
11use hir::{diagnostics::Diagnostic, Semantics}; 10use 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 @@
1use hir::{db::AstDatabase, diagnostics::UnresolvedModule, Semantics}; 1use hir::db::AstDatabase;
2use ide_assists::{Assist, AssistResolveStrategy}; 2use ide_assists::Assist;
3use ide_db::{base_db::AnchoredPathBuf, source_change::FileSystemEdit, RootDatabase}; 3use ide_db::{base_db::AnchoredPathBuf, source_change::FileSystemEdit};
4use syntax::AstNode; 4use syntax::AstNode;
5 5
6use crate::diagnostics::{fix, DiagnosticWithFixes}; 6use crate::diagnostics::{fix, Diagnostic, DiagnosticsContext};
7 7
8impl 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>, 11pub(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 { 20fn 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)]
33mod tests { 39mod 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
49mod foo;
50 mod bar;
51//^^^^^^^^ unresolved module
52mod 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() {