aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_analysis/src')
-rw-r--r--crates/ra_analysis/src/arena.rs66
-rw-r--r--crates/ra_analysis/src/completion/mod.rs10
-rw-r--r--crates/ra_analysis/src/completion/reference_completion.rs16
-rw-r--r--crates/ra_analysis/src/db.rs126
-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.rs137
-rw-r--r--crates/ra_analysis/src/descriptors/module/imp.rs229
-rw-r--r--crates/ra_analysis/src/descriptors/module/mod.rs378
-rw-r--r--crates/ra_analysis/src/descriptors/module/nameres.rs549
-rw-r--r--crates/ra_analysis/src/descriptors/path.rs148
-rw-r--r--crates/ra_analysis/src/imp.rs238
-rw-r--r--crates/ra_analysis/src/input.rs79
-rw-r--r--crates/ra_analysis/src/lib.rs144
-rw-r--r--crates/ra_analysis/src/loc2id.rs141
-rw-r--r--crates/ra_analysis/src/symbol_index.rs28
-rw-r--r--crates/ra_analysis/src/syntax_ptr.rs79
18 files changed, 285 insertions, 2665 deletions
diff --git a/crates/ra_analysis/src/arena.rs b/crates/ra_analysis/src/arena.rs
deleted file mode 100644
index a752ec0c1..000000000
--- a/crates/ra_analysis/src/arena.rs
+++ /dev/null
@@ -1,66 +0,0 @@
1//! A simple id-based arena, similar to https://github.com/fitzgen/id-arena.
2//! We use our own version for more compact id's and to allow inherent impls
3//! on Ids.
4
5use std::{
6 fmt,
7 hash::{Hash, Hasher},
8 marker::PhantomData,
9};
10
11pub(crate) struct Id<T> {
12 idx: u32,
13 _ty: PhantomData<fn() -> T>,
14}
15
16impl<T> fmt::Debug for Id<T> {
17 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
18 f.debug_tuple("Id").field(&self.idx).finish()
19 }
20}
21impl<T> Copy for Id<T> {}
22impl<T> Clone for Id<T> {
23 fn clone(&self) -> Id<T> {
24 *self
25 }
26}
27
28impl<T> PartialEq for Id<T> {
29 fn eq(&self, other: &Id<T>) -> bool {
30 self.idx == other.idx
31 }
32}
33
34impl<T> Eq for Id<T> {}
35
36impl<T> Hash for Id<T> {
37 fn hash<H: Hasher>(&self, h: &mut H) {
38 self.idx.hash(h);
39 }
40}
41
42#[derive(Debug, PartialEq, Eq)]
43pub(crate) struct ArenaBehavior<T> {
44 _ty: PhantomData<T>,
45}
46
47impl<T> id_arena::ArenaBehavior for ArenaBehavior<T> {
48 type Id = Id<T>;
49 fn new_arena_id() -> usize {
50 0
51 }
52 fn new_id(_arena_id: usize, index: usize) -> Id<T> {
53 Id {
54 idx: index as u32,
55 _ty: PhantomData,
56 }
57 }
58 fn index(id: Id<T>) -> usize {
59 id.idx as usize
60 }
61 fn arena_id(_id: Id<T>) -> usize {
62 0
63 }
64}
65
66pub(crate) type Arena<T> = id_arena::Arena<T, ArenaBehavior<T>>;
diff --git a/crates/ra_analysis/src/completion/mod.rs b/crates/ra_analysis/src/completion/mod.rs
index 5ef278127..e5ba92acd 100644
--- a/crates/ra_analysis/src/completion/mod.rs
+++ b/crates/ra_analysis/src/completion/mod.rs
@@ -7,13 +7,11 @@ use ra_syntax::{
7 AstNode, AtomEdit, 7 AstNode, AtomEdit,
8 SyntaxNodeRef, 8 SyntaxNodeRef,
9}; 9};
10use ra_db::SyntaxDatabase;
10use rustc_hash::{FxHashMap}; 11use rustc_hash::{FxHashMap};
11 12
12use crate::{ 13use crate::{
13 db::{self, SyntaxDatabase}, 14 db,
14 descriptors::{
15 module::{ModuleDescriptor}
16 },
17 Cancelable, FilePosition 15 Cancelable, FilePosition
18}; 16};
19 17
@@ -31,14 +29,14 @@ pub(crate) fn completions(
31 db: &db::RootDatabase, 29 db: &db::RootDatabase,
32 position: FilePosition, 30 position: FilePosition,
33) -> Cancelable<Option<Vec<CompletionItem>>> { 31) -> Cancelable<Option<Vec<CompletionItem>>> {
34 let original_file = db.file_syntax(position.file_id); 32 let original_file = db.source_file(position.file_id);
35 // Insert a fake ident to get a valid parse tree 33 // Insert a fake ident to get a valid parse tree
36 let file = { 34 let file = {
37 let edit = AtomEdit::insert(position.offset, "intellijRulezz".to_string()); 35 let edit = AtomEdit::insert(position.offset, "intellijRulezz".to_string());
38 original_file.reparse(&edit) 36 original_file.reparse(&edit)
39 }; 37 };
40 38
41 let module = ctry!(ModuleDescriptor::guess_from_position(db, position)?); 39 let module = ctry!(hir::Module::guess_from_position(db, position)?);
42 40
43 let mut res = Vec::new(); 41 let mut res = Vec::new();
44 let mut has_completions = false; 42 let mut has_completions = false;
diff --git a/crates/ra_analysis/src/completion/reference_completion.rs b/crates/ra_analysis/src/completion/reference_completion.rs
index 858b52e76..e1a2d5241 100644
--- a/crates/ra_analysis/src/completion/reference_completion.rs
+++ b/crates/ra_analysis/src/completion/reference_completion.rs
@@ -6,23 +6,23 @@ use ra_syntax::{
6 ast::{self, LoopBodyOwner}, 6 ast::{self, LoopBodyOwner},
7 SyntaxKind::*, 7 SyntaxKind::*,
8}; 8};
9use hir::{
10 self,
11 FnScopes,
12 Def,
13 Path,
14};
9 15
10use crate::{ 16use crate::{
11 db::RootDatabase, 17 db::RootDatabase,
12 completion::CompletionItem, 18 completion::CompletionItem,
13 descriptors::{
14 module::{ModuleDescriptor},
15 function::FnScopes,
16 Def,
17 Path,
18 },
19 Cancelable 19 Cancelable
20}; 20};
21 21
22pub(super) fn completions( 22pub(super) fn completions(
23 acc: &mut Vec<CompletionItem>, 23 acc: &mut Vec<CompletionItem>,
24 db: &RootDatabase, 24 db: &RootDatabase,
25 module: &ModuleDescriptor, 25 module: &hir::Module,
26 file: &SourceFileNode, 26 file: &SourceFileNode,
27 name_ref: ast::NameRef, 27 name_ref: ast::NameRef,
28) -> Cancelable<()> { 28) -> Cancelable<()> {
@@ -150,7 +150,7 @@ fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<Completi
150fn complete_path( 150fn complete_path(
151 acc: &mut Vec<CompletionItem>, 151 acc: &mut Vec<CompletionItem>,
152 db: &RootDatabase, 152 db: &RootDatabase,
153 module: &ModuleDescriptor, 153 module: &hir::Module,
154 mut path: Path, 154 mut path: Path,
155) -> Cancelable<()> { 155) -> Cancelable<()> {
156 if path.segments.is_empty() { 156 if path.segments.is_empty() {
diff --git a/crates/ra_analysis/src/db.rs b/crates/ra_analysis/src/db.rs
index 4e34265fb..7fc3fe31b 100644
--- a/crates/ra_analysis/src/db.rs
+++ b/crates/ra_analysis/src/db.rs
@@ -1,17 +1,12 @@
1use std::sync::Arc; 1use std::sync::Arc;
2#[cfg(test)] 2#[cfg(test)]
3use parking_lot::Mutex; 3use parking_lot::Mutex;
4use ra_editor::LineIndex;
5use ra_syntax::{SourceFileNode, SyntaxNode};
6use salsa::{self, Database}; 4use salsa::{self, Database};
5use ra_db::{LocationIntener, BaseDatabase};
6use hir::{self, DefId, DefLoc, FnId, SourceItemId};
7 7
8use crate::{ 8use crate::{
9 db, 9 symbol_index,
10 descriptors,
11 symbol_index::SymbolIndex,
12 syntax_ptr::SyntaxPtr,
13 loc2id::{IdMaps, IdDatabase},
14 Cancelable, Canceled, FileId,
15}; 10};
16 11
17#[derive(Debug)] 12#[derive(Debug)]
@@ -22,7 +17,13 @@ pub(crate) struct RootDatabase {
22 events: (), 17 events: (),
23 18
24 runtime: salsa::Runtime<RootDatabase>, 19 runtime: salsa::Runtime<RootDatabase>,
25 id_maps: IdMaps, 20 id_maps: Arc<IdMaps>,
21}
22
23#[derive(Debug, Default)]
24struct IdMaps {
25 fns: LocationIntener<SourceItemId, FnId>,
26 defs: LocationIntener<DefLoc, DefId>,
26} 27}
27 28
28impl salsa::Database for RootDatabase { 29impl salsa::Database for RootDatabase {
@@ -47,26 +48,18 @@ impl Default for RootDatabase {
47 let mut db = RootDatabase { 48 let mut db = RootDatabase {
48 events: Default::default(), 49 events: Default::default(),
49 runtime: salsa::Runtime::default(), 50 runtime: salsa::Runtime::default(),
50 id_maps: IdMaps::default(), 51 id_maps: Default::default(),
51 }; 52 };
52 db.query_mut(crate::input::SourceRootQuery) 53 db.query_mut(ra_db::SourceRootQuery)
53 .set(crate::input::WORKSPACE, Default::default()); 54 .set(ra_db::WORKSPACE, Default::default());
54 db.query_mut(crate::input::CrateGraphQuery) 55 db.query_mut(ra_db::CrateGraphQuery)
55 .set((), Default::default()); 56 .set((), Default::default());
56 db.query_mut(crate::input::LibrariesQuery) 57 db.query_mut(ra_db::LibrariesQuery)
57 .set((), Default::default()); 58 .set((), Default::default());
58 db 59 db
59 } 60 }
60} 61}
61 62
62pub(crate) fn check_canceled(db: &impl salsa::Database) -> Cancelable<()> {
63 if db.salsa_runtime().is_current_revision_canceled() {
64 Err(Canceled)
65 } else {
66 Ok(())
67 }
68}
69
70impl salsa::ParallelDatabase for RootDatabase { 63impl salsa::ParallelDatabase for RootDatabase {
71 fn snapshot(&self) -> salsa::Snapshot<RootDatabase> { 64 fn snapshot(&self) -> salsa::Snapshot<RootDatabase> {
72 salsa::Snapshot::new(RootDatabase { 65 salsa::Snapshot::new(RootDatabase {
@@ -77,9 +70,17 @@ impl salsa::ParallelDatabase for RootDatabase {
77 } 70 }
78} 71}
79 72
80impl IdDatabase for RootDatabase { 73impl BaseDatabase for RootDatabase {}
81 fn id_maps(&self) -> &IdMaps { 74
82 &self.id_maps 75impl AsRef<LocationIntener<DefLoc, DefId>> for RootDatabase {
76 fn as_ref(&self) -> &LocationIntener<DefLoc, DefId> {
77 &self.id_maps.defs
78 }
79}
80
81impl AsRef<LocationIntener<hir::SourceItemId, FnId>> for RootDatabase {
82 fn as_ref(&self) -> &LocationIntener<hir::SourceItemId, FnId> {
83 &self.id_maps.fns
83 } 84 }
84} 85}
85 86
@@ -108,63 +109,30 @@ impl RootDatabase {
108 109
109salsa::database_storage! { 110salsa::database_storage! {
110 pub(crate) struct RootDatabaseStorage for RootDatabase { 111 pub(crate) struct RootDatabaseStorage for RootDatabase {
111 impl crate::input::FilesDatabase { 112 impl ra_db::FilesDatabase {
112 fn file_text() for crate::input::FileTextQuery; 113 fn file_text() for ra_db::FileTextQuery;
113 fn file_source_root() for crate::input::FileSourceRootQuery; 114 fn file_source_root() for ra_db::FileSourceRootQuery;
114 fn source_root() for crate::input::SourceRootQuery; 115 fn source_root() for ra_db::SourceRootQuery;
115 fn libraries() for crate::input::LibrariesQuery; 116 fn libraries() for ra_db::LibrariesQuery;
116 fn library_symbols() for crate::input::LibrarySymbolsQuery; 117 fn crate_graph() for ra_db::CrateGraphQuery;
117 fn crate_graph() for crate::input::CrateGraphQuery;
118 } 118 }
119 impl SyntaxDatabase { 119 impl ra_db::SyntaxDatabase {
120 fn file_syntax() for FileSyntaxQuery; 120 fn source_file() for ra_db::SourceFileQuery;
121 fn file_lines() for FileLinesQuery; 121 fn file_lines() for ra_db::FileLinesQuery;
122 fn file_symbols() for FileSymbolsQuery;
123 fn resolve_syntax_ptr() for ResolveSyntaxPtrQuery;
124 } 122 }
125 impl descriptors::DescriptorDatabase { 123 impl symbol_index::SymbolsDatabase {
126 fn module_tree() for descriptors::ModuleTreeQuery; 124 fn file_symbols() for symbol_index::FileSymbolsQuery;
127 fn fn_scopes() for descriptors::FnScopesQuery; 125 fn library_symbols() for symbol_index::LibrarySymbolsQuery;
128 fn _file_items() for descriptors::FileItemsQuery;
129 fn _file_item() for descriptors::FileItemQuery;
130 fn _input_module_items() for descriptors::InputModuleItemsQuery;
131 fn _item_map() for descriptors::ItemMapQuery;
132 fn _fn_syntax() for descriptors::FnSyntaxQuery;
133 fn _submodules() for descriptors::SubmodulesQuery;
134 } 126 }
135 } 127 impl hir::db::HirDatabase {
136} 128 fn module_tree() for hir::db::ModuleTreeQuery;
137 129 fn fn_scopes() for hir::db::FnScopesQuery;
138salsa::query_group! { 130 fn file_items() for hir::db::SourceFileItemsQuery;
139 pub(crate) trait SyntaxDatabase: crate::input::FilesDatabase { 131 fn file_item() for hir::db::FileItemQuery;
140 fn file_syntax(file_id: FileId) -> SourceFileNode { 132 fn input_module_items() for hir::db::InputModuleItemsQuery;
141 type FileSyntaxQuery; 133 fn item_map() for hir::db::ItemMapQuery;
142 } 134 fn fn_syntax() for hir::db::FnSyntaxQuery;
143 fn file_lines(file_id: FileId) -> Arc<LineIndex> { 135 fn submodules() for hir::db::SubmodulesQuery;
144 type FileLinesQuery;
145 }
146 fn file_symbols(file_id: FileId) -> Cancelable<Arc<SymbolIndex>> {
147 type FileSymbolsQuery;
148 }
149 fn resolve_syntax_ptr(ptr: SyntaxPtr) -> SyntaxNode {
150 type ResolveSyntaxPtrQuery;
151 // Don't retain syntax trees in memory
152 storage dependencies;
153 use fn crate::syntax_ptr::resolve_syntax_ptr;
154 } 136 }
155 } 137 }
156} 138}
157
158fn file_syntax(db: &impl SyntaxDatabase, file_id: FileId) -> SourceFileNode {
159 let text = db.file_text(file_id);
160 SourceFileNode::parse(&*text)
161}
162fn file_lines(db: &impl SyntaxDatabase, file_id: FileId) -> Arc<LineIndex> {
163 let text = db.file_text(file_id);
164 Arc::new(LineIndex::new(&*text))
165}
166fn file_symbols(db: &impl SyntaxDatabase, file_id: FileId) -> Cancelable<Arc<SymbolIndex>> {
167 db::check_canceled(db)?;
168 let syntax = db.file_syntax(file_id);
169 Ok(Arc::new(SymbolIndex::for_file(file_id, syntax)))
170}
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 5307a0a8e..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.alloc(ScopeData {
62 parent: None,
63 entries: vec![],
64 })
65 }
66 fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
67 self.scopes.alloc(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 7a1bcf447..000000000
--- a/crates/ra_analysis/src/descriptors/mod.rs
+++ /dev/null
@@ -1,137 +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, SyntaxNode,
10};
11
12use crate::{
13 FileId,
14 db::SyntaxDatabase,
15 descriptors::function::{resolve_local_name, FnId, FnScopes},
16 descriptors::module::{
17 ModuleId, ModuleTree, ModuleSource, ModuleDescriptor,
18 nameres::{ItemMap, InputModuleItems, FileItems}
19 },
20 input::SourceRootId,
21 loc2id::{IdDatabase, DefId, DefLoc},
22 syntax_ptr::LocalSyntaxPtr,
23 Cancelable,
24};
25
26pub(crate) use self::path::{Path, PathKind};
27pub(crate) use self::module::nameres::FileItemId;
28
29salsa::query_group! {
30 pub(crate) trait DescriptorDatabase: SyntaxDatabase + IdDatabase {
31 fn fn_scopes(fn_id: FnId) -> Arc<FnScopes> {
32 type FnScopesQuery;
33 use fn function::imp::fn_scopes;
34 }
35
36 fn _file_items(file_id: FileId) -> Arc<FileItems> {
37 type FileItemsQuery;
38 storage dependencies;
39 use fn module::nameres::file_items;
40 }
41
42 fn _file_item(file_id: FileId, file_item_id: FileItemId) -> SyntaxNode {
43 type FileItemQuery;
44 storage dependencies;
45 use fn module::nameres::file_item;
46 }
47
48 fn _input_module_items(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable<Arc<InputModuleItems>> {
49 type InputModuleItemsQuery;
50 use fn module::nameres::input_module_items;
51 }
52 fn _item_map(source_root_id: SourceRootId) -> Cancelable<Arc<ItemMap>> {
53 type ItemMapQuery;
54 use fn module::nameres::item_map;
55 }
56 fn _module_tree(source_root_id: SourceRootId) -> Cancelable<Arc<ModuleTree>> {
57 type ModuleTreeQuery;
58 use fn module::imp::module_tree;
59 }
60 fn _fn_syntax(fn_id: FnId) -> FnDefNode {
61 type FnSyntaxQuery;
62 // Don't retain syntax trees in memory
63 storage dependencies;
64 use fn function::imp::fn_syntax;
65 }
66 fn _submodules(source: ModuleSource) -> Cancelable<Arc<Vec<module::imp::Submodule>>> {
67 type SubmodulesQuery;
68 use fn module::imp::submodules;
69 }
70 }
71}
72
73pub(crate) enum Def {
74 Module(ModuleDescriptor),
75 Item,
76}
77
78impl DefId {
79 pub(crate) fn resolve(self, db: &impl DescriptorDatabase) -> Cancelable<Def> {
80 let loc = db.id_maps().def_loc(self);
81 let res = match loc {
82 DefLoc::Module { id, source_root } => {
83 let descr = ModuleDescriptor::new(db, source_root, id)?;
84 Def::Module(descr)
85 }
86 DefLoc::Item { .. } => Def::Item,
87 };
88 Ok(res)
89 }
90}
91
92#[derive(Debug)]
93pub struct ReferenceDescriptor {
94 pub range: TextRange,
95 pub name: String,
96}
97
98#[derive(Debug)]
99pub struct DeclarationDescriptor<'a> {
100 pat: ast::BindPat<'a>,
101 pub range: TextRange,
102}
103
104impl<'a> DeclarationDescriptor<'a> {
105 pub fn new(pat: ast::BindPat) -> DeclarationDescriptor {
106 let range = pat.syntax().range();
107
108 DeclarationDescriptor { pat, range }
109 }
110
111 pub fn find_all_refs(&self) -> Vec<ReferenceDescriptor> {
112 let name_ptr = LocalSyntaxPtr::new(self.pat.syntax());
113
114 let fn_def = match self.pat.syntax().ancestors().find_map(ast::FnDef::cast) {
115 Some(def) => def,
116 None => return Default::default(),
117 };
118
119 let fn_scopes = FnScopes::new(fn_def);
120
121 let refs: Vec<_> = fn_def
122 .syntax()
123 .descendants()
124 .filter_map(ast::NameRef::cast)
125 .filter(|name_ref| match resolve_local_name(*name_ref, &fn_scopes) {
126 None => false,
127 Some(entry) => entry.ptr() == name_ptr,
128 })
129 .map(|name_ref| ReferenceDescriptor {
130 name: name_ref.syntax().text().to_string(),
131 range: name_ref.syntax().range(),
132 })
133 .collect();
134
135 refs
136 }
137}
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 78911d5d9..000000000
--- a/crates/ra_analysis/src/descriptors/module/mod.rs
+++ /dev/null
@@ -1,378 +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 loc2id::{DefLoc, DefId},
21};
22
23pub(crate) use self::nameres::ModuleScope;
24
25/// `ModuleDescriptor` is API entry point to get all the information
26/// about a particular module.
27#[derive(Debug, Clone)]
28pub(crate) struct ModuleDescriptor {
29 tree: Arc<ModuleTree>,
30 source_root_id: SourceRootId,
31 module_id: ModuleId,
32}
33
34impl ModuleDescriptor {
35 /// Lookup `ModuleDescriptor` by `FileId`. Note that this is inherently
36 /// lossy transformation: in general, a single source might correspond to
37 /// several modules.
38 pub fn guess_from_file_id(
39 db: &impl DescriptorDatabase,
40 file_id: FileId,
41 ) -> Cancelable<Option<ModuleDescriptor>> {
42 ModuleDescriptor::guess_from_source(db, file_id, ModuleSource::SourceFile(file_id))
43 }
44
45 /// Lookup `ModuleDescriptor` by position in the source code. Note that this
46 /// is inherently lossy transformation: in general, a single source might
47 /// correspond to several modules.
48 pub fn guess_from_position(
49 db: &impl DescriptorDatabase,
50 position: FilePosition,
51 ) -> Cancelable<Option<ModuleDescriptor>> {
52 let file = db.file_syntax(position.file_id);
53 let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset)
54 {
55 Some(m) if !m.has_semi() => ModuleSource::new_inline(position.file_id, m),
56 _ => ModuleSource::SourceFile(position.file_id),
57 };
58 ModuleDescriptor::guess_from_source(db, position.file_id, module_source)
59 }
60
61 fn guess_from_source(
62 db: &impl DescriptorDatabase,
63 file_id: FileId,
64 module_source: ModuleSource,
65 ) -> Cancelable<Option<ModuleDescriptor>> {
66 let source_root_id = db.file_source_root(file_id);
67 let module_tree = db._module_tree(source_root_id)?;
68
69 let res = match module_tree.any_module_for_source(module_source) {
70 None => None,
71 Some(module_id) => Some(ModuleDescriptor {
72 tree: module_tree,
73 source_root_id,
74 module_id,
75 }),
76 };
77 Ok(res)
78 }
79
80 pub(super) fn new(
81 db: &impl DescriptorDatabase,
82 source_root_id: SourceRootId,
83 module_id: ModuleId,
84 ) -> Cancelable<ModuleDescriptor> {
85 let module_tree = db._module_tree(source_root_id)?;
86 let res = ModuleDescriptor {
87 tree: module_tree,
88 source_root_id,
89 module_id,
90 };
91 Ok(res)
92 }
93
94 /// Returns `mod foo;` or `mod foo {}` node whihc declared this module.
95 /// Returns `None` for the root module
96 pub fn parent_link_source(
97 &self,
98 db: &impl DescriptorDatabase,
99 ) -> Option<(FileId, ast::ModuleNode)> {
100 let link = self.module_id.parent_link(&self.tree)?;
101 let file_id = link.owner(&self.tree).source(&self.tree).file_id();
102 let src = link.bind_source(&self.tree, db);
103 Some((file_id, src))
104 }
105
106 pub fn source(&self) -> ModuleSource {
107 self.module_id.source(&self.tree)
108 }
109
110 /// Parent module. Returns `None` if this is a root module.
111 pub fn parent(&self) -> Option<ModuleDescriptor> {
112 let parent_id = self.module_id.parent(&self.tree)?;
113 Some(ModuleDescriptor {
114 module_id: parent_id,
115 ..self.clone()
116 })
117 }
118
119 /// The root of the tree this module is part of
120 pub fn crate_root(&self) -> ModuleDescriptor {
121 let root_id = self.module_id.crate_root(&self.tree);
122 ModuleDescriptor {
123 module_id: root_id,
124 ..self.clone()
125 }
126 }
127
128 /// `name` is `None` for the crate's root module
129 #[allow(unused)]
130 pub fn name(&self) -> Option<SmolStr> {
131 let link = self.module_id.parent_link(&self.tree)?;
132 Some(link.name(&self.tree))
133 }
134
135 pub fn def_id(&self, db: &impl DescriptorDatabase) -> DefId {
136 let def_loc = DefLoc::Module {
137 id: self.module_id,
138 source_root: self.source_root_id,
139 };
140 db.id_maps().def_id(def_loc)
141 }
142
143 /// Finds a child module with the specified name.
144 pub fn child(&self, name: &str) -> Option<ModuleDescriptor> {
145 let child_id = self.module_id.child(&self.tree, name)?;
146 Some(ModuleDescriptor {
147 module_id: child_id,
148 ..self.clone()
149 })
150 }
151
152 /// Returns a `ModuleScope`: a set of items, visible in this module.
153 pub(crate) fn scope(&self, db: &impl DescriptorDatabase) -> Cancelable<ModuleScope> {
154 let item_map = db._item_map(self.source_root_id)?;
155 let res = item_map.per_module[&self.module_id].clone();
156 Ok(res)
157 }
158
159 pub(crate) fn resolve_path(
160 &self,
161 db: &impl DescriptorDatabase,
162 path: Path,
163 ) -> Cancelable<Option<DefId>> {
164 let mut curr = match path.kind {
165 PathKind::Crate => self.crate_root(),
166 PathKind::Self_ | PathKind::Plain => self.clone(),
167 PathKind::Super => ctry!(self.parent()),
168 }
169 .def_id(db);
170
171 let segments = path.segments;
172 for name in segments.iter() {
173 let module = match db.id_maps().def_loc(curr) {
174 DefLoc::Module { id, source_root } => ModuleDescriptor::new(db, source_root, id)?,
175 _ => return Ok(None),
176 };
177 let scope = module.scope(db)?;
178 curr = ctry!(ctry!(scope.get(&name)).def_id);
179 }
180 Ok(Some(curr))
181 }
182
183 pub fn problems(&self, db: &impl DescriptorDatabase) -> Vec<(SyntaxNode, Problem)> {
184 self.module_id.problems(&self.tree, db)
185 }
186}
187
188/// Phisically, rust source is organized as a set of files, but logically it is
189/// organized as a tree of modules. Usually, a single file corresponds to a
190/// single module, but it is not nessary the case.
191///
192/// Module encapsulate the logic of transitioning from the fuzzy world of files
193/// (which can have multiple parents) to the precise world of modules (which
194/// always have one parent).
195#[derive(Default, Debug, PartialEq, Eq)]
196pub(crate) struct ModuleTree {
197 mods: Arena<ModuleData>,
198 links: Arena<LinkData>,
199}
200
201impl ModuleTree {
202 fn modules<'a>(&'a self) -> impl Iterator<Item = ModuleId> + 'a {
203 self.mods.iter().map(|(id, _)| id)
204 }
205
206 fn modules_for_source(&self, source: ModuleSource) -> Vec<ModuleId> {
207 self.mods
208 .iter()
209 .filter(|(_idx, it)| it.source == source)
210 .map(|(idx, _)| idx)
211 .collect()
212 }
213
214 fn any_module_for_source(&self, source: ModuleSource) -> Option<ModuleId> {
215 self.modules_for_source(source).pop()
216 }
217}
218
219/// `ModuleSource` is the syntax tree element that produced this module:
220/// either a file, or an inlinde module.
221#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
222pub(crate) enum ModuleSource {
223 SourceFile(FileId),
224 #[allow(dead_code)]
225 Module(SyntaxPtr),
226}
227
228/// An owned syntax node for a module. Unlike `ModuleSource`,
229/// this holds onto the AST for the whole file.
230enum ModuleSourceNode {
231 SourceFile(ast::SourceFileNode),
232 Module(ast::ModuleNode),
233}
234
235pub(crate) type ModuleId = Id<ModuleData>;
236type LinkId = Id<LinkData>;
237
238#[derive(Clone, Debug, Hash, PartialEq, Eq)]
239pub enum Problem {
240 UnresolvedModule {
241 candidate: RelativePathBuf,
242 },
243 NotDirOwner {
244 move_to: RelativePathBuf,
245 candidate: RelativePathBuf,
246 },
247}
248
249impl ModuleId {
250 fn source(self, tree: &ModuleTree) -> ModuleSource {
251 tree.mods[self].source
252 }
253 fn parent_link(self, tree: &ModuleTree) -> Option<LinkId> {
254 tree.mods[self].parent
255 }
256 fn parent(self, tree: &ModuleTree) -> Option<ModuleId> {
257 let link = self.parent_link(tree)?;
258 Some(tree.links[link].owner)
259 }
260 fn crate_root(self, tree: &ModuleTree) -> ModuleId {
261 generate(Some(self), move |it| it.parent(tree))
262 .last()
263 .unwrap()
264 }
265 fn child(self, tree: &ModuleTree, name: &str) -> Option<ModuleId> {
266 let link = tree.mods[self]
267 .children
268 .iter()
269 .map(|&it| &tree.links[it])
270 .find(|it| it.name == name)?;
271 Some(*link.points_to.first()?)
272 }
273 fn children<'a>(self, tree: &'a ModuleTree) -> impl Iterator<Item = (SmolStr, ModuleId)> + 'a {
274 tree.mods[self].children.iter().filter_map(move |&it| {
275 let link = &tree.links[it];
276 let module = *link.points_to.first()?;
277 Some((link.name.clone(), module))
278 })
279 }
280 fn problems(self, tree: &ModuleTree, db: &impl SyntaxDatabase) -> Vec<(SyntaxNode, Problem)> {
281 tree.mods[self]
282 .children
283 .iter()
284 .filter_map(|&it| {
285 let p = tree.links[it].problem.clone()?;
286 let s = it.bind_source(tree, db);
287 let s = s.borrowed().name().unwrap().syntax().owned();
288 Some((s, p))
289 })
290 .collect()
291 }
292}
293
294impl LinkId {
295 fn owner(self, tree: &ModuleTree) -> ModuleId {
296 tree.links[self].owner
297 }
298 fn name(self, tree: &ModuleTree) -> SmolStr {
299 tree.links[self].name.clone()
300 }
301 fn bind_source<'a>(self, tree: &ModuleTree, db: &impl SyntaxDatabase) -> ast::ModuleNode {
302 let owner = self.owner(tree);
303 match owner.source(tree).resolve(db) {
304 ModuleSourceNode::SourceFile(root) => {
305 let ast = imp::modules(root.borrowed())
306 .find(|(name, _)| name == &tree.links[self].name)
307 .unwrap()
308 .1;
309 ast.owned()
310 }
311 ModuleSourceNode::Module(it) => it,
312 }
313 }
314}
315
316#[derive(Debug, PartialEq, Eq, Hash)]
317pub(crate) struct ModuleData {
318 source: ModuleSource,
319 parent: Option<LinkId>,
320 children: Vec<LinkId>,
321}
322
323impl ModuleSource {
324 fn new_inline(file_id: FileId, module: ast::Module) -> ModuleSource {
325 assert!(!module.has_semi());
326 let ptr = SyntaxPtr::new(file_id, module.syntax());
327 ModuleSource::Module(ptr)
328 }
329
330 pub(crate) fn as_file(self) -> Option<FileId> {
331 match self {
332 ModuleSource::SourceFile(f) => Some(f),
333 ModuleSource::Module(..) => None,
334 }
335 }
336
337 pub(crate) fn file_id(self) -> FileId {
338 match self {
339 ModuleSource::SourceFile(f) => f,
340 ModuleSource::Module(ptr) => ptr.file_id(),
341 }
342 }
343
344 fn resolve(self, db: &impl SyntaxDatabase) -> ModuleSourceNode {
345 match self {
346 ModuleSource::SourceFile(file_id) => {
347 let syntax = db.file_syntax(file_id);
348 ModuleSourceNode::SourceFile(syntax.ast().owned())
349 }
350 ModuleSource::Module(ptr) => {
351 let syntax = db.resolve_syntax_ptr(ptr);
352 let syntax = syntax.borrowed();
353 let module = ast::Module::cast(syntax).unwrap();
354 ModuleSourceNode::Module(module.owned())
355 }
356 }
357 }
358}
359
360#[derive(Hash, Debug, PartialEq, Eq)]
361struct LinkData {
362 owner: ModuleId,
363 name: SmolStr,
364 points_to: Vec<ModuleId>,
365 problem: Option<Problem>,
366}
367
368impl ModuleTree {
369 fn push_mod(&mut self, data: ModuleData) -> ModuleId {
370 self.mods.alloc(data)
371 }
372 fn push_link(&mut self, data: LinkData) -> LinkId {
373 let owner = data.owner;
374 let id = self.links.alloc(data);
375 self.mods[owner].children.push(id);
376 id
377 }
378}
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 d2964f67f..000000000
--- a/crates/ra_analysis/src/descriptors/module/nameres.rs
+++ /dev/null
@@ -1,549 +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 ops::Index,
21};
22
23use rustc_hash::FxHashMap;
24
25use ra_syntax::{
26 SyntaxNode, SyntaxNodeRef, TextRange,
27 SmolStr, SyntaxKind::{self, *},
28 ast::{self, ModuleItemOwner, AstNode}
29};
30
31use crate::{
32 Cancelable, FileId,
33 loc2id::{DefId, DefLoc},
34 descriptors::{
35 Path, PathKind,
36 DescriptorDatabase,
37 module::{ModuleId, ModuleTree, ModuleSourceNode},
38 },
39 input::SourceRootId,
40 arena::{Arena, Id}
41};
42
43/// Identifier of item within a specific file. This is stable over reparses, so
44/// it's OK to use it as a salsa key/value.
45pub(crate) type FileItemId = Id<SyntaxNode>;
46
47/// Maps item's `SyntaxNode`s to `FileItemId` and back.
48#[derive(Debug, PartialEq, Eq, Default)]
49pub(crate) struct FileItems {
50 arena: Arena<SyntaxNode>,
51}
52
53impl FileItems {
54 fn alloc(&mut self, item: SyntaxNode) -> FileItemId {
55 self.arena.alloc(item)
56 }
57 fn id_of(&self, item: SyntaxNodeRef) -> FileItemId {
58 let (id, _item) = self
59 .arena
60 .iter()
61 .find(|(_id, i)| i.borrowed() == item)
62 .unwrap();
63 id
64 }
65}
66
67impl Index<FileItemId> for FileItems {
68 type Output = SyntaxNode;
69 fn index(&self, idx: FileItemId) -> &SyntaxNode {
70 &self.arena[idx]
71 }
72}
73
74pub(crate) fn file_items(db: &impl DescriptorDatabase, file_id: FileId) -> Arc<FileItems> {
75 let source_file = db.file_syntax(file_id);
76 let source_file = source_file.borrowed();
77 let mut res = FileItems::default();
78 source_file
79 .syntax()
80 .descendants()
81 .filter_map(ast::ModuleItem::cast)
82 .map(|it| it.syntax().owned())
83 .for_each(|it| {
84 res.alloc(it);
85 });
86 Arc::new(res)
87}
88
89pub(crate) fn file_item(
90 db: &impl DescriptorDatabase,
91 file_id: FileId,
92 file_item_id: FileItemId,
93) -> SyntaxNode {
94 db._file_items(file_id)[file_item_id].clone()
95}
96
97/// Item map is the result of the name resolution. Item map contains, for each
98/// module, the set of visible items.
99#[derive(Default, Debug, PartialEq, Eq)]
100pub(crate) struct ItemMap {
101 pub(crate) per_module: FxHashMap<ModuleId, ModuleScope>,
102}
103
104#[derive(Debug, Default, PartialEq, Eq, Clone)]
105pub(crate) struct ModuleScope {
106 items: FxHashMap<SmolStr, Resolution>,
107}
108
109impl ModuleScope {
110 pub(crate) fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a SmolStr, &Resolution)> + 'a {
111 self.items.iter()
112 }
113 pub(crate) fn get(&self, name: &SmolStr) -> Option<&Resolution> {
114 self.items.get(name)
115 }
116}
117
118/// A set of items and imports declared inside a module, without relation to
119/// other modules.
120///
121/// This stands in-between raw syntax and name resolution and alow us to avoid
122/// recomputing name res: if `InputModuleItems` are the same, we can avoid
123/// running name resolution.
124#[derive(Debug, Default, PartialEq, Eq)]
125pub(crate) struct InputModuleItems {
126 items: Vec<ModuleItem>,
127 imports: Vec<Import>,
128}
129
130#[derive(Debug, PartialEq, Eq)]
131struct ModuleItem {
132 id: FileItemId,
133 name: SmolStr,
134 kind: SyntaxKind,
135 vis: Vis,
136}
137
138#[derive(Debug, PartialEq, Eq)]
139enum Vis {
140 // Priv,
141 Other,
142}
143
144#[derive(Debug, Clone, PartialEq, Eq)]
145struct Import {
146 path: Path,
147 kind: ImportKind,
148}
149
150#[derive(Debug, Clone, Copy, PartialEq, Eq)]
151pub(crate) struct NamedImport {
152 file_item_id: FileItemId,
153 relative_range: TextRange,
154}
155
156impl NamedImport {
157 pub(crate) fn range(&self, db: &impl DescriptorDatabase, file_id: FileId) -> TextRange {
158 let syntax = db._file_item(file_id, self.file_item_id);
159 let offset = syntax.borrowed().range().start();
160 self.relative_range + offset
161 }
162}
163
164#[derive(Debug, Clone, PartialEq, Eq)]
165enum ImportKind {
166 Glob,
167 Named(NamedImport),
168}
169
170pub(crate) fn input_module_items(
171 db: &impl DescriptorDatabase,
172 source_root: SourceRootId,
173 module_id: ModuleId,
174) -> Cancelable<Arc<InputModuleItems>> {
175 let module_tree = db._module_tree(source_root)?;
176 let source = module_id.source(&module_tree);
177 let file_items = db._file_items(source.file_id());
178 let res = match source.resolve(db) {
179 ModuleSourceNode::SourceFile(it) => {
180 let items = it.borrowed().items();
181 InputModuleItems::new(&file_items, items)
182 }
183 ModuleSourceNode::Module(it) => {
184 let items = it
185 .borrowed()
186 .item_list()
187 .into_iter()
188 .flat_map(|it| it.items());
189 InputModuleItems::new(&file_items, items)
190 }
191 };
192 Ok(Arc::new(res))
193}
194
195pub(crate) fn item_map(
196 db: &impl DescriptorDatabase,
197 source_root: SourceRootId,
198) -> Cancelable<Arc<ItemMap>> {
199 let start = Instant::now();
200 let module_tree = db._module_tree(source_root)?;
201 let input = module_tree
202 .modules()
203 .map(|id| {
204 let items = db._input_module_items(source_root, id)?;
205 Ok((id, items))
206 })
207 .collect::<Cancelable<FxHashMap<_, _>>>()?;
208 let mut resolver = Resolver {
209 db: db,
210 input: &input,
211 source_root,
212 module_tree,
213 result: ItemMap::default(),
214 };
215 resolver.resolve()?;
216 let res = resolver.result;
217 let elapsed = start.elapsed();
218 log::info!("item_map: {:?}", elapsed);
219 Ok(Arc::new(res))
220}
221
222/// Resolution is basically `DefId` atm, but it should account for stuff like
223/// multiple namespaces, ambiguity and errors.
224#[derive(Debug, Clone, PartialEq, Eq)]
225pub(crate) struct Resolution {
226 /// None for unresolved
227 pub(crate) def_id: Option<DefId>,
228 /// ident by whitch this is imported into local scope.
229 pub(crate) import: Option<NamedImport>,
230}
231
232// #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
233// enum Namespace {
234// Types,
235// Values,
236// }
237
238// #[derive(Debug)]
239// struct PerNs<T> {
240// types: Option<T>,
241// values: Option<T>,
242// }
243
244impl InputModuleItems {
245 fn new<'a>(
246 file_items: &FileItems,
247 items: impl Iterator<Item = ast::ModuleItem<'a>>,
248 ) -> InputModuleItems {
249 let mut res = InputModuleItems::default();
250 for item in items {
251 res.add_item(file_items, item);
252 }
253 res
254 }
255
256 fn add_item(&mut self, file_items: &FileItems, item: ast::ModuleItem) -> Option<()> {
257 match item {
258 ast::ModuleItem::StructDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
259 ast::ModuleItem::EnumDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
260 ast::ModuleItem::FnDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
261 ast::ModuleItem::TraitDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
262 ast::ModuleItem::TypeDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
263 ast::ModuleItem::ImplItem(_) => {
264 // impls don't define items
265 }
266 ast::ModuleItem::UseItem(it) => self.add_use_item(file_items, it),
267 ast::ModuleItem::ExternCrateItem(_) => {
268 // TODO
269 }
270 ast::ModuleItem::ConstDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
271 ast::ModuleItem::StaticDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
272 ast::ModuleItem::Module(it) => self.items.push(ModuleItem::new(file_items, it)?),
273 }
274 Some(())
275 }
276
277 fn add_use_item(&mut self, file_items: &FileItems, item: ast::UseItem) {
278 let file_item_id = file_items.id_of(item.syntax());
279 let start_offset = item.syntax().range().start();
280 Path::expand_use_item(item, |path, range| {
281 let kind = match range {
282 None => ImportKind::Glob,
283 Some(range) => ImportKind::Named(NamedImport {
284 file_item_id,
285 relative_range: range - start_offset,
286 }),
287 };
288 self.imports.push(Import { kind, path })
289 })
290 }
291}
292
293impl ModuleItem {
294 fn new<'a>(file_items: &FileItems, item: impl ast::NameOwner<'a>) -> Option<ModuleItem> {
295 let name = item.name()?.text();
296 let kind = item.syntax().kind();
297 let vis = Vis::Other;
298 let id = file_items.id_of(item.syntax());
299 let res = ModuleItem {
300 id,
301 name,
302 kind,
303 vis,
304 };
305 Some(res)
306 }
307}
308
309struct Resolver<'a, DB> {
310 db: &'a DB,
311 input: &'a FxHashMap<ModuleId, Arc<InputModuleItems>>,
312 source_root: SourceRootId,
313 module_tree: Arc<ModuleTree>,
314 result: ItemMap,
315}
316
317impl<'a, DB> Resolver<'a, DB>
318where
319 DB: DescriptorDatabase,
320{
321 fn resolve(&mut self) -> Cancelable<()> {
322 for (&module_id, items) in self.input.iter() {
323 self.populate_module(module_id, items)
324 }
325
326 for &module_id in self.input.keys() {
327 crate::db::check_canceled(self.db)?;
328 self.resolve_imports(module_id);
329 }
330 Ok(())
331 }
332
333 fn populate_module(&mut self, module_id: ModuleId, input: &InputModuleItems) {
334 let file_id = module_id.source(&self.module_tree).file_id();
335
336 let mut module_items = ModuleScope::default();
337
338 for import in input.imports.iter() {
339 if let Some(name) = import.path.segments.iter().last() {
340 if let ImportKind::Named(import) = import.kind {
341 module_items.items.insert(
342 name.clone(),
343 Resolution {
344 def_id: None,
345 import: Some(import),
346 },
347 );
348 }
349 }
350 }
351
352 for item in input.items.iter() {
353 if item.kind == MODULE {
354 // handle submodules separatelly
355 continue;
356 }
357 let def_loc = DefLoc::Item {
358 file_id,
359 id: item.id,
360 };
361 let def_id = self.db.id_maps().def_id(def_loc);
362 let resolution = Resolution {
363 def_id: Some(def_id),
364 import: None,
365 };
366 module_items.items.insert(item.name.clone(), resolution);
367 }
368
369 for (name, mod_id) in module_id.children(&self.module_tree) {
370 let def_loc = DefLoc::Module {
371 id: mod_id,
372 source_root: self.source_root,
373 };
374 let def_id = self.db.id_maps().def_id(def_loc);
375 let resolution = Resolution {
376 def_id: Some(def_id),
377 import: None,
378 };
379 module_items.items.insert(name, resolution);
380 }
381
382 self.result.per_module.insert(module_id, module_items);
383 }
384
385 fn resolve_imports(&mut self, module_id: ModuleId) {
386 for import in self.input[&module_id].imports.iter() {
387 self.resolve_import(module_id, import);
388 }
389 }
390
391 fn resolve_import(&mut self, module_id: ModuleId, import: &Import) {
392 let ptr = match import.kind {
393 ImportKind::Glob => return,
394 ImportKind::Named(ptr) => ptr,
395 };
396
397 let mut curr = match import.path.kind {
398 // TODO: handle extern crates
399 PathKind::Plain => return,
400 PathKind::Self_ => module_id,
401 PathKind::Super => {
402 match module_id.parent(&self.module_tree) {
403 Some(it) => it,
404 // TODO: error
405 None => return,
406 }
407 }
408 PathKind::Crate => module_id.crate_root(&self.module_tree),
409 };
410
411 for (i, name) in import.path.segments.iter().enumerate() {
412 let is_last = i == import.path.segments.len() - 1;
413
414 let def_id = match self.result.per_module[&curr].items.get(name) {
415 None => return,
416 Some(res) => match res.def_id {
417 Some(it) => it,
418 None => return,
419 },
420 };
421
422 if !is_last {
423 curr = match self.db.id_maps().def_loc(def_id) {
424 DefLoc::Module { id, .. } => id,
425 _ => return,
426 }
427 } else {
428 self.update(module_id, |items| {
429 let res = Resolution {
430 def_id: Some(def_id),
431 import: Some(ptr),
432 };
433 items.items.insert(name.clone(), res);
434 })
435 }
436 }
437 }
438
439 fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleScope)) {
440 let module_items = self.result.per_module.get_mut(&module_id).unwrap();
441 f(module_items)
442 }
443}
444
445#[cfg(test)]
446mod tests {
447 use crate::{
448 AnalysisChange,
449 mock_analysis::{MockAnalysis, analysis_and_position},
450 descriptors::{DescriptorDatabase, module::ModuleDescriptor},
451 input::FilesDatabase,
452};
453 use super::*;
454
455 fn item_map(fixture: &str) -> (Arc<ItemMap>, ModuleId) {
456 let (analysis, pos) = analysis_and_position(fixture);
457 let db = analysis.imp.db;
458 let source_root = db.file_source_root(pos.file_id);
459 let descr = ModuleDescriptor::guess_from_position(&*db, pos)
460 .unwrap()
461 .unwrap();
462 let module_id = descr.module_id;
463 (db._item_map(source_root).unwrap(), module_id)
464 }
465
466 #[test]
467 fn test_item_map() {
468 let (item_map, module_id) = item_map(
469 "
470 //- /lib.rs
471 mod foo;
472
473 use crate::foo::bar::Baz;
474 <|>
475
476 //- /foo/mod.rs
477 pub mod bar;
478
479 //- /foo/bar.rs
480 pub struct Baz;
481 ",
482 );
483 let name = SmolStr::from("Baz");
484 let resolution = &item_map.per_module[&module_id].items[&name];
485 assert!(resolution.def_id.is_some());
486 }
487
488 #[test]
489 fn typing_inside_a_function_should_not_invalidate_item_map() {
490 let mock_analysis = MockAnalysis::with_files(
491 "
492 //- /lib.rs
493 mod foo;
494
495 use crate::foo::bar::Baz;
496
497 fn foo() -> i32 {
498 1 + 1
499 }
500 //- /foo/mod.rs
501 pub mod bar;
502
503 //- /foo/bar.rs
504 pub struct Baz;
505 ",
506 );
507
508 let file_id = mock_analysis.id_of("/lib.rs");
509 let mut host = mock_analysis.analysis_host();
510
511 let source_root = host.analysis().imp.db.file_source_root(file_id);
512
513 {
514 let db = host.analysis().imp.db;
515 let events = db.log_executed(|| {
516 db._item_map(source_root).unwrap();
517 });
518 assert!(format!("{:?}", events).contains("_item_map"))
519 }
520
521 let mut change = AnalysisChange::new();
522
523 change.change_file(
524 file_id,
525 "
526 mod foo;
527
528 use crate::foo::bar::Baz;
529
530 fn foo() -> i32 { 92 }
531 "
532 .to_string(),
533 );
534
535 host.apply_change(change);
536
537 {
538 let db = host.analysis().imp.db;
539 let events = db.log_executed(|| {
540 db._item_map(source_root).unwrap();
541 });
542 assert!(
543 !format!("{:?}", events).contains("_item_map"),
544 "{:#?}",
545 events
546 )
547 }
548 }
549}
diff --git a/crates/ra_analysis/src/descriptors/path.rs b/crates/ra_analysis/src/descriptors/path.rs
deleted file mode 100644
index 8279daf4b..000000000
--- a/crates/ra_analysis/src/descriptors/path.rs
+++ /dev/null
@@ -1,148 +0,0 @@
1use ra_syntax::{SmolStr, ast, AstNode, TextRange};
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub(crate) struct Path {
5 pub(crate) kind: PathKind,
6 pub(crate) segments: Vec<SmolStr>,
7}
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub(crate) enum PathKind {
11 Plain,
12 Self_,
13 Super,
14 Crate,
15}
16
17impl Path {
18 /// Calls `cb` with all paths, represented by this use item.
19 pub(crate) fn expand_use_item(item: ast::UseItem, mut cb: impl FnMut(Path, Option<TextRange>)) {
20 if let Some(tree) = item.use_tree() {
21 expand_use_tree(None, tree, &mut cb);
22 }
23 }
24
25 /// Converts an `ast::Path` to `Path`. Works with use trees.
26 pub(crate) fn from_ast(mut path: ast::Path) -> Option<Path> {
27 let mut kind = PathKind::Plain;
28 let mut segments = Vec::new();
29 loop {
30 let segment = path.segment()?;
31 match segment.kind()? {
32 ast::PathSegmentKind::Name(name) => segments.push(name.text()),
33 ast::PathSegmentKind::CrateKw => {
34 kind = PathKind::Crate;
35 break;
36 }
37 ast::PathSegmentKind::SelfKw => {
38 kind = PathKind::Self_;
39 break;
40 }
41 ast::PathSegmentKind::SuperKw => {
42 kind = PathKind::Super;
43 break;
44 }
45 }
46 path = match qualifier(path) {
47 Some(it) => it,
48 None => break,
49 };
50 }
51 segments.reverse();
52 return Some(Path { kind, segments });
53
54 fn qualifier(path: ast::Path) -> Option<ast::Path> {
55 if let Some(q) = path.qualifier() {
56 return Some(q);
57 }
58 // TODO: this bottom up traversal is not too precise.
59 // Should we handle do a top-down analysiss, recording results?
60 let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?;
61 let use_tree = use_tree_list.parent_use_tree();
62 use_tree.path()
63 }
64 }
65
66 /// `true` is this path is a single identifier, like `foo`
67 pub(crate) fn is_ident(&self) -> bool {
68 self.kind == PathKind::Plain && self.segments.len() == 1
69 }
70}
71
72fn expand_use_tree(
73 prefix: Option<Path>,
74 tree: ast::UseTree,
75 cb: &mut impl FnMut(Path, Option<TextRange>),
76) {
77 if let Some(use_tree_list) = tree.use_tree_list() {
78 let prefix = match tree.path() {
79 None => prefix,
80 Some(path) => match convert_path(prefix, path) {
81 Some(it) => Some(it),
82 None => return, // TODO: report errors somewhere
83 },
84 };
85 for tree in use_tree_list.use_trees() {
86 expand_use_tree(prefix.clone(), tree, cb);
87 }
88 } else {
89 if let Some(ast_path) = tree.path() {
90 if let Some(path) = convert_path(prefix, ast_path) {
91 let range = if tree.has_star() {
92 None
93 } else {
94 let range = ast_path.segment().unwrap().syntax().range();
95 Some(range)
96 };
97 cb(path, range)
98 }
99 }
100 }
101}
102
103fn convert_path(prefix: Option<Path>, path: ast::Path) -> Option<Path> {
104 let prefix = if let Some(qual) = path.qualifier() {
105 Some(convert_path(prefix, qual)?)
106 } else {
107 None
108 };
109 let segment = path.segment()?;
110 let res = match segment.kind()? {
111 ast::PathSegmentKind::Name(name) => {
112 let mut res = prefix.unwrap_or_else(|| Path {
113 kind: PathKind::Plain,
114 segments: Vec::with_capacity(1),
115 });
116 res.segments.push(name.text());
117 res
118 }
119 ast::PathSegmentKind::CrateKw => {
120 if prefix.is_some() {
121 return None;
122 }
123 Path {
124 kind: PathKind::Crate,
125 segments: Vec::new(),
126 }
127 }
128 ast::PathSegmentKind::SelfKw => {
129 if prefix.is_some() {
130 return None;
131 }
132 Path {
133 kind: PathKind::Self_,
134 segments: Vec::new(),
135 }
136 }
137 ast::PathSegmentKind::SuperKw => {
138 if prefix.is_some() {
139 return None;
140 }
141 Path {
142 kind: PathKind::Super,
143 segments: Vec::new(),
144 }
145 }
146 };
147 Some(res)
148}
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs
index 8a41b3152..f5cb3550e 100644
--- a/crates/ra_analysis/src/imp.rs
+++ b/crates/ra_analysis/src/imp.rs
@@ -1,94 +1,33 @@
1use std::{ 1use std::{
2 fmt, 2 fmt,
3 hash::{Hash, Hasher},
4 sync::Arc, 3 sync::Arc,
5}; 4};
6 5
7use ra_editor::{self, find_node_at_offset, FileSymbol, LineIndex, LocalEdit}; 6use ra_editor::{self, find_node_at_offset, FileSymbol, LineIndex, LocalEdit};
8use ra_syntax::{ 7use ra_syntax::{
9 ast::{self, ArgListOwner, Expr, NameOwner}, 8 ast::{self, ArgListOwner, Expr, NameOwner},
10 AstNode, SourceFileNode, SmolStr, 9 AstNode, SourceFileNode,
11 SyntaxKind::*, 10 SyntaxKind::*,
12 SyntaxNodeRef, TextRange, TextUnit, 11 SyntaxNodeRef, TextRange, TextUnit,
13}; 12};
13use ra_db::{FilesDatabase, SourceRoot, SourceRootId, WORKSPACE, SyntaxDatabase, SourceFileQuery};
14use rayon::prelude::*; 14use rayon::prelude::*;
15use relative_path::RelativePath;
16use rustc_hash::FxHashSet; 15use rustc_hash::FxHashSet;
17use salsa::{Database, ParallelDatabase}; 16use salsa::{Database, ParallelDatabase};
17use hir::{
18 self,
19 FnSignatureInfo,
20 Problem,
21};
18 22
19use crate::{ 23use crate::{
20 completion::{completions, CompletionItem}, 24 completion::{completions, CompletionItem},
21 db::{self, FileSyntaxQuery, SyntaxDatabase}, 25 db,
22 descriptors::{ 26 symbol_index::{SymbolIndex, SymbolsDatabase},
23 function::{FnDescriptor, FnId}, 27 AnalysisChange, Cancelable, CrateId, Diagnostic, FileId,
24 module::{ModuleDescriptor, Problem},
25 DeclarationDescriptor, DescriptorDatabase,
26 },
27 input::{FilesDatabase, SourceRoot, SourceRootId, WORKSPACE},
28 symbol_index::SymbolIndex,
29 AnalysisChange, Cancelable, CrateGraph, CrateId, Diagnostic, FileId, FileResolver,
30 FileSystemEdit, FilePosition, Query, SourceChange, SourceFileNodeEdit, 28 FileSystemEdit, FilePosition, Query, SourceChange, SourceFileNodeEdit,
31}; 29};
32 30
33#[derive(Clone, Debug)]
34pub(crate) struct FileResolverImp {
35 inner: Arc<FileResolver>,
36}
37
38impl PartialEq for FileResolverImp {
39 fn eq(&self, other: &FileResolverImp) -> bool {
40 self.inner() == other.inner()
41 }
42}
43
44impl Eq for FileResolverImp {}
45
46impl Hash for FileResolverImp {
47 fn hash<H: Hasher>(&self, hasher: &mut H) {
48 self.inner().hash(hasher);
49 }
50}
51
52impl FileResolverImp {
53 pub(crate) fn new(inner: Arc<FileResolver>) -> FileResolverImp {
54 FileResolverImp { inner }
55 }
56 pub(crate) fn file_stem(&self, file_id: FileId) -> String {
57 self.inner.file_stem(file_id)
58 }
59 pub(crate) fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option<FileId> {
60 self.inner.resolve(file_id, path)
61 }
62 pub(crate) fn debug_path(&self, file_id: FileId) -> Option<std::path::PathBuf> {
63 self.inner.debug_path(file_id)
64 }
65 fn inner(&self) -> *const FileResolver {
66 &*self.inner
67 }
68}
69
70impl Default for FileResolverImp {
71 fn default() -> FileResolverImp {
72 #[derive(Debug)]
73 struct DummyResolver;
74 impl FileResolver for DummyResolver {
75 fn file_stem(&self, _file_: FileId) -> String {
76 panic!("file resolver not set")
77 }
78 fn resolve(
79 &self,
80 _file_id: FileId,
81 _path: &::relative_path::RelativePath,
82 ) -> Option<FileId> {
83 panic!("file resolver not set")
84 }
85 }
86 FileResolverImp {
87 inner: Arc::new(DummyResolver),
88 }
89 }
90}
91
92#[derive(Debug, Default)] 31#[derive(Debug, Default)]
93pub(crate) struct AnalysisHostImpl { 32pub(crate) struct AnalysisHostImpl {
94 db: db::RootDatabase, 33 db: db::RootDatabase,
@@ -105,7 +44,7 @@ impl AnalysisHostImpl {
105 44
106 for (file_id, text) in change.files_changed { 45 for (file_id, text) in change.files_changed {
107 self.db 46 self.db
108 .query_mut(crate::input::FileTextQuery) 47 .query_mut(ra_db::FileTextQuery)
109 .set(file_id, Arc::new(text)) 48 .set(file_id, Arc::new(text))
110 } 49 }
111 if !(change.files_added.is_empty() && change.files_removed.is_empty()) { 50 if !(change.files_added.is_empty() && change.files_removed.is_empty()) {
@@ -115,22 +54,22 @@ impl AnalysisHostImpl {
115 let mut source_root = SourceRoot::clone(&self.db.source_root(WORKSPACE)); 54 let mut source_root = SourceRoot::clone(&self.db.source_root(WORKSPACE));
116 for (file_id, text) in change.files_added { 55 for (file_id, text) in change.files_added {
117 self.db 56 self.db
118 .query_mut(crate::input::FileTextQuery) 57 .query_mut(ra_db::FileTextQuery)
119 .set(file_id, Arc::new(text)); 58 .set(file_id, Arc::new(text));
120 self.db 59 self.db
121 .query_mut(crate::input::FileSourceRootQuery) 60 .query_mut(ra_db::FileSourceRootQuery)
122 .set(file_id, crate::input::WORKSPACE); 61 .set(file_id, ra_db::WORKSPACE);
123 source_root.files.insert(file_id); 62 source_root.files.insert(file_id);
124 } 63 }
125 for file_id in change.files_removed { 64 for file_id in change.files_removed {
126 self.db 65 self.db
127 .query_mut(crate::input::FileTextQuery) 66 .query_mut(ra_db::FileTextQuery)
128 .set(file_id, Arc::new(String::new())); 67 .set(file_id, Arc::new(String::new()));
129 source_root.files.remove(&file_id); 68 source_root.files.remove(&file_id);
130 } 69 }
131 source_root.file_resolver = file_resolver; 70 source_root.file_resolver = file_resolver;
132 self.db 71 self.db
133 .query_mut(crate::input::SourceRootQuery) 72 .query_mut(ra_db::SourceRootQuery)
134 .set(WORKSPACE, Arc::new(source_root)) 73 .set(WORKSPACE, Arc::new(source_root))
135 } 74 }
136 if !change.libraries_added.is_empty() { 75 if !change.libraries_added.is_empty() {
@@ -147,10 +86,10 @@ impl AnalysisHostImpl {
147 library.file_resolver.debug_path(file_id) 86 library.file_resolver.debug_path(file_id)
148 ); 87 );
149 self.db 88 self.db
150 .query_mut(crate::input::FileSourceRootQuery) 89 .query_mut(ra_db::FileSourceRootQuery)
151 .set_constant(file_id, source_root_id); 90 .set_constant(file_id, source_root_id);
152 self.db 91 self.db
153 .query_mut(crate::input::FileTextQuery) 92 .query_mut(ra_db::FileTextQuery)
154 .set_constant(file_id, Arc::new(text)); 93 .set_constant(file_id, Arc::new(text));
155 } 94 }
156 let source_root = SourceRoot { 95 let source_root = SourceRoot {
@@ -158,19 +97,19 @@ impl AnalysisHostImpl {
158 file_resolver: library.file_resolver, 97 file_resolver: library.file_resolver,
159 }; 98 };
160 self.db 99 self.db
161 .query_mut(crate::input::SourceRootQuery) 100 .query_mut(ra_db::SourceRootQuery)
162 .set(source_root_id, Arc::new(source_root)); 101 .set(source_root_id, Arc::new(source_root));
163 self.db 102 self.db
164 .query_mut(crate::input::LibrarySymbolsQuery) 103 .query_mut(crate::symbol_index::LibrarySymbolsQuery)
165 .set(source_root_id, Arc::new(library.symbol_index)); 104 .set(source_root_id, Arc::new(library.symbol_index));
166 } 105 }
167 self.db 106 self.db
168 .query_mut(crate::input::LibrariesQuery) 107 .query_mut(ra_db::LibrariesQuery)
169 .set((), Arc::new(libraries)); 108 .set((), Arc::new(libraries));
170 } 109 }
171 if let Some(crate_graph) = change.crate_graph { 110 if let Some(crate_graph) = change.crate_graph {
172 self.db 111 self.db
173 .query_mut(crate::input::CrateGraphQuery) 112 .query_mut(ra_db::CrateGraphQuery)
174 .set((), Arc::new(crate_graph)) 113 .set((), Arc::new(crate_graph))
175 } 114 }
176 } 115 }
@@ -189,7 +128,7 @@ impl fmt::Debug for AnalysisImpl {
189 128
190impl AnalysisImpl { 129impl AnalysisImpl {
191 pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode { 130 pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode {
192 self.db.file_syntax(file_id) 131 self.db.source_file(file_id)
193 } 132 }
194 pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> { 133 pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> {
195 self.db.file_lines(file_id) 134 self.db.file_lines(file_id)
@@ -220,14 +159,14 @@ impl AnalysisImpl {
220 .collect() 159 .collect()
221 }; 160 };
222 self.db 161 self.db
223 .query(FileSyntaxQuery) 162 .query(SourceFileQuery)
224 .sweep(salsa::SweepStrategy::default().discard_values()); 163 .sweep(salsa::SweepStrategy::default().discard_values());
225 Ok(query.search(&buf)) 164 Ok(query.search(&buf))
226 } 165 }
227 /// This return `Vec`: a module may be included from several places. We 166 /// This return `Vec`: a module may be included from several places. We
228 /// don't handle this case yet though, so the Vec has length at most one. 167 /// don't handle this case yet though, so the Vec has length at most one.
229 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> { 168 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> {
230 let descr = match ModuleDescriptor::guess_from_position(&*self.db, position)? { 169 let descr = match hir::Module::guess_from_position(&*self.db, position)? {
231 None => return Ok(Vec::new()), 170 None => return Ok(Vec::new()),
232 Some(it) => it, 171 Some(it) => it,
233 }; 172 };
@@ -246,7 +185,7 @@ impl AnalysisImpl {
246 } 185 }
247 /// Returns `Vec` for the same reason as `parent_module` 186 /// Returns `Vec` for the same reason as `parent_module`
248 pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { 187 pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> {
249 let descr = match ModuleDescriptor::guess_from_file_id(&*self.db, file_id)? { 188 let descr = match hir::Module::guess_from_file_id(&*self.db, file_id)? {
250 None => return Ok(Vec::new()), 189 None => return Ok(Vec::new()),
251 Some(it) => it, 190 Some(it) => it,
252 }; 191 };
@@ -261,7 +200,7 @@ impl AnalysisImpl {
261 Ok(crate_id.into_iter().collect()) 200 Ok(crate_id.into_iter().collect())
262 } 201 }
263 pub fn crate_root(&self, crate_id: CrateId) -> FileId { 202 pub fn crate_root(&self, crate_id: CrateId) -> FileId {
264 self.db.crate_graph().crate_roots[&crate_id] 203 self.db.crate_graph().crate_root(crate_id)
265 } 204 }
266 pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> { 205 pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> {
267 completions(&self.db, position) 206 completions(&self.db, position)
@@ -270,33 +209,36 @@ impl AnalysisImpl {
270 &self, 209 &self,
271 position: FilePosition, 210 position: FilePosition,
272 ) -> Cancelable<Vec<(FileId, FileSymbol)>> { 211 ) -> Cancelable<Vec<(FileId, FileSymbol)>> {
273 let file = self.db.file_syntax(position.file_id); 212 let file = self.db.source_file(position.file_id);
274 let syntax = file.syntax(); 213 let syntax = file.syntax();
275 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) { 214 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) {
276 // First try to resolve the symbol locally 215 if let Some(fn_descr) =
277 return if let Some((name, range)) = 216 hir::Function::guess_for_name_ref(&*self.db, position.file_id, name_ref)
278 resolve_local_name(&self.db, position.file_id, name_ref)
279 { 217 {
280 let mut vec = vec![]; 218 let scope = fn_descr.scope(&*self.db);
281 vec.push(( 219 // First try to resolve the symbol locally
282 position.file_id, 220 return if let Some(entry) = scope.resolve_local_name(name_ref) {
283 FileSymbol { 221 let mut vec = vec![];
284 name, 222 vec.push((
285 node_range: range, 223 position.file_id,
286 kind: NAME, 224 FileSymbol {
287 }, 225 name: entry.name().clone(),
288 )); 226 node_range: entry.ptr().range(),
289 Ok(vec) 227 kind: NAME,
290 } else { 228 },
291 // If that fails try the index based approach. 229 ));
292 self.index_resolve(name_ref) 230 Ok(vec)
293 }; 231 } else {
232 // If that fails try the index based approach.
233 self.index_resolve(name_ref)
234 };
235 }
294 } 236 }
295 if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) { 237 if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) {
296 if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { 238 if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) {
297 if module.has_semi() { 239 if module.has_semi() {
298 let parent_module = 240 let parent_module =
299 ModuleDescriptor::guess_from_file_id(&*self.db, position.file_id)?; 241 hir::Module::guess_from_file_id(&*self.db, position.file_id)?;
300 let child_name = module.name(); 242 let child_name = module.name();
301 match (parent_module, child_name) { 243 match (parent_module, child_name) {
302 (Some(parent_module), Some(child_name)) => { 244 (Some(parent_module), Some(child_name)) => {
@@ -319,32 +261,42 @@ impl AnalysisImpl {
319 } 261 }
320 262
321 pub fn find_all_refs(&self, position: FilePosition) -> Vec<(FileId, TextRange)> { 263 pub fn find_all_refs(&self, position: FilePosition) -> Vec<(FileId, TextRange)> {
322 let file = self.db.file_syntax(position.file_id); 264 let file = self.db.source_file(position.file_id);
323 let syntax = file.syntax();
324
325 // Find the binding associated with the offset 265 // Find the binding associated with the offset
326 let maybe_binding = 266 let (binding, descr) = match find_binding(&self.db, &file, position) {
327 find_node_at_offset::<ast::BindPat>(syntax, position.offset).or_else(|| {
328 let name_ref = find_node_at_offset::<ast::NameRef>(syntax, position.offset)?;
329 let resolved = resolve_local_name(&self.db, position.file_id, name_ref)?;
330 find_node_at_offset::<ast::BindPat>(syntax, resolved.1.end())
331 });
332
333 let binding = match maybe_binding {
334 None => return Vec::new(), 267 None => return Vec::new(),
335 Some(it) => it, 268 Some(it) => it,
336 }; 269 };
337 270
338 let decl = DeclarationDescriptor::new(binding); 271 let mut ret = vec![(position.file_id, binding.syntax().range())];
339
340 let mut ret = vec![(position.file_id, decl.range)];
341 ret.extend( 272 ret.extend(
342 decl.find_all_refs() 273 descr
274 .scope(&*self.db)
275 .find_all_refs(binding)
343 .into_iter() 276 .into_iter()
344 .map(|ref_desc| (position.file_id, ref_desc.range)), 277 .map(|ref_desc| (position.file_id, ref_desc.range)),
345 ); 278 );
346 279
347 ret 280 return ret;
281
282 fn find_binding<'a>(
283 db: &db::RootDatabase,
284 source_file: &'a SourceFileNode,
285 position: FilePosition,
286 ) -> Option<(ast::BindPat<'a>, hir::Function)> {
287 let syntax = source_file.syntax();
288 if let Some(binding) = find_node_at_offset::<ast::BindPat>(syntax, position.offset) {
289 let descr = hir::Function::guess_for_bind_pat(db, position.file_id, binding)?;
290 return Some((binding, descr));
291 };
292 let name_ref = find_node_at_offset::<ast::NameRef>(syntax, position.offset)?;
293 let descr = hir::Function::guess_for_name_ref(db, position.file_id, name_ref)?;
294 let scope = descr.scope(db);
295 let resolved = scope.resolve_local_name(name_ref)?;
296 let resolved = resolved.ptr().resolve(source_file);
297 let binding = find_node_at_offset::<ast::BindPat>(syntax, resolved.range().end())?;
298 Some((binding, descr))
299 }
348 } 300 }
349 301
350 pub fn doc_comment_for( 302 pub fn doc_comment_for(
@@ -352,13 +304,13 @@ impl AnalysisImpl {
352 file_id: FileId, 304 file_id: FileId,
353 symbol: FileSymbol, 305 symbol: FileSymbol,
354 ) -> Cancelable<Option<String>> { 306 ) -> Cancelable<Option<String>> {
355 let file = self.db.file_syntax(file_id); 307 let file = self.db.source_file(file_id);
356 308
357 Ok(symbol.docs(&file)) 309 Ok(symbol.docs(&file))
358 } 310 }
359 311
360 pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> { 312 pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> {
361 let syntax = self.db.file_syntax(file_id); 313 let syntax = self.db.source_file(file_id);
362 314
363 let mut res = ra_editor::diagnostics(&syntax) 315 let mut res = ra_editor::diagnostics(&syntax)
364 .into_iter() 316 .into_iter()
@@ -368,7 +320,7 @@ impl AnalysisImpl {
368 fix: None, 320 fix: None,
369 }) 321 })
370 .collect::<Vec<_>>(); 322 .collect::<Vec<_>>();
371 if let Some(m) = ModuleDescriptor::guess_from_file_id(&*self.db, file_id)? { 323 if let Some(m) = hir::Module::guess_from_file_id(&*self.db, file_id)? {
372 for (name_node, problem) in m.problems(&*self.db) { 324 for (name_node, problem) in m.problems(&*self.db) {
373 let diag = match problem { 325 let diag = match problem {
374 Problem::UnresolvedModule { candidate } => { 326 Problem::UnresolvedModule { candidate } => {
@@ -445,8 +397,8 @@ impl AnalysisImpl {
445 pub fn resolve_callable( 397 pub fn resolve_callable(
446 &self, 398 &self,
447 position: FilePosition, 399 position: FilePosition,
448 ) -> Cancelable<Option<(FnDescriptor, Option<usize>)>> { 400 ) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> {
449 let file = self.db.file_syntax(position.file_id); 401 let file = self.db.source_file(position.file_id);
450 let syntax = file.syntax(); 402 let syntax = file.syntax();
451 403
452 // Find the calling expression and it's NameRef 404 // Find the calling expression and it's NameRef
@@ -455,11 +407,12 @@ impl AnalysisImpl {
455 407
456 // Resolve the function's NameRef (NOTE: this isn't entirely accurate). 408 // Resolve the function's NameRef (NOTE: this isn't entirely accurate).
457 let file_symbols = self.index_resolve(name_ref)?; 409 let file_symbols = self.index_resolve(name_ref)?;
458 for (fn_fiel_id, fs) in file_symbols { 410 for (fn_file_id, fs) in file_symbols {
459 if fs.kind == FN_DEF { 411 if fs.kind == FN_DEF {
460 let fn_file = self.db.file_syntax(fn_fiel_id); 412 let fn_file = self.db.source_file(fn_file_id);
461 if let Some(fn_def) = find_node_at_offset(fn_file.syntax(), fs.node_range.start()) { 413 if let Some(fn_def) = find_node_at_offset(fn_file.syntax(), fs.node_range.start()) {
462 if let Some(descriptor) = FnDescriptor::new(fn_def) { 414 let descr = hir::Function::guess_from_source(&*self.db, fn_file_id, fn_def);
415 if let Some(descriptor) = descr.signature_info(&*self.db) {
463 // If we have a calling expression let's find which argument we are on 416 // If we have a calling expression let's find which argument we are on
464 let mut current_parameter = None; 417 let mut current_parameter = None;
465 418
@@ -532,16 +485,6 @@ impl SourceChange {
532 } 485 }
533} 486}
534 487
535impl CrateGraph {
536 fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> {
537 let (&crate_id, _) = self
538 .crate_roots
539 .iter()
540 .find(|(_crate_id, &root_id)| root_id == file_id)?;
541 Some(crate_id)
542 }
543}
544
545enum FnCallNode<'a> { 488enum FnCallNode<'a> {
546 CallExpr(ast::CallExpr<'a>), 489 CallExpr(ast::CallExpr<'a>),
547 MethodCallExpr(ast::MethodCallExpr<'a>), 490 MethodCallExpr(ast::MethodCallExpr<'a>),
@@ -580,16 +523,3 @@ impl<'a> FnCallNode<'a> {
580 } 523 }
581 } 524 }
582} 525}
583
584fn resolve_local_name(
585 db: &db::RootDatabase,
586 file_id: FileId,
587 name_ref: ast::NameRef,
588) -> Option<(SmolStr, TextRange)> {
589 let fn_def = name_ref.syntax().ancestors().find_map(ast::FnDef::cast)?;
590 let fn_id = FnId::get(db, file_id, fn_def);
591 let scopes = db.fn_scopes(fn_id);
592 let scope_entry = crate::descriptors::function::resolve_local_name(name_ref, &scopes)?;
593 let syntax = db.resolve_syntax_ptr(scope_entry.ptr().into_global(file_id));
594 Some((scope_entry.name().clone(), syntax.range()))
595}
diff --git a/crates/ra_analysis/src/input.rs b/crates/ra_analysis/src/input.rs
deleted file mode 100644
index a78b6e397..000000000
--- a/crates/ra_analysis/src/input.rs
+++ /dev/null
@@ -1,79 +0,0 @@
1use std::{fmt, sync::Arc};
2
3use relative_path::RelativePath;
4use rustc_hash::FxHashMap;
5use rustc_hash::FxHashSet;
6use salsa;
7
8use crate::{symbol_index::SymbolIndex, FileResolverImp};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
11pub struct FileId(pub u32);
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
14pub struct CrateId(pub u32);
15
16#[derive(Debug, Clone, Default, PartialEq, Eq)]
17pub struct CrateGraph {
18 pub(crate) crate_roots: FxHashMap<CrateId, FileId>,
19}
20
21impl CrateGraph {
22 pub fn new() -> CrateGraph {
23 CrateGraph::default()
24 }
25 pub fn add_crate_root(&mut self, file_id: FileId) -> CrateId {
26 let crate_id = CrateId(self.crate_roots.len() as u32);
27 let prev = self.crate_roots.insert(crate_id, file_id);
28 assert!(prev.is_none());
29 crate_id
30 }
31}
32
33pub trait FileResolver: fmt::Debug + Send + Sync + 'static {
34 fn file_stem(&self, file_id: FileId) -> String;
35 fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option<FileId>;
36 fn debug_path(&self, _file_id: FileId) -> Option<std::path::PathBuf> {
37 None
38 }
39}
40
41salsa::query_group! {
42 pub(crate) trait FilesDatabase: salsa::Database {
43 fn file_text(file_id: FileId) -> Arc<String> {
44 type FileTextQuery;
45 storage input;
46 }
47 fn file_source_root(file_id: FileId) -> SourceRootId {
48 type FileSourceRootQuery;
49 storage input;
50 }
51 fn source_root(id: SourceRootId) -> Arc<SourceRoot> {
52 type SourceRootQuery;
53 storage input;
54 }
55 fn libraries() -> Arc<Vec<SourceRootId>> {
56 type LibrariesQuery;
57 storage input;
58 }
59 fn library_symbols(id: SourceRootId) -> Arc<SymbolIndex> {
60 type LibrarySymbolsQuery;
61 storage input;
62 }
63 fn crate_graph() -> Arc<CrateGraph> {
64 type CrateGraphQuery;
65 storage input;
66 }
67 }
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
71pub(crate) struct SourceRootId(pub(crate) u32);
72
73#[derive(Default, Clone, Debug, PartialEq, Eq)]
74pub(crate) struct SourceRoot {
75 pub(crate) file_resolver: FileResolverImp,
76 pub(crate) files: FxHashSet<FileId>,
77}
78
79pub(crate) const WORKSPACE: SourceRootId = SourceRootId(0);
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs
index 0fbfd8a40..350a6d627 100644
--- a/crates/ra_analysis/src/lib.rs
+++ b/crates/ra_analysis/src/lib.rs
@@ -18,49 +18,36 @@ macro_rules! ctry {
18 }; 18 };
19} 19}
20 20
21mod arena;
22mod db; 21mod db;
23mod loc2id;
24mod input;
25mod imp; 22mod imp;
26mod completion; 23mod completion;
27mod descriptors;
28mod symbol_index; 24mod symbol_index;
29mod syntax_ptr;
30pub mod mock_analysis; 25pub mod mock_analysis;
31 26
32use std::{fmt, sync::Arc}; 27use std::{fmt, sync::Arc};
33 28
34use ra_syntax::{AtomEdit, SourceFileNode, TextRange, TextUnit}; 29use ra_syntax::{AtomEdit, SourceFileNode, TextRange, TextUnit};
30use ra_db::FileResolverImp;
35use rayon::prelude::*; 31use rayon::prelude::*;
36use relative_path::RelativePathBuf; 32use relative_path::RelativePathBuf;
37 33
38use crate::{ 34use crate::{
39 imp::{AnalysisHostImpl, AnalysisImpl, FileResolverImp}, 35 imp::{AnalysisHostImpl, AnalysisImpl},
40 symbol_index::SymbolIndex, 36 symbol_index::SymbolIndex,
41}; 37};
42 38
43pub use crate::{ 39pub use crate::{
44 completion::CompletionItem, 40 completion::CompletionItem,
45 descriptors::function::FnDescriptor,
46 input::{CrateGraph, CrateId, FileId, FileResolver},
47}; 41};
48pub use ra_editor::{ 42pub use ra_editor::{
49 FileSymbol, Fold, FoldKind, HighlightedRange, LineIndex, Runnable, RunnableKind, StructureNode, 43 FileSymbol, Fold, FoldKind, HighlightedRange, LineIndex, Runnable, RunnableKind, StructureNode,
50}; 44};
45pub use hir::FnSignatureInfo;
51 46
52#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 47pub use ra_db::{
53pub struct Canceled; 48 Canceled, Cancelable, FilePosition,
54 49 CrateGraph, CrateId, FileId, FileResolver
55pub type Cancelable<T> = Result<T, Canceled>; 50};
56
57impl std::fmt::Display for Canceled {
58 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59 fmt.write_str("Canceled")
60 }
61}
62
63impl std::error::Error for Canceled {}
64 51
65#[derive(Default)] 52#[derive(Default)]
66pub struct AnalysisChange { 53pub struct AnalysisChange {
@@ -130,12 +117,6 @@ impl AnalysisHost {
130 } 117 }
131} 118}
132 119
133#[derive(Clone, Copy, Debug)]
134pub struct FilePosition {
135 pub file_id: FileId,
136 pub offset: TextUnit,
137}
138
139#[derive(Debug)] 120#[derive(Debug)]
140pub struct SourceChange { 121pub struct SourceChange {
141 pub label: String, 122 pub label: String,
@@ -305,7 +286,7 @@ impl Analysis {
305 pub fn resolve_callable( 286 pub fn resolve_callable(
306 &self, 287 &self,
307 position: FilePosition, 288 position: FilePosition,
308 ) -> Cancelable<Option<(FnDescriptor, Option<usize>)>> { 289 ) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> {
309 self.imp.resolve_callable(position) 290 self.imp.resolve_callable(position)
310 } 291 }
311} 292}
@@ -336,3 +317,112 @@ fn analysis_is_send() {
336 fn is_send<T: Send>() {} 317 fn is_send<T: Send>() {}
337 is_send::<Analysis>(); 318 is_send::<Analysis>();
338} 319}
320
321//TODO: move to hir
322#[cfg(test)]
323mod hir_namres_tests {
324 use std::sync::Arc;
325 use ra_db::FilesDatabase;
326 use ra_syntax::SmolStr;
327 use hir::{self, db::HirDatabase};
328
329 use crate::{
330 AnalysisChange,
331 mock_analysis::{MockAnalysis, analysis_and_position},
332};
333
334 fn item_map(fixture: &str) -> (Arc<hir::ItemMap>, hir::ModuleId) {
335 let (analysis, pos) = analysis_and_position(fixture);
336 let db = analysis.imp.db;
337 let source_root = db.file_source_root(pos.file_id);
338 let descr = hir::Module::guess_from_position(&*db, pos)
339 .unwrap()
340 .unwrap();
341 let module_id = descr.module_id;
342 (db.item_map(source_root).unwrap(), module_id)
343 }
344
345 #[test]
346 fn test_item_map() {
347 let (item_map, module_id) = item_map(
348 "
349 //- /lib.rs
350 mod foo;
351
352 use crate::foo::bar::Baz;
353 <|>
354
355 //- /foo/mod.rs
356 pub mod bar;
357
358 //- /foo/bar.rs
359 pub struct Baz;
360 ",
361 );
362 let name = SmolStr::from("Baz");
363 let resolution = &item_map.per_module[&module_id].items[&name];
364 assert!(resolution.def_id.is_some());
365 }
366
367 #[test]
368 fn typing_inside_a_function_should_not_invalidate_item_map() {
369 let mock_analysis = MockAnalysis::with_files(
370 "
371 //- /lib.rs
372 mod foo;
373
374 use crate::foo::bar::Baz;
375
376 fn foo() -> i32 {
377 1 + 1
378 }
379 //- /foo/mod.rs
380 pub mod bar;
381
382 //- /foo/bar.rs
383 pub struct Baz;
384 ",
385 );
386
387 let file_id = mock_analysis.id_of("/lib.rs");
388 let mut host = mock_analysis.analysis_host();
389
390 let source_root = host.analysis().imp.db.file_source_root(file_id);
391
392 {
393 let db = host.analysis().imp.db;
394 let events = db.log_executed(|| {
395 db.item_map(source_root).unwrap();
396 });
397 assert!(format!("{:?}", events).contains("item_map"))
398 }
399
400 let mut change = AnalysisChange::new();
401
402 change.change_file(
403 file_id,
404 "
405 mod foo;
406
407 use crate::foo::bar::Baz;
408
409 fn foo() -> i32 { 92 }
410 "
411 .to_string(),
412 );
413
414 host.apply_change(change);
415
416 {
417 let db = host.analysis().imp.db;
418 let events = db.log_executed(|| {
419 db.item_map(source_root).unwrap();
420 });
421 assert!(
422 !format!("{:?}", events).contains("_item_map"),
423 "{:#?}",
424 events
425 )
426 }
427 }
428}
diff --git a/crates/ra_analysis/src/loc2id.rs b/crates/ra_analysis/src/loc2id.rs
deleted file mode 100644
index c7c799a91..000000000
--- a/crates/ra_analysis/src/loc2id.rs
+++ /dev/null
@@ -1,141 +0,0 @@
1use parking_lot::Mutex;
2
3use std::{
4 hash::Hash,
5 sync::Arc,
6};
7
8use rustc_hash::FxHashMap;
9
10use crate::{
11 FileId,
12 descriptors::FileItemId,
13 descriptors::module::ModuleId,
14 syntax_ptr::SyntaxPtr,
15 input::SourceRootId,
16};
17
18/// There are two principle ways to refer to things:
19/// - by their locatinon (module in foo/bar/baz.rs at line 42)
20/// - by their numeric id (module `ModuleId(42)`)
21///
22/// The first one is more powerful (you can actually find the thing in question
23/// by id), but the second one is so much more compact.
24///
25/// `Loc2IdMap` allows us to have a cake an eat it as well: by maintaining a
26/// bidirectional mapping between positional and numeric ids, we can use compact
27/// representation wich still allows us to get the actual item
28#[derive(Debug)]
29pub(crate) struct Loc2IdMap<L, ID>
30where
31 ID: NumericId,
32 L: Clone + Eq + Hash,
33{
34 loc2id: FxHashMap<L, ID>,
35 id2loc: FxHashMap<ID, L>,
36}
37
38impl<L, ID> Default for Loc2IdMap<L, ID>
39where
40 ID: NumericId,
41 L: Clone + Eq + Hash,
42{
43 fn default() -> Self {
44 Loc2IdMap {
45 loc2id: FxHashMap::default(),
46 id2loc: FxHashMap::default(),
47 }
48 }
49}
50
51impl<L, ID> Loc2IdMap<L, ID>
52where
53 ID: NumericId,
54 L: Clone + Eq + Hash,
55{
56 pub fn loc2id(&mut self, loc: &L) -> ID {
57 match self.loc2id.get(loc) {
58 Some(id) => return id.clone(),
59 None => (),
60 }
61 let id = self.loc2id.len();
62 assert!(id < u32::max_value() as usize);
63 let id = ID::from_u32(id as u32);
64 self.loc2id.insert(loc.clone(), id.clone());
65 self.id2loc.insert(id.clone(), loc.clone());
66 id
67 }
68
69 pub fn id2loc(&self, id: ID) -> L {
70 self.id2loc[&id].clone()
71 }
72}
73
74pub(crate) trait NumericId: Clone + Eq + Hash {
75 fn from_u32(id: u32) -> Self;
76 fn to_u32(self) -> u32;
77}
78
79macro_rules! impl_numeric_id {
80 ($id:ident) => {
81 impl NumericId for $id {
82 fn from_u32(id: u32) -> Self {
83 $id(id)
84 }
85 fn to_u32(self) -> u32 {
86 self.0
87 }
88 }
89 };
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
93pub(crate) struct FnId(u32);
94impl_numeric_id!(FnId);
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
97pub(crate) struct DefId(u32);
98impl_numeric_id!(DefId);
99
100#[derive(Clone, Debug, PartialEq, Eq, Hash)]
101pub(crate) enum DefLoc {
102 Module {
103 id: ModuleId,
104 source_root: SourceRootId,
105 },
106 Item {
107 file_id: FileId,
108 id: FileItemId,
109 },
110}
111
112pub(crate) trait IdDatabase: salsa::Database {
113 fn id_maps(&self) -> &IdMaps;
114}
115
116#[derive(Debug, Default, Clone)]
117pub(crate) struct IdMaps {
118 inner: Arc<IdMapsInner>,
119}
120
121impl IdMaps {
122 pub(crate) fn fn_id(&self, ptr: SyntaxPtr) -> FnId {
123 self.inner.fns.lock().loc2id(&ptr)
124 }
125 pub(crate) fn fn_ptr(&self, fn_id: FnId) -> SyntaxPtr {
126 self.inner.fns.lock().id2loc(fn_id)
127 }
128
129 pub(crate) fn def_id(&self, loc: DefLoc) -> DefId {
130 self.inner.defs.lock().loc2id(&loc)
131 }
132 pub(crate) fn def_loc(&self, def_id: DefId) -> DefLoc {
133 self.inner.defs.lock().id2loc(def_id)
134 }
135}
136
137#[derive(Debug, Default)]
138struct IdMapsInner {
139 fns: Mutex<Loc2IdMap<SyntaxPtr, FnId>>,
140 defs: Mutex<Loc2IdMap<DefLoc, DefId>>,
141}
diff --git a/crates/ra_analysis/src/symbol_index.rs b/crates/ra_analysis/src/symbol_index.rs
index 3a0667ecd..b48a37229 100644
--- a/crates/ra_analysis/src/symbol_index.rs
+++ b/crates/ra_analysis/src/symbol_index.rs
@@ -4,14 +4,36 @@ use std::{
4}; 4};
5 5
6use fst::{self, Streamer}; 6use fst::{self, Streamer};
7use ra_editor::{file_symbols, FileSymbol}; 7use ra_editor::{self, FileSymbol};
8use ra_syntax::{ 8use ra_syntax::{
9 SourceFileNode, 9 SourceFileNode,
10 SyntaxKind::{self, *}, 10 SyntaxKind::{self, *},
11}; 11};
12use ra_db::{SyntaxDatabase, SourceRootId};
12use rayon::prelude::*; 13use rayon::prelude::*;
13 14
14use crate::{FileId, Query}; 15use crate::{
16 Cancelable,
17 FileId, Query,
18};
19
20salsa::query_group! {
21 pub(crate) trait SymbolsDatabase: SyntaxDatabase {
22 fn file_symbols(file_id: FileId) -> Cancelable<Arc<SymbolIndex>> {
23 type FileSymbolsQuery;
24 }
25 fn library_symbols(id: SourceRootId) -> Arc<SymbolIndex> {
26 type LibrarySymbolsQuery;
27 storage input;
28 }
29 }
30}
31
32fn file_symbols(db: &impl SyntaxDatabase, file_id: FileId) -> Cancelable<Arc<SymbolIndex>> {
33 db.check_canceled()?;
34 let syntax = db.source_file(file_id);
35 Ok(Arc::new(SymbolIndex::for_file(file_id, syntax)))
36}
15 37
16#[derive(Default, Debug)] 38#[derive(Default, Debug)]
17pub(crate) struct SymbolIndex { 39pub(crate) struct SymbolIndex {
@@ -39,7 +61,7 @@ impl SymbolIndex {
39 ) -> SymbolIndex { 61 ) -> SymbolIndex {
40 let mut symbols = files 62 let mut symbols = files
41 .flat_map(|(file_id, file)| { 63 .flat_map(|(file_id, file)| {
42 file_symbols(&file) 64 ra_editor::file_symbols(&file)
43 .into_iter() 65 .into_iter()
44 .map(move |symbol| (symbol.name.as_str().to_lowercase(), (file_id, symbol))) 66 .map(move |symbol| (symbol.name.as_str().to_lowercase(), (file_id, symbol)))
45 .collect::<Vec<_>>() 67 .collect::<Vec<_>>()
diff --git a/crates/ra_analysis/src/syntax_ptr.rs b/crates/ra_analysis/src/syntax_ptr.rs
deleted file mode 100644
index e45934ce0..000000000
--- a/crates/ra_analysis/src/syntax_ptr.rs
+++ /dev/null
@@ -1,79 +0,0 @@
1use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, TextRange};
2
3use crate::db::SyntaxDatabase;
4use crate::FileId;
5
6pub(crate) fn resolve_syntax_ptr(db: &impl SyntaxDatabase, ptr: SyntaxPtr) -> SyntaxNode {
7 let syntax = db.file_syntax(ptr.file_id);
8 ptr.local.resolve(&syntax)
9}
10
11/// SyntaxPtr is a cheap `Copy` id which identifies a particular syntax node,
12/// without retaining syntax tree in memory. You need to explicitly `resolve`
13/// `SyntaxPtr` to get a `SyntaxNode`
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15pub(crate) struct SyntaxPtr {
16 file_id: FileId,
17 local: LocalSyntaxPtr,
18}
19
20impl SyntaxPtr {
21 pub(crate) fn new(file_id: FileId, node: SyntaxNodeRef) -> SyntaxPtr {
22 let local = LocalSyntaxPtr::new(node);
23 SyntaxPtr { file_id, local }
24 }
25
26 pub(crate) fn file_id(self) -> FileId {
27 self.file_id
28 }
29}
30
31/// A pionter to a syntax node inside a file.
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
33pub(crate) struct LocalSyntaxPtr {
34 range: TextRange,
35 kind: SyntaxKind,
36}
37
38impl LocalSyntaxPtr {
39 pub(crate) fn new(node: SyntaxNodeRef) -> LocalSyntaxPtr {
40 LocalSyntaxPtr {
41 range: node.range(),
42 kind: node.kind(),
43 }
44 }
45
46 pub(crate) fn resolve(self, file: &SourceFileNode) -> SyntaxNode {
47 let mut curr = file.syntax();
48 loop {
49 if curr.range() == self.range && curr.kind() == self.kind {
50 return curr.owned();
51 }
52 curr = curr
53 .children()
54 .find(|it| self.range.is_subrange(&it.range()))
55 .unwrap_or_else(|| panic!("can't resolve local ptr to SyntaxNode: {:?}", self))
56 }
57 }
58
59 pub(crate) fn into_global(self, file_id: FileId) -> SyntaxPtr {
60 SyntaxPtr {
61 file_id,
62 local: self,
63 }
64 }
65}
66
67#[test]
68fn test_local_syntax_ptr() {
69 use ra_syntax::{ast, AstNode};
70 let file = SourceFileNode::parse("struct Foo { f: u32, }");
71 let field = file
72 .syntax()
73 .descendants()
74 .find_map(ast::NamedFieldDef::cast)
75 .unwrap();
76 let ptr = LocalSyntaxPtr::new(field.syntax());
77 let field_syntax = ptr.resolve(&file);
78 assert_eq!(field.syntax(), field_syntax);
79}