mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-25 20:43:21 +00:00
fix: Show what file paths were expected for unresolved modules
This commit is contained in:
parent
6c8c02f625
commit
a9dd606387
12 changed files with 133 additions and 59 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -504,6 +504,7 @@ name = "hir_def"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anymap",
|
"anymap",
|
||||||
|
"arrayvec",
|
||||||
"base_db",
|
"base_db",
|
||||||
"cfg",
|
"cfg",
|
||||||
"cov-mark",
|
"cov-mark",
|
||||||
|
|
|
@ -54,7 +54,7 @@ diagnostics![
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct UnresolvedModule {
|
pub struct UnresolvedModule {
|
||||||
pub decl: InFile<AstPtr<ast::Module>>,
|
pub decl: InFile<AstPtr<ast::Module>>,
|
||||||
pub candidate: String,
|
pub candidates: Box<[String]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
@ -593,12 +593,12 @@ impl Module {
|
||||||
|
|
||||||
fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, diag: &DefDiagnostic) {
|
fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, diag: &DefDiagnostic) {
|
||||||
match &diag.kind {
|
match &diag.kind {
|
||||||
DefDiagnosticKind::UnresolvedModule { ast: declaration, candidate } => {
|
DefDiagnosticKind::UnresolvedModule { ast: declaration, candidates } => {
|
||||||
let decl = declaration.to_node(db.upcast());
|
let decl = declaration.to_node(db.upcast());
|
||||||
acc.push(
|
acc.push(
|
||||||
UnresolvedModule {
|
UnresolvedModule {
|
||||||
decl: InFile::new(declaration.file_id, AstPtr::new(&decl)),
|
decl: InFile::new(declaration.file_id, AstPtr::new(&decl)),
|
||||||
candidate: candidate.clone(),
|
candidates: candidates.clone(),
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
)
|
)
|
||||||
|
|
|
@ -24,6 +24,7 @@ fst = { version = "0.4", default-features = false }
|
||||||
itertools = "0.10.0"
|
itertools = "0.10.0"
|
||||||
indexmap = "1.7.0"
|
indexmap = "1.7.0"
|
||||||
smallvec = "1.4.0"
|
smallvec = "1.4.0"
|
||||||
|
arrayvec = "0.7.2"
|
||||||
la-arena = { version = "0.3.0", path = "../../lib/arena" }
|
la-arena = { version = "0.3.0", path = "../../lib/arena" }
|
||||||
|
|
||||||
stdx = { path = "../stdx", version = "0.0.0" }
|
stdx = { path = "../stdx", version = "0.0.0" }
|
||||||
|
|
|
@ -709,7 +709,7 @@ pub enum ModKind {
|
||||||
Inline { items: Box<[ModItem]> },
|
Inline { items: Box<[ModItem]> },
|
||||||
|
|
||||||
/// `mod m;`
|
/// `mod m;`
|
||||||
Outline {},
|
Outline,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
|
|
@ -401,7 +401,7 @@ impl<'a> Ctx<'a> {
|
||||||
let name = module.name()?.as_name();
|
let name = module.name()?.as_name();
|
||||||
let visibility = self.lower_visibility(module);
|
let visibility = self.lower_visibility(module);
|
||||||
let kind = if module.semicolon_token().is_some() {
|
let kind = if module.semicolon_token().is_some() {
|
||||||
ModKind::Outline {}
|
ModKind::Outline
|
||||||
} else {
|
} else {
|
||||||
ModKind::Inline {
|
ModKind::Inline {
|
||||||
items: module
|
items: module
|
||||||
|
|
|
@ -423,7 +423,7 @@ impl<'a> Printer<'a> {
|
||||||
});
|
});
|
||||||
wln!(self, "}}");
|
wln!(self, "}}");
|
||||||
}
|
}
|
||||||
ModKind::Outline {} => {
|
ModKind::Outline => {
|
||||||
wln!(self, ";");
|
wln!(self, ";");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1713,7 +1713,7 @@ impl ModCollector<'_, '_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// out of line module, resolve, parse and recurse
|
// out of line module, resolve, parse and recurse
|
||||||
ModKind::Outline {} => {
|
ModKind::Outline => {
|
||||||
let ast_id = AstId::new(self.tree_id.file_id(), module.ast_id);
|
let ast_id = AstId::new(self.tree_id.file_id(), module.ast_id);
|
||||||
let db = self.def_collector.db;
|
let db = self.def_collector.db;
|
||||||
match self.mod_dir.resolve_declaration(db, self.file_id(), &module.name, path_attr)
|
match self.mod_dir.resolve_declaration(db, self.file_id(), &module.name, path_attr)
|
||||||
|
@ -1751,9 +1751,9 @@ impl ModCollector<'_, '_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(candidate) => {
|
Err(candidates) => {
|
||||||
self.def_collector.def_map.diagnostics.push(
|
self.def_collector.def_map.diagnostics.push(
|
||||||
DefDiagnostic::unresolved_module(self.module_id, ast_id, candidate),
|
DefDiagnostic::unresolved_module(self.module_id, ast_id, candidates),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,7 +15,7 @@ use crate::{
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum DefDiagnosticKind {
|
pub enum DefDiagnosticKind {
|
||||||
UnresolvedModule { ast: AstId<ast::Module>, candidate: String },
|
UnresolvedModule { ast: AstId<ast::Module>, candidates: Box<[String]> },
|
||||||
|
|
||||||
UnresolvedExternCrate { ast: AstId<ast::ExternCrate> },
|
UnresolvedExternCrate { ast: AstId<ast::ExternCrate> },
|
||||||
|
|
||||||
|
@ -46,11 +46,11 @@ impl DefDiagnostic {
|
||||||
pub(super) fn unresolved_module(
|
pub(super) fn unresolved_module(
|
||||||
container: LocalModuleId,
|
container: LocalModuleId,
|
||||||
declaration: AstId<ast::Module>,
|
declaration: AstId<ast::Module>,
|
||||||
candidate: String,
|
candidates: Box<[String]>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
in_module: container,
|
in_module: container,
|
||||||
kind: DefDiagnosticKind::UnresolvedModule { ast: declaration, candidate },
|
kind: DefDiagnosticKind::UnresolvedModule { ast: declaration, candidates },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
//! This module resolves `mod foo;` declaration to file.
|
//! This module resolves `mod foo;` declaration to file.
|
||||||
|
use arrayvec::ArrayVec;
|
||||||
use base_db::{AnchoredPath, FileId};
|
use base_db::{AnchoredPath, FileId};
|
||||||
use hir_expand::name::Name;
|
use hir_expand::name::Name;
|
||||||
use limit::Limit;
|
use limit::Limit;
|
||||||
|
@ -63,22 +64,21 @@ impl ModDir {
|
||||||
file_id: HirFileId,
|
file_id: HirFileId,
|
||||||
name: &Name,
|
name: &Name,
|
||||||
attr_path: Option<&SmolStr>,
|
attr_path: Option<&SmolStr>,
|
||||||
) -> Result<(FileId, bool, ModDir), String> {
|
) -> Result<(FileId, bool, ModDir), Box<[String]>> {
|
||||||
let orig_file_id = file_id.original_file(db.upcast());
|
let orig_file_id = file_id.original_file(db.upcast());
|
||||||
|
|
||||||
let mut candidate_files = Vec::new();
|
let mut candidate_files = ArrayVec::<_, 2>::new();
|
||||||
match attr_path {
|
match attr_path {
|
||||||
Some(attr_path) => {
|
Some(attr_path) => {
|
||||||
candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner))
|
candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner))
|
||||||
}
|
}
|
||||||
|
None if file_id.is_include_macro(db.upcast()) => {
|
||||||
|
candidate_files.push(format!("{}.rs", name));
|
||||||
|
candidate_files.push(format!("{}/mod.rs", name));
|
||||||
|
}
|
||||||
None => {
|
None => {
|
||||||
if file_id.is_include_macro(db.upcast()) {
|
candidate_files.push(format!("{}{}.rs", self.dir_path.0, name));
|
||||||
candidate_files.push(format!("{}.rs", name));
|
candidate_files.push(format!("{}{}/mod.rs", self.dir_path.0, name));
|
||||||
candidate_files.push(format!("{}/mod.rs", name));
|
|
||||||
} else {
|
|
||||||
candidate_files.push(format!("{}{}.rs", self.dir_path.0, name));
|
|
||||||
candidate_files.push(format!("{}{}/mod.rs", self.dir_path.0, name));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ impl ModDir {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(candidate_files.remove(0))
|
Err(candidate_files.into_iter().collect())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use hir::db::AstDatabase;
|
use hir::db::AstDatabase;
|
||||||
use ide_db::{assists::Assist, base_db::AnchoredPathBuf, source_change::FileSystemEdit};
|
use ide_db::{assists::Assist, base_db::AnchoredPathBuf, source_change::FileSystemEdit};
|
||||||
|
use itertools::Itertools;
|
||||||
use syntax::AstNode;
|
use syntax::AstNode;
|
||||||
|
|
||||||
use crate::{fix, Diagnostic, DiagnosticsContext};
|
use crate::{fix, Diagnostic, DiagnosticsContext};
|
||||||
|
@ -13,7 +14,17 @@ pub(crate) fn unresolved_module(
|
||||||
) -> Diagnostic {
|
) -> Diagnostic {
|
||||||
Diagnostic::new(
|
Diagnostic::new(
|
||||||
"unresolved-module",
|
"unresolved-module",
|
||||||
"unresolved module",
|
match &*d.candidates {
|
||||||
|
[] => "unresolved module".to_string(),
|
||||||
|
[candidate] => format!("unresolved module, can't find module file: {}", candidate),
|
||||||
|
[candidates @ .., last] => {
|
||||||
|
format!(
|
||||||
|
"unresolved module, can't find module file: {}, or {}",
|
||||||
|
candidates.iter().format(", "),
|
||||||
|
last
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
ctx.sema.diagnostics_display_range(d.decl.clone().map(|it| it.into())).range,
|
ctx.sema.diagnostics_display_range(d.decl.clone().map(|it| it.into())).range,
|
||||||
)
|
)
|
||||||
.with_fixes(fixes(ctx, d))
|
.with_fixes(fixes(ctx, d))
|
||||||
|
@ -22,19 +33,26 @@ pub(crate) fn unresolved_module(
|
||||||
fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedModule) -> Option<Vec<Assist>> {
|
fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedModule) -> Option<Vec<Assist>> {
|
||||||
let root = ctx.sema.db.parse_or_expand(d.decl.file_id)?;
|
let root = ctx.sema.db.parse_or_expand(d.decl.file_id)?;
|
||||||
let unresolved_module = d.decl.value.to_node(&root);
|
let unresolved_module = d.decl.value.to_node(&root);
|
||||||
Some(vec![fix(
|
Some(
|
||||||
"create_module",
|
d.candidates
|
||||||
"Create module",
|
.iter()
|
||||||
FileSystemEdit::CreateFile {
|
.map(|candidate| {
|
||||||
dst: AnchoredPathBuf {
|
fix(
|
||||||
anchor: d.decl.file_id.original_file(ctx.sema.db),
|
"create_module",
|
||||||
path: d.candidate.clone(),
|
"Create module",
|
||||||
},
|
FileSystemEdit::CreateFile {
|
||||||
initial_contents: "".to_string(),
|
dst: AnchoredPathBuf {
|
||||||
}
|
anchor: d.decl.file_id.original_file(ctx.sema.db),
|
||||||
.into(),
|
path: candidate.clone(),
|
||||||
unresolved_module.syntax().text_range(),
|
},
|
||||||
)])
|
initial_contents: "".to_string(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
unresolved_module.syntax().text_range(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -50,7 +68,7 @@ mod tests {
|
||||||
//- /lib.rs
|
//- /lib.rs
|
||||||
mod foo;
|
mod foo;
|
||||||
mod bar;
|
mod bar;
|
||||||
//^^^^^^^^ 💡 error: unresolved module
|
//^^^^^^^^ 💡 error: unresolved module, can't find module file: bar.rs, or bar/mod.rs
|
||||||
mod baz {}
|
mod baz {}
|
||||||
//- /foo.rs
|
//- /foo.rs
|
||||||
"#,
|
"#,
|
||||||
|
@ -67,7 +85,7 @@ mod baz {}
|
||||||
code: DiagnosticCode(
|
code: DiagnosticCode(
|
||||||
"unresolved-module",
|
"unresolved-module",
|
||||||
),
|
),
|
||||||
message: "unresolved module",
|
message: "unresolved module, can't find module file: foo.rs, or foo/mod.rs",
|
||||||
range: 0..8,
|
range: 0..8,
|
||||||
severity: Error,
|
severity: Error,
|
||||||
unused: false,
|
unused: false,
|
||||||
|
@ -100,6 +118,32 @@ mod baz {}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
Assist {
|
||||||
|
id: AssistId(
|
||||||
|
"create_module",
|
||||||
|
QuickFix,
|
||||||
|
),
|
||||||
|
label: "Create module",
|
||||||
|
group: None,
|
||||||
|
target: 0..8,
|
||||||
|
source_change: Some(
|
||||||
|
SourceChange {
|
||||||
|
source_file_edits: {},
|
||||||
|
file_system_edits: [
|
||||||
|
CreateFile {
|
||||||
|
dst: AnchoredPathBuf {
|
||||||
|
anchor: FileId(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
path: "foo/mod.rs",
|
||||||
|
},
|
||||||
|
initial_contents: "",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
is_snippet: false,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
|
@ -463,18 +463,32 @@ fn main() {}
|
||||||
partial_result_params: PartialResultParams::default(),
|
partial_result_params: PartialResultParams::default(),
|
||||||
work_done_progress_params: WorkDoneProgressParams::default(),
|
work_done_progress_params: WorkDoneProgressParams::default(),
|
||||||
},
|
},
|
||||||
json!([{
|
json!([
|
||||||
"edit": {
|
{
|
||||||
"documentChanges": [
|
"title": "Create module",
|
||||||
{
|
"kind": "quickfix",
|
||||||
"kind": "create",
|
"edit": {
|
||||||
"uri": "file:///[..]/src/bar.rs"
|
"documentChanges": [
|
||||||
|
{
|
||||||
|
"kind": "create",
|
||||||
|
"uri": "file://[..]/src/bar.rs"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"kind": "quickfix",
|
{
|
||||||
"title": "Create module"
|
"title": "Create module",
|
||||||
}]),
|
"kind": "quickfix",
|
||||||
|
"edit": {
|
||||||
|
"documentChanges": [
|
||||||
|
{
|
||||||
|
"kind": "create",
|
||||||
|
"uri": "file://[..]src/bar/mod.rs"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
server.request::<CodeActionRequest>(
|
server.request::<CodeActionRequest>(
|
||||||
|
@ -492,7 +506,7 @@ fn main() {}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_missing_module_code_action_in_json_project() {
|
fn test_missing_module_code_action_in_json_project() {
|
||||||
if skip_slow_tests() {
|
if skip_slow_tests() {
|
||||||
return;
|
// return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let tmp_dir = TestDir::new();
|
let tmp_dir = TestDir::new();
|
||||||
|
@ -533,18 +547,32 @@ fn main() {{}}
|
||||||
partial_result_params: PartialResultParams::default(),
|
partial_result_params: PartialResultParams::default(),
|
||||||
work_done_progress_params: WorkDoneProgressParams::default(),
|
work_done_progress_params: WorkDoneProgressParams::default(),
|
||||||
},
|
},
|
||||||
json!([{
|
json!([
|
||||||
"edit": {
|
{
|
||||||
"documentChanges": [
|
"title": "Create module",
|
||||||
{
|
"kind": "quickfix",
|
||||||
"kind": "create",
|
"edit": {
|
||||||
"uri": "file://[..]/src/bar.rs"
|
"documentChanges": [
|
||||||
|
{
|
||||||
|
"kind": "create",
|
||||||
|
"uri": "file://[..]/src/bar.rs"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"kind": "quickfix",
|
{
|
||||||
"title": "Create module"
|
"title": "Create module",
|
||||||
}]),
|
"kind": "quickfix",
|
||||||
|
"edit": {
|
||||||
|
"documentChanges": [
|
||||||
|
{
|
||||||
|
"kind": "create",
|
||||||
|
"uri": "file://[..]src/bar/mod.rs"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
server.request::<CodeActionRequest>(
|
server.request::<CodeActionRequest>(
|
||||||
|
|
Loading…
Reference in a new issue