]>
Commit | Line | Data |
---|---|---|
064997fb FG |
1 | use hir::db::AstDatabase; |
2 | use ide_db::{assists::Assist, base_db::AnchoredPathBuf, source_change::FileSystemEdit}; | |
3 | use itertools::Itertools; | |
4 | use syntax::AstNode; | |
5 | ||
6 | use crate::{fix, Diagnostic, DiagnosticsContext}; | |
7 | ||
8 | // Diagnostic: unresolved-module | |
9 | // | |
10 | // This diagnostic is triggered if rust-analyzer is unable to discover referred module. | |
11 | pub(crate) fn unresolved_module( | |
12 | ctx: &DiagnosticsContext<'_>, | |
13 | d: &hir::UnresolvedModule, | |
14 | ) -> Diagnostic { | |
15 | Diagnostic::new( | |
16 | "unresolved-module", | |
17 | match &*d.candidates { | |
18 | [] => "unresolved module".to_string(), | |
f25598a0 | 19 | [candidate] => format!("unresolved module, can't find module file: {candidate}"), |
064997fb FG |
20 | [candidates @ .., last] => { |
21 | format!( | |
22 | "unresolved module, can't find module file: {}, or {}", | |
23 | candidates.iter().format(", "), | |
24 | last | |
25 | ) | |
26 | } | |
27 | }, | |
28 | ctx.sema.diagnostics_display_range(d.decl.clone().map(|it| it.into())).range, | |
29 | ) | |
30 | .with_fixes(fixes(ctx, d)) | |
31 | } | |
32 | ||
33 | fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedModule) -> Option<Vec<Assist>> { | |
34 | let root = ctx.sema.db.parse_or_expand(d.decl.file_id)?; | |
35 | let unresolved_module = d.decl.value.to_node(&root); | |
36 | Some( | |
37 | d.candidates | |
38 | .iter() | |
39 | .map(|candidate| { | |
40 | fix( | |
41 | "create_module", | |
42 | &format!("Create module at `{candidate}`"), | |
43 | FileSystemEdit::CreateFile { | |
44 | dst: AnchoredPathBuf { | |
45 | anchor: d.decl.file_id.original_file(ctx.sema.db), | |
46 | path: candidate.clone(), | |
47 | }, | |
48 | initial_contents: "".to_string(), | |
49 | } | |
50 | .into(), | |
51 | unresolved_module.syntax().text_range(), | |
52 | ) | |
53 | }) | |
54 | .collect(), | |
55 | ) | |
56 | } | |
57 | ||
58 | #[cfg(test)] | |
59 | mod tests { | |
60 | use expect_test::expect; | |
61 | ||
62 | use crate::tests::{check_diagnostics, check_expect}; | |
63 | ||
64 | #[test] | |
65 | fn unresolved_module() { | |
66 | check_diagnostics( | |
67 | r#" | |
68 | //- /lib.rs | |
69 | mod foo; | |
70 | mod bar; | |
71 | //^^^^^^^^ 💡 error: unresolved module, can't find module file: bar.rs, or bar/mod.rs | |
72 | mod baz {} | |
73 | //- /foo.rs | |
74 | "#, | |
75 | ); | |
76 | } | |
77 | ||
78 | #[test] | |
79 | fn test_unresolved_module_diagnostic() { | |
80 | check_expect( | |
81 | r#"mod foo;"#, | |
82 | expect![[r#" | |
83 | [ | |
84 | Diagnostic { | |
85 | code: DiagnosticCode( | |
86 | "unresolved-module", | |
87 | ), | |
88 | message: "unresolved module, can't find module file: foo.rs, or foo/mod.rs", | |
89 | range: 0..8, | |
90 | severity: Error, | |
91 | unused: false, | |
92 | experimental: false, | |
93 | fixes: Some( | |
94 | [ | |
95 | Assist { | |
96 | id: AssistId( | |
97 | "create_module", | |
98 | QuickFix, | |
99 | ), | |
100 | label: "Create module at `foo.rs`", | |
101 | group: None, | |
102 | target: 0..8, | |
103 | source_change: Some( | |
104 | SourceChange { | |
105 | source_file_edits: {}, | |
106 | file_system_edits: [ | |
107 | CreateFile { | |
108 | dst: AnchoredPathBuf { | |
109 | anchor: FileId( | |
110 | 0, | |
111 | ), | |
112 | path: "foo.rs", | |
113 | }, | |
114 | initial_contents: "", | |
115 | }, | |
116 | ], | |
117 | is_snippet: false, | |
118 | }, | |
119 | ), | |
120 | trigger_signature_help: false, | |
121 | }, | |
122 | Assist { | |
123 | id: AssistId( | |
124 | "create_module", | |
125 | QuickFix, | |
126 | ), | |
127 | label: "Create module at `foo/mod.rs`", | |
128 | group: None, | |
129 | target: 0..8, | |
130 | source_change: Some( | |
131 | SourceChange { | |
132 | source_file_edits: {}, | |
133 | file_system_edits: [ | |
134 | CreateFile { | |
135 | dst: AnchoredPathBuf { | |
136 | anchor: FileId( | |
137 | 0, | |
138 | ), | |
139 | path: "foo/mod.rs", | |
140 | }, | |
141 | initial_contents: "", | |
142 | }, | |
143 | ], | |
144 | is_snippet: false, | |
145 | }, | |
146 | ), | |
147 | trigger_signature_help: false, | |
148 | }, | |
149 | ], | |
150 | ), | |
151 | }, | |
152 | ] | |
153 | "#]], | |
154 | ); | |
155 | } | |
156 | } |