aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2018-11-21 11:57:32 +0000
committerbors[bot] <bors[bot]@users.noreply.github.com>2018-11-21 11:57:32 +0000
commit031bc868293539714157e3d93cc338b011f5661d (patch)
tree624843cfddada855a6a80380e817bb188596c441
parent713c3ea30b38199e05e1f70094d85177f16915f8 (diff)
parent5a61b218f8869532ccb6be9dccff9e7d8641bf49 (diff)
Merge #236
236: WIP: Module name resolution r=matklad a=matklad work towards #231 Co-authored-by: Aleksey Kladov <[email protected]>
-rw-r--r--.travis.yml2
-rw-r--r--crates/ra_analysis/src/completion/mod.rs10
-rw-r--r--crates/ra_analysis/src/completion/reference_completion.rs26
-rw-r--r--crates/ra_analysis/src/db.rs7
-rw-r--r--crates/ra_analysis/src/descriptors/mod.rs14
-rw-r--r--crates/ra_analysis/src/descriptors/module/imp.rs21
-rw-r--r--crates/ra_analysis/src/descriptors/module/mod.rs37
-rw-r--r--crates/ra_analysis/src/descriptors/module/nameres.rs453
-rw-r--r--crates/ra_analysis/src/descriptors/module/scope.rs124
-rw-r--r--crates/ra_analysis/src/loc2id.rs25
-rw-r--r--crates/ra_analysis/tests/tests.rs8
-rw-r--r--crates/ra_syntax/src/ast/mod.rs6
-rw-r--r--crates/tools/src/lib.rs2
13 files changed, 556 insertions, 179 deletions
diff --git a/.travis.yml b/.travis.yml
index 36fd746dd..012453497 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -9,7 +9,7 @@ env:
9 9
10build: &rust_build 10build: &rust_build
11 language: rust 11 language: rust
12 rust: beta 12 rust: beta-2018-11-19
13 script: 13 script:
14 - cargo gen-tests --verify 14 - cargo gen-tests --verify
15 - cargo gen-syntax --verify 15 - cargo gen-syntax --verify
diff --git a/crates/ra_analysis/src/completion/mod.rs b/crates/ra_analysis/src/completion/mod.rs
index 5e3ee79dd..a8a752fc7 100644
--- a/crates/ra_analysis/src/completion/mod.rs
+++ b/crates/ra_analysis/src/completion/mod.rs
@@ -204,9 +204,9 @@ mod tests {
204 <|> 204 <|>
205 } 205 }
206 ", 206 ",
207 r#"[CompletionItem { label: "Foo", lookup: None, snippet: None }, 207 r#"[CompletionItem { label: "quux", lookup: None, snippet: None },
208 CompletionItem { label: "Baz", lookup: None, snippet: None }, 208 CompletionItem { label: "Foo", lookup: None, snippet: None },
209 CompletionItem { label: "quux", lookup: None, snippet: None }]"#, 209 CompletionItem { label: "Baz", lookup: None, snippet: None }]"#,
210 ); 210 );
211 } 211 }
212 212
@@ -230,8 +230,8 @@ mod tests {
230 fn quux() { <|> } 230 fn quux() { <|> }
231 } 231 }
232 ", 232 ",
233 r#"[CompletionItem { label: "Bar", lookup: None, snippet: None }, 233 r#"[CompletionItem { label: "quux", lookup: None, snippet: None },
234 CompletionItem { label: "quux", lookup: None, snippet: None }]"#, 234 CompletionItem { label: "Bar", lookup: None, snippet: None }]"#,
235 ); 235 );
236 } 236 }
237 237
diff --git a/crates/ra_analysis/src/completion/reference_completion.rs b/crates/ra_analysis/src/completion/reference_completion.rs
index c94d9af75..84383b547 100644
--- a/crates/ra_analysis/src/completion/reference_completion.rs
+++ b/crates/ra_analysis/src/completion/reference_completion.rs
@@ -39,14 +39,17 @@ pub(super) fn completions(
39 let module_scope = module.scope(db)?; 39 let module_scope = module.scope(db)?;
40 acc.extend( 40 acc.extend(
41 module_scope 41 module_scope
42 .entries() 42 .items
43 .iter() 43 .iter()
44 .filter(|entry| { 44 .filter(|(_name, res)| {
45 // Don't expose this item 45 // Don't expose this item
46 !entry.ptr().range().is_subrange(&name_ref.syntax().range()) 46 match res.import_name {
47 None => true,
48 Some(ptr) => !ptr.range().is_subrange(&name_ref.syntax().range()),
49 }
47 }) 50 })
48 .map(|entry| CompletionItem { 51 .map(|(name, _res)| CompletionItem {
49 label: entry.name().to_string(), 52 label: name.to_string(),
50 lookup: None, 53 lookup: None,
51 snippet: None, 54 snippet: None,
52 }), 55 }),
@@ -173,11 +176,14 @@ fn complete_path(
173 Some(it) => it, 176 Some(it) => it,
174 }; 177 };
175 let module_scope = target_module.scope(db)?; 178 let module_scope = target_module.scope(db)?;
176 let completions = module_scope.entries().iter().map(|entry| CompletionItem { 179 let completions = module_scope
177 label: entry.name().to_string(), 180 .items
178 lookup: None, 181 .iter()
179 snippet: None, 182 .map(|(name, _res)| CompletionItem {
180 }); 183 label: name.to_string(),
184 lookup: None,
185 snippet: None,
186 });
181 acc.extend(completions); 187 acc.extend(completions);
182 Ok(()) 188 Ok(())
183} 189}
diff --git a/crates/ra_analysis/src/db.rs b/crates/ra_analysis/src/db.rs
index 8133b7875..887d687ea 100644
--- a/crates/ra_analysis/src/db.rs
+++ b/crates/ra_analysis/src/db.rs
@@ -7,8 +7,8 @@ use salsa::{self, Database};
7use crate::{ 7use crate::{
8 db, 8 db,
9 descriptors::{ 9 descriptors::{
10 DescriptorDatabase, FnScopesQuery, FnSyntaxQuery, ModuleScopeQuery, ModuleTreeQuery, 10 DescriptorDatabase, FnScopesQuery, FnSyntaxQuery, ModuleTreeQuery,
11 SubmodulesQuery, 11 SubmodulesQuery, ItemMapQuery, InputModuleItemsQuery,
12 }, 12 },
13 symbol_index::SymbolIndex, 13 symbol_index::SymbolIndex,
14 syntax_ptr::SyntaxPtr, 14 syntax_ptr::SyntaxPtr,
@@ -85,8 +85,9 @@ salsa::database_storage! {
85 } 85 }
86 impl DescriptorDatabase { 86 impl DescriptorDatabase {
87 fn module_tree() for ModuleTreeQuery; 87 fn module_tree() for ModuleTreeQuery;
88 fn module_scope() for ModuleScopeQuery;
89 fn fn_scopes() for FnScopesQuery; 88 fn fn_scopes() for FnScopesQuery;
89 fn _input_module_items() for InputModuleItemsQuery;
90 fn _item_map() for ItemMapQuery;
90 fn _fn_syntax() for FnSyntaxQuery; 91 fn _fn_syntax() for FnSyntaxQuery;
91 fn _submodules() for SubmodulesQuery; 92 fn _submodules() for SubmodulesQuery;
92 } 93 }
diff --git a/crates/ra_analysis/src/descriptors/mod.rs b/crates/ra_analysis/src/descriptors/mod.rs
index a8489f89c..6b56d92e1 100644
--- a/crates/ra_analysis/src/descriptors/mod.rs
+++ b/crates/ra_analysis/src/descriptors/mod.rs
@@ -11,7 +11,7 @@ use ra_syntax::{
11use crate::{ 11use crate::{
12 db::SyntaxDatabase, 12 db::SyntaxDatabase,
13 descriptors::function::{resolve_local_name, FnId, FnScopes}, 13 descriptors::function::{resolve_local_name, FnId, FnScopes},
14 descriptors::module::{ModuleId, ModuleScope, ModuleTree, ModuleSource}, 14 descriptors::module::{ModuleId, ModuleTree, ModuleSource, nameres::{ItemMap, InputModuleItems}},
15 input::SourceRootId, 15 input::SourceRootId,
16 loc2id::IdDatabase, 16 loc2id::IdDatabase,
17 syntax_ptr::LocalSyntaxPtr, 17 syntax_ptr::LocalSyntaxPtr,
@@ -25,14 +25,18 @@ salsa::query_group! {
25 use fn function::imp::fn_scopes; 25 use fn function::imp::fn_scopes;
26 } 26 }
27 27
28 fn _input_module_items(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable<Arc<InputModuleItems>> {
29 type InputModuleItemsQuery;
30 use fn module::nameres::input_module_items;
31 }
32 fn _item_map(source_root_id: SourceRootId) -> Cancelable<Arc<ItemMap>> {
33 type ItemMapQuery;
34 use fn module::nameres::item_map;
35 }
28 fn _module_tree(source_root_id: SourceRootId) -> Cancelable<Arc<ModuleTree>> { 36 fn _module_tree(source_root_id: SourceRootId) -> Cancelable<Arc<ModuleTree>> {
29 type ModuleTreeQuery; 37 type ModuleTreeQuery;
30 use fn module::imp::module_tree; 38 use fn module::imp::module_tree;
31 } 39 }
32 fn _module_scope(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable<Arc<ModuleScope>> {
33 type ModuleScopeQuery;
34 use fn module::imp::module_scope;
35 }
36 fn _fn_syntax(fn_id: FnId) -> FnDefNode { 40 fn _fn_syntax(fn_id: FnId) -> FnDefNode {
37 type FnSyntaxQuery; 41 type FnSyntaxQuery;
38 // Don't retain syntax trees in memory 42 // Don't retain syntax trees in memory
diff --git a/crates/ra_analysis/src/descriptors/module/imp.rs b/crates/ra_analysis/src/descriptors/module/imp.rs
index defe87216..d4dce861f 100644
--- a/crates/ra_analysis/src/descriptors/module/imp.rs
+++ b/crates/ra_analysis/src/descriptors/module/imp.rs
@@ -1,7 +1,7 @@
1use std::sync::Arc; 1use std::sync::Arc;
2 2
3use ra_syntax::{ 3use ra_syntax::{
4 ast::{self, ModuleItemOwner, NameOwner}, 4 ast::{self, NameOwner},
5 SmolStr, 5 SmolStr,
6}; 6};
7use relative_path::RelativePathBuf; 7use relative_path::RelativePathBuf;
@@ -15,7 +15,7 @@ use crate::{
15}; 15};
16 16
17use super::{ 17use super::{
18 LinkData, LinkId, ModuleData, ModuleId, ModuleScope, ModuleSource, ModuleSourceNode, 18 LinkData, LinkId, ModuleData, ModuleId, ModuleSource, ModuleSourceNode,
19 ModuleTree, Problem, 19 ModuleTree, Problem,
20}; 20};
21 21
@@ -81,23 +81,6 @@ pub(crate) fn modules<'a>(
81 }) 81 })
82} 82}
83 83
84pub(crate) fn module_scope(
85 db: &impl DescriptorDatabase,
86 source_root_id: SourceRootId,
87 module_id: ModuleId,
88) -> Cancelable<Arc<ModuleScope>> {
89 let tree = db._module_tree(source_root_id)?;
90 let source = module_id.source(&tree).resolve(db);
91 let res = match source {
92 ModuleSourceNode::SourceFile(it) => ModuleScope::new(it.borrowed().items()),
93 ModuleSourceNode::Module(it) => match it.borrowed().item_list() {
94 Some(items) => ModuleScope::new(items.items()),
95 None => ModuleScope::new(std::iter::empty()),
96 },
97 };
98 Ok(Arc::new(res))
99}
100
101pub(crate) fn module_tree( 84pub(crate) fn module_tree(
102 db: &impl DescriptorDatabase, 85 db: &impl DescriptorDatabase,
103 source_root: SourceRootId, 86 source_root: SourceRootId,
diff --git a/crates/ra_analysis/src/descriptors/module/mod.rs b/crates/ra_analysis/src/descriptors/module/mod.rs
index 047454cff..cfdffcdbc 100644
--- a/crates/ra_analysis/src/descriptors/module/mod.rs
+++ b/crates/ra_analysis/src/descriptors/module/mod.rs
@@ -1,5 +1,5 @@
1pub(super) mod imp; 1pub(super) mod imp;
2pub(crate) mod scope; 2pub(super) mod nameres;
3 3
4use std::sync::Arc; 4use std::sync::Arc;
5 5
@@ -18,7 +18,7 @@ use crate::{
18 input::SourceRootId 18 input::SourceRootId
19}; 19};
20 20
21pub(crate) use self::scope::ModuleScope; 21pub(crate) use self::{nameres::ModuleScope};
22 22
23/// `ModuleDescriptor` is API entry point to get all the information 23/// `ModuleDescriptor` is API entry point to get all the information
24/// about a particular module. 24/// about a particular module.
@@ -102,9 +102,11 @@ impl ModuleDescriptor {
102 102
103 /// The root of the tree this module is part of 103 /// The root of the tree this module is part of
104 pub fn crate_root(&self) -> ModuleDescriptor { 104 pub fn crate_root(&self) -> ModuleDescriptor {
105 generate(Some(self.clone()), |it| it.parent()) 105 let root_id = self.module_id.crate_root(&self.tree);
106 .last() 106 ModuleDescriptor {
107 .unwrap() 107 module_id: root_id,
108 ..self.clone()
109 }
108 } 110 }
109 111
110 /// `name` is `None` for the crate's root module 112 /// `name` is `None` for the crate's root module
@@ -123,8 +125,10 @@ impl ModuleDescriptor {
123 } 125 }
124 126
125 /// Returns a `ModuleScope`: a set of items, visible in this module. 127 /// Returns a `ModuleScope`: a set of items, visible in this module.
126 pub fn scope(&self, db: &impl DescriptorDatabase) -> Cancelable<Arc<ModuleScope>> { 128 pub(crate) fn scope(&self, db: &impl DescriptorDatabase) -> Cancelable<ModuleScope> {
127 db._module_scope(self.source_root_id, self.module_id) 129 let item_map = db._item_map(self.source_root_id)?;
130 let res = item_map.per_module[&self.module_id].clone();
131 Ok(res)
128 } 132 }
129 133
130 pub fn problems(&self, db: &impl DescriptorDatabase) -> Vec<(SyntaxNode, Problem)> { 134 pub fn problems(&self, db: &impl DescriptorDatabase) -> Vec<(SyntaxNode, Problem)> {
@@ -146,6 +150,13 @@ pub(crate) struct ModuleTree {
146} 150}
147 151
148impl ModuleTree { 152impl ModuleTree {
153 fn modules<'a>(&'a self) -> impl Iterator<Item = ModuleId> + 'a {
154 self.mods
155 .iter()
156 .enumerate()
157 .map(|(idx, _)| ModuleId(idx as u32))
158 }
159
149 fn modules_for_source(&self, source: ModuleSource) -> Vec<ModuleId> { 160 fn modules_for_source(&self, source: ModuleSource) -> Vec<ModuleId> {
150 self.mods 161 self.mods
151 .iter() 162 .iter()
@@ -204,6 +215,11 @@ impl ModuleId {
204 let link = self.parent_link(tree)?; 215 let link = self.parent_link(tree)?;
205 Some(tree.link(link).owner) 216 Some(tree.link(link).owner)
206 } 217 }
218 fn crate_root(self, tree: &ModuleTree) -> ModuleId {
219 generate(Some(self), move |it| it.parent(tree))
220 .last()
221 .unwrap()
222 }
207 fn child(self, tree: &ModuleTree, name: &str) -> Option<ModuleId> { 223 fn child(self, tree: &ModuleTree, name: &str) -> Option<ModuleId> {
208 let link = tree 224 let link = tree
209 .module(self) 225 .module(self)
@@ -213,6 +229,13 @@ impl ModuleId {
213 .find(|it| it.name == name)?; 229 .find(|it| it.name == name)?;
214 Some(*link.points_to.first()?) 230 Some(*link.points_to.first()?)
215 } 231 }
232 fn children<'a>(self, tree: &'a ModuleTree) -> impl Iterator<Item = (SmolStr, ModuleId)> + 'a {
233 tree.module(self).children.iter().filter_map(move |&it| {
234 let link = tree.link(it);
235 let module = *link.points_to.first()?;
236 Some((link.name.clone(), module))
237 })
238 }
216 fn problems(self, tree: &ModuleTree, db: &impl SyntaxDatabase) -> Vec<(SyntaxNode, Problem)> { 239 fn problems(self, tree: &ModuleTree, db: &impl SyntaxDatabase) -> Vec<(SyntaxNode, Problem)> {
217 tree.module(self) 240 tree.module(self)
218 .children 241 .children
diff --git a/crates/ra_analysis/src/descriptors/module/nameres.rs b/crates/ra_analysis/src/descriptors/module/nameres.rs
new file mode 100644
index 000000000..c5bf467ca
--- /dev/null
+++ b/crates/ra_analysis/src/descriptors/module/nameres.rs
@@ -0,0 +1,453 @@
1//! Name resolution algorithm
2use std::{
3 sync::Arc,
4 time::Instant,
5};
6
7use rustc_hash::FxHashMap;
8
9use ra_syntax::{
10 SmolStr, SyntaxKind::{self, *},
11 ast::{self, AstNode, ModuleItemOwner}
12};
13
14use crate::{
15 Cancelable,
16 loc2id::{DefId, DefLoc},
17 descriptors::{
18 DescriptorDatabase,
19 module::{ModuleId, ModuleTree, ModuleSourceNode},
20 },
21 syntax_ptr::{LocalSyntaxPtr},
22 input::SourceRootId,
23};
24
25/// Item map is the result of the name resolution. Item map contains, for each
26/// module, the set of visible items.
27#[derive(Default, Debug, PartialEq, Eq)]
28pub(crate) struct ItemMap {
29 pub(crate) per_module: FxHashMap<ModuleId, ModuleScope>,
30}
31
32#[derive(Debug, Default, PartialEq, Eq, Clone)]
33pub(crate) struct ModuleScope {
34 pub(crate) items: FxHashMap<SmolStr, Resolution>,
35 pub(crate) import_resolutions: FxHashMap<LocalSyntaxPtr, DefId>,
36}
37
38/// A set of items and imports declared inside a module, without relation to
39/// other modules.
40///
41/// This stands in-between raw syntax and name resolution and alow us to avoid
42/// recomputing name res: if `InputModuleItems` are the same, we can avoid
43/// running name resolution.
44#[derive(Debug, Default, PartialEq, Eq)]
45pub(crate) struct InputModuleItems {
46 items: Vec<ModuleItem>,
47 glob_imports: Vec<Path>,
48 imports: Vec<Path>,
49}
50
51#[derive(Debug, Clone, PartialEq, Eq)]
52struct Path {
53 kind: PathKind,
54 segments: Vec<(LocalSyntaxPtr, SmolStr)>,
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58enum PathKind {
59 Abs,
60 Self_,
61 Super,
62 Crate,
63}
64
65pub(crate) fn input_module_items(
66 db: &impl DescriptorDatabase,
67 source_root: SourceRootId,
68 module_id: ModuleId,
69) -> Cancelable<Arc<InputModuleItems>> {
70 let module_tree = db._module_tree(source_root)?;
71 let source = module_id.source(&module_tree);
72 let res = match source.resolve(db) {
73 ModuleSourceNode::SourceFile(it) => {
74 let items = it.borrowed().items();
75 InputModuleItems::new(items)
76 }
77 ModuleSourceNode::Module(it) => {
78 let items = it
79 .borrowed()
80 .item_list()
81 .into_iter()
82 .flat_map(|it| it.items());
83 InputModuleItems::new(items)
84 }
85 };
86 Ok(Arc::new(res))
87}
88
89pub(crate) fn item_map(
90 db: &impl DescriptorDatabase,
91 source_root: SourceRootId,
92) -> Cancelable<Arc<ItemMap>> {
93 let start = Instant::now();
94 let module_tree = db._module_tree(source_root)?;
95 let input = module_tree
96 .modules()
97 .map(|id| {
98 let items = db._input_module_items(source_root, id)?;
99 Ok((id, items))
100 })
101 .collect::<Cancelable<FxHashMap<_, _>>>()?;
102
103 let mut resolver = Resolver {
104 db: db,
105 input: &input,
106 source_root,
107 module_tree,
108 result: ItemMap::default(),
109 };
110 resolver.resolve()?;
111 let res = resolver.result;
112 let elapsed = start.elapsed();
113 log::info!("item_map: {:?}", elapsed);
114 Ok(Arc::new(res))
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 /// TODO: make this offset-independent.
125 pub(crate) import_name: Option<LocalSyntaxPtr>,
126}
127
128// #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
129// enum Namespace {
130// Types,
131// Values,
132// }
133
134// #[derive(Debug)]
135// struct PerNs<T> {
136// types: Option<T>,
137// values: Option<T>,
138// }
139
140#[derive(Debug, PartialEq, Eq)]
141struct ModuleItem {
142 ptr: LocalSyntaxPtr,
143 name: SmolStr,
144 kind: SyntaxKind,
145 vis: Vis,
146}
147
148#[derive(Debug, PartialEq, Eq)]
149enum Vis {
150 // Priv,
151 Other,
152}
153
154impl InputModuleItems {
155 fn new<'a>(items: impl Iterator<Item = ast::ModuleItem<'a>>) -> InputModuleItems {
156 let mut res = InputModuleItems::default();
157 for item in items {
158 res.add_item(item);
159 }
160 res
161 }
162
163 fn add_item(&mut self, item: ast::ModuleItem) -> Option<()> {
164 match item {
165 ast::ModuleItem::StructDef(it) => self.items.push(ModuleItem::new(it)?),
166 ast::ModuleItem::EnumDef(it) => self.items.push(ModuleItem::new(it)?),
167 ast::ModuleItem::FnDef(it) => self.items.push(ModuleItem::new(it)?),
168 ast::ModuleItem::TraitDef(it) => self.items.push(ModuleItem::new(it)?),
169 ast::ModuleItem::TypeDef(it) => self.items.push(ModuleItem::new(it)?),
170 ast::ModuleItem::ImplItem(_) => {
171 // impls don't define items
172 }
173 ast::ModuleItem::UseItem(it) => self.add_use_item(it),
174 ast::ModuleItem::ExternCrateItem(_) => {
175 // TODO
176 }
177 ast::ModuleItem::ConstDef(it) => self.items.push(ModuleItem::new(it)?),
178 ast::ModuleItem::StaticDef(it) => self.items.push(ModuleItem::new(it)?),
179 ast::ModuleItem::Module(it) => self.items.push(ModuleItem::new(it)?),
180 }
181 Some(())
182 }
183
184 fn add_use_item(&mut self, item: ast::UseItem) {
185 if let Some(tree) = item.use_tree() {
186 self.add_use_tree(None, tree);
187 }
188 }
189
190 fn add_use_tree(&mut self, prefix: Option<Path>, tree: ast::UseTree) {
191 if let Some(use_tree_list) = tree.use_tree_list() {
192 let prefix = match tree.path() {
193 None => prefix,
194 Some(path) => match convert_path(prefix, path) {
195 Some(it) => Some(it),
196 None => return, // TODO: report errors somewhere
197 },
198 };
199 for tree in use_tree_list.use_trees() {
200 self.add_use_tree(prefix.clone(), tree);
201 }
202 } else {
203 if let Some(path) = tree.path() {
204 if let Some(path) = convert_path(prefix, path) {
205 if tree.has_star() {
206 &mut self.glob_imports
207 } else {
208 &mut self.imports
209 }
210 .push(path);
211 }
212 }
213 }
214 }
215}
216
217fn convert_path(prefix: Option<Path>, path: ast::Path) -> Option<Path> {
218 let prefix = if let Some(qual) = path.qualifier() {
219 Some(convert_path(prefix, qual)?)
220 } else {
221 None
222 };
223 let segment = path.segment()?;
224 let res = match segment.kind()? {
225 ast::PathSegmentKind::Name(name) => {
226 let mut res = prefix.unwrap_or_else(|| Path {
227 kind: PathKind::Abs,
228 segments: Vec::with_capacity(1),
229 });
230 let ptr = LocalSyntaxPtr::new(name.syntax());
231 res.segments.push((ptr, name.text()));
232 res
233 }
234 ast::PathSegmentKind::CrateKw => {
235 if prefix.is_some() {
236 return None;
237 }
238 Path {
239 kind: PathKind::Crate,
240 segments: Vec::new(),
241 }
242 }
243 ast::PathSegmentKind::SelfKw => {
244 if prefix.is_some() {
245 return None;
246 }
247 Path {
248 kind: PathKind::Self_,
249 segments: Vec::new(),
250 }
251 }
252 ast::PathSegmentKind::SuperKw => {
253 if prefix.is_some() {
254 return None;
255 }
256 Path {
257 kind: PathKind::Super,
258 segments: Vec::new(),
259 }
260 }
261 };
262 Some(res)
263}
264
265impl ModuleItem {
266 fn new<'a>(item: impl ast::NameOwner<'a>) -> Option<ModuleItem> {
267 let name = item.name()?.text();
268 let ptr = LocalSyntaxPtr::new(item.syntax());
269 let kind = item.syntax().kind();
270 let vis = Vis::Other;
271 let res = ModuleItem {
272 ptr,
273 name,
274 kind,
275 vis,
276 };
277 Some(res)
278 }
279}
280
281struct Resolver<'a, DB> {
282 db: &'a DB,
283 input: &'a FxHashMap<ModuleId, Arc<InputModuleItems>>,
284 source_root: SourceRootId,
285 module_tree: Arc<ModuleTree>,
286 result: ItemMap,
287}
288
289impl<'a, DB> Resolver<'a, DB>
290where
291 DB: DescriptorDatabase,
292{
293 fn resolve(&mut self) -> Cancelable<()> {
294 for (&module_id, items) in self.input.iter() {
295 self.populate_module(module_id, items)
296 }
297
298 for &module_id in self.input.keys() {
299 crate::db::check_canceled(self.db)?;
300 self.resolve_imports(module_id);
301 }
302 Ok(())
303 }
304
305 fn populate_module(&mut self, module_id: ModuleId, input: &InputModuleItems) {
306 let file_id = module_id.source(&self.module_tree).file_id();
307
308 let mut module_items = ModuleScope::default();
309
310 for import in input.imports.iter() {
311 if let Some((ptr, name)) = import.segments.last() {
312 module_items.items.insert(
313 name.clone(),
314 Resolution {
315 def_id: None,
316 import_name: Some(*ptr),
317 },
318 );
319 }
320 }
321
322 for item in input.items.iter() {
323 if item.kind == MODULE {
324 // handle submodules separatelly
325 continue;
326 }
327 let ptr = item.ptr.into_global(file_id);
328 let def_loc = DefLoc::Item { ptr };
329 let def_id = self.db.id_maps().def_id(def_loc);
330 let resolution = Resolution {
331 def_id: Some(def_id),
332 import_name: None,
333 };
334 module_items.items.insert(item.name.clone(), resolution);
335 }
336
337 for (name, mod_id) in module_id.children(&self.module_tree) {
338 let def_loc = DefLoc::Module {
339 id: mod_id,
340 source_root: self.source_root,
341 };
342 let def_id = self.db.id_maps().def_id(def_loc);
343 let resolution = Resolution {
344 def_id: Some(def_id),
345 import_name: None,
346 };
347 module_items.items.insert(name, resolution);
348 }
349
350 self.result.per_module.insert(module_id, module_items);
351 }
352
353 fn resolve_imports(&mut self, module_id: ModuleId) {
354 for import in self.input[&module_id].imports.iter() {
355 self.resolve_import(module_id, import);
356 }
357 }
358
359 fn resolve_import(&mut self, module_id: ModuleId, import: &Path) {
360 let mut curr = match import.kind {
361 // TODO: handle extern crates
362 PathKind::Abs => return,
363 PathKind::Self_ => module_id,
364 PathKind::Super => {
365 match module_id.parent(&self.module_tree) {
366 Some(it) => it,
367 // TODO: error
368 None => return,
369 }
370 }
371 PathKind::Crate => module_id.crate_root(&self.module_tree),
372 };
373
374 for (i, (ptr, name)) in import.segments.iter().enumerate() {
375 let is_last = i == import.segments.len() - 1;
376
377 let def_id = match self.result.per_module[&curr].items.get(name) {
378 None => return,
379 Some(res) => match res.def_id {
380 Some(it) => it,
381 None => return,
382 },
383 };
384
385 self.update(module_id, |items| {
386 items.import_resolutions.insert(*ptr, def_id);
387 });
388
389 if !is_last {
390 curr = match self.db.id_maps().def_loc(def_id) {
391 DefLoc::Module { id, .. } => id,
392 _ => return,
393 }
394 } else {
395 self.update(module_id, |items| {
396 let res = Resolution {
397 def_id: Some(def_id),
398 import_name: Some(*ptr),
399 };
400 items.items.insert(name.clone(), res);
401 })
402 }
403 }
404 }
405
406 fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleScope)) {
407 let module_items = self.result.per_module.get_mut(&module_id).unwrap();
408 f(module_items)
409 }
410}
411
412#[cfg(test)]
413mod tests {
414 use crate::{
415 mock_analysis::analysis_and_position,
416 descriptors::{DescriptorDatabase, module::ModuleDescriptor},
417 input::FilesDatabase,
418};
419 use super::*;
420
421 fn item_map(fixture: &str) -> (Arc<ItemMap>, ModuleId) {
422 let (analysis, pos) = analysis_and_position(fixture);
423 let db = analysis.imp.db;
424 let source_root = db.file_source_root(pos.file_id);
425 let descr = ModuleDescriptor::guess_from_position(&*db, pos)
426 .unwrap()
427 .unwrap();
428 let module_id = descr.module_id;
429 (db._item_map(source_root).unwrap(), module_id)
430 }
431
432 #[test]
433 fn test_item_map() {
434 let (item_map, module_id) = item_map(
435 "
436 //- /lib.rs
437 mod foo;
438
439 use crate::foo::bar::Baz;
440 <|>
441
442 //- /foo/mod.rs
443 pub mod bar;
444
445 //- /foo/bar.rs
446 pub struct Baz;
447 ",
448 );
449 let name = SmolStr::from("Baz");
450 let resolution = &item_map.per_module[&module_id].items[&name];
451 assert!(resolution.def_id.is_some());
452 }
453}
diff --git a/crates/ra_analysis/src/descriptors/module/scope.rs b/crates/ra_analysis/src/descriptors/module/scope.rs
deleted file mode 100644
index 4490228e4..000000000
--- a/crates/ra_analysis/src/descriptors/module/scope.rs
+++ /dev/null
@@ -1,124 +0,0 @@
1//! Backend for module-level scope resolution & completion
2
3use ra_syntax::{ast, AstNode, SmolStr};
4
5use crate::syntax_ptr::LocalSyntaxPtr;
6
7/// `ModuleScope` contains all named items declared in the scope.
8#[derive(Debug, PartialEq, Eq)]
9pub(crate) struct ModuleScope {
10 entries: Vec<Entry>,
11}
12
13/// `Entry` is a single named declaration iside a module.
14#[derive(Debug, PartialEq, Eq)]
15pub(crate) struct Entry {
16 ptr: LocalSyntaxPtr,
17 kind: EntryKind,
18 name: SmolStr,
19}
20
21#[derive(Debug, PartialEq, Eq)]
22enum EntryKind {
23 Item,
24 Import,
25}
26
27impl ModuleScope {
28 pub(super) fn new<'a>(items: impl Iterator<Item = ast::ModuleItem<'a>>) -> ModuleScope {
29 let mut entries = Vec::new();
30 for item in items {
31 let entry = match item {
32 ast::ModuleItem::StructDef(item) => Entry::new(item),
33 ast::ModuleItem::EnumDef(item) => Entry::new(item),
34 ast::ModuleItem::FnDef(item) => Entry::new(item),
35 ast::ModuleItem::ConstDef(item) => Entry::new(item),
36 ast::ModuleItem::StaticDef(item) => Entry::new(item),
37 ast::ModuleItem::TraitDef(item) => Entry::new(item),
38 ast::ModuleItem::TypeDef(item) => Entry::new(item),
39 ast::ModuleItem::Module(item) => Entry::new(item),
40 ast::ModuleItem::UseItem(item) => {
41 if let Some(tree) = item.use_tree() {
42 collect_imports(tree, &mut entries);
43 }
44 continue;
45 }
46 ast::ModuleItem::ExternCrateItem(_) | ast::ModuleItem::ImplItem(_) => continue,
47 };
48 entries.extend(entry)
49 }
50
51 ModuleScope { entries }
52 }
53
54 pub fn entries(&self) -> &[Entry] {
55 self.entries.as_slice()
56 }
57}
58
59impl Entry {
60 fn new<'a>(item: impl ast::NameOwner<'a>) -> Option<Entry> {
61 let name = item.name()?;
62 Some(Entry {
63 name: name.text(),
64 ptr: LocalSyntaxPtr::new(name.syntax()),
65 kind: EntryKind::Item,
66 })
67 }
68 fn new_import(path: ast::Path) -> Option<Entry> {
69 let name_ref = path.segment()?.name_ref()?;
70 Some(Entry {
71 name: name_ref.text(),
72 ptr: LocalSyntaxPtr::new(name_ref.syntax()),
73 kind: EntryKind::Import,
74 })
75 }
76 pub fn name(&self) -> &SmolStr {
77 &self.name
78 }
79 pub fn ptr(&self) -> LocalSyntaxPtr {
80 self.ptr
81 }
82}
83
84fn collect_imports(tree: ast::UseTree, acc: &mut Vec<Entry>) {
85 if let Some(use_tree_list) = tree.use_tree_list() {
86 return use_tree_list
87 .use_trees()
88 .for_each(|it| collect_imports(it, acc));
89 }
90 if let Some(path) = tree.path() {
91 acc.extend(Entry::new_import(path));
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98 use ra_syntax::{ast::ModuleItemOwner, SourceFileNode};
99
100 fn do_check(code: &str, expected: &[&str]) {
101 let file = SourceFileNode::parse(&code);
102 let scope = ModuleScope::new(file.ast().items());
103 let actual = scope.entries.iter().map(|it| it.name()).collect::<Vec<_>>();
104 assert_eq!(expected, actual.as_slice());
105 }
106
107 #[test]
108 fn test_module_scope() {
109 do_check(
110 "
111 struct Foo;
112 enum Bar {}
113 mod baz {}
114 fn quux() {}
115 use x::{
116 y::z,
117 t,
118 };
119 type T = ();
120 ",
121 &["Foo", "Bar", "baz", "quux", "z", "t", "T"],
122 )
123 }
124}
diff --git a/crates/ra_analysis/src/loc2id.rs b/crates/ra_analysis/src/loc2id.rs
index 8c297156a..e4b55f9b0 100644
--- a/crates/ra_analysis/src/loc2id.rs
+++ b/crates/ra_analysis/src/loc2id.rs
@@ -8,7 +8,9 @@ use std::{
8use rustc_hash::FxHashMap; 8use rustc_hash::FxHashMap;
9 9
10use crate::{ 10use crate::{
11 descriptors::module::ModuleId,
11 syntax_ptr::SyntaxPtr, 12 syntax_ptr::SyntaxPtr,
13 input::SourceRootId,
12}; 14};
13 15
14/// There are two principle ways to refer to things: 16/// There are two principle ways to refer to things:
@@ -89,6 +91,21 @@ macro_rules! impl_numeric_id {
89pub(crate) struct FnId(u32); 91pub(crate) struct FnId(u32);
90impl_numeric_id!(FnId); 92impl_numeric_id!(FnId);
91 93
94#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
95pub(crate) struct DefId(u32);
96impl_numeric_id!(DefId);
97
98#[derive(Clone, Debug, PartialEq, Eq, Hash)]
99pub(crate) enum DefLoc {
100 Module {
101 id: ModuleId,
102 source_root: SourceRootId,
103 },
104 Item {
105 ptr: SyntaxPtr,
106 },
107}
108
92pub(crate) trait IdDatabase: salsa::Database { 109pub(crate) trait IdDatabase: salsa::Database {
93 fn id_maps(&self) -> &IdMaps; 110 fn id_maps(&self) -> &IdMaps;
94} 111}
@@ -105,9 +122,17 @@ impl IdMaps {
105 pub(crate) fn fn_ptr(&self, fn_id: FnId) -> SyntaxPtr { 122 pub(crate) fn fn_ptr(&self, fn_id: FnId) -> SyntaxPtr {
106 self.inner.fns.lock().id2loc(fn_id) 123 self.inner.fns.lock().id2loc(fn_id)
107 } 124 }
125
126 pub(crate) fn def_id(&self, loc: DefLoc) -> DefId {
127 self.inner.defs.lock().loc2id(&loc)
128 }
129 pub(crate) fn def_loc(&self, def_id: DefId) -> DefLoc {
130 self.inner.defs.lock().id2loc(def_id)
131 }
108} 132}
109 133
110#[derive(Debug, Default)] 134#[derive(Debug, Default)]
111struct IdMapsInner { 135struct IdMapsInner {
112 fns: Mutex<Loc2IdMap<SyntaxPtr, FnId>>, 136 fns: Mutex<Loc2IdMap<SyntaxPtr, FnId>>,
137 defs: Mutex<Loc2IdMap<DefLoc, DefId>>,
113} 138}
diff --git a/crates/ra_analysis/tests/tests.rs b/crates/ra_analysis/tests/tests.rs
index 719c166b5..72b2ebf97 100644
--- a/crates/ra_analysis/tests/tests.rs
+++ b/crates/ra_analysis/tests/tests.rs
@@ -447,8 +447,8 @@ fn test_complete_crate_path() {
447 ); 447 );
448 let completions = analysis.completions(position).unwrap().unwrap(); 448 let completions = analysis.completions(position).unwrap().unwrap();
449 assert_eq_dbg( 449 assert_eq_dbg(
450 r#"[CompletionItem { label: "foo", lookup: None, snippet: None }, 450 r#"[CompletionItem { label: "Spam", lookup: None, snippet: None },
451 CompletionItem { label: "Spam", lookup: None, snippet: None }]"#, 451 CompletionItem { label: "foo", lookup: None, snippet: None }]"#,
452 &completions, 452 &completions,
453 ); 453 );
454} 454}
@@ -466,8 +466,8 @@ fn test_complete_crate_path_with_braces() {
466 ); 466 );
467 let completions = analysis.completions(position).unwrap().unwrap(); 467 let completions = analysis.completions(position).unwrap().unwrap();
468 assert_eq_dbg( 468 assert_eq_dbg(
469 r#"[CompletionItem { label: "foo", lookup: None, snippet: None }, 469 r#"[CompletionItem { label: "Spam", lookup: None, snippet: None },
470 CompletionItem { label: "Spam", lookup: None, snippet: None }]"#, 470 CompletionItem { label: "foo", lookup: None, snippet: None }]"#,
471 &completions, 471 &completions,
472 ); 472 );
473} 473}
diff --git a/crates/ra_syntax/src/ast/mod.rs b/crates/ra_syntax/src/ast/mod.rs
index 7077e3492..91c67119f 100644
--- a/crates/ra_syntax/src/ast/mod.rs
+++ b/crates/ra_syntax/src/ast/mod.rs
@@ -315,6 +315,12 @@ impl<'a> PathSegment<'a> {
315 } 315 }
316} 316}
317 317
318impl<'a> UseTree<'a> {
319 pub fn has_star(self) -> bool {
320 self.syntax().children().any(|it| it.kind() == STAR)
321 }
322}
323
318impl<'a> UseTreeList<'a> { 324impl<'a> UseTreeList<'a> {
319 pub fn parent_use_tree(self) -> UseTree<'a> { 325 pub fn parent_use_tree(self) -> UseTree<'a> {
320 self.syntax() 326 self.syntax()
diff --git a/crates/tools/src/lib.rs b/crates/tools/src/lib.rs
index 8b8e9974e..e81761cf2 100644
--- a/crates/tools/src/lib.rs
+++ b/crates/tools/src/lib.rs
@@ -17,7 +17,7 @@ pub type Result<T> = ::std::result::Result<T, failure::Error>;
17pub const GRAMMAR: &str = "crates/ra_syntax/src/grammar.ron"; 17pub const GRAMMAR: &str = "crates/ra_syntax/src/grammar.ron";
18pub const SYNTAX_KINDS: &str = "crates/ra_syntax/src/syntax_kinds/generated.rs.tera"; 18pub const SYNTAX_KINDS: &str = "crates/ra_syntax/src/syntax_kinds/generated.rs.tera";
19pub const AST: &str = "crates/ra_syntax/src/ast/generated.rs.tera"; 19pub const AST: &str = "crates/ra_syntax/src/ast/generated.rs.tera";
20const TOOLCHAIN: &str = "beta-2018-10-30"; 20const TOOLCHAIN: &str = "beta-2018-11-19";
21 21
22#[derive(Debug)] 22#[derive(Debug)]
23pub struct Test { 23pub struct Test {