aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis/src/hir/module
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_analysis/src/hir/module')
-rw-r--r--crates/ra_analysis/src/hir/module/imp.rs195
-rw-r--r--crates/ra_analysis/src/hir/module/mod.rs379
-rw-r--r--crates/ra_analysis/src/hir/module/nameres.rs446
3 files changed, 0 insertions, 1020 deletions
diff --git a/crates/ra_analysis/src/hir/module/imp.rs b/crates/ra_analysis/src/hir/module/imp.rs
deleted file mode 100644
index c8f7ed58d..000000000
--- a/crates/ra_analysis/src/hir/module/imp.rs
+++ /dev/null
@@ -1,195 +0,0 @@
1use std::sync::Arc;
2
3use ra_syntax::{
4 ast::{self, NameOwner},
5 SmolStr,
6};
7use relative_path::RelativePathBuf;
8use rustc_hash::{FxHashMap, FxHashSet};
9use ra_db::{SourceRoot, SourceRootId, FileResolverImp};
10
11use crate::{
12 hir::HirDatabase,
13 Cancelable, FileId,
14};
15
16use super::{
17 LinkData, LinkId, ModuleData, ModuleId, ModuleSource,
18 ModuleTree, Problem,
19};
20
21#[derive(Clone, Hash, PartialEq, Eq, Debug)]
22pub(crate) enum Submodule {
23 Declaration(SmolStr),
24 Definition(SmolStr, ModuleSource),
25}
26
27impl Submodule {
28 fn name(&self) -> &SmolStr {
29 match self {
30 Submodule::Declaration(name) => name,
31 Submodule::Definition(name, _) => name,
32 }
33 }
34}
35
36pub(crate) fn modules<'a>(
37 root: impl ast::ModuleItemOwner<'a>,
38) -> impl Iterator<Item = (SmolStr, ast::Module<'a>)> {
39 root.items()
40 .filter_map(|item| match item {
41 ast::ModuleItem::Module(m) => Some(m),
42 _ => None,
43 })
44 .filter_map(|module| {
45 let name = module.name()?.text();
46 Some((name, module))
47 })
48}
49
50pub(crate) fn module_tree(
51 db: &impl HirDatabase,
52 source_root: SourceRootId,
53) -> Cancelable<Arc<ModuleTree>> {
54 db.check_canceled()?;
55 let res = create_module_tree(db, source_root)?;
56 Ok(Arc::new(res))
57}
58
59fn create_module_tree<'a>(
60 db: &impl HirDatabase,
61 source_root: SourceRootId,
62) -> Cancelable<ModuleTree> {
63 let mut tree = ModuleTree::default();
64
65 let mut roots = FxHashMap::default();
66 let mut visited = FxHashSet::default();
67
68 let source_root = db.source_root(source_root);
69 for &file_id in source_root.files.iter() {
70 let source = ModuleSource::SourceFile(file_id);
71 if visited.contains(&source) {
72 continue; // TODO: use explicit crate_roots here
73 }
74 assert!(!roots.contains_key(&file_id));
75 let module_id = build_subtree(
76 db,
77 &source_root,
78 &mut tree,
79 &mut visited,
80 &mut roots,
81 None,
82 source,
83 )?;
84 roots.insert(file_id, module_id);
85 }
86 Ok(tree)
87}
88
89fn build_subtree(
90 db: &impl HirDatabase,
91 source_root: &SourceRoot,
92 tree: &mut ModuleTree,
93 visited: &mut FxHashSet<ModuleSource>,
94 roots: &mut FxHashMap<FileId, ModuleId>,
95 parent: Option<LinkId>,
96 source: ModuleSource,
97) -> Cancelable<ModuleId> {
98 visited.insert(source);
99 let id = tree.push_mod(ModuleData {
100 source,
101 parent,
102 children: Vec::new(),
103 });
104 for sub in db.submodules(source)?.iter() {
105 let link = tree.push_link(LinkData {
106 name: sub.name().clone(),
107 owner: id,
108 points_to: Vec::new(),
109 problem: None,
110 });
111
112 let (points_to, problem) = match sub {
113 Submodule::Declaration(name) => {
114 let (points_to, problem) =
115 resolve_submodule(source, &name, &source_root.file_resolver);
116 let points_to = points_to
117 .into_iter()
118 .map(|file_id| match roots.remove(&file_id) {
119 Some(module_id) => {
120 tree.mods[module_id].parent = Some(link);
121 Ok(module_id)
122 }
123 None => build_subtree(
124 db,
125 source_root,
126 tree,
127 visited,
128 roots,
129 Some(link),
130 ModuleSource::SourceFile(file_id),
131 ),
132 })
133 .collect::<Cancelable<Vec<_>>>()?;
134 (points_to, problem)
135 }
136 Submodule::Definition(_name, submodule_source) => {
137 let points_to = build_subtree(
138 db,
139 source_root,
140 tree,
141 visited,
142 roots,
143 Some(link),
144 *submodule_source,
145 )?;
146 (vec![points_to], None)
147 }
148 };
149
150 tree.links[link].points_to = points_to;
151 tree.links[link].problem = problem;
152 }
153 Ok(id)
154}
155
156fn resolve_submodule(
157 source: ModuleSource,
158 name: &SmolStr,
159 file_resolver: &FileResolverImp,
160) -> (Vec<FileId>, Option<Problem>) {
161 let file_id = match source {
162 ModuleSource::SourceFile(it) => it,
163 ModuleSource::Module(..) => {
164 // TODO
165 return (Vec::new(), None);
166 }
167 };
168 let mod_name = file_resolver.file_stem(file_id);
169 let is_dir_owner = mod_name == "mod" || mod_name == "lib" || mod_name == "main";
170
171 let file_mod = RelativePathBuf::from(format!("../{}.rs", name));
172 let dir_mod = RelativePathBuf::from(format!("../{}/mod.rs", name));
173 let points_to: Vec<FileId>;
174 let problem: Option<Problem>;
175 if is_dir_owner {
176 points_to = [&file_mod, &dir_mod]
177 .iter()
178 .filter_map(|path| file_resolver.resolve(file_id, path))
179 .collect();
180 problem = if points_to.is_empty() {
181 Some(Problem::UnresolvedModule {
182 candidate: file_mod,
183 })
184 } else {
185 None
186 }
187 } else {
188 points_to = Vec::new();
189 problem = Some(Problem::NotDirOwner {
190 move_to: RelativePathBuf::from(format!("../{}/mod.rs", mod_name)),
191 candidate: file_mod,
192 });
193 }
194 (points_to, problem)
195}
diff --git a/crates/ra_analysis/src/hir/module/mod.rs b/crates/ra_analysis/src/hir/module/mod.rs
deleted file mode 100644
index d2096b01e..000000000
--- a/crates/ra_analysis/src/hir/module/mod.rs
+++ /dev/null
@@ -1,379 +0,0 @@
1pub(super) mod imp;
2pub(super) mod nameres;
3
4use std::sync::Arc;
5
6use ra_editor::find_node_at_offset;
7
8use ra_syntax::{
9 algo::generate,
10 ast::{self, AstNode, NameOwner},
11 SmolStr, SyntaxNode,
12};
13use ra_db::SourceRootId;
14use relative_path::RelativePathBuf;
15
16use crate::{
17 FileId, FilePosition, Cancelable,
18 hir::{DefLoc, DefId, Path, PathKind, HirDatabase, SourceItemId},
19 arena::{Arena, Id},
20};
21
22pub(crate) use self::nameres::ModuleScope;
23
24/// `Module` is API entry point to get all the information
25/// about a particular module.
26#[derive(Debug, Clone)]
27pub(crate) struct Module {
28 tree: Arc<ModuleTree>,
29 source_root_id: SourceRootId,
30 module_id: ModuleId,
31}
32
33impl Module {
34 /// Lookup `Module` by `FileId`. Note that this is inherently
35 /// lossy transformation: in general, a single source might correspond to
36 /// several modules.
37 pub fn guess_from_file_id(
38 db: &impl HirDatabase,
39 file_id: FileId,
40 ) -> Cancelable<Option<Module>> {
41 Module::guess_from_source(db, file_id, ModuleSource::SourceFile(file_id))
42 }
43
44 /// Lookup `Module` by position in the source code. Note that this
45 /// is inherently lossy transformation: in general, a single source might
46 /// correspond to several modules.
47 pub fn guess_from_position(
48 db: &impl HirDatabase,
49 position: FilePosition,
50 ) -> Cancelable<Option<Module>> {
51 let file = db.source_file(position.file_id);
52 let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset)
53 {
54 Some(m) if !m.has_semi() => ModuleSource::new_inline(db, position.file_id, m),
55 _ => ModuleSource::SourceFile(position.file_id),
56 };
57 Module::guess_from_source(db, position.file_id, module_source)
58 }
59
60 fn guess_from_source(
61 db: &impl HirDatabase,
62 file_id: FileId,
63 module_source: ModuleSource,
64 ) -> Cancelable<Option<Module>> {
65 let source_root_id = db.file_source_root(file_id);
66 let module_tree = db.module_tree(source_root_id)?;
67
68 let res = match module_tree.any_module_for_source(module_source) {
69 None => None,
70 Some(module_id) => Some(Module {
71 tree: module_tree,
72 source_root_id,
73 module_id,
74 }),
75 };
76 Ok(res)
77 }
78
79 pub(super) fn new(
80 db: &impl HirDatabase,
81 source_root_id: SourceRootId,
82 module_id: ModuleId,
83 ) -> Cancelable<Module> {
84 let module_tree = db.module_tree(source_root_id)?;
85 let res = Module {
86 tree: module_tree,
87 source_root_id,
88 module_id,
89 };
90 Ok(res)
91 }
92
93 /// Returns `mod foo;` or `mod foo {}` node whihc declared this module.
94 /// Returns `None` for the root module
95 pub fn parent_link_source(&self, db: &impl HirDatabase) -> Option<(FileId, ast::ModuleNode)> {
96 let link = self.module_id.parent_link(&self.tree)?;
97 let file_id = link.owner(&self.tree).source(&self.tree).file_id();
98 let src = link.bind_source(&self.tree, db);
99 Some((file_id, src))
100 }
101
102 pub fn source(&self) -> ModuleSource {
103 self.module_id.source(&self.tree)
104 }
105
106 /// Parent module. Returns `None` if this is a root module.
107 pub fn parent(&self) -> Option<Module> {
108 let parent_id = self.module_id.parent(&self.tree)?;
109 Some(Module {
110 module_id: parent_id,
111 ..self.clone()
112 })
113 }
114
115 /// The root of the tree this module is part of
116 pub fn crate_root(&self) -> Module {
117 let root_id = self.module_id.crate_root(&self.tree);
118 Module {
119 module_id: root_id,
120 ..self.clone()
121 }
122 }
123
124 /// `name` is `None` for the crate's root module
125 #[allow(unused)]
126 pub fn name(&self) -> Option<SmolStr> {
127 let link = self.module_id.parent_link(&self.tree)?;
128 Some(link.name(&self.tree))
129 }
130
131 pub fn def_id(&self, db: &impl HirDatabase) -> DefId {
132 let def_loc = DefLoc::Module {
133 id: self.module_id,
134 source_root: self.source_root_id,
135 };
136 def_loc.id(db)
137 }
138
139 /// Finds a child module with the specified name.
140 pub fn child(&self, name: &str) -> Option<Module> {
141 let child_id = self.module_id.child(&self.tree, name)?;
142 Some(Module {
143 module_id: child_id,
144 ..self.clone()
145 })
146 }
147
148 /// Returns a `ModuleScope`: a set of items, visible in this module.
149 pub(crate) fn scope(&self, db: &impl HirDatabase) -> Cancelable<ModuleScope> {
150 let item_map = db.item_map(self.source_root_id)?;
151 let res = item_map.per_module[&self.module_id].clone();
152 Ok(res)
153 }
154
155 pub(crate) fn resolve_path(
156 &self,
157 db: &impl HirDatabase,
158 path: Path,
159 ) -> Cancelable<Option<DefId>> {
160 let mut curr = match path.kind {
161 PathKind::Crate => self.crate_root(),
162 PathKind::Self_ | PathKind::Plain => self.clone(),
163 PathKind::Super => ctry!(self.parent()),
164 }
165 .def_id(db);
166
167 let segments = path.segments;
168 for name in segments.iter() {
169 let module = match curr.loc(db) {
170 DefLoc::Module { id, source_root } => Module::new(db, source_root, id)?,
171 _ => return Ok(None),
172 };
173 let scope = module.scope(db)?;
174 curr = ctry!(ctry!(scope.get(&name)).def_id);
175 }
176 Ok(Some(curr))
177 }
178
179 pub fn problems(&self, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> {
180 self.module_id.problems(&self.tree, db)
181 }
182}
183
184/// Phisically, rust source is organized as a set of files, but logically it is
185/// organized as a tree of modules. Usually, a single file corresponds to a
186/// single module, but it is not nessary the case.
187///
188/// Module encapsulate the logic of transitioning from the fuzzy world of files
189/// (which can have multiple parents) to the precise world of modules (which
190/// always have one parent).
191#[derive(Default, Debug, PartialEq, Eq)]
192pub(crate) struct ModuleTree {
193 mods: Arena<ModuleData>,
194 links: Arena<LinkData>,
195}
196
197impl ModuleTree {
198 pub(in crate::hir) fn modules<'a>(&'a self) -> impl Iterator<Item = ModuleId> + 'a {
199 self.mods.iter().map(|(id, _)| id)
200 }
201
202 fn modules_for_source(&self, source: ModuleSource) -> Vec<ModuleId> {
203 self.mods
204 .iter()
205 .filter(|(_idx, it)| it.source == source)
206 .map(|(idx, _)| idx)
207 .collect()
208 }
209
210 fn any_module_for_source(&self, source: ModuleSource) -> Option<ModuleId> {
211 self.modules_for_source(source).pop()
212 }
213}
214
215/// `ModuleSource` is the syntax tree element that produced this module:
216/// either a file, or an inlinde module.
217#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
218pub(crate) enum ModuleSource {
219 SourceFile(FileId),
220 Module(SourceItemId),
221}
222
223/// An owned syntax node for a module. Unlike `ModuleSource`,
224/// this holds onto the AST for the whole file.
225pub(crate) enum ModuleSourceNode {
226 SourceFile(ast::SourceFileNode),
227 Module(ast::ModuleNode),
228}
229
230pub(crate) type ModuleId = Id<ModuleData>;
231type LinkId = Id<LinkData>;
232
233#[derive(Clone, Debug, Hash, PartialEq, Eq)]
234pub enum Problem {
235 UnresolvedModule {
236 candidate: RelativePathBuf,
237 },
238 NotDirOwner {
239 move_to: RelativePathBuf,
240 candidate: RelativePathBuf,
241 },
242}
243
244impl ModuleId {
245 pub(in crate::hir) fn source(self, tree: &ModuleTree) -> ModuleSource {
246 tree.mods[self].source
247 }
248 fn parent_link(self, tree: &ModuleTree) -> Option<LinkId> {
249 tree.mods[self].parent
250 }
251 fn parent(self, tree: &ModuleTree) -> Option<ModuleId> {
252 let link = self.parent_link(tree)?;
253 Some(tree.links[link].owner)
254 }
255 fn crate_root(self, tree: &ModuleTree) -> ModuleId {
256 generate(Some(self), move |it| it.parent(tree))
257 .last()
258 .unwrap()
259 }
260 fn child(self, tree: &ModuleTree, name: &str) -> Option<ModuleId> {
261 let link = tree.mods[self]
262 .children
263 .iter()
264 .map(|&it| &tree.links[it])
265 .find(|it| it.name == name)?;
266 Some(*link.points_to.first()?)
267 }
268 fn children<'a>(self, tree: &'a ModuleTree) -> impl Iterator<Item = (SmolStr, ModuleId)> + 'a {
269 tree.mods[self].children.iter().filter_map(move |&it| {
270 let link = &tree.links[it];
271 let module = *link.points_to.first()?;
272 Some((link.name.clone(), module))
273 })
274 }
275 fn problems(self, tree: &ModuleTree, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> {
276 tree.mods[self]
277 .children
278 .iter()
279 .filter_map(|&it| {
280 let p = tree.links[it].problem.clone()?;
281 let s = it.bind_source(tree, db);
282 let s = s.borrowed().name().unwrap().syntax().owned();
283 Some((s, p))
284 })
285 .collect()
286 }
287}
288
289impl LinkId {
290 fn owner(self, tree: &ModuleTree) -> ModuleId {
291 tree.links[self].owner
292 }
293 fn name(self, tree: &ModuleTree) -> SmolStr {
294 tree.links[self].name.clone()
295 }
296 fn bind_source<'a>(self, tree: &ModuleTree, db: &impl HirDatabase) -> ast::ModuleNode {
297 let owner = self.owner(tree);
298 match owner.source(tree).resolve(db) {
299 ModuleSourceNode::SourceFile(root) => {
300 let ast = imp::modules(root.borrowed())
301 .find(|(name, _)| name == &tree.links[self].name)
302 .unwrap()
303 .1;
304 ast.owned()
305 }
306 ModuleSourceNode::Module(it) => it,
307 }
308 }
309}
310
311#[derive(Debug, PartialEq, Eq, Hash)]
312pub(crate) struct ModuleData {
313 source: ModuleSource,
314 parent: Option<LinkId>,
315 children: Vec<LinkId>,
316}
317
318impl ModuleSource {
319 pub(crate) fn new_inline(
320 db: &impl HirDatabase,
321 file_id: FileId,
322 module: ast::Module,
323 ) -> ModuleSource {
324 assert!(!module.has_semi());
325 let items = db.file_items(file_id);
326 let item_id = items.id_of(module.syntax());
327 let id = SourceItemId { file_id, item_id };
328 ModuleSource::Module(id)
329 }
330
331 pub(crate) fn as_file(self) -> Option<FileId> {
332 match self {
333 ModuleSource::SourceFile(f) => Some(f),
334 ModuleSource::Module(..) => None,
335 }
336 }
337
338 pub(crate) fn file_id(self) -> FileId {
339 match self {
340 ModuleSource::SourceFile(f) => f,
341 ModuleSource::Module(source_item_id) => source_item_id.file_id,
342 }
343 }
344
345 pub(crate) fn resolve(self, db: &impl HirDatabase) -> ModuleSourceNode {
346 match self {
347 ModuleSource::SourceFile(file_id) => {
348 let syntax = db.source_file(file_id);
349 ModuleSourceNode::SourceFile(syntax.ast().owned())
350 }
351 ModuleSource::Module(item_id) => {
352 let syntax = db.file_item(item_id);
353 let syntax = syntax.borrowed();
354 let module = ast::Module::cast(syntax).unwrap();
355 ModuleSourceNode::Module(module.owned())
356 }
357 }
358 }
359}
360
361#[derive(Hash, Debug, PartialEq, Eq)]
362struct LinkData {
363 owner: ModuleId,
364 name: SmolStr,
365 points_to: Vec<ModuleId>,
366 problem: Option<Problem>,
367}
368
369impl ModuleTree {
370 fn push_mod(&mut self, data: ModuleData) -> ModuleId {
371 self.mods.alloc(data)
372 }
373 fn push_link(&mut self, data: LinkData) -> LinkId {
374 let owner = data.owner;
375 let id = self.links.alloc(data);
376 self.mods[owner].children.push(id);
377 id
378 }
379}
diff --git a/crates/ra_analysis/src/hir/module/nameres.rs b/crates/ra_analysis/src/hir/module/nameres.rs
deleted file mode 100644
index d4ecc010b..000000000
--- a/crates/ra_analysis/src/hir/module/nameres.rs
+++ /dev/null
@@ -1,446 +0,0 @@
1//! Name resolution algorithm. The end result of the algorithm is `ItemMap`: a
2//! map with maps each module to it's scope: the set of items, visible in the
3//! module. That is, we only resolve imports here, name resolution of item
4//! bodies will be done in a separate step.
5//!
6//! Like Rustc, we use an interative per-crate algorithm: we start with scopes
7//! containing only directly defined items, and then iteratively resolve
8//! imports.
9//!
10//! To make this work nicely in the IDE scenarios, we place `InputModuleItems`
11//! in between raw syntax and name resolution. `InputModuleItems` are computed
12//! using only the module's syntax, and it is all directly defined items plus
13//! imports. The plain is to make `InputModuleItems` independent of local
14//! modifications (that is, typing inside a function shold not change IMIs),
15//! such that the results of name resolution can be preserved unless the module
16//! structure itself is modified.
17use std::{
18 sync::Arc,
19};
20
21use rustc_hash::FxHashMap;
22use ra_syntax::{
23 TextRange,
24 SmolStr, SyntaxKind::{self, *},
25 ast::{self, AstNode}
26};
27use ra_db::SourceRootId;
28
29use crate::{
30 Cancelable, FileId,
31 hir::{
32 DefId, DefLoc,
33 SourceItemId, SourceFileItemId, SourceFileItems,
34 Path, PathKind,
35 HirDatabase,
36 module::{ModuleId, ModuleTree},
37 },
38};
39
40/// Item map is the result of the name resolution. Item map contains, for each
41/// module, the set of visible items.
42#[derive(Default, Debug, PartialEq, Eq)]
43pub(crate) struct ItemMap {
44 pub(crate) per_module: FxHashMap<ModuleId, ModuleScope>,
45}
46
47#[derive(Debug, Default, PartialEq, Eq, Clone)]
48pub(crate) struct ModuleScope {
49 items: FxHashMap<SmolStr, Resolution>,
50}
51
52impl ModuleScope {
53 pub(crate) fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a SmolStr, &Resolution)> + 'a {
54 self.items.iter()
55 }
56 pub(crate) fn get(&self, name: &SmolStr) -> Option<&Resolution> {
57 self.items.get(name)
58 }
59}
60
61/// A set of items and imports declared inside a module, without relation to
62/// other modules.
63///
64/// This stands in-between raw syntax and name resolution and alow us to avoid
65/// recomputing name res: if `InputModuleItems` are the same, we can avoid
66/// running name resolution.
67#[derive(Debug, Default, PartialEq, Eq)]
68pub(crate) struct InputModuleItems {
69 items: Vec<ModuleItem>,
70 imports: Vec<Import>,
71}
72
73#[derive(Debug, PartialEq, Eq)]
74struct ModuleItem {
75 id: SourceFileItemId,
76 name: SmolStr,
77 kind: SyntaxKind,
78 vis: Vis,
79}
80
81#[derive(Debug, PartialEq, Eq)]
82enum Vis {
83 // Priv,
84 Other,
85}
86
87#[derive(Debug, Clone, PartialEq, Eq)]
88struct Import {
89 path: Path,
90 kind: ImportKind,
91}
92
93#[derive(Debug, Clone, Copy, PartialEq, Eq)]
94pub(crate) struct NamedImport {
95 file_item_id: SourceFileItemId,
96 relative_range: TextRange,
97}
98
99impl NamedImport {
100 pub(crate) fn range(&self, db: &impl HirDatabase, file_id: FileId) -> TextRange {
101 let source_item_id = SourceItemId {
102 file_id,
103 item_id: self.file_item_id,
104 };
105 let syntax = db.file_item(source_item_id);
106 let offset = syntax.borrowed().range().start();
107 self.relative_range + offset
108 }
109}
110
111#[derive(Debug, Clone, PartialEq, Eq)]
112enum ImportKind {
113 Glob,
114 Named(NamedImport),
115}
116
117/// Resolution is basically `DefId` atm, but it should account for stuff like
118/// multiple namespaces, ambiguity and errors.
119#[derive(Debug, Clone, PartialEq, Eq)]
120pub(crate) struct Resolution {
121 /// None for unresolved
122 pub(crate) def_id: Option<DefId>,
123 /// ident by whitch this is imported into local scope.
124 pub(crate) import: Option<NamedImport>,
125}
126
127// #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
128// enum Namespace {
129// Types,
130// Values,
131// }
132
133// #[derive(Debug)]
134// struct PerNs<T> {
135// types: Option<T>,
136// values: Option<T>,
137// }
138
139impl InputModuleItems {
140 pub(in crate::hir) fn new<'a>(
141 file_items: &SourceFileItems,
142 items: impl Iterator<Item = ast::ModuleItem<'a>>,
143 ) -> InputModuleItems {
144 let mut res = InputModuleItems::default();
145 for item in items {
146 res.add_item(file_items, item);
147 }
148 res
149 }
150
151 fn add_item(&mut self, file_items: &SourceFileItems, item: ast::ModuleItem) -> Option<()> {
152 match item {
153 ast::ModuleItem::StructDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
154 ast::ModuleItem::EnumDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
155 ast::ModuleItem::FnDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
156 ast::ModuleItem::TraitDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
157 ast::ModuleItem::TypeDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
158 ast::ModuleItem::ImplItem(_) => {
159 // impls don't define items
160 }
161 ast::ModuleItem::UseItem(it) => self.add_use_item(file_items, it),
162 ast::ModuleItem::ExternCrateItem(_) => {
163 // TODO
164 }
165 ast::ModuleItem::ConstDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
166 ast::ModuleItem::StaticDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
167 ast::ModuleItem::Module(it) => self.items.push(ModuleItem::new(file_items, it)?),
168 }
169 Some(())
170 }
171
172 fn add_use_item(&mut self, file_items: &SourceFileItems, item: ast::UseItem) {
173 let file_item_id = file_items.id_of(item.syntax());
174 let start_offset = item.syntax().range().start();
175 Path::expand_use_item(item, |path, range| {
176 let kind = match range {
177 None => ImportKind::Glob,
178 Some(range) => ImportKind::Named(NamedImport {
179 file_item_id,
180 relative_range: range - start_offset,
181 }),
182 };
183 self.imports.push(Import { kind, path })
184 })
185 }
186}
187
188impl ModuleItem {
189 fn new<'a>(file_items: &SourceFileItems, item: impl ast::NameOwner<'a>) -> Option<ModuleItem> {
190 let name = item.name()?.text();
191 let kind = item.syntax().kind();
192 let vis = Vis::Other;
193 let id = file_items.id_of(item.syntax());
194 let res = ModuleItem {
195 id,
196 name,
197 kind,
198 vis,
199 };
200 Some(res)
201 }
202}
203
204pub(in crate::hir) struct Resolver<'a, DB> {
205 pub db: &'a DB,
206 pub input: &'a FxHashMap<ModuleId, Arc<InputModuleItems>>,
207 pub source_root: SourceRootId,
208 pub module_tree: Arc<ModuleTree>,
209 pub result: ItemMap,
210}
211
212impl<'a, DB> Resolver<'a, DB>
213where
214 DB: HirDatabase,
215{
216 pub(in crate::hir) fn resolve(mut self) -> Cancelable<ItemMap> {
217 for (&module_id, items) in self.input.iter() {
218 self.populate_module(module_id, items)
219 }
220
221 for &module_id in self.input.keys() {
222 self.db.check_canceled()?;
223 self.resolve_imports(module_id);
224 }
225 Ok(self.result)
226 }
227
228 fn populate_module(&mut self, module_id: ModuleId, input: &InputModuleItems) {
229 let file_id = module_id.source(&self.module_tree).file_id();
230
231 let mut module_items = ModuleScope::default();
232
233 for import in input.imports.iter() {
234 if let Some(name) = import.path.segments.iter().last() {
235 if let ImportKind::Named(import) = import.kind {
236 module_items.items.insert(
237 name.clone(),
238 Resolution {
239 def_id: None,
240 import: Some(import),
241 },
242 );
243 }
244 }
245 }
246
247 for item in input.items.iter() {
248 if item.kind == MODULE {
249 // handle submodules separatelly
250 continue;
251 }
252 let def_loc = DefLoc::Item {
253 source_item_id: SourceItemId {
254 file_id,
255 item_id: item.id,
256 },
257 };
258 let def_id = def_loc.id(self.db);
259 let resolution = Resolution {
260 def_id: Some(def_id),
261 import: None,
262 };
263 module_items.items.insert(item.name.clone(), resolution);
264 }
265
266 for (name, mod_id) in module_id.children(&self.module_tree) {
267 let def_loc = DefLoc::Module {
268 id: mod_id,
269 source_root: self.source_root,
270 };
271 let def_id = def_loc.id(self.db);
272 let resolution = Resolution {
273 def_id: Some(def_id),
274 import: None,
275 };
276 module_items.items.insert(name, resolution);
277 }
278
279 self.result.per_module.insert(module_id, module_items);
280 }
281
282 fn resolve_imports(&mut self, module_id: ModuleId) {
283 for import in self.input[&module_id].imports.iter() {
284 self.resolve_import(module_id, import);
285 }
286 }
287
288 fn resolve_import(&mut self, module_id: ModuleId, import: &Import) {
289 let ptr = match import.kind {
290 ImportKind::Glob => return,
291 ImportKind::Named(ptr) => ptr,
292 };
293
294 let mut curr = match import.path.kind {
295 // TODO: handle extern crates
296 PathKind::Plain => return,
297 PathKind::Self_ => module_id,
298 PathKind::Super => {
299 match module_id.parent(&self.module_tree) {
300 Some(it) => it,
301 // TODO: error
302 None => return,
303 }
304 }
305 PathKind::Crate => module_id.crate_root(&self.module_tree),
306 };
307
308 for (i, name) in import.path.segments.iter().enumerate() {
309 let is_last = i == import.path.segments.len() - 1;
310
311 let def_id = match self.result.per_module[&curr].items.get(name) {
312 None => return,
313 Some(res) => match res.def_id {
314 Some(it) => it,
315 None => return,
316 },
317 };
318
319 if !is_last {
320 curr = match def_id.loc(self.db) {
321 DefLoc::Module { id, .. } => id,
322 _ => return,
323 }
324 } else {
325 self.update(module_id, |items| {
326 let res = Resolution {
327 def_id: Some(def_id),
328 import: Some(ptr),
329 };
330 items.items.insert(name.clone(), res);
331 })
332 }
333 }
334 }
335
336 fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleScope)) {
337 let module_items = self.result.per_module.get_mut(&module_id).unwrap();
338 f(module_items)
339 }
340}
341
342#[cfg(test)]
343mod tests {
344 use ra_db::FilesDatabase;
345 use crate::{
346 AnalysisChange,
347 mock_analysis::{MockAnalysis, analysis_and_position},
348 hir::{self, HirDatabase},
349};
350 use super::*;
351
352 fn item_map(fixture: &str) -> (Arc<ItemMap>, ModuleId) {
353 let (analysis, pos) = analysis_and_position(fixture);
354 let db = analysis.imp.db;
355 let source_root = db.file_source_root(pos.file_id);
356 let descr = hir::Module::guess_from_position(&*db, pos)
357 .unwrap()
358 .unwrap();
359 let module_id = descr.module_id;
360 (db.item_map(source_root).unwrap(), module_id)
361 }
362
363 #[test]
364 fn test_item_map() {
365 let (item_map, module_id) = item_map(
366 "
367 //- /lib.rs
368 mod foo;
369
370 use crate::foo::bar::Baz;
371 <|>
372
373 //- /foo/mod.rs
374 pub mod bar;
375
376 //- /foo/bar.rs
377 pub struct Baz;
378 ",
379 );
380 let name = SmolStr::from("Baz");
381 let resolution = &item_map.per_module[&module_id].items[&name];
382 assert!(resolution.def_id.is_some());
383 }
384
385 #[test]
386 fn typing_inside_a_function_should_not_invalidate_item_map() {
387 let mock_analysis = MockAnalysis::with_files(
388 "
389 //- /lib.rs
390 mod foo;
391
392 use crate::foo::bar::Baz;
393
394 fn foo() -> i32 {
395 1 + 1
396 }
397 //- /foo/mod.rs
398 pub mod bar;
399
400 //- /foo/bar.rs
401 pub struct Baz;
402 ",
403 );
404
405 let file_id = mock_analysis.id_of("/lib.rs");
406 let mut host = mock_analysis.analysis_host();
407
408 let source_root = host.analysis().imp.db.file_source_root(file_id);
409
410 {
411 let db = host.analysis().imp.db;
412 let events = db.log_executed(|| {
413 db.item_map(source_root).unwrap();
414 });
415 assert!(format!("{:?}", events).contains("item_map"))
416 }
417
418 let mut change = AnalysisChange::new();
419
420 change.change_file(
421 file_id,
422 "
423 mod foo;
424
425 use crate::foo::bar::Baz;
426
427 fn foo() -> i32 { 92 }
428 "
429 .to_string(),
430 );
431
432 host.apply_change(change);
433
434 {
435 let db = host.analysis().imp.db;
436 let events = db.log_executed(|| {
437 db.item_map(source_root).unwrap();
438 });
439 assert!(
440 !format!("{:?}", events).contains("_item_map"),
441 "{:#?}",
442 events
443 )
444 }
445 }
446}