mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-26 20:09:19 +00:00
internal: refactor unresolved import diagnostic
This commit is contained in:
parent
39f190b72c
commit
6d104de15a
6 changed files with 127 additions and 92 deletions
|
@ -32,7 +32,7 @@ macro_rules! diagnostics {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
diagnostics![UnresolvedModule, UnresolvedExternCrate, MissingFields];
|
diagnostics![UnresolvedModule, UnresolvedExternCrate, UnresolvedImport, MissingFields];
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct UnresolvedModule {
|
pub struct UnresolvedModule {
|
||||||
|
@ -47,30 +47,7 @@ pub struct UnresolvedExternCrate {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct UnresolvedImport {
|
pub struct UnresolvedImport {
|
||||||
pub file: HirFileId,
|
pub decl: InFile<AstPtr<ast::UseTree>>,
|
||||||
pub node: AstPtr<ast::UseTree>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Diagnostic for UnresolvedImport {
|
|
||||||
fn code(&self) -> DiagnosticCode {
|
|
||||||
DiagnosticCode("unresolved-import")
|
|
||||||
}
|
|
||||||
fn message(&self) -> String {
|
|
||||||
"unresolved import".to_string()
|
|
||||||
}
|
|
||||||
fn display_source(&self) -> InFile<SyntaxNodePtr> {
|
|
||||||
InFile::new(self.file, self.node.clone().into())
|
|
||||||
}
|
|
||||||
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
fn is_experimental(&self) -> bool {
|
|
||||||
// This currently results in false positives in the following cases:
|
|
||||||
// - `cfg_if!`-generated code in libstd (we don't load the sysroot correctly)
|
|
||||||
// - `core::arch` (we don't handle `#[path = "../<path>"]` correctly)
|
|
||||||
// - proc macros and/or proc macro generated code
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Diagnostic: unresolved-macro-call
|
// Diagnostic: unresolved-macro-call
|
||||||
|
|
|
@ -498,7 +498,10 @@ impl Module {
|
||||||
let import = &item_tree[id.value];
|
let import = &item_tree[id.value];
|
||||||
|
|
||||||
let use_tree = import.use_tree_to_ast(db.upcast(), file_id, *index);
|
let use_tree = import.use_tree_to_ast(db.upcast(), file_id, *index);
|
||||||
sink.push(UnresolvedImport { file: file_id, node: AstPtr::new(&use_tree) });
|
acc.push(
|
||||||
|
UnresolvedImport { decl: InFile::new(file_id, AstPtr::new(&use_tree)) }
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } => {
|
DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } => {
|
||||||
|
|
|
@ -12,43 +12,6 @@ fn check_no_diagnostics(ra_fixture: &str) {
|
||||||
db.check_no_diagnostics();
|
db.check_no_diagnostics();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn unresolved_import() {
|
|
||||||
check_diagnostics(
|
|
||||||
r"
|
|
||||||
use does_exist;
|
|
||||||
use does_not_exist;
|
|
||||||
//^^^^^^^^^^^^^^^^^^^ UnresolvedImport
|
|
||||||
|
|
||||||
mod does_exist {}
|
|
||||||
",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn dedup_unresolved_import_from_unresolved_crate() {
|
|
||||||
check_diagnostics(
|
|
||||||
r"
|
|
||||||
//- /main.rs crate:main
|
|
||||||
mod a {
|
|
||||||
extern crate doesnotexist;
|
|
||||||
//^^^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedExternCrate
|
|
||||||
|
|
||||||
// Should not error, since we already errored for the missing crate.
|
|
||||||
use doesnotexist::{self, bla, *};
|
|
||||||
|
|
||||||
use crate::doesnotexist;
|
|
||||||
//^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedImport
|
|
||||||
}
|
|
||||||
|
|
||||||
mod m {
|
|
||||||
use super::doesnotexist;
|
|
||||||
//^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedImport
|
|
||||||
}
|
|
||||||
",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn inactive_item() {
|
fn inactive_item() {
|
||||||
// Additional tests in `cfg` crate. This only tests disabled cfgs.
|
// Additional tests in `cfg` crate. This only tests disabled cfgs.
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
mod unresolved_module;
|
mod unresolved_module;
|
||||||
mod unresolved_extern_crate;
|
mod unresolved_extern_crate;
|
||||||
|
mod unresolved_import;
|
||||||
mod missing_fields;
|
mod missing_fields;
|
||||||
|
|
||||||
mod fixes;
|
mod fixes;
|
||||||
|
@ -43,17 +44,39 @@ pub struct Diagnostic {
|
||||||
pub fixes: Option<Vec<Assist>>,
|
pub fixes: Option<Vec<Assist>>,
|
||||||
pub unused: bool,
|
pub unused: bool,
|
||||||
pub code: Option<DiagnosticCode>,
|
pub code: Option<DiagnosticCode>,
|
||||||
|
pub experimental: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Diagnostic {
|
impl Diagnostic {
|
||||||
fn new(code: &'static str, message: impl Into<String>, range: TextRange) -> Diagnostic {
|
fn new(code: &'static str, message: impl Into<String>, range: TextRange) -> Diagnostic {
|
||||||
let message = message.into();
|
let message = message.into();
|
||||||
let code = Some(DiagnosticCode(code));
|
let code = Some(DiagnosticCode(code));
|
||||||
Self { message, range, severity: Severity::Error, fixes: None, unused: false, code }
|
Self {
|
||||||
|
message,
|
||||||
|
range,
|
||||||
|
severity: Severity::Error,
|
||||||
|
fixes: None,
|
||||||
|
unused: false,
|
||||||
|
code,
|
||||||
|
experimental: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn experimental(mut self) -> Diagnostic {
|
||||||
|
self.experimental = true;
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn error(range: TextRange, message: String) -> Self {
|
fn error(range: TextRange, message: String) -> Self {
|
||||||
Self { message, range, severity: Severity::Error, fixes: None, unused: false, code: None }
|
Self {
|
||||||
|
message,
|
||||||
|
range,
|
||||||
|
severity: Severity::Error,
|
||||||
|
fixes: None,
|
||||||
|
unused: false,
|
||||||
|
code: None,
|
||||||
|
experimental: false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hint(range: TextRange, message: String) -> Self {
|
fn hint(range: TextRange, message: String) -> Self {
|
||||||
|
@ -64,6 +87,7 @@ impl Diagnostic {
|
||||||
fixes: None,
|
fixes: None,
|
||||||
unused: false,
|
unused: false,
|
||||||
code: None,
|
code: None,
|
||||||
|
experimental: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,6 +258,7 @@ pub(crate) fn diagnostics(
|
||||||
let d = match diag {
|
let d = match diag {
|
||||||
AnyDiagnostic::UnresolvedModule(d) => unresolved_module::unresolved_module(&ctx, &d),
|
AnyDiagnostic::UnresolvedModule(d) => unresolved_module::unresolved_module(&ctx, &d),
|
||||||
AnyDiagnostic::UnresolvedExternCrate(d) => unresolved_extern_crate::unresolved_extern_crate(&ctx, &d),
|
AnyDiagnostic::UnresolvedExternCrate(d) => unresolved_extern_crate::unresolved_extern_crate(&ctx, &d),
|
||||||
|
AnyDiagnostic::UnresolvedImport(d) => unresolved_import::unresolved_import(&ctx, &d),
|
||||||
AnyDiagnostic::MissingFields(d) => missing_fields::missing_fields(&ctx, &d),
|
AnyDiagnostic::MissingFields(d) => missing_fields::missing_fields(&ctx, &d),
|
||||||
};
|
};
|
||||||
if let Some(code) = d.code {
|
if let Some(code) = d.code {
|
||||||
|
@ -241,6 +266,9 @@ pub(crate) fn diagnostics(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ctx.config.disable_experimental && d.experimental {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
res.push(d)
|
res.push(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -462,33 +490,6 @@ foo::bar!(92);
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn unresolved_import_in_use_tree() {
|
|
||||||
// Only the relevant part of a nested `use` item should be highlighted.
|
|
||||||
check_diagnostics(
|
|
||||||
r#"
|
|
||||||
use does_exist::{Exists, DoesntExist};
|
|
||||||
//^^^^^^^^^^^ unresolved import
|
|
||||||
|
|
||||||
use {does_not_exist::*, does_exist};
|
|
||||||
//^^^^^^^^^^^^^^^^^ unresolved import
|
|
||||||
|
|
||||||
use does_not_exist::{
|
|
||||||
a,
|
|
||||||
//^ unresolved import
|
|
||||||
b,
|
|
||||||
//^ unresolved import
|
|
||||||
c,
|
|
||||||
//^ unresolved import
|
|
||||||
};
|
|
||||||
|
|
||||||
mod does_exist {
|
|
||||||
pub struct Exists;
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn range_mapping_out_of_macros() {
|
fn range_mapping_out_of_macros() {
|
||||||
// FIXME: this is very wrong, but somewhat tricky to fix.
|
// FIXME: this is very wrong, but somewhat tricky to fix.
|
||||||
|
|
90
crates/ide/src/diagnostics/unresolved_import.rs
Normal file
90
crates/ide/src/diagnostics/unresolved_import.rs
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
use crate::diagnostics::{Diagnostic, DiagnosticsContext};
|
||||||
|
|
||||||
|
// Diagnostic: unresolved-import
|
||||||
|
//
|
||||||
|
// This diagnostic is triggered if rust-analyzer is unable to resolve a path in
|
||||||
|
// a `use` declaration.
|
||||||
|
pub(super) fn unresolved_import(
|
||||||
|
ctx: &DiagnosticsContext<'_>,
|
||||||
|
d: &hir::UnresolvedImport,
|
||||||
|
) -> Diagnostic {
|
||||||
|
Diagnostic::new(
|
||||||
|
"unresolved-import",
|
||||||
|
"unresolved import",
|
||||||
|
ctx.sema.diagnostics_display_range(d.decl.clone().map(|it| it.into())).range,
|
||||||
|
)
|
||||||
|
// This currently results in false positives in the following cases:
|
||||||
|
// - `cfg_if!`-generated code in libstd (we don't load the sysroot correctly)
|
||||||
|
// - `core::arch` (we don't handle `#[path = "../<path>"]` correctly)
|
||||||
|
// - proc macros and/or proc macro generated code
|
||||||
|
.experimental()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::diagnostics::tests::check_diagnostics;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unresolved_import() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
use does_exist;
|
||||||
|
use does_not_exist;
|
||||||
|
//^^^^^^^^^^^^^^ unresolved import
|
||||||
|
|
||||||
|
mod does_exist {}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unresolved_import_in_use_tree() {
|
||||||
|
// Only the relevant part of a nested `use` item should be highlighted.
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
use does_exist::{Exists, DoesntExist};
|
||||||
|
//^^^^^^^^^^^ unresolved import
|
||||||
|
|
||||||
|
use {does_not_exist::*, does_exist};
|
||||||
|
//^^^^^^^^^^^^^^^^^ unresolved import
|
||||||
|
|
||||||
|
use does_not_exist::{
|
||||||
|
a,
|
||||||
|
//^ unresolved import
|
||||||
|
b,
|
||||||
|
//^ unresolved import
|
||||||
|
c,
|
||||||
|
//^ unresolved import
|
||||||
|
};
|
||||||
|
|
||||||
|
mod does_exist {
|
||||||
|
pub struct Exists;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dedup_unresolved_import_from_unresolved_crate() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
//- /main.rs crate:main
|
||||||
|
mod a {
|
||||||
|
extern crate doesnotexist;
|
||||||
|
//^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate
|
||||||
|
|
||||||
|
// Should not error, since we already errored for the missing crate.
|
||||||
|
use doesnotexist::{self, bla, *};
|
||||||
|
|
||||||
|
use crate::doesnotexist;
|
||||||
|
//^^^^^^^^^^^^^^^^^^^ unresolved import
|
||||||
|
}
|
||||||
|
|
||||||
|
mod m {
|
||||||
|
use super::doesnotexist;
|
||||||
|
//^^^^^^^^^^^^^^^^^^^ unresolved import
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -104,6 +104,7 @@ mod baz {}
|
||||||
"unresolved-module",
|
"unresolved-module",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
experimental: false,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
"#]],
|
"#]],
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue