mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-25 12:33:33 +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"
|
||||
dependencies = [
|
||||
"anymap",
|
||||
"arrayvec",
|
||||
"base_db",
|
||||
"cfg",
|
||||
"cov-mark",
|
||||
|
|
|
@ -54,7 +54,7 @@ diagnostics![
|
|||
#[derive(Debug)]
|
||||
pub struct UnresolvedModule {
|
||||
pub decl: InFile<AstPtr<ast::Module>>,
|
||||
pub candidate: String,
|
||||
pub candidates: Box<[String]>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -593,12 +593,12 @@ impl Module {
|
|||
|
||||
fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, diag: &DefDiagnostic) {
|
||||
match &diag.kind {
|
||||
DefDiagnosticKind::UnresolvedModule { ast: declaration, candidate } => {
|
||||
DefDiagnosticKind::UnresolvedModule { ast: declaration, candidates } => {
|
||||
let decl = declaration.to_node(db.upcast());
|
||||
acc.push(
|
||||
UnresolvedModule {
|
||||
decl: InFile::new(declaration.file_id, AstPtr::new(&decl)),
|
||||
candidate: candidate.clone(),
|
||||
candidates: candidates.clone(),
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
|
|
|
@ -24,6 +24,7 @@ fst = { version = "0.4", default-features = false }
|
|||
itertools = "0.10.0"
|
||||
indexmap = "1.7.0"
|
||||
smallvec = "1.4.0"
|
||||
arrayvec = "0.7.2"
|
||||
la-arena = { version = "0.3.0", path = "../../lib/arena" }
|
||||
|
||||
stdx = { path = "../stdx", version = "0.0.0" }
|
||||
|
|
|
@ -709,7 +709,7 @@ pub enum ModKind {
|
|||
Inline { items: Box<[ModItem]> },
|
||||
|
||||
/// `mod m;`
|
||||
Outline {},
|
||||
Outline,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
|
|
|
@ -401,7 +401,7 @@ impl<'a> Ctx<'a> {
|
|||
let name = module.name()?.as_name();
|
||||
let visibility = self.lower_visibility(module);
|
||||
let kind = if module.semicolon_token().is_some() {
|
||||
ModKind::Outline {}
|
||||
ModKind::Outline
|
||||
} else {
|
||||
ModKind::Inline {
|
||||
items: module
|
||||
|
|
|
@ -423,7 +423,7 @@ impl<'a> Printer<'a> {
|
|||
});
|
||||
wln!(self, "}}");
|
||||
}
|
||||
ModKind::Outline {} => {
|
||||
ModKind::Outline => {
|
||||
wln!(self, ";");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1713,7 +1713,7 @@ impl ModCollector<'_, '_> {
|
|||
}
|
||||
}
|
||||
// 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 db = self.def_collector.db;
|
||||
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(
|
||||
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)]
|
||||
pub enum DefDiagnosticKind {
|
||||
UnresolvedModule { ast: AstId<ast::Module>, candidate: String },
|
||||
UnresolvedModule { ast: AstId<ast::Module>, candidates: Box<[String]> },
|
||||
|
||||
UnresolvedExternCrate { ast: AstId<ast::ExternCrate> },
|
||||
|
||||
|
@ -46,11 +46,11 @@ impl DefDiagnostic {
|
|||
pub(super) fn unresolved_module(
|
||||
container: LocalModuleId,
|
||||
declaration: AstId<ast::Module>,
|
||||
candidate: String,
|
||||
candidates: Box<[String]>,
|
||||
) -> Self {
|
||||
Self {
|
||||
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.
|
||||
use arrayvec::ArrayVec;
|
||||
use base_db::{AnchoredPath, FileId};
|
||||
use hir_expand::name::Name;
|
||||
use limit::Limit;
|
||||
|
@ -63,22 +64,21 @@ impl ModDir {
|
|||
file_id: HirFileId,
|
||||
name: &Name,
|
||||
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 mut candidate_files = Vec::new();
|
||||
let mut candidate_files = ArrayVec::<_, 2>::new();
|
||||
match attr_path {
|
||||
Some(attr_path) => {
|
||||
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 => {
|
||||
if file_id.is_include_macro(db.upcast()) {
|
||||
candidate_files.push(format!("{}.rs", 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));
|
||||
}
|
||||
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 ide_db::{assists::Assist, base_db::AnchoredPathBuf, source_change::FileSystemEdit};
|
||||
use itertools::Itertools;
|
||||
use syntax::AstNode;
|
||||
|
||||
use crate::{fix, Diagnostic, DiagnosticsContext};
|
||||
|
@ -13,7 +14,17 @@ pub(crate) fn unresolved_module(
|
|||
) -> Diagnostic {
|
||||
Diagnostic::new(
|
||||
"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,
|
||||
)
|
||||
.with_fixes(fixes(ctx, d))
|
||||
|
@ -22,19 +33,26 @@ pub(crate) fn unresolved_module(
|
|||
fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedModule) -> Option<Vec<Assist>> {
|
||||
let root = ctx.sema.db.parse_or_expand(d.decl.file_id)?;
|
||||
let unresolved_module = d.decl.value.to_node(&root);
|
||||
Some(vec![fix(
|
||||
"create_module",
|
||||
"Create module",
|
||||
FileSystemEdit::CreateFile {
|
||||
dst: AnchoredPathBuf {
|
||||
anchor: d.decl.file_id.original_file(ctx.sema.db),
|
||||
path: d.candidate.clone(),
|
||||
},
|
||||
initial_contents: "".to_string(),
|
||||
}
|
||||
.into(),
|
||||
unresolved_module.syntax().text_range(),
|
||||
)])
|
||||
Some(
|
||||
d.candidates
|
||||
.iter()
|
||||
.map(|candidate| {
|
||||
fix(
|
||||
"create_module",
|
||||
"Create module",
|
||||
FileSystemEdit::CreateFile {
|
||||
dst: AnchoredPathBuf {
|
||||
anchor: d.decl.file_id.original_file(ctx.sema.db),
|
||||
path: candidate.clone(),
|
||||
},
|
||||
initial_contents: "".to_string(),
|
||||
}
|
||||
.into(),
|
||||
unresolved_module.syntax().text_range(),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -50,7 +68,7 @@ mod tests {
|
|||
//- /lib.rs
|
||||
mod foo;
|
||||
mod bar;
|
||||
//^^^^^^^^ 💡 error: unresolved module
|
||||
//^^^^^^^^ 💡 error: unresolved module, can't find module file: bar.rs, or bar/mod.rs
|
||||
mod baz {}
|
||||
//- /foo.rs
|
||||
"#,
|
||||
|
@ -67,7 +85,7 @@ mod baz {}
|
|||
code: DiagnosticCode(
|
||||
"unresolved-module",
|
||||
),
|
||||
message: "unresolved module",
|
||||
message: "unresolved module, can't find module file: foo.rs, or foo/mod.rs",
|
||||
range: 0..8,
|
||||
severity: Error,
|
||||
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(),
|
||||
work_done_progress_params: WorkDoneProgressParams::default(),
|
||||
},
|
||||
json!([{
|
||||
"edit": {
|
||||
"documentChanges": [
|
||||
{
|
||||
"kind": "create",
|
||||
"uri": "file:///[..]/src/bar.rs"
|
||||
json!([
|
||||
{
|
||||
"title": "Create module",
|
||||
"kind": "quickfix",
|
||||
"edit": {
|
||||
"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>(
|
||||
|
@ -492,7 +506,7 @@ fn main() {}
|
|||
#[test]
|
||||
fn test_missing_module_code_action_in_json_project() {
|
||||
if skip_slow_tests() {
|
||||
return;
|
||||
// return;
|
||||
}
|
||||
|
||||
let tmp_dir = TestDir::new();
|
||||
|
@ -533,18 +547,32 @@ fn main() {{}}
|
|||
partial_result_params: PartialResultParams::default(),
|
||||
work_done_progress_params: WorkDoneProgressParams::default(),
|
||||
},
|
||||
json!([{
|
||||
"edit": {
|
||||
"documentChanges": [
|
||||
{
|
||||
"kind": "create",
|
||||
"uri": "file://[..]/src/bar.rs"
|
||||
json!([
|
||||
{
|
||||
"title": "Create module",
|
||||
"kind": "quickfix",
|
||||
"edit": {
|
||||
"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>(
|
||||
|
|
Loading…
Reference in a new issue