ide_diagnostics/handlers/
unresolved_module.rs

1use hir::db::ExpandDatabase;
2use ide_db::{assists::Assist, base_db::AnchoredPathBuf, source_change::FileSystemEdit};
3use itertools::Itertools;
4use syntax::AstNode;
5
6use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix};
7
8// Diagnostic: unresolved-module
9//
10// This diagnostic is triggered if rust-analyzer is unable to discover referred module.
11pub(crate) fn unresolved_module(
12    ctx: &DiagnosticsContext<'_>,
13    d: &hir::UnresolvedModule,
14) -> Diagnostic {
15    Diagnostic::new_with_syntax_node_ptr(
16        ctx,
17        DiagnosticCode::RustcHardError("E0583"),
18        match &*d.candidates {
19            [] => "unresolved module".to_owned(),
20            [candidate] => format!("unresolved module, can't find module file: {candidate}"),
21            [candidates @ .., last] => {
22                format!(
23                    "unresolved module, can't find module file: {}, or {}",
24                    candidates.iter().format(", "),
25                    last
26                )
27            }
28        },
29        d.decl.map(|it| it.into()),
30    )
31    .stable()
32    .with_fixes(fixes(ctx, d))
33}
34
35fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedModule) -> Option<Vec<Assist>> {
36    let root = ctx.sema.db.parse_or_expand(d.decl.file_id);
37    let unresolved_module = d.decl.value.to_node(&root);
38    Some(
39        d.candidates
40            .iter()
41            .map(|candidate| {
42                fix(
43                    "create_module",
44                    &format!("Create module at `{candidate}`"),
45                    FileSystemEdit::CreateFile {
46                        dst: AnchoredPathBuf {
47                            anchor: d.decl.file_id.original_file(ctx.sema.db).file_id(ctx.sema.db),
48                            path: candidate.clone(),
49                        },
50                        initial_contents: "".to_owned(),
51                    }
52                    .into(),
53                    unresolved_module.syntax().text_range(),
54                )
55            })
56            .collect(),
57    )
58}
59
60#[cfg(test)]
61mod tests {
62    use crate::tests::check_diagnostics;
63
64    #[test]
65    fn unresolved_module() {
66        check_diagnostics(
67            r#"
68//- /lib.rs
69mod foo;
70  mod bar;
71//^^^^^^^^ 💡 error: unresolved module, can't find module file: bar.rs, or bar/mod.rs
72mod baz {}
73//- /foo.rs
74"#,
75        );
76    }
77
78    #[test]
79    fn test_unresolved_module_diagnostic() {
80        check_diagnostics(
81            r#"
82  mod foo;
83//^^^^^^^^ 💡 error: unresolved module, can't find module file: foo.rs, or foo/mod.rs
84"#,
85        );
86    }
87}