Skip to content

Commit 6d104de

Browse files
committed
internal: refactor unresolved import diagnostic
1 parent 39f190b commit 6d104de

File tree

6 files changed

+127
-92
lines changed

6 files changed

+127
-92
lines changed

crates/hir/src/diagnostics.rs

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ macro_rules! diagnostics {
3232
};
3333
}
3434

35-
diagnostics![UnresolvedModule, UnresolvedExternCrate, MissingFields];
35+
diagnostics![UnresolvedModule, UnresolvedExternCrate, UnresolvedImport, MissingFields];
3636

3737
#[derive(Debug)]
3838
pub struct UnresolvedModule {
@@ -47,30 +47,7 @@ pub struct UnresolvedExternCrate {
4747

4848
#[derive(Debug)]
4949
pub struct UnresolvedImport {
50-
pub file: HirFileId,
51-
pub node: AstPtr<ast::UseTree>,
52-
}
53-
54-
impl Diagnostic for UnresolvedImport {
55-
fn code(&self) -> DiagnosticCode {
56-
DiagnosticCode("unresolved-import")
57-
}
58-
fn message(&self) -> String {
59-
"unresolved import".to_string()
60-
}
61-
fn display_source(&self) -> InFile<SyntaxNodePtr> {
62-
InFile::new(self.file, self.node.clone().into())
63-
}
64-
fn as_any(&self) -> &(dyn Any + Send + 'static) {
65-
self
66-
}
67-
fn is_experimental(&self) -> bool {
68-
// This currently results in false positives in the following cases:
69-
// - `cfg_if!`-generated code in libstd (we don't load the sysroot correctly)
70-
// - `core::arch` (we don't handle `#[path = "../<path>"]` correctly)
71-
// - proc macros and/or proc macro generated code
72-
true
73-
}
50+
pub decl: InFile<AstPtr<ast::UseTree>>,
7451
}
7552

7653
// Diagnostic: unresolved-macro-call

crates/hir/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,10 @@ impl Module {
498498
let import = &item_tree[id.value];
499499

500500
let use_tree = import.use_tree_to_ast(db.upcast(), file_id, *index);
501-
sink.push(UnresolvedImport { file: file_id, node: AstPtr::new(&use_tree) });
501+
acc.push(
502+
UnresolvedImport { decl: InFile::new(file_id, AstPtr::new(&use_tree)) }
503+
.into(),
504+
);
502505
}
503506

504507
DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } => {

crates/hir_def/src/nameres/tests/diagnostics.rs

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,43 +12,6 @@ fn check_no_diagnostics(ra_fixture: &str) {
1212
db.check_no_diagnostics();
1313
}
1414

15-
#[test]
16-
fn unresolved_import() {
17-
check_diagnostics(
18-
r"
19-
use does_exist;
20-
use does_not_exist;
21-
//^^^^^^^^^^^^^^^^^^^ UnresolvedImport
22-
23-
mod does_exist {}
24-
",
25-
);
26-
}
27-
28-
#[test]
29-
fn dedup_unresolved_import_from_unresolved_crate() {
30-
check_diagnostics(
31-
r"
32-
//- /main.rs crate:main
33-
mod a {
34-
extern crate doesnotexist;
35-
//^^^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedExternCrate
36-
37-
// Should not error, since we already errored for the missing crate.
38-
use doesnotexist::{self, bla, *};
39-
40-
use crate::doesnotexist;
41-
//^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedImport
42-
}
43-
44-
mod m {
45-
use super::doesnotexist;
46-
//^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedImport
47-
}
48-
",
49-
);
50-
}
51-
5215
#[test]
5316
fn inactive_item() {
5417
// Additional tests in `cfg` crate. This only tests disabled cfgs.

crates/ide/src/diagnostics.rs

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
77
mod unresolved_module;
88
mod unresolved_extern_crate;
9+
mod unresolved_import;
910
mod missing_fields;
1011

1112
mod fixes;
@@ -43,17 +44,39 @@ pub struct Diagnostic {
4344
pub fixes: Option<Vec<Assist>>,
4445
pub unused: bool,
4546
pub code: Option<DiagnosticCode>,
47+
pub experimental: bool,
4648
}
4749

4850
impl Diagnostic {
4951
fn new(code: &'static str, message: impl Into<String>, range: TextRange) -> Diagnostic {
5052
let message = message.into();
5153
let code = Some(DiagnosticCode(code));
52-
Self { message, range, severity: Severity::Error, fixes: None, unused: false, code }
54+
Self {
55+
message,
56+
range,
57+
severity: Severity::Error,
58+
fixes: None,
59+
unused: false,
60+
code,
61+
experimental: false,
62+
}
63+
}
64+
65+
fn experimental(mut self) -> Diagnostic {
66+
self.experimental = true;
67+
self
5368
}
5469

5570
fn error(range: TextRange, message: String) -> Self {
56-
Self { message, range, severity: Severity::Error, fixes: None, unused: false, code: None }
71+
Self {
72+
message,
73+
range,
74+
severity: Severity::Error,
75+
fixes: None,
76+
unused: false,
77+
code: None,
78+
experimental: false,
79+
}
5780
}
5881

5982
fn hint(range: TextRange, message: String) -> Self {
@@ -64,6 +87,7 @@ impl Diagnostic {
6487
fixes: None,
6588
unused: false,
6689
code: None,
90+
experimental: false,
6791
}
6892
}
6993

@@ -234,13 +258,17 @@ pub(crate) fn diagnostics(
234258
let d = match diag {
235259
AnyDiagnostic::UnresolvedModule(d) => unresolved_module::unresolved_module(&ctx, &d),
236260
AnyDiagnostic::UnresolvedExternCrate(d) => unresolved_extern_crate::unresolved_extern_crate(&ctx, &d),
261+
AnyDiagnostic::UnresolvedImport(d) => unresolved_import::unresolved_import(&ctx, &d),
237262
AnyDiagnostic::MissingFields(d) => missing_fields::missing_fields(&ctx, &d),
238263
};
239264
if let Some(code) = d.code {
240265
if ctx.config.disabled.contains(code.as_str()) {
241266
continue;
242267
}
243268
}
269+
if ctx.config.disable_experimental && d.experimental {
270+
continue;
271+
}
244272
res.push(d)
245273
}
246274

@@ -462,33 +490,6 @@ foo::bar!(92);
462490
);
463491
}
464492

465-
#[test]
466-
fn unresolved_import_in_use_tree() {
467-
// Only the relevant part of a nested `use` item should be highlighted.
468-
check_diagnostics(
469-
r#"
470-
use does_exist::{Exists, DoesntExist};
471-
//^^^^^^^^^^^ unresolved import
472-
473-
use {does_not_exist::*, does_exist};
474-
//^^^^^^^^^^^^^^^^^ unresolved import
475-
476-
use does_not_exist::{
477-
a,
478-
//^ unresolved import
479-
b,
480-
//^ unresolved import
481-
c,
482-
//^ unresolved import
483-
};
484-
485-
mod does_exist {
486-
pub struct Exists;
487-
}
488-
"#,
489-
);
490-
}
491-
492493
#[test]
493494
fn range_mapping_out_of_macros() {
494495
// FIXME: this is very wrong, but somewhat tricky to fix.
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
use crate::diagnostics::{Diagnostic, DiagnosticsContext};
2+
3+
// Diagnostic: unresolved-import
4+
//
5+
// This diagnostic is triggered if rust-analyzer is unable to resolve a path in
6+
// a `use` declaration.
7+
pub(super) fn unresolved_import(
8+
ctx: &DiagnosticsContext<'_>,
9+
d: &hir::UnresolvedImport,
10+
) -> Diagnostic {
11+
Diagnostic::new(
12+
"unresolved-import",
13+
"unresolved import",
14+
ctx.sema.diagnostics_display_range(d.decl.clone().map(|it| it.into())).range,
15+
)
16+
// This currently results in false positives in the following cases:
17+
// - `cfg_if!`-generated code in libstd (we don't load the sysroot correctly)
18+
// - `core::arch` (we don't handle `#[path = "../<path>"]` correctly)
19+
// - proc macros and/or proc macro generated code
20+
.experimental()
21+
}
22+
23+
#[cfg(test)]
24+
mod tests {
25+
use crate::diagnostics::tests::check_diagnostics;
26+
27+
#[test]
28+
fn unresolved_import() {
29+
check_diagnostics(
30+
r#"
31+
use does_exist;
32+
use does_not_exist;
33+
//^^^^^^^^^^^^^^ unresolved import
34+
35+
mod does_exist {}
36+
"#,
37+
);
38+
}
39+
40+
#[test]
41+
fn unresolved_import_in_use_tree() {
42+
// Only the relevant part of a nested `use` item should be highlighted.
43+
check_diagnostics(
44+
r#"
45+
use does_exist::{Exists, DoesntExist};
46+
//^^^^^^^^^^^ unresolved import
47+
48+
use {does_not_exist::*, does_exist};
49+
//^^^^^^^^^^^^^^^^^ unresolved import
50+
51+
use does_not_exist::{
52+
a,
53+
//^ unresolved import
54+
b,
55+
//^ unresolved import
56+
c,
57+
//^ unresolved import
58+
};
59+
60+
mod does_exist {
61+
pub struct Exists;
62+
}
63+
"#,
64+
);
65+
}
66+
67+
#[test]
68+
fn dedup_unresolved_import_from_unresolved_crate() {
69+
check_diagnostics(
70+
r#"
71+
//- /main.rs crate:main
72+
mod a {
73+
extern crate doesnotexist;
74+
//^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate
75+
76+
// Should not error, since we already errored for the missing crate.
77+
use doesnotexist::{self, bla, *};
78+
79+
use crate::doesnotexist;
80+
//^^^^^^^^^^^^^^^^^^^ unresolved import
81+
}
82+
83+
mod m {
84+
use super::doesnotexist;
85+
//^^^^^^^^^^^^^^^^^^^ unresolved import
86+
}
87+
"#,
88+
);
89+
}
90+
}

crates/ide/src/diagnostics/unresolved_module.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ mod baz {}
104104
"unresolved-module",
105105
),
106106
),
107+
experimental: false,
107108
},
108109
]
109110
"#]],

0 commit comments

Comments
 (0)