aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis/src/descriptors
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_analysis/src/descriptors')
-rw-r--r--crates/ra_analysis/src/descriptors/function/imp.rs21
-rw-r--r--crates/ra_analysis/src/descriptors/function/mod.rs137
-rw-r--r--crates/ra_analysis/src/descriptors/function/scope.rs424
-rw-r--r--crates/ra_analysis/src/descriptors/mod.rs101
-rw-r--r--crates/ra_analysis/src/descriptors/module/imp.rs229
-rw-r--r--crates/ra_analysis/src/descriptors/module/mod.rs344
-rw-r--r--crates/ra_analysis/src/descriptors/module/nameres.rs461
-rw-r--r--crates/ra_analysis/src/descriptors/path.rs153
8 files changed, 0 insertions, 1870 deletions
diff --git a/crates/ra_analysis/src/descriptors/function/imp.rs b/crates/ra_analysis/src/descriptors/function/imp.rs
deleted file mode 100644
index e09deba0f..000000000
--- a/crates/ra_analysis/src/descriptors/function/imp.rs
+++ /dev/null
@@ -1,21 +0,0 @@
1use std::sync::Arc;
2
3use ra_syntax::ast::{AstNode, FnDef, FnDefNode};
4
5use crate::descriptors::{
6 function::{FnId, FnScopes},
7 DescriptorDatabase,
8};
9
10/// Resolve `FnId` to the corresponding `SyntaxNode`
11pub(crate) fn fn_syntax(db: &impl DescriptorDatabase, fn_id: FnId) -> FnDefNode {
12 let ptr = db.id_maps().fn_ptr(fn_id);
13 let syntax = db.resolve_syntax_ptr(ptr);
14 FnDef::cast(syntax.borrowed()).unwrap().owned()
15}
16
17pub(crate) fn fn_scopes(db: &impl DescriptorDatabase, fn_id: FnId) -> Arc<FnScopes> {
18 let syntax = db._fn_syntax(fn_id);
19 let res = FnScopes::new(syntax.borrowed());
20 Arc::new(res)
21}
diff --git a/crates/ra_analysis/src/descriptors/function/mod.rs b/crates/ra_analysis/src/descriptors/function/mod.rs
deleted file mode 100644
index 86eee5e93..000000000
--- a/crates/ra_analysis/src/descriptors/function/mod.rs
+++ /dev/null
@@ -1,137 +0,0 @@
1pub(super) mod imp;
2mod scope;
3
4use std::cmp::{max, min};
5
6use ra_syntax::{
7 ast::{self, AstNode, DocCommentsOwner, NameOwner},
8 TextRange, TextUnit,
9};
10
11use crate::{
12 syntax_ptr::SyntaxPtr, FileId,
13 loc2id::IdDatabase,
14};
15
16pub(crate) use self::scope::{resolve_local_name, FnScopes};
17pub(crate) use crate::loc2id::FnId;
18
19impl FnId {
20 pub(crate) fn get(db: &impl IdDatabase, file_id: FileId, fn_def: ast::FnDef) -> FnId {
21 let ptr = SyntaxPtr::new(file_id, fn_def.syntax());
22 db.id_maps().fn_id(ptr)
23 }
24}
25
26#[derive(Debug, Clone)]
27pub struct FnDescriptor {
28 pub name: String,
29 pub label: String,
30 pub ret_type: Option<String>,
31 pub params: Vec<String>,
32 pub doc: Option<String>,
33}
34
35impl FnDescriptor {
36 pub fn new(node: ast::FnDef) -> Option<Self> {
37 let name = node.name()?.text().to_string();
38
39 let mut doc = None;
40
41 // Strip the body out for the label.
42 let mut label: String = if let Some(body) = node.body() {
43 let body_range = body.syntax().range();
44 let label: String = node
45 .syntax()
46 .children()
47 .filter(|child| !child.range().is_subrange(&body_range))
48 .map(|node| node.text().to_string())
49 .collect();
50 label
51 } else {
52 node.syntax().text().to_string()
53 };
54
55 if let Some((comment_range, docs)) = FnDescriptor::extract_doc_comments(node) {
56 let comment_range = comment_range
57 .checked_sub(node.syntax().range().start())
58 .unwrap();
59 let start = comment_range.start().to_usize();
60 let end = comment_range.end().to_usize();
61
62 // Remove the comment from the label
63 label.replace_range(start..end, "");
64
65 // Massage markdown
66 let mut processed_lines = Vec::new();
67 let mut in_code_block = false;
68 for line in docs.lines() {
69 if line.starts_with("```") {
70 in_code_block = !in_code_block;
71 }
72
73 let line = if in_code_block && line.starts_with("```") && !line.contains("rust") {
74 "```rust".into()
75 } else {
76 line.to_string()
77 };
78
79 processed_lines.push(line);
80 }
81
82 if !processed_lines.is_empty() {
83 doc = Some(processed_lines.join("\n"));
84 }
85 }
86
87 let params = FnDescriptor::param_list(node);
88 let ret_type = node.ret_type().map(|r| r.syntax().text().to_string());
89
90 Some(FnDescriptor {
91 name,
92 ret_type,
93 params,
94 label: label.trim().to_owned(),
95 doc,
96 })
97 }
98
99 fn extract_doc_comments(node: ast::FnDef) -> Option<(TextRange, String)> {
100 if node.doc_comments().count() == 0 {
101 return None;
102 }
103
104 let comment_text = node.doc_comment_text();
105
106 let (begin, end) = node
107 .doc_comments()
108 .map(|comment| comment.syntax().range())
109 .map(|range| (range.start().to_usize(), range.end().to_usize()))
110 .fold((std::usize::MAX, std::usize::MIN), |acc, range| {
111 (min(acc.0, range.0), max(acc.1, range.1))
112 });
113
114 let range = TextRange::from_to(TextUnit::from_usize(begin), TextUnit::from_usize(end));
115
116 Some((range, comment_text))
117 }
118
119 fn param_list(node: ast::FnDef) -> Vec<String> {
120 let mut res = vec![];
121 if let Some(param_list) = node.param_list() {
122 if let Some(self_param) = param_list.self_param() {
123 res.push(self_param.syntax().text().to_string())
124 }
125
126 // Maybe use param.pat here? See if we can just extract the name?
127 //res.extend(param_list.params().map(|p| p.syntax().text().to_string()));
128 res.extend(
129 param_list
130 .params()
131 .filter_map(|p| p.pat())
132 .map(|pat| pat.syntax().text().to_string()),
133 );
134 }
135 res
136 }
137}
diff --git a/crates/ra_analysis/src/descriptors/function/scope.rs b/crates/ra_analysis/src/descriptors/function/scope.rs
deleted file mode 100644
index 54d7fa456..000000000
--- a/crates/ra_analysis/src/descriptors/function/scope.rs
+++ /dev/null
@@ -1,424 +0,0 @@
1use rustc_hash::{FxHashMap, FxHashSet};
2
3use ra_syntax::{
4 algo::generate,
5 ast::{self, ArgListOwner, LoopBodyOwner, NameOwner},
6 AstNode, SmolStr, SyntaxNodeRef,
7};
8
9use crate::{
10 syntax_ptr::LocalSyntaxPtr,
11 arena::{Arena, Id},
12};
13
14pub(crate) type ScopeId = Id<ScopeData>;
15
16#[derive(Debug, PartialEq, Eq)]
17pub struct FnScopes {
18 pub(crate) self_param: Option<LocalSyntaxPtr>,
19 scopes: Arena<ScopeData>,
20 scope_for: FxHashMap<LocalSyntaxPtr, ScopeId>,
21}
22
23#[derive(Debug, PartialEq, Eq)]
24pub struct ScopeEntry {
25 name: SmolStr,
26 ptr: LocalSyntaxPtr,
27}
28
29#[derive(Debug, PartialEq, Eq)]
30pub(crate) struct ScopeData {
31 parent: Option<ScopeId>,
32 entries: Vec<ScopeEntry>,
33}
34
35impl FnScopes {
36 pub(crate) fn new(fn_def: ast::FnDef) -> FnScopes {
37 let mut scopes = FnScopes {
38 self_param: fn_def
39 .param_list()
40 .and_then(|it| it.self_param())
41 .map(|it| LocalSyntaxPtr::new(it.syntax())),
42 scopes: Arena::default(),
43 scope_for: FxHashMap::default(),
44 };
45 let root = scopes.root_scope();
46 scopes.add_params_bindings(root, fn_def.param_list());
47 if let Some(body) = fn_def.body() {
48 compute_block_scopes(body, &mut scopes, root)
49 }
50 scopes
51 }
52 pub(crate) fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
53 &self.scopes[scope].entries
54 }
55 pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item = ScopeId> + 'a {
56 generate(self.scope_for(node), move |&scope| {
57 self.scopes[scope].parent
58 })
59 }
60 fn root_scope(&mut self) -> ScopeId {
61 self.scopes.push(ScopeData {
62 parent: None,
63 entries: vec![],
64 })
65 }
66 fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
67 self.scopes.push(ScopeData {
68 parent: Some(parent),
69 entries: vec![],
70 })
71 }
72 fn add_bindings(&mut self, scope: ScopeId, pat: ast::Pat) {
73 let entries = pat
74 .syntax()
75 .descendants()
76 .filter_map(ast::BindPat::cast)
77 .filter_map(ScopeEntry::new);
78 self.scopes[scope].entries.extend(entries);
79 }
80 fn add_params_bindings(&mut self, scope: ScopeId, params: Option<ast::ParamList>) {
81 params
82 .into_iter()
83 .flat_map(|it| it.params())
84 .filter_map(|it| it.pat())
85 .for_each(|it| self.add_bindings(scope, it));
86 }
87 fn set_scope(&mut self, node: SyntaxNodeRef, scope: ScopeId) {
88 self.scope_for.insert(LocalSyntaxPtr::new(node), scope);
89 }
90 fn scope_for(&self, node: SyntaxNodeRef) -> Option<ScopeId> {
91 node.ancestors()
92 .map(LocalSyntaxPtr::new)
93 .filter_map(|it| self.scope_for.get(&it).map(|&scope| scope))
94 .next()
95 }
96}
97
98impl ScopeEntry {
99 fn new(pat: ast::BindPat) -> Option<ScopeEntry> {
100 let name = pat.name()?;
101 let res = ScopeEntry {
102 name: name.text(),
103 ptr: LocalSyntaxPtr::new(pat.syntax()),
104 };
105 Some(res)
106 }
107 pub(crate) fn name(&self) -> &SmolStr {
108 &self.name
109 }
110 pub(crate) fn ptr(&self) -> LocalSyntaxPtr {
111 self.ptr
112 }
113}
114
115fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: ScopeId) {
116 for stmt in block.statements() {
117 match stmt {
118 ast::Stmt::LetStmt(stmt) => {
119 if let Some(expr) = stmt.initializer() {
120 scopes.set_scope(expr.syntax(), scope);
121 compute_expr_scopes(expr, scopes, scope);
122 }
123 scope = scopes.new_scope(scope);
124 if let Some(pat) = stmt.pat() {
125 scopes.add_bindings(scope, pat);
126 }
127 }
128 ast::Stmt::ExprStmt(expr_stmt) => {
129 if let Some(expr) = expr_stmt.expr() {
130 scopes.set_scope(expr.syntax(), scope);
131 compute_expr_scopes(expr, scopes, scope);
132 }
133 }
134 }
135 }
136 if let Some(expr) = block.expr() {
137 scopes.set_scope(expr.syntax(), scope);
138 compute_expr_scopes(expr, scopes, scope);
139 }
140}
141
142fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) {
143 match expr {
144 ast::Expr::IfExpr(e) => {
145 let cond_scope = e
146 .condition()
147 .and_then(|cond| compute_cond_scopes(cond, scopes, scope));
148 if let Some(block) = e.then_branch() {
149 compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
150 }
151 if let Some(block) = e.else_branch() {
152 compute_block_scopes(block, scopes, scope);
153 }
154 }
155 ast::Expr::BlockExpr(e) => {
156 if let Some(block) = e.block() {
157 compute_block_scopes(block, scopes, scope);
158 }
159 }
160 ast::Expr::LoopExpr(e) => {
161 if let Some(block) = e.loop_body() {
162 compute_block_scopes(block, scopes, scope);
163 }
164 }
165 ast::Expr::WhileExpr(e) => {
166 let cond_scope = e
167 .condition()
168 .and_then(|cond| compute_cond_scopes(cond, scopes, scope));
169 if let Some(block) = e.loop_body() {
170 compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
171 }
172 }
173 ast::Expr::ForExpr(e) => {
174 if let Some(expr) = e.iterable() {
175 compute_expr_scopes(expr, scopes, scope);
176 }
177 let mut scope = scope;
178 if let Some(pat) = e.pat() {
179 scope = scopes.new_scope(scope);
180 scopes.add_bindings(scope, pat);
181 }
182 if let Some(block) = e.loop_body() {
183 compute_block_scopes(block, scopes, scope);
184 }
185 }
186 ast::Expr::LambdaExpr(e) => {
187 let scope = scopes.new_scope(scope);
188 scopes.add_params_bindings(scope, e.param_list());
189 if let Some(body) = e.body() {
190 scopes.set_scope(body.syntax(), scope);
191 compute_expr_scopes(body, scopes, scope);
192 }
193 }
194 ast::Expr::CallExpr(e) => {
195 compute_call_scopes(e.expr(), e.arg_list(), scopes, scope);
196 }
197 ast::Expr::MethodCallExpr(e) => {
198 compute_call_scopes(e.expr(), e.arg_list(), scopes, scope);
199 }
200 ast::Expr::MatchExpr(e) => {
201 if let Some(expr) = e.expr() {
202 compute_expr_scopes(expr, scopes, scope);
203 }
204 for arm in e.match_arm_list().into_iter().flat_map(|it| it.arms()) {
205 let scope = scopes.new_scope(scope);
206 for pat in arm.pats() {
207 scopes.add_bindings(scope, pat);
208 }
209 if let Some(expr) = arm.expr() {
210 compute_expr_scopes(expr, scopes, scope);
211 }
212 }
213 }
214 _ => expr
215 .syntax()
216 .children()
217 .filter_map(ast::Expr::cast)
218 .for_each(|expr| compute_expr_scopes(expr, scopes, scope)),
219 };
220
221 fn compute_call_scopes(
222 receiver: Option<ast::Expr>,
223 arg_list: Option<ast::ArgList>,
224 scopes: &mut FnScopes,
225 scope: ScopeId,
226 ) {
227 arg_list
228 .into_iter()
229 .flat_map(|it| it.args())
230 .chain(receiver)
231 .for_each(|expr| compute_expr_scopes(expr, scopes, scope));
232 }
233
234 fn compute_cond_scopes(
235 cond: ast::Condition,
236 scopes: &mut FnScopes,
237 scope: ScopeId,
238 ) -> Option<ScopeId> {
239 if let Some(expr) = cond.expr() {
240 compute_expr_scopes(expr, scopes, scope);
241 }
242 if let Some(pat) = cond.pat() {
243 let s = scopes.new_scope(scope);
244 scopes.add_bindings(s, pat);
245 Some(s)
246 } else {
247 None
248 }
249 }
250}
251
252pub fn resolve_local_name<'a>(
253 name_ref: ast::NameRef,
254 scopes: &'a FnScopes,
255) -> Option<&'a ScopeEntry> {
256 let mut shadowed = FxHashSet::default();
257 let ret = scopes
258 .scope_chain(name_ref.syntax())
259 .flat_map(|scope| scopes.entries(scope).iter())
260 .filter(|entry| shadowed.insert(entry.name()))
261 .filter(|entry| entry.name() == &name_ref.text())
262 .nth(0);
263 ret
264}
265
266#[cfg(test)]
267mod tests {
268 use ra_editor::find_node_at_offset;
269 use ra_syntax::SourceFileNode;
270 use test_utils::extract_offset;
271
272 use super::*;
273
274 fn do_check(code: &str, expected: &[&str]) {
275 let (off, code) = extract_offset(code);
276 let code = {
277 let mut buf = String::new();
278 let off = u32::from(off) as usize;
279 buf.push_str(&code[..off]);
280 buf.push_str("marker");
281 buf.push_str(&code[off..]);
282 buf
283 };
284 let file = SourceFileNode::parse(&code);
285 let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap();
286 let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
287 let scopes = FnScopes::new(fn_def);
288 let actual = scopes
289 .scope_chain(marker.syntax())
290 .flat_map(|scope| scopes.entries(scope))
291 .map(|it| it.name())
292 .collect::<Vec<_>>();
293 assert_eq!(actual.as_slice(), expected);
294 }
295
296 #[test]
297 fn test_lambda_scope() {
298 do_check(
299 r"
300 fn quux(foo: i32) {
301 let f = |bar, baz: i32| {
302 <|>
303 };
304 }",
305 &["bar", "baz", "foo"],
306 );
307 }
308
309 #[test]
310 fn test_call_scope() {
311 do_check(
312 r"
313 fn quux() {
314 f(|x| <|> );
315 }",
316 &["x"],
317 );
318 }
319
320 #[test]
321 fn test_metod_call_scope() {
322 do_check(
323 r"
324 fn quux() {
325 z.f(|x| <|> );
326 }",
327 &["x"],
328 );
329 }
330
331 #[test]
332 fn test_loop_scope() {
333 do_check(
334 r"
335 fn quux() {
336 loop {
337 let x = ();
338 <|>
339 };
340 }",
341 &["x"],
342 );
343 }
344
345 #[test]
346 fn test_match() {
347 do_check(
348 r"
349 fn quux() {
350 match () {
351 Some(x) => {
352 <|>
353 }
354 };
355 }",
356 &["x"],
357 );
358 }
359
360 #[test]
361 fn test_shadow_variable() {
362 do_check(
363 r"
364 fn foo(x: String) {
365 let x : &str = &x<|>;
366 }",
367 &["x"],
368 );
369 }
370
371 fn do_check_local_name(code: &str, expected_offset: u32) {
372 let (off, code) = extract_offset(code);
373 let file = SourceFileNode::parse(&code);
374 let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
375 let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap();
376
377 let scopes = FnScopes::new(fn_def);
378
379 let local_name_entry = resolve_local_name(name_ref, &scopes).unwrap();
380 let local_name = local_name_entry.ptr().resolve(&file);
381 let expected_name =
382 find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()).unwrap();
383 assert_eq!(local_name.range(), expected_name.syntax().range());
384 }
385
386 #[test]
387 fn test_resolve_local_name() {
388 do_check_local_name(
389 r#"
390 fn foo(x: i32, y: u32) {
391 {
392 let z = x * 2;
393 }
394 {
395 let t = x<|> * 3;
396 }
397 }"#,
398 21,
399 );
400 }
401
402 #[test]
403 fn test_resolve_local_name_declaration() {
404 do_check_local_name(
405 r#"
406 fn foo(x: String) {
407 let x : &str = &x<|>;
408 }"#,
409 21,
410 );
411 }
412
413 #[test]
414 fn test_resolve_local_name_shadow() {
415 do_check_local_name(
416 r"
417 fn foo(x: String) {
418 let x : &str = &x;
419 x<|>
420 }",
421 46,
422 );
423 }
424}
diff --git a/crates/ra_analysis/src/descriptors/mod.rs b/crates/ra_analysis/src/descriptors/mod.rs
deleted file mode 100644
index 97750ea64..000000000
--- a/crates/ra_analysis/src/descriptors/mod.rs
+++ /dev/null
@@ -1,101 +0,0 @@
1pub(crate) mod function;
2pub(crate) mod module;
3mod path;
4
5use std::sync::Arc;
6
7use ra_syntax::{
8 ast::{self, FnDefNode, AstNode},
9 TextRange,
10};
11
12use crate::{
13 db::SyntaxDatabase,
14 descriptors::function::{resolve_local_name, FnId, FnScopes},
15 descriptors::module::{ModuleId, ModuleTree, ModuleSource, nameres::{ItemMap, InputModuleItems}},
16 input::SourceRootId,
17 loc2id::IdDatabase,
18 syntax_ptr::LocalSyntaxPtr,
19 Cancelable,
20};
21
22pub(crate) use self::path::{Path, PathKind};
23
24salsa::query_group! {
25 pub(crate) trait DescriptorDatabase: SyntaxDatabase + IdDatabase {
26 fn fn_scopes(fn_id: FnId) -> Arc<FnScopes> {
27 type FnScopesQuery;
28 use fn function::imp::fn_scopes;
29 }
30
31 fn _input_module_items(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable<Arc<InputModuleItems>> {
32 type InputModuleItemsQuery;
33 use fn module::nameres::input_module_items;
34 }
35 fn _item_map(source_root_id: SourceRootId) -> Cancelable<Arc<ItemMap>> {
36 type ItemMapQuery;
37 use fn module::nameres::item_map;
38 }
39 fn _module_tree(source_root_id: SourceRootId) -> Cancelable<Arc<ModuleTree>> {
40 type ModuleTreeQuery;
41 use fn module::imp::module_tree;
42 }
43 fn _fn_syntax(fn_id: FnId) -> FnDefNode {
44 type FnSyntaxQuery;
45 // Don't retain syntax trees in memory
46 storage volatile;
47 use fn function::imp::fn_syntax;
48 }
49 fn _submodules(source: ModuleSource) -> Cancelable<Arc<Vec<module::imp::Submodule>>> {
50 type SubmodulesQuery;
51 use fn module::imp::submodules;
52 }
53 }
54}
55
56#[derive(Debug)]
57pub struct ReferenceDescriptor {
58 pub range: TextRange,
59 pub name: String,
60}
61
62#[derive(Debug)]
63pub struct DeclarationDescriptor<'a> {
64 pat: ast::BindPat<'a>,
65 pub range: TextRange,
66}
67
68impl<'a> DeclarationDescriptor<'a> {
69 pub fn new(pat: ast::BindPat) -> DeclarationDescriptor {
70 let range = pat.syntax().range();
71
72 DeclarationDescriptor { pat, range }
73 }
74
75 pub fn find_all_refs(&self) -> Vec<ReferenceDescriptor> {
76 let name_ptr = LocalSyntaxPtr::new(self.pat.syntax());
77
78 let fn_def = match self.pat.syntax().ancestors().find_map(ast::FnDef::cast) {
79 Some(def) => def,
80 None => return Default::default(),
81 };
82
83 let fn_scopes = FnScopes::new(fn_def);
84
85 let refs: Vec<_> = fn_def
86 .syntax()
87 .descendants()
88 .filter_map(ast::NameRef::cast)
89 .filter(|name_ref| match resolve_local_name(*name_ref, &fn_scopes) {
90 None => false,
91 Some(entry) => entry.ptr() == name_ptr,
92 })
93 .map(|name_ref| ReferenceDescriptor {
94 name: name_ref.syntax().text().to_string(),
95 range: name_ref.syntax().range(),
96 })
97 .collect();
98
99 refs
100 }
101}
diff --git a/crates/ra_analysis/src/descriptors/module/imp.rs b/crates/ra_analysis/src/descriptors/module/imp.rs
deleted file mode 100644
index 80892acb7..000000000
--- a/crates/ra_analysis/src/descriptors/module/imp.rs
+++ /dev/null
@@ -1,229 +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};
9
10use crate::{
11 db,
12 descriptors::DescriptorDatabase,
13 input::{SourceRoot, SourceRootId},
14 Cancelable, FileId, FileResolverImp,
15};
16
17use super::{
18 LinkData, LinkId, ModuleData, ModuleId, ModuleSource, ModuleSourceNode,
19 ModuleTree, Problem,
20};
21
22#[derive(Clone, Hash, PartialEq, Eq, Debug)]
23pub(crate) enum Submodule {
24 Declaration(SmolStr),
25 Definition(SmolStr, ModuleSource),
26}
27
28impl Submodule {
29 fn name(&self) -> &SmolStr {
30 match self {
31 Submodule::Declaration(name) => name,
32 Submodule::Definition(name, _) => name,
33 }
34 }
35}
36
37pub(crate) fn submodules(
38 db: &impl DescriptorDatabase,
39 source: ModuleSource,
40) -> Cancelable<Arc<Vec<Submodule>>> {
41 db::check_canceled(db)?;
42 let file_id = source.file_id();
43 let submodules = match source.resolve(db) {
44 ModuleSourceNode::SourceFile(it) => collect_submodules(file_id, it.borrowed()),
45 ModuleSourceNode::Module(it) => it
46 .borrowed()
47 .item_list()
48 .map(|it| collect_submodules(file_id, it))
49 .unwrap_or_else(Vec::new),
50 };
51 return Ok(Arc::new(submodules));
52
53 fn collect_submodules<'a>(
54 file_id: FileId,
55 root: impl ast::ModuleItemOwner<'a>,
56 ) -> Vec<Submodule> {
57 modules(root)
58 .map(|(name, m)| {
59 if m.has_semi() {
60 Submodule::Declaration(name)
61 } else {
62 let src = ModuleSource::new_inline(file_id, m);
63 Submodule::Definition(name, src)
64 }
65 })
66 .collect()
67 }
68}
69
70pub(crate) fn modules<'a>(
71 root: impl ast::ModuleItemOwner<'a>,
72) -> impl Iterator<Item = (SmolStr, ast::Module<'a>)> {
73 root.items()
74 .filter_map(|item| match item {
75 ast::ModuleItem::Module(m) => Some(m),
76 _ => None,
77 })
78 .filter_map(|module| {
79 let name = module.name()?.text();
80 Some((name, module))
81 })
82}
83
84pub(crate) fn module_tree(
85 db: &impl DescriptorDatabase,
86 source_root: SourceRootId,
87) -> Cancelable<Arc<ModuleTree>> {
88 db::check_canceled(db)?;
89 let res = create_module_tree(db, source_root)?;
90 Ok(Arc::new(res))
91}
92
93fn create_module_tree<'a>(
94 db: &impl DescriptorDatabase,
95 source_root: SourceRootId,
96) -> Cancelable<ModuleTree> {
97 let mut tree = ModuleTree::default();
98
99 let mut roots = FxHashMap::default();
100 let mut visited = FxHashSet::default();
101
102 let source_root = db.source_root(source_root);
103 for &file_id in source_root.files.iter() {
104 let source = ModuleSource::SourceFile(file_id);
105 if visited.contains(&source) {
106 continue; // TODO: use explicit crate_roots here
107 }
108 assert!(!roots.contains_key(&file_id));
109 let module_id = build_subtree(
110 db,
111 &source_root,
112 &mut tree,
113 &mut visited,
114 &mut roots,
115 None,
116 source,
117 )?;
118 roots.insert(file_id, module_id);
119 }
120 Ok(tree)
121}
122
123fn build_subtree(
124 db: &impl DescriptorDatabase,
125 source_root: &SourceRoot,
126 tree: &mut ModuleTree,
127 visited: &mut FxHashSet<ModuleSource>,
128 roots: &mut FxHashMap<FileId, ModuleId>,
129 parent: Option<LinkId>,
130 source: ModuleSource,
131) -> Cancelable<ModuleId> {
132 visited.insert(source);
133 let id = tree.push_mod(ModuleData {
134 source,
135 parent,
136 children: Vec::new(),
137 });
138 for sub in db._submodules(source)?.iter() {
139 let link = tree.push_link(LinkData {
140 name: sub.name().clone(),
141 owner: id,
142 points_to: Vec::new(),
143 problem: None,
144 });
145
146 let (points_to, problem) = match sub {
147 Submodule::Declaration(name) => {
148 let (points_to, problem) =
149 resolve_submodule(source, &name, &source_root.file_resolver);
150 let points_to = points_to
151 .into_iter()
152 .map(|file_id| match roots.remove(&file_id) {
153 Some(module_id) => {
154 tree.mods[module_id].parent = Some(link);
155 Ok(module_id)
156 }
157 None => build_subtree(
158 db,
159 source_root,
160 tree,
161 visited,
162 roots,
163 Some(link),
164 ModuleSource::SourceFile(file_id),
165 ),
166 })
167 .collect::<Cancelable<Vec<_>>>()?;
168 (points_to, problem)
169 }
170 Submodule::Definition(_name, submodule_source) => {
171 let points_to = build_subtree(
172 db,
173 source_root,
174 tree,
175 visited,
176 roots,
177 Some(link),
178 *submodule_source,
179 )?;
180 (vec![points_to], None)
181 }
182 };
183
184 tree.links[link].points_to = points_to;
185 tree.links[link].problem = problem;
186 }
187 Ok(id)
188}
189
190fn resolve_submodule(
191 source: ModuleSource,
192 name: &SmolStr,
193 file_resolver: &FileResolverImp,
194) -> (Vec<FileId>, Option<Problem>) {
195 let file_id = match source {
196 ModuleSource::SourceFile(it) => it,
197 ModuleSource::Module(..) => {
198 // TODO
199 return (Vec::new(), None);
200 }
201 };
202 let mod_name = file_resolver.file_stem(file_id);
203 let is_dir_owner = mod_name == "mod" || mod_name == "lib" || mod_name == "main";
204
205 let file_mod = RelativePathBuf::from(format!("../{}.rs", name));
206 let dir_mod = RelativePathBuf::from(format!("../{}/mod.rs", name));
207 let points_to: Vec<FileId>;
208 let problem: Option<Problem>;
209 if is_dir_owner {
210 points_to = [&file_mod, &dir_mod]
211 .iter()
212 .filter_map(|path| file_resolver.resolve(file_id, path))
213 .collect();
214 problem = if points_to.is_empty() {
215 Some(Problem::UnresolvedModule {
216 candidate: file_mod,
217 })
218 } else {
219 None
220 }
221 } else {
222 points_to = Vec::new();
223 problem = Some(Problem::NotDirOwner {
224 move_to: RelativePathBuf::from(format!("../{}/mod.rs", mod_name)),
225 candidate: file_mod,
226 });
227 }
228 (points_to, problem)
229}
diff --git a/crates/ra_analysis/src/descriptors/module/mod.rs b/crates/ra_analysis/src/descriptors/module/mod.rs
deleted file mode 100644
index 54ff95b66..000000000
--- a/crates/ra_analysis/src/descriptors/module/mod.rs
+++ /dev/null
@@ -1,344 +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 relative_path::RelativePathBuf;
14
15use crate::{
16 db::SyntaxDatabase, syntax_ptr::SyntaxPtr, FileId, FilePosition, Cancelable,
17 descriptors::{Path, PathKind, DescriptorDatabase},
18 input::SourceRootId,
19 arena::{Arena, Id},
20};
21
22pub(crate) use self::nameres::ModuleScope;
23
24/// `ModuleDescriptor` is API entry point to get all the information
25/// about a particular module.
26#[derive(Debug, Clone)]
27pub(crate) struct ModuleDescriptor {
28 tree: Arc<ModuleTree>,
29 source_root_id: SourceRootId,
30 module_id: ModuleId,
31}
32
33impl ModuleDescriptor {
34 /// Lookup `ModuleDescriptor` 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 DescriptorDatabase,
39 file_id: FileId,
40 ) -> Cancelable<Option<ModuleDescriptor>> {
41 ModuleDescriptor::guess_from_source(db, file_id, ModuleSource::SourceFile(file_id))
42 }
43
44 /// Lookup `ModuleDescriptor` 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 DescriptorDatabase,
49 position: FilePosition,
50 ) -> Cancelable<Option<ModuleDescriptor>> {
51 let file = db.file_syntax(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(position.file_id, m),
55 _ => ModuleSource::SourceFile(position.file_id),
56 };
57 ModuleDescriptor::guess_from_source(db, position.file_id, module_source)
58 }
59
60 fn guess_from_source(
61 db: &impl DescriptorDatabase,
62 file_id: FileId,
63 module_source: ModuleSource,
64 ) -> Cancelable<Option<ModuleDescriptor>> {
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(ModuleDescriptor {
71 tree: module_tree,
72 source_root_id,
73 module_id,
74 }),
75 };
76 Ok(res)
77 }
78
79 /// Returns `mod foo;` or `mod foo {}` node whihc declared this module.
80 /// Returns `None` for the root module
81 pub fn parent_link_source(
82 &self,
83 db: &impl DescriptorDatabase,
84 ) -> Option<(FileId, ast::ModuleNode)> {
85 let link = self.module_id.parent_link(&self.tree)?;
86 let file_id = link.owner(&self.tree).source(&self.tree).file_id();
87 let src = link.bind_source(&self.tree, db);
88 Some((file_id, src))
89 }
90
91 pub fn source(&self) -> ModuleSource {
92 self.module_id.source(&self.tree)
93 }
94
95 /// Parent module. Returns `None` if this is a root module.
96 pub fn parent(&self) -> Option<ModuleDescriptor> {
97 let parent_id = self.module_id.parent(&self.tree)?;
98 Some(ModuleDescriptor {
99 module_id: parent_id,
100 ..self.clone()
101 })
102 }
103
104 /// The root of the tree this module is part of
105 pub fn crate_root(&self) -> ModuleDescriptor {
106 let root_id = self.module_id.crate_root(&self.tree);
107 ModuleDescriptor {
108 module_id: root_id,
109 ..self.clone()
110 }
111 }
112
113 /// `name` is `None` for the crate's root module
114 #[allow(unused)]
115 pub fn name(&self) -> Option<SmolStr> {
116 let link = self.module_id.parent_link(&self.tree)?;
117 Some(link.name(&self.tree))
118 }
119
120 /// Finds a child module with the specified name.
121 pub fn child(&self, name: &str) -> Option<ModuleDescriptor> {
122 let child_id = self.module_id.child(&self.tree, name)?;
123 Some(ModuleDescriptor {
124 module_id: child_id,
125 ..self.clone()
126 })
127 }
128
129 /// Returns a `ModuleScope`: a set of items, visible in this module.
130 pub(crate) fn scope(&self, db: &impl DescriptorDatabase) -> Cancelable<ModuleScope> {
131 let item_map = db._item_map(self.source_root_id)?;
132 let res = item_map.per_module[&self.module_id].clone();
133 Ok(res)
134 }
135
136 pub(crate) fn resolve_path(&self, path: Path) -> Option<ModuleDescriptor> {
137 let mut curr = match path.kind {
138 PathKind::Crate => self.crate_root(),
139 PathKind::Self_ | PathKind::Plain => self.clone(),
140 PathKind::Super => self.parent()?,
141 };
142 let segments = path.segments;
143 for name in segments {
144 curr = curr.child(&name)?;
145 }
146 Some(curr)
147 }
148
149 pub fn problems(&self, db: &impl DescriptorDatabase) -> Vec<(SyntaxNode, Problem)> {
150 self.module_id.problems(&self.tree, db)
151 }
152}
153
154/// Phisically, rust source is organized as a set of files, but logically it is
155/// organized as a tree of modules. Usually, a single file corresponds to a
156/// single module, but it is not nessary the case.
157///
158/// Module encapsulate the logic of transitioning from the fuzzy world of files
159/// (which can have multiple parents) to the precise world of modules (which
160/// always have one parent).
161#[derive(Default, Debug, PartialEq, Eq)]
162pub(crate) struct ModuleTree {
163 mods: Arena<ModuleData>,
164 links: Arena<LinkData>,
165}
166
167impl ModuleTree {
168 fn modules<'a>(&'a self) -> impl Iterator<Item = ModuleId> + 'a {
169 self.mods.keys()
170 }
171
172 fn modules_for_source(&self, source: ModuleSource) -> Vec<ModuleId> {
173 self.mods
174 .items()
175 .filter(|(_idx, it)| it.source == source)
176 .map(|(idx, _)| idx)
177 .collect()
178 }
179
180 fn any_module_for_source(&self, source: ModuleSource) -> Option<ModuleId> {
181 self.modules_for_source(source).pop()
182 }
183}
184
185/// `ModuleSource` is the syntax tree element that produced this module:
186/// either a file, or an inlinde module.
187#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
188pub(crate) enum ModuleSource {
189 SourceFile(FileId),
190 #[allow(dead_code)]
191 Module(SyntaxPtr),
192}
193
194/// An owned syntax node for a module. Unlike `ModuleSource`,
195/// this holds onto the AST for the whole file.
196enum ModuleSourceNode {
197 SourceFile(ast::SourceFileNode),
198 Module(ast::ModuleNode),
199}
200
201pub(crate) type ModuleId = Id<ModuleData>;
202type LinkId = Id<LinkData>;
203
204#[derive(Clone, Debug, Hash, PartialEq, Eq)]
205pub enum Problem {
206 UnresolvedModule {
207 candidate: RelativePathBuf,
208 },
209 NotDirOwner {
210 move_to: RelativePathBuf,
211 candidate: RelativePathBuf,
212 },
213}
214
215impl ModuleId {
216 fn source(self, tree: &ModuleTree) -> ModuleSource {
217 tree.mods[self].source
218 }
219 fn parent_link(self, tree: &ModuleTree) -> Option<LinkId> {
220 tree.mods[self].parent
221 }
222 fn parent(self, tree: &ModuleTree) -> Option<ModuleId> {
223 let link = self.parent_link(tree)?;
224 Some(tree.links[link].owner)
225 }
226 fn crate_root(self, tree: &ModuleTree) -> ModuleId {
227 generate(Some(self), move |it| it.parent(tree))
228 .last()
229 .unwrap()
230 }
231 fn child(self, tree: &ModuleTree, name: &str) -> Option<ModuleId> {
232 let link = tree.mods[self]
233 .children
234 .iter()
235 .map(|&it| &tree.links[it])
236 .find(|it| it.name == name)?;
237 Some(*link.points_to.first()?)
238 }
239 fn children<'a>(self, tree: &'a ModuleTree) -> impl Iterator<Item = (SmolStr, ModuleId)> + 'a {
240 tree.mods[self].children.iter().filter_map(move |&it| {
241 let link = &tree.links[it];
242 let module = *link.points_to.first()?;
243 Some((link.name.clone(), module))
244 })
245 }
246 fn problems(self, tree: &ModuleTree, db: &impl SyntaxDatabase) -> Vec<(SyntaxNode, Problem)> {
247 tree.mods[self]
248 .children
249 .iter()
250 .filter_map(|&it| {
251 let p = tree.links[it].problem.clone()?;
252 let s = it.bind_source(tree, db);
253 let s = s.borrowed().name().unwrap().syntax().owned();
254 Some((s, p))
255 })
256 .collect()
257 }
258}
259
260impl LinkId {
261 fn owner(self, tree: &ModuleTree) -> ModuleId {
262 tree.links[self].owner
263 }
264 fn name(self, tree: &ModuleTree) -> SmolStr {
265 tree.links[self].name.clone()
266 }
267 fn bind_source<'a>(self, tree: &ModuleTree, db: &impl SyntaxDatabase) -> ast::ModuleNode {
268 let owner = self.owner(tree);
269 match owner.source(tree).resolve(db) {
270 ModuleSourceNode::SourceFile(root) => {
271 let ast = imp::modules(root.borrowed())
272 .find(|(name, _)| name == &tree.links[self].name)
273 .unwrap()
274 .1;
275 ast.owned()
276 }
277 ModuleSourceNode::Module(it) => it,
278 }
279 }
280}
281
282#[derive(Debug, PartialEq, Eq, Hash)]
283pub(crate) struct ModuleData {
284 source: ModuleSource,
285 parent: Option<LinkId>,
286 children: Vec<LinkId>,
287}
288
289impl ModuleSource {
290 fn new_inline(file_id: FileId, module: ast::Module) -> ModuleSource {
291 assert!(!module.has_semi());
292 let ptr = SyntaxPtr::new(file_id, module.syntax());
293 ModuleSource::Module(ptr)
294 }
295
296 pub(crate) fn as_file(self) -> Option<FileId> {
297 match self {
298 ModuleSource::SourceFile(f) => Some(f),
299 ModuleSource::Module(..) => None,
300 }
301 }
302
303 pub(crate) fn file_id(self) -> FileId {
304 match self {
305 ModuleSource::SourceFile(f) => f,
306 ModuleSource::Module(ptr) => ptr.file_id(),
307 }
308 }
309
310 fn resolve(self, db: &impl SyntaxDatabase) -> ModuleSourceNode {
311 match self {
312 ModuleSource::SourceFile(file_id) => {
313 let syntax = db.file_syntax(file_id);
314 ModuleSourceNode::SourceFile(syntax.ast().owned())
315 }
316 ModuleSource::Module(ptr) => {
317 let syntax = db.resolve_syntax_ptr(ptr);
318 let syntax = syntax.borrowed();
319 let module = ast::Module::cast(syntax).unwrap();
320 ModuleSourceNode::Module(module.owned())
321 }
322 }
323 }
324}
325
326#[derive(Hash, Debug, PartialEq, Eq)]
327struct LinkData {
328 owner: ModuleId,
329 name: SmolStr,
330 points_to: Vec<ModuleId>,
331 problem: Option<Problem>,
332}
333
334impl ModuleTree {
335 fn push_mod(&mut self, data: ModuleData) -> ModuleId {
336 self.mods.push(data)
337 }
338 fn push_link(&mut self, data: LinkData) -> LinkId {
339 let owner = data.owner;
340 let id = self.links.push(data);
341 self.mods[owner].children.push(id);
342 id
343 }
344}
diff --git a/crates/ra_analysis/src/descriptors/module/nameres.rs b/crates/ra_analysis/src/descriptors/module/nameres.rs
deleted file mode 100644
index 648ec5e43..000000000
--- a/crates/ra_analysis/src/descriptors/module/nameres.rs
+++ /dev/null
@@ -1,461 +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 time::Instant,
20};
21
22use rustc_hash::FxHashMap;
23
24use ra_syntax::{
25 SmolStr, SyntaxKind::{self, *},
26 ast::{self, ModuleItemOwner}
27};
28
29use crate::{
30 Cancelable,
31 loc2id::{DefId, DefLoc},
32 descriptors::{
33 Path, PathKind,
34 DescriptorDatabase,
35 module::{ModuleId, ModuleTree, ModuleSourceNode},
36 },
37 syntax_ptr::{LocalSyntaxPtr},
38 input::SourceRootId,
39};
40
41/// Item map is the result of the name resolution. Item map contains, for each
42/// module, the set of visible items.
43#[derive(Default, Debug, PartialEq, Eq)]
44pub(crate) struct ItemMap {
45 pub(crate) per_module: FxHashMap<ModuleId, ModuleScope>,
46}
47
48#[derive(Debug, Default, PartialEq, Eq, Clone)]
49pub(crate) struct ModuleScope {
50 pub(crate) items: FxHashMap<SmolStr, Resolution>,
51}
52
53/// A set of items and imports declared inside a module, without relation to
54/// other modules.
55///
56/// This stands in-between raw syntax and name resolution and alow us to avoid
57/// recomputing name res: if `InputModuleItems` are the same, we can avoid
58/// running name resolution.
59#[derive(Debug, Default, PartialEq, Eq)]
60pub(crate) struct InputModuleItems {
61 items: Vec<ModuleItem>,
62 imports: Vec<Import>,
63}
64
65#[derive(Debug, Clone, PartialEq, Eq)]
66struct Import {
67 path: Path,
68 kind: ImportKind,
69}
70
71#[derive(Debug, Clone, PartialEq, Eq)]
72enum ImportKind {
73 Glob,
74 // TODO: make offset independent
75 Named(LocalSyntaxPtr),
76}
77
78pub(crate) fn input_module_items(
79 db: &impl DescriptorDatabase,
80 source_root: SourceRootId,
81 module_id: ModuleId,
82) -> Cancelable<Arc<InputModuleItems>> {
83 let module_tree = db._module_tree(source_root)?;
84 let source = module_id.source(&module_tree);
85 let res = match source.resolve(db) {
86 ModuleSourceNode::SourceFile(it) => {
87 let items = it.borrowed().items();
88 InputModuleItems::new(items)
89 }
90 ModuleSourceNode::Module(it) => {
91 let items = it
92 .borrowed()
93 .item_list()
94 .into_iter()
95 .flat_map(|it| it.items());
96 InputModuleItems::new(items)
97 }
98 };
99 Ok(Arc::new(res))
100}
101
102pub(crate) fn item_map(
103 db: &impl DescriptorDatabase,
104 source_root: SourceRootId,
105) -> Cancelable<Arc<ItemMap>> {
106 let start = Instant::now();
107 let module_tree = db._module_tree(source_root)?;
108 let input = module_tree
109 .modules()
110 .map(|id| {
111 let items = db._input_module_items(source_root, id)?;
112 Ok((id, items))
113 })
114 .collect::<Cancelable<FxHashMap<_, _>>>()?;
115
116 let mut resolver = Resolver {
117 db: db,
118 input: &input,
119 source_root,
120 module_tree,
121 result: ItemMap::default(),
122 };
123 resolver.resolve()?;
124 let res = resolver.result;
125 let elapsed = start.elapsed();
126 log::info!("item_map: {:?}", elapsed);
127 Ok(Arc::new(res))
128}
129
130/// Resolution is basically `DefId` atm, but it should account for stuff like
131/// multiple namespaces, ambiguity and errors.
132#[derive(Debug, Clone, PartialEq, Eq)]
133pub(crate) struct Resolution {
134 /// None for unresolved
135 pub(crate) def_id: Option<DefId>,
136 /// ident by whitch this is imported into local scope.
137 /// TODO: make this offset-independent.
138 pub(crate) import_name: Option<LocalSyntaxPtr>,
139}
140
141// #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
142// enum Namespace {
143// Types,
144// Values,
145// }
146
147// #[derive(Debug)]
148// struct PerNs<T> {
149// types: Option<T>,
150// values: Option<T>,
151// }
152
153#[derive(Debug, PartialEq, Eq)]
154struct ModuleItem {
155 ptr: LocalSyntaxPtr,
156 name: SmolStr,
157 kind: SyntaxKind,
158 vis: Vis,
159}
160
161#[derive(Debug, PartialEq, Eq)]
162enum Vis {
163 // Priv,
164 Other,
165}
166
167impl InputModuleItems {
168 fn new<'a>(items: impl Iterator<Item = ast::ModuleItem<'a>>) -> InputModuleItems {
169 let mut res = InputModuleItems::default();
170 for item in items {
171 res.add_item(item);
172 }
173 res
174 }
175
176 fn add_item(&mut self, item: ast::ModuleItem) -> Option<()> {
177 match item {
178 ast::ModuleItem::StructDef(it) => self.items.push(ModuleItem::new(it)?),
179 ast::ModuleItem::EnumDef(it) => self.items.push(ModuleItem::new(it)?),
180 ast::ModuleItem::FnDef(it) => self.items.push(ModuleItem::new(it)?),
181 ast::ModuleItem::TraitDef(it) => self.items.push(ModuleItem::new(it)?),
182 ast::ModuleItem::TypeDef(it) => self.items.push(ModuleItem::new(it)?),
183 ast::ModuleItem::ImplItem(_) => {
184 // impls don't define items
185 }
186 ast::ModuleItem::UseItem(it) => self.add_use_item(it),
187 ast::ModuleItem::ExternCrateItem(_) => {
188 // TODO
189 }
190 ast::ModuleItem::ConstDef(it) => self.items.push(ModuleItem::new(it)?),
191 ast::ModuleItem::StaticDef(it) => self.items.push(ModuleItem::new(it)?),
192 ast::ModuleItem::Module(it) => self.items.push(ModuleItem::new(it)?),
193 }
194 Some(())
195 }
196
197 fn add_use_item(&mut self, item: ast::UseItem) {
198 Path::expand_use_item(item, |path, ptr| {
199 let kind = match ptr {
200 None => ImportKind::Glob,
201 Some(ptr) => ImportKind::Named(ptr),
202 };
203 self.imports.push(Import { kind, path })
204 })
205 }
206}
207
208impl ModuleItem {
209 fn new<'a>(item: impl ast::NameOwner<'a>) -> Option<ModuleItem> {
210 let name = item.name()?.text();
211 let ptr = LocalSyntaxPtr::new(item.syntax());
212 let kind = item.syntax().kind();
213 let vis = Vis::Other;
214 let res = ModuleItem {
215 ptr,
216 name,
217 kind,
218 vis,
219 };
220 Some(res)
221 }
222}
223
224struct Resolver<'a, DB> {
225 db: &'a DB,
226 input: &'a FxHashMap<ModuleId, Arc<InputModuleItems>>,
227 source_root: SourceRootId,
228 module_tree: Arc<ModuleTree>,
229 result: ItemMap,
230}
231
232impl<'a, DB> Resolver<'a, DB>
233where
234 DB: DescriptorDatabase,
235{
236 fn resolve(&mut self) -> Cancelable<()> {
237 for (&module_id, items) in self.input.iter() {
238 self.populate_module(module_id, items)
239 }
240
241 for &module_id in self.input.keys() {
242 crate::db::check_canceled(self.db)?;
243 self.resolve_imports(module_id);
244 }
245 Ok(())
246 }
247
248 fn populate_module(&mut self, module_id: ModuleId, input: &InputModuleItems) {
249 let file_id = module_id.source(&self.module_tree).file_id();
250
251 let mut module_items = ModuleScope::default();
252
253 for import in input.imports.iter() {
254 if let Some(name) = import.path.segments.iter().last() {
255 if let ImportKind::Named(ptr) = import.kind {
256 module_items.items.insert(
257 name.clone(),
258 Resolution {
259 def_id: None,
260 import_name: Some(ptr),
261 },
262 );
263 }
264 }
265 }
266
267 for item in input.items.iter() {
268 if item.kind == MODULE {
269 // handle submodules separatelly
270 continue;
271 }
272 let ptr = item.ptr.into_global(file_id);
273 let def_loc = DefLoc::Item { ptr };
274 let def_id = self.db.id_maps().def_id(def_loc);
275 let resolution = Resolution {
276 def_id: Some(def_id),
277 import_name: None,
278 };
279 module_items.items.insert(item.name.clone(), resolution);
280 }
281
282 for (name, mod_id) in module_id.children(&self.module_tree) {
283 let def_loc = DefLoc::Module {
284 id: mod_id,
285 source_root: self.source_root,
286 };
287 let def_id = self.db.id_maps().def_id(def_loc);
288 let resolution = Resolution {
289 def_id: Some(def_id),
290 import_name: None,
291 };
292 module_items.items.insert(name, resolution);
293 }
294
295 self.result.per_module.insert(module_id, module_items);
296 }
297
298 fn resolve_imports(&mut self, module_id: ModuleId) {
299 for import in self.input[&module_id].imports.iter() {
300 self.resolve_import(module_id, import);
301 }
302 }
303
304 fn resolve_import(&mut self, module_id: ModuleId, import: &Import) {
305 let ptr = match import.kind {
306 ImportKind::Glob => return,
307 ImportKind::Named(ptr) => ptr,
308 };
309
310 let mut curr = match import.path.kind {
311 // TODO: handle extern crates
312 PathKind::Plain => return,
313 PathKind::Self_ => module_id,
314 PathKind::Super => {
315 match module_id.parent(&self.module_tree) {
316 Some(it) => it,
317 // TODO: error
318 None => return,
319 }
320 }
321 PathKind::Crate => module_id.crate_root(&self.module_tree),
322 };
323
324 for (i, name) in import.path.segments.iter().enumerate() {
325 let is_last = i == import.path.segments.len() - 1;
326
327 let def_id = match self.result.per_module[&curr].items.get(name) {
328 None => return,
329 Some(res) => match res.def_id {
330 Some(it) => it,
331 None => return,
332 },
333 };
334
335 if !is_last {
336 curr = match self.db.id_maps().def_loc(def_id) {
337 DefLoc::Module { id, .. } => id,
338 _ => return,
339 }
340 } else {
341 self.update(module_id, |items| {
342 let res = Resolution {
343 def_id: Some(def_id),
344 import_name: Some(ptr),
345 };
346 items.items.insert(name.clone(), res);
347 })
348 }
349 }
350 }
351
352 fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleScope)) {
353 let module_items = self.result.per_module.get_mut(&module_id).unwrap();
354 f(module_items)
355 }
356}
357
358#[cfg(test)]
359mod tests {
360 use crate::{
361 AnalysisChange,
362 mock_analysis::{MockAnalysis, analysis_and_position},
363 descriptors::{DescriptorDatabase, module::ModuleDescriptor},
364 input::FilesDatabase,
365};
366 use super::*;
367
368 fn item_map(fixture: &str) -> (Arc<ItemMap>, ModuleId) {
369 let (analysis, pos) = analysis_and_position(fixture);
370 let db = analysis.imp.db;
371 let source_root = db.file_source_root(pos.file_id);
372 let descr = ModuleDescriptor::guess_from_position(&*db, pos)
373 .unwrap()
374 .unwrap();
375 let module_id = descr.module_id;
376 (db._item_map(source_root).unwrap(), module_id)
377 }
378
379 #[test]
380 fn test_item_map() {
381 let (item_map, module_id) = item_map(
382 "
383 //- /lib.rs
384 mod foo;
385
386 use crate::foo::bar::Baz;
387 <|>
388
389 //- /foo/mod.rs
390 pub mod bar;
391
392 //- /foo/bar.rs
393 pub struct Baz;
394 ",
395 );
396 let name = SmolStr::from("Baz");
397 let resolution = &item_map.per_module[&module_id].items[&name];
398 assert!(resolution.def_id.is_some());
399 }
400
401 #[test]
402 fn typing_inside_a_function_should_not_invalidate_item_map() {
403 let mock_analysis = MockAnalysis::with_files(
404 "
405 //- /lib.rs
406 mod foo;
407
408 use crate::foo::bar::Baz;
409
410 fn foo() -> i32 {
411 1 + 1
412 }
413 //- /foo/mod.rs
414 pub mod bar;
415
416 //- /foo/bar.rs
417 pub struct Baz;
418 ",
419 );
420
421 let file_id = mock_analysis.id_of("/lib.rs");
422 let mut host = mock_analysis.analysis_host();
423
424 let source_root = host.analysis().imp.db.file_source_root(file_id);
425
426 {
427 let db = host.analysis().imp.db;
428 let events = db.log_executed(|| {
429 db._item_map(source_root).unwrap();
430 });
431 assert!(format!("{:?}", events).contains("_item_map"))
432 }
433
434 let mut change = AnalysisChange::new();
435
436 change.change_file(
437 file_id,
438 "
439 mod foo;
440
441 use crate::foo::bar::Baz;
442
443 fn foo() -> i32 { 92 }
444 "
445 .to_string(),
446 );
447
448 host.apply_change(change);
449
450 {
451 let db = host.analysis().imp.db;
452 let events = db.log_executed(|| {
453 db._item_map(source_root).unwrap();
454 });
455 // assert!(
456 // !format!("{:?}", events).contains("_item_map"),
457 // "{:#?}", events
458 // )
459 }
460 }
461}
diff --git a/crates/ra_analysis/src/descriptors/path.rs b/crates/ra_analysis/src/descriptors/path.rs
deleted file mode 100644
index 99fca18b1..000000000
--- a/crates/ra_analysis/src/descriptors/path.rs
+++ /dev/null
@@ -1,153 +0,0 @@
1use ra_syntax::{SmolStr, ast, AstNode};
2
3use crate::syntax_ptr::LocalSyntaxPtr;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub(crate) struct Path {
7 pub(crate) kind: PathKind,
8 pub(crate) segments: Vec<SmolStr>,
9}
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub(crate) enum PathKind {
13 Plain,
14 Self_,
15 Super,
16 Crate,
17}
18
19impl Path {
20 /// Calls `cb` with all paths, represented by this use item.
21 pub(crate) fn expand_use_item(
22 item: ast::UseItem,
23 mut cb: impl FnMut(Path, Option<LocalSyntaxPtr>),
24 ) {
25 if let Some(tree) = item.use_tree() {
26 expand_use_tree(None, tree, &mut cb);
27 }
28 }
29
30 /// Converts an `ast::Path` to `Path`. Works with use trees.
31 pub(crate) fn from_ast(mut path: ast::Path) -> Option<Path> {
32 let mut kind = PathKind::Plain;
33 let mut segments = Vec::new();
34 loop {
35 let segment = path.segment()?;
36 match segment.kind()? {
37 ast::PathSegmentKind::Name(name) => segments.push(name.text()),
38 ast::PathSegmentKind::CrateKw => {
39 kind = PathKind::Crate;
40 break;
41 }
42 ast::PathSegmentKind::SelfKw => {
43 kind = PathKind::Self_;
44 break;
45 }
46 ast::PathSegmentKind::SuperKw => {
47 kind = PathKind::Super;
48 break;
49 }
50 }
51 path = match qualifier(path) {
52 Some(it) => it,
53 None => break,
54 };
55 }
56 segments.reverse();
57 return Some(Path { kind, segments });
58
59 fn qualifier(path: ast::Path) -> Option<ast::Path> {
60 if let Some(q) = path.qualifier() {
61 return Some(q);
62 }
63 // TODO: this bottom up traversal is not too precise.
64 // Should we handle do a top-down analysiss, recording results?
65 let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?;
66 let use_tree = use_tree_list.parent_use_tree();
67 use_tree.path()
68 }
69 }
70
71 /// `true` is this path is a single identifier, like `foo`
72 pub(crate) fn is_ident(&self) -> bool {
73 self.kind == PathKind::Plain && self.segments.len() == 1
74 }
75}
76
77fn expand_use_tree(
78 prefix: Option<Path>,
79 tree: ast::UseTree,
80 cb: &mut impl FnMut(Path, Option<LocalSyntaxPtr>),
81) {
82 if let Some(use_tree_list) = tree.use_tree_list() {
83 let prefix = match tree.path() {
84 None => prefix,
85 Some(path) => match convert_path(prefix, path) {
86 Some(it) => Some(it),
87 None => return, // TODO: report errors somewhere
88 },
89 };
90 for tree in use_tree_list.use_trees() {
91 expand_use_tree(prefix.clone(), tree, cb);
92 }
93 } else {
94 if let Some(ast_path) = tree.path() {
95 if let Some(path) = convert_path(prefix, ast_path) {
96 let ptr = if tree.has_star() {
97 None
98 } else {
99 let ptr = LocalSyntaxPtr::new(ast_path.segment().unwrap().syntax());
100 Some(ptr)
101 };
102 cb(path, ptr)
103 }
104 }
105 }
106}
107
108fn convert_path(prefix: Option<Path>, path: ast::Path) -> Option<Path> {
109 let prefix = if let Some(qual) = path.qualifier() {
110 Some(convert_path(prefix, qual)?)
111 } else {
112 None
113 };
114 let segment = path.segment()?;
115 let res = match segment.kind()? {
116 ast::PathSegmentKind::Name(name) => {
117 let mut res = prefix.unwrap_or_else(|| Path {
118 kind: PathKind::Plain,
119 segments: Vec::with_capacity(1),
120 });
121 res.segments.push(name.text());
122 res
123 }
124 ast::PathSegmentKind::CrateKw => {
125 if prefix.is_some() {
126 return None;
127 }
128 Path {
129 kind: PathKind::Crate,
130 segments: Vec::new(),
131 }
132 }
133 ast::PathSegmentKind::SelfKw => {
134 if prefix.is_some() {
135 return None;
136 }
137 Path {
138 kind: PathKind::Self_,
139 segments: Vec::new(),
140 }
141 }
142 ast::PathSegmentKind::SuperKw => {
143 if prefix.is_some() {
144 return None;
145 }
146 Path {
147 kind: PathKind::Super,
148 segments: Vec::new(),
149 }
150 }
151 };
152 Some(res)
153}