diff options
Diffstat (limited to 'crates/ra_hir_def/src/nameres.rs')
-rw-r--r-- | crates/ra_hir_def/src/nameres.rs | 327 |
1 files changed, 0 insertions, 327 deletions
diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs deleted file mode 100644 index 3d9b55a73..000000000 --- a/crates/ra_hir_def/src/nameres.rs +++ /dev/null | |||
@@ -1,327 +0,0 @@ | |||
1 | //! This module implements import-resolution/macro expansion algorithm. | ||
2 | //! | ||
3 | //! The result of this module is `CrateDefMap`: a data structure which contains: | ||
4 | //! | ||
5 | //! * a tree of modules for the crate | ||
6 | //! * for each module, a set of items visible in the module (directly declared | ||
7 | //! or imported) | ||
8 | //! | ||
9 | //! Note that `CrateDefMap` contains fully macro expanded code. | ||
10 | //! | ||
11 | //! Computing `CrateDefMap` can be partitioned into several logically | ||
12 | //! independent "phases". The phases are mutually recursive though, there's no | ||
13 | //! strict ordering. | ||
14 | //! | ||
15 | //! ## Collecting RawItems | ||
16 | //! | ||
17 | //! This happens in the `raw` module, which parses a single source file into a | ||
18 | //! set of top-level items. Nested imports are desugared to flat imports in this | ||
19 | //! phase. Macro calls are represented as a triple of (Path, Option<Name>, | ||
20 | //! TokenTree). | ||
21 | //! | ||
22 | //! ## Collecting Modules | ||
23 | //! | ||
24 | //! This happens in the `collector` module. In this phase, we recursively walk | ||
25 | //! tree of modules, collect raw items from submodules, populate module scopes | ||
26 | //! with defined items (so, we assign item ids in this phase) and record the set | ||
27 | //! of unresolved imports and macros. | ||
28 | //! | ||
29 | //! While we walk tree of modules, we also record macro_rules definitions and | ||
30 | //! expand calls to macro_rules defined macros. | ||
31 | //! | ||
32 | //! ## Resolving Imports | ||
33 | //! | ||
34 | //! We maintain a list of currently unresolved imports. On every iteration, we | ||
35 | //! try to resolve some imports from this list. If the import is resolved, we | ||
36 | //! record it, by adding an item to current module scope and, if necessary, by | ||
37 | //! recursively populating glob imports. | ||
38 | //! | ||
39 | //! ## Resolving Macros | ||
40 | //! | ||
41 | //! macro_rules from the same crate use a global mutable namespace. We expand | ||
42 | //! them immediately, when we collect modules. | ||
43 | //! | ||
44 | //! Macros from other crates (including proc-macros) can be used with | ||
45 | //! `foo::bar!` syntax. We handle them similarly to imports. There's a list of | ||
46 | //! unexpanded macros. On every iteration, we try to resolve each macro call | ||
47 | //! path and, upon success, we run macro expansion and "collect module" phase on | ||
48 | //! the result | ||
49 | |||
50 | mod collector; | ||
51 | mod mod_resolution; | ||
52 | mod path_resolution; | ||
53 | |||
54 | #[cfg(test)] | ||
55 | mod tests; | ||
56 | |||
57 | use std::sync::Arc; | ||
58 | |||
59 | use hir_expand::{diagnostics::DiagnosticSink, name::Name, InFile}; | ||
60 | use ra_arena::Arena; | ||
61 | use ra_db::{CrateId, Edition, FileId}; | ||
62 | use ra_prof::profile; | ||
63 | use ra_syntax::ast; | ||
64 | use rustc_hash::FxHashMap; | ||
65 | use stdx::format_to; | ||
66 | |||
67 | use crate::{ | ||
68 | db::DefDatabase, | ||
69 | item_scope::{BuiltinShadowMode, ItemScope}, | ||
70 | nameres::{diagnostics::DefDiagnostic, path_resolution::ResolveMode}, | ||
71 | path::ModPath, | ||
72 | per_ns::PerNs, | ||
73 | AstId, LocalModuleId, ModuleDefId, ModuleId, | ||
74 | }; | ||
75 | |||
76 | /// Contains all top-level defs from a macro-expanded crate | ||
77 | #[derive(Debug, PartialEq, Eq)] | ||
78 | pub struct CrateDefMap { | ||
79 | pub root: LocalModuleId, | ||
80 | pub modules: Arena<ModuleData>, | ||
81 | pub(crate) krate: CrateId, | ||
82 | /// The prelude module for this crate. This either comes from an import | ||
83 | /// marked with the `prelude_import` attribute, or (in the normal case) from | ||
84 | /// a dependency (`std` or `core`). | ||
85 | pub(crate) prelude: Option<ModuleId>, | ||
86 | pub(crate) extern_prelude: FxHashMap<Name, ModuleDefId>, | ||
87 | |||
88 | edition: Edition, | ||
89 | diagnostics: Vec<DefDiagnostic>, | ||
90 | } | ||
91 | |||
92 | impl std::ops::Index<LocalModuleId> for CrateDefMap { | ||
93 | type Output = ModuleData; | ||
94 | fn index(&self, id: LocalModuleId) -> &ModuleData { | ||
95 | &self.modules[id] | ||
96 | } | ||
97 | } | ||
98 | |||
99 | #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] | ||
100 | pub enum ModuleOrigin { | ||
101 | CrateRoot { | ||
102 | definition: FileId, | ||
103 | }, | ||
104 | /// Note that non-inline modules, by definition, live inside non-macro file. | ||
105 | File { | ||
106 | is_mod_rs: bool, | ||
107 | declaration: AstId<ast::Module>, | ||
108 | definition: FileId, | ||
109 | }, | ||
110 | Inline { | ||
111 | definition: AstId<ast::Module>, | ||
112 | }, | ||
113 | } | ||
114 | |||
115 | impl Default for ModuleOrigin { | ||
116 | fn default() -> Self { | ||
117 | ModuleOrigin::CrateRoot { definition: FileId(0) } | ||
118 | } | ||
119 | } | ||
120 | |||
121 | impl ModuleOrigin { | ||
122 | fn declaration(&self) -> Option<AstId<ast::Module>> { | ||
123 | match self { | ||
124 | ModuleOrigin::File { declaration: module, .. } | ||
125 | | ModuleOrigin::Inline { definition: module, .. } => Some(*module), | ||
126 | ModuleOrigin::CrateRoot { .. } => None, | ||
127 | } | ||
128 | } | ||
129 | |||
130 | pub fn file_id(&self) -> Option<FileId> { | ||
131 | match self { | ||
132 | ModuleOrigin::File { definition, .. } | ModuleOrigin::CrateRoot { definition } => { | ||
133 | Some(*definition) | ||
134 | } | ||
135 | _ => None, | ||
136 | } | ||
137 | } | ||
138 | |||
139 | pub fn is_inline(&self) -> bool { | ||
140 | match self { | ||
141 | ModuleOrigin::Inline { .. } => true, | ||
142 | ModuleOrigin::CrateRoot { .. } | ModuleOrigin::File { .. } => false, | ||
143 | } | ||
144 | } | ||
145 | |||
146 | /// Returns a node which defines this module. | ||
147 | /// That is, a file or a `mod foo {}` with items. | ||
148 | fn definition_source(&self, db: &dyn DefDatabase) -> InFile<ModuleSource> { | ||
149 | match self { | ||
150 | ModuleOrigin::File { definition, .. } | ModuleOrigin::CrateRoot { definition } => { | ||
151 | let file_id = *definition; | ||
152 | let sf = db.parse(file_id).tree(); | ||
153 | InFile::new(file_id.into(), ModuleSource::SourceFile(sf)) | ||
154 | } | ||
155 | ModuleOrigin::Inline { definition } => InFile::new( | ||
156 | definition.file_id, | ||
157 | ModuleSource::Module(definition.to_node(db.upcast())), | ||
158 | ), | ||
159 | } | ||
160 | } | ||
161 | } | ||
162 | |||
163 | #[derive(Default, Debug, PartialEq, Eq)] | ||
164 | pub struct ModuleData { | ||
165 | pub parent: Option<LocalModuleId>, | ||
166 | pub children: FxHashMap<Name, LocalModuleId>, | ||
167 | pub scope: ItemScope, | ||
168 | |||
169 | /// Where does this module come from? | ||
170 | pub origin: ModuleOrigin, | ||
171 | } | ||
172 | |||
173 | impl CrateDefMap { | ||
174 | pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { | ||
175 | let _p = profile("crate_def_map_query").detail(|| { | ||
176 | db.crate_graph()[krate] | ||
177 | .display_name | ||
178 | .as_ref() | ||
179 | .map(ToString::to_string) | ||
180 | .unwrap_or_default() | ||
181 | }); | ||
182 | let def_map = { | ||
183 | let edition = db.crate_graph()[krate].edition; | ||
184 | let mut modules: Arena<ModuleData> = Arena::default(); | ||
185 | let root = modules.alloc(ModuleData::default()); | ||
186 | CrateDefMap { | ||
187 | krate, | ||
188 | edition, | ||
189 | extern_prelude: FxHashMap::default(), | ||
190 | prelude: None, | ||
191 | root, | ||
192 | modules, | ||
193 | diagnostics: Vec::new(), | ||
194 | } | ||
195 | }; | ||
196 | let def_map = collector::collect_defs(db, def_map); | ||
197 | Arc::new(def_map) | ||
198 | } | ||
199 | |||
200 | pub fn add_diagnostics( | ||
201 | &self, | ||
202 | db: &dyn DefDatabase, | ||
203 | module: LocalModuleId, | ||
204 | sink: &mut DiagnosticSink, | ||
205 | ) { | ||
206 | self.diagnostics.iter().for_each(|it| it.add_to(db, module, sink)) | ||
207 | } | ||
208 | |||
209 | pub fn modules_for_file(&self, file_id: FileId) -> impl Iterator<Item = LocalModuleId> + '_ { | ||
210 | self.modules | ||
211 | .iter() | ||
212 | .filter(move |(_id, data)| data.origin.file_id() == Some(file_id)) | ||
213 | .map(|(id, _data)| id) | ||
214 | } | ||
215 | |||
216 | pub(crate) fn resolve_path( | ||
217 | &self, | ||
218 | db: &dyn DefDatabase, | ||
219 | original_module: LocalModuleId, | ||
220 | path: &ModPath, | ||
221 | shadow: BuiltinShadowMode, | ||
222 | ) -> (PerNs, Option<usize>) { | ||
223 | let res = | ||
224 | self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path, shadow); | ||
225 | (res.resolved_def, res.segment_index) | ||
226 | } | ||
227 | |||
228 | // FIXME: this can use some more human-readable format (ideally, an IR | ||
229 | // even), as this should be a great debugging aid. | ||
230 | pub fn dump(&self) -> String { | ||
231 | let mut buf = String::new(); | ||
232 | go(&mut buf, self, "crate", self.root); | ||
233 | return buf; | ||
234 | |||
235 | fn go(buf: &mut String, map: &CrateDefMap, path: &str, module: LocalModuleId) { | ||
236 | format_to!(buf, "{}\n", path); | ||
237 | |||
238 | let mut entries: Vec<_> = map.modules[module].scope.resolutions().collect(); | ||
239 | entries.sort_by_key(|(name, _)| name.clone()); | ||
240 | |||
241 | for (name, def) in entries { | ||
242 | format_to!(buf, "{}:", name.map_or("_".to_string(), |name| name.to_string())); | ||
243 | |||
244 | if def.types.is_some() { | ||
245 | buf.push_str(" t"); | ||
246 | } | ||
247 | if def.values.is_some() { | ||
248 | buf.push_str(" v"); | ||
249 | } | ||
250 | if def.macros.is_some() { | ||
251 | buf.push_str(" m"); | ||
252 | } | ||
253 | if def.is_none() { | ||
254 | buf.push_str(" _"); | ||
255 | } | ||
256 | |||
257 | buf.push_str("\n"); | ||
258 | } | ||
259 | |||
260 | for (name, child) in map.modules[module].children.iter() { | ||
261 | let path = format!("{}::{}", path, name); | ||
262 | buf.push('\n'); | ||
263 | go(buf, map, &path, *child); | ||
264 | } | ||
265 | } | ||
266 | } | ||
267 | } | ||
268 | |||
269 | impl ModuleData { | ||
270 | /// Returns a node which defines this module. That is, a file or a `mod foo {}` with items. | ||
271 | pub fn definition_source(&self, db: &dyn DefDatabase) -> InFile<ModuleSource> { | ||
272 | self.origin.definition_source(db) | ||
273 | } | ||
274 | |||
275 | /// Returns a node which declares this module, either a `mod foo;` or a `mod foo {}`. | ||
276 | /// `None` for the crate root or block. | ||
277 | pub fn declaration_source(&self, db: &dyn DefDatabase) -> Option<InFile<ast::Module>> { | ||
278 | let decl = self.origin.declaration()?; | ||
279 | let value = decl.to_node(db.upcast()); | ||
280 | Some(InFile { file_id: decl.file_id, value }) | ||
281 | } | ||
282 | } | ||
283 | |||
284 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
285 | pub enum ModuleSource { | ||
286 | SourceFile(ast::SourceFile), | ||
287 | Module(ast::Module), | ||
288 | } | ||
289 | |||
290 | mod diagnostics { | ||
291 | use hir_expand::diagnostics::DiagnosticSink; | ||
292 | use ra_syntax::{ast, AstPtr}; | ||
293 | |||
294 | use crate::{db::DefDatabase, diagnostics::UnresolvedModule, nameres::LocalModuleId, AstId}; | ||
295 | |||
296 | #[derive(Debug, PartialEq, Eq)] | ||
297 | pub(super) enum DefDiagnostic { | ||
298 | UnresolvedModule { | ||
299 | module: LocalModuleId, | ||
300 | declaration: AstId<ast::Module>, | ||
301 | candidate: String, | ||
302 | }, | ||
303 | } | ||
304 | |||
305 | impl DefDiagnostic { | ||
306 | pub(super) fn add_to( | ||
307 | &self, | ||
308 | db: &dyn DefDatabase, | ||
309 | target_module: LocalModuleId, | ||
310 | sink: &mut DiagnosticSink, | ||
311 | ) { | ||
312 | match self { | ||
313 | DefDiagnostic::UnresolvedModule { module, declaration, candidate } => { | ||
314 | if *module != target_module { | ||
315 | return; | ||
316 | } | ||
317 | let decl = declaration.to_node(db.upcast()); | ||
318 | sink.push(UnresolvedModule { | ||
319 | file: declaration.file_id, | ||
320 | decl: AstPtr::new(&decl), | ||
321 | candidate: candidate.clone(), | ||
322 | }) | ||
323 | } | ||
324 | } | ||
325 | } | ||
326 | } | ||
327 | } | ||