aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/gen_lsp_server/src/msg.rs2
-rw-r--r--crates/ra_analysis/Cargo.toml2
-rw-r--r--crates/ra_analysis/src/arena.rs96
-rw-r--r--crates/ra_analysis/src/completion/mod.rs13
-rw-r--r--crates/ra_analysis/src/completion/reference_completion.rs44
-rw-r--r--crates/ra_analysis/src/db.rs164
-rw-r--r--crates/ra_analysis/src/descriptors/function/imp.rs21
-rw-r--r--crates/ra_analysis/src/descriptors/mod.rs101
-rw-r--r--crates/ra_analysis/src/imp.rs248
-rw-r--r--crates/ra_analysis/src/lib.rs44
-rw-r--r--crates/ra_analysis/src/loc2id.rs138
-rw-r--r--crates/ra_analysis/src/mock_analysis.rs40
-rw-r--r--crates/ra_analysis/src/symbol_index.rs28
-rw-r--r--crates/ra_analysis/src/syntax_ptr.rs84
-rw-r--r--crates/ra_analysis/tests/tests.rs6
-rw-r--r--crates/ra_db/Cargo.toml14
-rw-r--r--crates/ra_db/src/file_resolver.rs76
-rw-r--r--crates/ra_db/src/input.rs (renamed from crates/ra_analysis/src/input.rs)38
-rw-r--r--crates/ra_db/src/lib.rs83
-rw-r--r--crates/ra_db/src/loc2id.rs100
-rw-r--r--crates/ra_db/src/mock.rs51
-rw-r--r--crates/ra_db/src/syntax_ptr.rs48
-rw-r--r--crates/ra_editor/src/code_actions.rs90
-rw-r--r--crates/ra_editor/src/edit.rs2
-rw-r--r--crates/ra_hir/Cargo.toml17
-rw-r--r--crates/ra_hir/src/arena.rs66
-rw-r--r--crates/ra_hir/src/db.rs66
-rw-r--r--crates/ra_hir/src/function/mod.rs (renamed from crates/ra_analysis/src/descriptors/function/mod.rs)85
-rw-r--r--crates/ra_hir/src/function/scope.rs (renamed from crates/ra_analysis/src/descriptors/function/scope.rs)69
-rw-r--r--crates/ra_hir/src/lib.rs141
-rw-r--r--crates/ra_hir/src/mock.rs172
-rw-r--r--crates/ra_hir/src/module/imp.rs (renamed from crates/ra_analysis/src/descriptors/module/imp.rs)53
-rw-r--r--crates/ra_hir/src/module/mod.rs (renamed from crates/ra_analysis/src/descriptors/module/mod.rs)161
-rw-r--r--crates/ra_hir/src/module/nameres.rs (renamed from crates/ra_analysis/src/descriptors/module/nameres.rs)303
-rw-r--r--crates/ra_hir/src/path.rs (renamed from crates/ra_analysis/src/descriptors/path.rs)31
-rw-r--r--crates/ra_hir/src/query_definitions.rs154
-rw-r--r--crates/ra_lsp_server/src/conv.rs11
-rw-r--r--crates/ra_lsp_server/src/main.rs24
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs13
-rw-r--r--crates/ra_lsp_server/src/path_map.rs2
-rw-r--r--crates/ra_lsp_server/src/server_world.rs2
41 files changed, 1680 insertions, 1223 deletions
diff --git a/crates/gen_lsp_server/src/msg.rs b/crates/gen_lsp_server/src/msg.rs
index e0d0aeab5..e1b27c808 100644
--- a/crates/gen_lsp_server/src/msg.rs
+++ b/crates/gen_lsp_server/src/msg.rs
@@ -94,7 +94,7 @@ impl RawRequest {
94 R::Params: Serialize, 94 R::Params: Serialize,
95 { 95 {
96 RawRequest { 96 RawRequest {
97 id: id, 97 id,
98 method: R::METHOD.to_string(), 98 method: R::METHOD.to_string(),
99 params: to_value(params).unwrap(), 99 params: to_value(params).unwrap(),
100 } 100 }
diff --git a/crates/ra_analysis/Cargo.toml b/crates/ra_analysis/Cargo.toml
index b4a1a09b5..fe9765a66 100644
--- a/crates/ra_analysis/Cargo.toml
+++ b/crates/ra_analysis/Cargo.toml
@@ -14,4 +14,6 @@ rustc-hash = "1.0"
14parking_lot = "0.6.4" 14parking_lot = "0.6.4"
15ra_syntax = { path = "../ra_syntax" } 15ra_syntax = { path = "../ra_syntax" }
16ra_editor = { path = "../ra_editor" } 16ra_editor = { path = "../ra_editor" }
17ra_db = { path = "../ra_db" }
18hir = { path = "../ra_hir", package = "ra_hir" }
17test_utils = { path = "../test_utils" } 19test_utils = { path = "../test_utils" }
diff --git a/crates/ra_analysis/src/arena.rs b/crates/ra_analysis/src/arena.rs
deleted file mode 100644
index 98ed89274..000000000
--- a/crates/ra_analysis/src/arena.rs
+++ /dev/null
@@ -1,96 +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 ops::{Index, IndexMut},
8 hash::{Hash, Hasher},
9 marker::PhantomData,
10};
11
12pub(crate) struct Id<T> {
13 idx: u32,
14 _ty: PhantomData<fn() -> T>,
15}
16
17impl<T> fmt::Debug for Id<T> {
18 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
19 f.debug_tuple("Id").field(&self.idx).finish()
20 }
21}
22impl<T> Copy for Id<T> {}
23impl<T> Clone for Id<T> {
24 fn clone(&self) -> Id<T> {
25 *self
26 }
27}
28
29impl<T> PartialEq for Id<T> {
30 fn eq(&self, other: &Id<T>) -> bool {
31 self.idx == other.idx
32 }
33}
34
35impl<T> Eq for Id<T> {}
36
37impl<T> Hash for Id<T> {
38 fn hash<H: Hasher>(&self, h: &mut H) {
39 self.idx.hash(h);
40 }
41}
42
43#[derive(Debug, PartialEq, Eq)]
44pub(crate) struct Arena<T> {
45 data: Vec<T>,
46}
47
48impl<T> Default for Arena<T> {
49 fn default() -> Arena<T> {
50 Arena { data: Vec::new() }
51 }
52}
53
54impl<T> Arena<T> {
55 pub(crate) fn push(&mut self, value: T) -> Id<T> {
56 let id = self.data.len() as u32;
57 self.data.push(value);
58 Id {
59 idx: id as u32,
60 _ty: PhantomData,
61 }
62 }
63
64 pub(crate) fn keys<'a>(&'a self) -> impl Iterator<Item = Id<T>> + 'a {
65 (0..(self.data.len() as u32)).into_iter().map(|idx| Id {
66 idx,
67 _ty: PhantomData,
68 })
69 }
70
71 pub(crate) fn items<'a>(&'a self) -> impl Iterator<Item = (Id<T>, &T)> + 'a {
72 self.data.iter().enumerate().map(|(idx, item)| {
73 let idx = idx as u32;
74 (
75 Id {
76 idx,
77 _ty: PhantomData,
78 },
79 item,
80 )
81 })
82 }
83}
84
85impl<T> Index<Id<T>> for Arena<T> {
86 type Output = T;
87 fn index(&self, id: Id<T>) -> &T {
88 &self.data[id.idx as usize]
89 }
90}
91
92impl<T> IndexMut<Id<T>> for Arena<T> {
93 fn index_mut(&mut self, id: Id<T>) -> &mut T {
94 &mut self.data[id.idx as usize]
95 }
96}
diff --git a/crates/ra_analysis/src/completion/mod.rs b/crates/ra_analysis/src/completion/mod.rs
index d7acca044..124da486a 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,17 +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 = match ModuleDescriptor::guess_from_position(db, position)? { 39 let module = ctry!(hir::Module::guess_from_position(db, position)?);
42 None => return Ok(None),
43 Some(it) => it,
44 };
45 40
46 let mut res = Vec::new(); 41 let mut res = Vec::new();
47 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 e52062107..8ea7478a8 100644
--- a/crates/ra_analysis/src/completion/reference_completion.rs
+++ b/crates/ra_analysis/src/completion/reference_completion.rs
@@ -6,22 +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 Path,
17 },
18 Cancelable 19 Cancelable
19}; 20};
20 21
21pub(super) fn completions( 22pub(super) fn completions(
22 acc: &mut Vec<CompletionItem>, 23 acc: &mut Vec<CompletionItem>,
23 db: &RootDatabase, 24 db: &RootDatabase,
24 module: &ModuleDescriptor, 25 module: &hir::Module,
25 file: &SourceFileNode, 26 file: &SourceFileNode,
26 name_ref: ast::NameRef, 27 name_ref: ast::NameRef,
27) -> Cancelable<()> { 28) -> Cancelable<()> {
@@ -42,13 +43,15 @@ pub(super) fn completions(
42 let module_scope = module.scope(db)?; 43 let module_scope = module.scope(db)?;
43 acc.extend( 44 acc.extend(
44 module_scope 45 module_scope
45 .items 46 .entries()
46 .iter()
47 .filter(|(_name, res)| { 47 .filter(|(_name, res)| {
48 // Don't expose this item 48 // Don't expose this item
49 match res.import_name { 49 match res.import {
50 None => true, 50 None => true,
51 Some(ptr) => !ptr.range().is_subrange(&name_ref.syntax().range()), 51 Some(import) => {
52 let range = import.range(db, module.source().file_id());
53 !range.is_subrange(&name_ref.syntax().range())
54 }
52 } 55 }
53 }) 56 })
54 .map(|(name, _res)| CompletionItem { 57 .map(|(name, _res)| CompletionItem {
@@ -147,26 +150,27 @@ fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<Completi
147fn complete_path( 150fn complete_path(
148 acc: &mut Vec<CompletionItem>, 151 acc: &mut Vec<CompletionItem>,
149 db: &RootDatabase, 152 db: &RootDatabase,
150 module: &ModuleDescriptor, 153 module: &hir::Module,
151 mut path: Path, 154 mut path: Path,
152) -> Cancelable<()> { 155) -> Cancelable<()> {
153 if path.segments.is_empty() { 156 if path.segments.is_empty() {
154 return Ok(()); 157 return Ok(());
155 } 158 }
156 path.segments.pop(); 159 path.segments.pop();
157 let target_module = match module.resolve_path(path) { 160 let def_id = match module.resolve_path(db, path)? {
158 None => return Ok(()), 161 None => return Ok(()),
159 Some(it) => it, 162 Some(it) => it,
160 }; 163 };
164 let target_module = match def_id.resolve(db)? {
165 Def::Module(it) => it,
166 Def::Item => return Ok(()),
167 };
161 let module_scope = target_module.scope(db)?; 168 let module_scope = target_module.scope(db)?;
162 let completions = module_scope 169 let completions = module_scope.entries().map(|(name, _res)| CompletionItem {
163 .items 170 label: name.to_string(),
164 .iter() 171 lookup: None,
165 .map(|(name, _res)| CompletionItem { 172 snippet: None,
166 label: name.to_string(), 173 });
167 lookup: None,
168 snippet: None,
169 });
170 acc.extend(completions); 174 acc.extend(completions);
171 Ok(()) 175 Ok(())
172} 176}
diff --git a/crates/ra_analysis/src/db.rs b/crates/ra_analysis/src/db.rs
index 6b56f99ac..df2ef293d 100644
--- a/crates/ra_analysis/src/db.rs
+++ b/crates/ra_analysis/src/db.rs
@@ -1,171 +1,95 @@
1use std::sync::Arc; 1use std::sync::Arc;
2#[cfg(test)]
3use parking_lot::Mutex;
4use ra_editor::LineIndex;
5use ra_syntax::{SourceFileNode, SyntaxNode};
6use salsa::{self, Database}; 2use salsa::{self, Database};
3use ra_db::{LocationIntener, BaseDatabase};
4use hir::{self, DefId, DefLoc, FnId, SourceItemId};
7 5
8use crate::{ 6use crate::{
9 db, 7 symbol_index,
10 descriptors::{
11 DescriptorDatabase, FnScopesQuery, FnSyntaxQuery, ModuleTreeQuery,
12 SubmodulesQuery, ItemMapQuery, InputModuleItemsQuery,
13 },
14 symbol_index::SymbolIndex,
15 syntax_ptr::SyntaxPtr,
16 loc2id::{IdMaps, IdDatabase},
17 Cancelable, Canceled, FileId,
18}; 8};
19 9
20#[derive(Debug)] 10#[derive(Debug)]
21pub(crate) struct RootDatabase { 11pub(crate) struct RootDatabase {
22 #[cfg(test)]
23 events: Mutex<Option<Vec<salsa::Event<RootDatabase>>>>,
24 #[cfg(not(test))]
25 events: (),
26
27 runtime: salsa::Runtime<RootDatabase>, 12 runtime: salsa::Runtime<RootDatabase>,
28 id_maps: IdMaps, 13 id_maps: Arc<IdMaps>,
14}
15
16#[derive(Debug, Default)]
17struct IdMaps {
18 fns: LocationIntener<SourceItemId, FnId>,
19 defs: LocationIntener<DefLoc, DefId>,
29} 20}
30 21
31impl salsa::Database for RootDatabase { 22impl salsa::Database for RootDatabase {
32 fn salsa_runtime(&self) -> &salsa::Runtime<RootDatabase> { 23 fn salsa_runtime(&self) -> &salsa::Runtime<RootDatabase> {
33 &self.runtime 24 &self.runtime
34 } 25 }
35
36 #[allow(unused)]
37 fn salsa_event(&self, event: impl Fn() -> salsa::Event<RootDatabase>) {
38 #[cfg(test)]
39 {
40 let mut events = self.events.lock();
41 if let Some(events) = &mut *events {
42 events.push(event());
43 }
44 }
45 }
46} 26}
47 27
48impl Default for RootDatabase { 28impl Default for RootDatabase {
49 fn default() -> RootDatabase { 29 fn default() -> RootDatabase {
50 let mut db = RootDatabase { 30 let mut db = RootDatabase {
51 events: Default::default(),
52 runtime: salsa::Runtime::default(), 31 runtime: salsa::Runtime::default(),
53 id_maps: IdMaps::default(), 32 id_maps: Default::default(),
54 }; 33 };
55 db.query_mut(crate::input::SourceRootQuery) 34 db.query_mut(ra_db::SourceRootQuery)
56 .set(crate::input::WORKSPACE, Default::default()); 35 .set(ra_db::WORKSPACE, Default::default());
57 db.query_mut(crate::input::CrateGraphQuery) 36 db.query_mut(ra_db::CrateGraphQuery)
58 .set((), Default::default()); 37 .set((), Default::default());
59 db.query_mut(crate::input::LibrariesQuery) 38 db.query_mut(ra_db::LibrariesQuery)
60 .set((), Default::default()); 39 .set((), Default::default());
61 db 40 db
62 } 41 }
63} 42}
64 43
65pub(crate) fn check_canceled(db: &impl salsa::Database) -> Cancelable<()> {
66 if db.salsa_runtime().is_current_revision_canceled() {
67 Err(Canceled)
68 } else {
69 Ok(())
70 }
71}
72
73impl salsa::ParallelDatabase for RootDatabase { 44impl salsa::ParallelDatabase for RootDatabase {
74 fn snapshot(&self) -> salsa::Snapshot<RootDatabase> { 45 fn snapshot(&self) -> salsa::Snapshot<RootDatabase> {
75 salsa::Snapshot::new(RootDatabase { 46 salsa::Snapshot::new(RootDatabase {
76 events: Default::default(),
77 runtime: self.runtime.snapshot(self), 47 runtime: self.runtime.snapshot(self),
78 id_maps: self.id_maps.clone(), 48 id_maps: self.id_maps.clone(),
79 }) 49 })
80 } 50 }
81} 51}
82 52
83impl IdDatabase for RootDatabase { 53impl BaseDatabase for RootDatabase {}
84 fn id_maps(&self) -> &IdMaps {
85 &self.id_maps
86 }
87}
88 54
89#[cfg(test)] 55impl AsRef<LocationIntener<DefLoc, DefId>> for RootDatabase {
90impl RootDatabase { 56 fn as_ref(&self) -> &LocationIntener<DefLoc, DefId> {
91 pub(crate) fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event<RootDatabase>> { 57 &self.id_maps.defs
92 *self.events.lock() = Some(Vec::new());
93 f();
94 let events = self.events.lock().take().unwrap();
95 events
96 } 58 }
59}
97 60
98 pub(crate) fn log_executed(&self, f: impl FnOnce()) -> Vec<String> { 61impl AsRef<LocationIntener<hir::SourceItemId, FnId>> for RootDatabase {
99 let events = self.log(f); 62 fn as_ref(&self) -> &LocationIntener<hir::SourceItemId, FnId> {
100 events 63 &self.id_maps.fns
101 .into_iter()
102 .filter_map(|e| match e.kind {
103 // This pretty horrible, but `Debug` is the only way to inspect
104 // QueryDescriptor at the moment.
105 salsa::EventKind::WillExecute { descriptor } => Some(format!("{:?}", descriptor)),
106 _ => None,
107 })
108 .collect()
109 } 64 }
110} 65}
111 66
112salsa::database_storage! { 67salsa::database_storage! {
113 pub(crate) struct RootDatabaseStorage for RootDatabase { 68 pub(crate) struct RootDatabaseStorage for RootDatabase {
114 impl crate::input::FilesDatabase { 69 impl ra_db::FilesDatabase {
115 fn file_text() for crate::input::FileTextQuery; 70 fn file_text() for ra_db::FileTextQuery;
116 fn file_source_root() for crate::input::FileSourceRootQuery; 71 fn file_source_root() for ra_db::FileSourceRootQuery;
117 fn source_root() for crate::input::SourceRootQuery; 72 fn source_root() for ra_db::SourceRootQuery;
118 fn libraries() for crate::input::LibrariesQuery; 73 fn libraries() for ra_db::LibrariesQuery;
119 fn library_symbols() for crate::input::LibrarySymbolsQuery; 74 fn crate_graph() for ra_db::CrateGraphQuery;
120 fn crate_graph() for crate::input::CrateGraphQuery;
121 }
122 impl SyntaxDatabase {
123 fn file_syntax() for FileSyntaxQuery;
124 fn file_lines() for FileLinesQuery;
125 fn file_symbols() for FileSymbolsQuery;
126 fn resolve_syntax_ptr() for ResolveSyntaxPtrQuery;
127 }
128 impl DescriptorDatabase {
129 fn module_tree() for ModuleTreeQuery;
130 fn fn_scopes() for FnScopesQuery;
131 fn _input_module_items() for InputModuleItemsQuery;
132 fn _item_map() for ItemMapQuery;
133 fn _fn_syntax() for FnSyntaxQuery;
134 fn _submodules() for SubmodulesQuery;
135 } 75 }
136 } 76 impl ra_db::SyntaxDatabase {
137} 77 fn source_file() for ra_db::SourceFileQuery;
138 78 fn file_lines() for ra_db::FileLinesQuery;
139salsa::query_group! {
140 pub(crate) trait SyntaxDatabase: crate::input::FilesDatabase {
141 fn file_syntax(file_id: FileId) -> SourceFileNode {
142 type FileSyntaxQuery;
143 } 79 }
144 fn file_lines(file_id: FileId) -> Arc<LineIndex> { 80 impl symbol_index::SymbolsDatabase {
145 type FileLinesQuery; 81 fn file_symbols() for symbol_index::FileSymbolsQuery;
82 fn library_symbols() for symbol_index::LibrarySymbolsQuery;
146 } 83 }
147 fn file_symbols(file_id: FileId) -> Cancelable<Arc<SymbolIndex>> { 84 impl hir::db::HirDatabase {
148 type FileSymbolsQuery; 85 fn module_tree() for hir::db::ModuleTreeQuery;
149 } 86 fn fn_scopes() for hir::db::FnScopesQuery;
150 fn resolve_syntax_ptr(ptr: SyntaxPtr) -> SyntaxNode { 87 fn file_items() for hir::db::SourceFileItemsQuery;
151 type ResolveSyntaxPtrQuery; 88 fn file_item() for hir::db::FileItemQuery;
152 // Don't retain syntax trees in memory 89 fn input_module_items() for hir::db::InputModuleItemsQuery;
153 storage volatile; 90 fn item_map() for hir::db::ItemMapQuery;
154 use fn crate::syntax_ptr::resolve_syntax_ptr; 91 fn fn_syntax() for hir::db::FnSyntaxQuery;
92 fn submodules() for hir::db::SubmodulesQuery;
155 } 93 }
156 } 94 }
157} 95}
158
159fn file_syntax(db: &impl SyntaxDatabase, file_id: FileId) -> SourceFileNode {
160 let text = db.file_text(file_id);
161 SourceFileNode::parse(&*text)
162}
163fn file_lines(db: &impl SyntaxDatabase, file_id: FileId) -> Arc<LineIndex> {
164 let text = db.file_text(file_id);
165 Arc::new(LineIndex::new(&*text))
166}
167fn file_symbols(db: &impl SyntaxDatabase, file_id: FileId) -> Cancelable<Arc<SymbolIndex>> {
168 db::check_canceled(db)?;
169 let syntax = db.file_syntax(file_id);
170 Ok(Arc::new(SymbolIndex::for_file(file_id, syntax)))
171}
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/mod.rs b/crates/ra_analysis/src/descriptors/mod.rs
deleted file mode 100644
index 97750ea64..000000000
--- a/crates/ra_analysis/src/descriptors/mod.rs
+++ /dev/null
@@ -1,101 +0,0 @@
1pub(crate) mod function;
2pub(crate) mod module;
3mod path;
4
5use std::sync::Arc;
6
7use ra_syntax::{
8 ast::{self, FnDefNode, AstNode},
9 TextRange,
10};
11
12use crate::{
13 db::SyntaxDatabase,
14 descriptors::function::{resolve_local_name, FnId, FnScopes},
15 descriptors::module::{ModuleId, ModuleTree, ModuleSource, nameres::{ItemMap, InputModuleItems}},
16 input::SourceRootId,
17 loc2id::IdDatabase,
18 syntax_ptr::LocalSyntaxPtr,
19 Cancelable,
20};
21
22pub(crate) use self::path::{Path, PathKind};
23
24salsa::query_group! {
25 pub(crate) trait DescriptorDatabase: SyntaxDatabase + IdDatabase {
26 fn fn_scopes(fn_id: FnId) -> Arc<FnScopes> {
27 type FnScopesQuery;
28 use fn function::imp::fn_scopes;
29 }
30
31 fn _input_module_items(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable<Arc<InputModuleItems>> {
32 type InputModuleItemsQuery;
33 use fn module::nameres::input_module_items;
34 }
35 fn _item_map(source_root_id: SourceRootId) -> Cancelable<Arc<ItemMap>> {
36 type ItemMapQuery;
37 use fn module::nameres::item_map;
38 }
39 fn _module_tree(source_root_id: SourceRootId) -> Cancelable<Arc<ModuleTree>> {
40 type ModuleTreeQuery;
41 use fn module::imp::module_tree;
42 }
43 fn _fn_syntax(fn_id: FnId) -> FnDefNode {
44 type FnSyntaxQuery;
45 // Don't retain syntax trees in memory
46 storage volatile;
47 use fn function::imp::fn_syntax;
48 }
49 fn _submodules(source: ModuleSource) -> Cancelable<Arc<Vec<module::imp::Submodule>>> {
50 type SubmodulesQuery;
51 use fn module::imp::submodules;
52 }
53 }
54}
55
56#[derive(Debug)]
57pub struct ReferenceDescriptor {
58 pub range: TextRange,
59 pub name: String,
60}
61
62#[derive(Debug)]
63pub struct DeclarationDescriptor<'a> {
64 pat: ast::BindPat<'a>,
65 pub range: TextRange,
66}
67
68impl<'a> DeclarationDescriptor<'a> {
69 pub fn new(pat: ast::BindPat) -> DeclarationDescriptor {
70 let range = pat.syntax().range();
71
72 DeclarationDescriptor { pat, range }
73 }
74
75 pub fn find_all_refs(&self) -> Vec<ReferenceDescriptor> {
76 let name_ptr = LocalSyntaxPtr::new(self.pat.syntax());
77
78 let fn_def = match self.pat.syntax().ancestors().find_map(ast::FnDef::cast) {
79 Some(def) => def,
80 None => return Default::default(),
81 };
82
83 let fn_scopes = FnScopes::new(fn_def);
84
85 let refs: Vec<_> = fn_def
86 .syntax()
87 .descendants()
88 .filter_map(ast::NameRef::cast)
89 .filter(|name_ref| match resolve_local_name(*name_ref, &fn_scopes) {
90 None => false,
91 Some(entry) => entry.ptr() == name_ptr,
92 })
93 .map(|name_ref| ReferenceDescriptor {
94 name: name_ref.syntax().text().to_string(),
95 range: name_ref.syntax().range(),
96 })
97 .collect();
98
99 refs
100 }
101}
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs
index ad6b52371..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,27 +397,22 @@ 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
453 let calling_node = match FnCallNode::with_node(syntax, position.offset) { 405 let calling_node = ctry!(FnCallNode::with_node(syntax, position.offset));
454 Some(node) => node, 406 let name_ref = ctry!(calling_node.name_ref());
455 None => return Ok(None),
456 };
457 let name_ref = match calling_node.name_ref() {
458 Some(name) => name,
459 None => return Ok(None),
460 };
461 407
462 // Resolve the function's NameRef (NOTE: this isn't entirely accurate). 408 // Resolve the function's NameRef (NOTE: this isn't entirely accurate).
463 let file_symbols = self.index_resolve(name_ref)?; 409 let file_symbols = self.index_resolve(name_ref)?;
464 for (fn_fiel_id, fs) in file_symbols { 410 for (fn_file_id, fs) in file_symbols {
465 if fs.kind == FN_DEF { 411 if fs.kind == FN_DEF {
466 let fn_file = self.db.file_syntax(fn_fiel_id); 412 let fn_file = self.db.source_file(fn_file_id);
467 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()) {
468 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) {
469 // 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
470 let mut current_parameter = None; 417 let mut current_parameter = None;
471 418
@@ -538,16 +485,6 @@ impl SourceChange {
538 } 485 }
539} 486}
540 487
541impl CrateGraph {
542 fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> {
543 let (&crate_id, _) = self
544 .crate_roots
545 .iter()
546 .find(|(_crate_id, &root_id)| root_id == file_id)?;
547 Some(crate_id)
548 }
549}
550
551enum FnCallNode<'a> { 488enum FnCallNode<'a> {
552 CallExpr(ast::CallExpr<'a>), 489 CallExpr(ast::CallExpr<'a>),
553 MethodCallExpr(ast::MethodCallExpr<'a>), 490 MethodCallExpr(ast::MethodCallExpr<'a>),
@@ -586,16 +523,3 @@ impl<'a> FnCallNode<'a> {
586 } 523 }
587 } 524 }
588} 525}
589
590fn resolve_local_name(
591 db: &db::RootDatabase,
592 file_id: FileId,
593 name_ref: ast::NameRef,
594) -> Option<(SmolStr, TextRange)> {
595 let fn_def = name_ref.syntax().ancestors().find_map(ast::FnDef::cast)?;
596 let fn_id = FnId::get(db, file_id, fn_def);
597 let scopes = db.fn_scopes(fn_id);
598 let scope_entry = crate::descriptors::function::resolve_local_name(name_ref, &scopes)?;
599 let syntax = db.resolve_syntax_ptr(scope_entry.ptr().into_global(file_id));
600 Some((scope_entry.name().clone(), syntax.range()))
601}
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs
index cedbd1fc8..12df580ba 100644
--- a/crates/ra_analysis/src/lib.rs
+++ b/crates/ra_analysis/src/lib.rs
@@ -9,49 +9,45 @@ extern crate relative_path;
9extern crate rustc_hash; 9extern crate rustc_hash;
10extern crate salsa; 10extern crate salsa;
11 11
12mod arena; 12macro_rules! ctry {
13 ($expr:expr) => {
14 match $expr {
15 None => return Ok(None),
16 Some(it) => it,
17 }
18 };
19}
20
13mod db; 21mod db;
14mod loc2id;
15mod input;
16mod imp; 22mod imp;
17mod completion; 23mod completion;
18mod descriptors;
19mod symbol_index; 24mod symbol_index;
20mod syntax_ptr;
21pub mod mock_analysis; 25pub mod mock_analysis;
22 26
23use std::{fmt, sync::Arc}; 27use std::{fmt, sync::Arc};
24 28
25use ra_syntax::{AtomEdit, SourceFileNode, TextRange, TextUnit}; 29use ra_syntax::{AtomEdit, SourceFileNode, TextRange, TextUnit};
30use ra_db::FileResolverImp;
26use rayon::prelude::*; 31use rayon::prelude::*;
27use relative_path::RelativePathBuf; 32use relative_path::RelativePathBuf;
28 33
29use crate::{ 34use crate::{
30 imp::{AnalysisHostImpl, AnalysisImpl, FileResolverImp}, 35 imp::{AnalysisHostImpl, AnalysisImpl},
31 symbol_index::SymbolIndex, 36 symbol_index::SymbolIndex,
32}; 37};
33 38
34pub use crate::{ 39pub use crate::{
35 completion::CompletionItem, 40 completion::CompletionItem,
36 descriptors::function::FnDescriptor,
37 input::{CrateGraph, CrateId, FileId, FileResolver},
38}; 41};
39pub use ra_editor::{ 42pub use ra_editor::{
40 FileSymbol, Fold, FoldKind, HighlightedRange, LineIndex, Runnable, RunnableKind, StructureNode, 43 FileSymbol, Fold, FoldKind, HighlightedRange, LineIndex, Runnable, RunnableKind, StructureNode,
41}; 44};
45pub use hir::FnSignatureInfo;
42 46
43#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 47pub use ra_db::{
44pub struct Canceled; 48 Canceled, Cancelable, FilePosition,
45 49 CrateGraph, CrateId, FileId, FileResolver
46pub type Cancelable<T> = Result<T, Canceled>; 50};
47
48impl std::fmt::Display for Canceled {
49 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50 fmt.write_str("Canceled")
51 }
52}
53
54impl std::error::Error for Canceled {}
55 51
56#[derive(Default)] 52#[derive(Default)]
57pub struct AnalysisChange { 53pub struct AnalysisChange {
@@ -121,12 +117,6 @@ impl AnalysisHost {
121 } 117 }
122} 118}
123 119
124#[derive(Clone, Copy, Debug)]
125pub struct FilePosition {
126 pub file_id: FileId,
127 pub offset: TextUnit,
128}
129
130#[derive(Debug)] 120#[derive(Debug)]
131pub struct SourceChange { 121pub struct SourceChange {
132 pub label: String, 122 pub label: String,
@@ -296,7 +286,7 @@ impl Analysis {
296 pub fn resolve_callable( 286 pub fn resolve_callable(
297 &self, 287 &self,
298 position: FilePosition, 288 position: FilePosition,
299 ) -> Cancelable<Option<(FnDescriptor, Option<usize>)>> { 289 ) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> {
300 self.imp.resolve_callable(position) 290 self.imp.resolve_callable(position)
301 } 291 }
302} 292}
diff --git a/crates/ra_analysis/src/loc2id.rs b/crates/ra_analysis/src/loc2id.rs
deleted file mode 100644
index e4b55f9b0..000000000
--- a/crates/ra_analysis/src/loc2id.rs
+++ /dev/null
@@ -1,138 +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 descriptors::module::ModuleId,
12 syntax_ptr::SyntaxPtr,
13 input::SourceRootId,
14};
15
16/// There are two principle ways to refer to things:
17/// - by their locatinon (module in foo/bar/baz.rs at line 42)
18/// - by their numeric id (module `ModuleId(42)`)
19///
20/// The first one is more powerful (you can actually find the thing in question
21/// by id), but the second one is so much more compact.
22///
23/// `Loc2IdMap` allows us to have a cake an eat it as well: by maintaining a
24/// bidirectional mapping between positional and numeric ids, we can use compact
25/// representation wich still allows us to get the actual item
26#[derive(Debug)]
27pub(crate) struct Loc2IdMap<L, ID>
28where
29 ID: NumericId,
30 L: Clone + Eq + Hash,
31{
32 loc2id: FxHashMap<L, ID>,
33 id2loc: FxHashMap<ID, L>,
34}
35
36impl<L, ID> Default for Loc2IdMap<L, ID>
37where
38 ID: NumericId,
39 L: Clone + Eq + Hash,
40{
41 fn default() -> Self {
42 Loc2IdMap {
43 loc2id: FxHashMap::default(),
44 id2loc: FxHashMap::default(),
45 }
46 }
47}
48
49impl<L, ID> Loc2IdMap<L, ID>
50where
51 ID: NumericId,
52 L: Clone + Eq + Hash,
53{
54 pub fn loc2id(&mut self, loc: &L) -> ID {
55 match self.loc2id.get(loc) {
56 Some(id) => return id.clone(),
57 None => (),
58 }
59 let id = self.loc2id.len();
60 assert!(id < u32::max_value() as usize);
61 let id = ID::from_u32(id as u32);
62 self.loc2id.insert(loc.clone(), id.clone());
63 self.id2loc.insert(id.clone(), loc.clone());
64 id
65 }
66
67 pub fn id2loc(&self, id: ID) -> L {
68 self.id2loc[&id].clone()
69 }
70}
71
72pub(crate) trait NumericId: Clone + Eq + Hash {
73 fn from_u32(id: u32) -> Self;
74 fn to_u32(self) -> u32;
75}
76
77macro_rules! impl_numeric_id {
78 ($id:ident) => {
79 impl NumericId for $id {
80 fn from_u32(id: u32) -> Self {
81 $id(id)
82 }
83 fn to_u32(self) -> u32 {
84 self.0
85 }
86 }
87 };
88}
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
91pub(crate) struct FnId(u32);
92impl_numeric_id!(FnId);
93
94#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
95pub(crate) struct DefId(u32);
96impl_numeric_id!(DefId);
97
98#[derive(Clone, Debug, PartialEq, Eq, Hash)]
99pub(crate) enum DefLoc {
100 Module {
101 id: ModuleId,
102 source_root: SourceRootId,
103 },
104 Item {
105 ptr: SyntaxPtr,
106 },
107}
108
109pub(crate) trait IdDatabase: salsa::Database {
110 fn id_maps(&self) -> &IdMaps;
111}
112
113#[derive(Debug, Default, Clone)]
114pub(crate) struct IdMaps {
115 inner: Arc<IdMapsInner>,
116}
117
118impl IdMaps {
119 pub(crate) fn fn_id(&self, ptr: SyntaxPtr) -> FnId {
120 self.inner.fns.lock().loc2id(&ptr)
121 }
122 pub(crate) fn fn_ptr(&self, fn_id: FnId) -> SyntaxPtr {
123 self.inner.fns.lock().id2loc(fn_id)
124 }
125
126 pub(crate) fn def_id(&self, loc: DefLoc) -> DefId {
127 self.inner.defs.lock().loc2id(&loc)
128 }
129 pub(crate) fn def_loc(&self, def_id: DefId) -> DefLoc {
130 self.inner.defs.lock().id2loc(def_id)
131 }
132}
133
134#[derive(Debug, Default)]
135struct IdMapsInner {
136 fns: Mutex<Loc2IdMap<SyntaxPtr, FnId>>,
137 defs: Mutex<Loc2IdMap<DefLoc, DefId>>,
138}
diff --git a/crates/ra_analysis/src/mock_analysis.rs b/crates/ra_analysis/src/mock_analysis.rs
index 8e8f969f4..0d9a7a147 100644
--- a/crates/ra_analysis/src/mock_analysis.rs
+++ b/crates/ra_analysis/src/mock_analysis.rs
@@ -1,9 +1,10 @@
1use std::sync::Arc; 1use std::sync::Arc;
2 2
3use relative_path::{RelativePath, RelativePathBuf}; 3use relative_path::{RelativePathBuf};
4use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER}; 4use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER};
5use ra_db::mock::FileMap;
5 6
6use crate::{Analysis, AnalysisChange, AnalysisHost, FileId, FileResolver, FilePosition}; 7use crate::{Analysis, AnalysisChange, AnalysisHost, FileId, FilePosition};
7 8
8/// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis 9/// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis
9/// from a set of in-memory files. 10/// from a set of in-memory files.
@@ -76,16 +77,15 @@ impl MockAnalysis {
76 } 77 }
77 pub fn analysis_host(self) -> AnalysisHost { 78 pub fn analysis_host(self) -> AnalysisHost {
78 let mut host = AnalysisHost::default(); 79 let mut host = AnalysisHost::default();
79 let mut file_map = Vec::new(); 80 let mut file_map = FileMap::default();
80 let mut change = AnalysisChange::new(); 81 let mut change = AnalysisChange::new();
81 for (id, (path, contents)) in self.files.into_iter().enumerate() { 82 for (path, contents) in self.files.into_iter() {
82 let file_id = FileId((id + 1) as u32);
83 assert!(path.starts_with('/')); 83 assert!(path.starts_with('/'));
84 let path = RelativePathBuf::from_path(&path[1..]).unwrap(); 84 let path = RelativePathBuf::from_path(&path[1..]).unwrap();
85 let file_id = file_map.add(path);
85 change.add_file(file_id, contents); 86 change.add_file(file_id, contents);
86 file_map.push((file_id, path));
87 } 87 }
88 change.set_file_resolver(Arc::new(FileMap(file_map))); 88 change.set_file_resolver(Arc::new(file_map));
89 host.apply_change(change); 89 host.apply_change(change);
90 host 90 host
91 } 91 }
@@ -113,29 +113,3 @@ pub fn single_file_with_position(code: &str) -> (Analysis, FilePosition) {
113 let pos = mock.add_file_with_position("/main.rs", code); 113 let pos = mock.add_file_with_position("/main.rs", code);
114 (mock.analysis(), pos) 114 (mock.analysis(), pos)
115} 115}
116
117#[derive(Debug)]
118struct FileMap(Vec<(FileId, RelativePathBuf)>);
119
120impl FileMap {
121 fn iter<'a>(&'a self) -> impl Iterator<Item = (FileId, &'a RelativePath)> + 'a {
122 self.0
123 .iter()
124 .map(|(id, path)| (*id, path.as_relative_path()))
125 }
126
127 fn path(&self, id: FileId) -> &RelativePath {
128 self.iter().find(|&(it, _)| it == id).unwrap().1
129 }
130}
131
132impl FileResolver for FileMap {
133 fn file_stem(&self, id: FileId) -> String {
134 self.path(id).file_stem().unwrap().to_string()
135 }
136 fn resolve(&self, id: FileId, rel: &RelativePath) -> Option<FileId> {
137 let path = self.path(id).join(rel).normalize();
138 let id = self.iter().find(|&(_, p)| path == p)?.0;
139 Some(id)
140 }
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 194b94584..000000000
--- a/crates/ra_analysis/src/syntax_ptr.rs
+++ /dev/null
@@ -1,84 +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 // Seems unfortunate to expose
67 pub(crate) fn range(self) -> TextRange {
68 self.range
69 }
70}
71
72#[test]
73fn test_local_syntax_ptr() {
74 use ra_syntax::{ast, AstNode};
75 let file = SourceFileNode::parse("struct Foo { f: u32, }");
76 let field = file
77 .syntax()
78 .descendants()
79 .find_map(ast::NamedFieldDef::cast)
80 .unwrap();
81 let ptr = LocalSyntaxPtr::new(field.syntax());
82 let field_syntax = ptr.resolve(&file);
83 assert_eq!(field.syntax(), field_syntax);
84}
diff --git a/crates/ra_analysis/tests/tests.rs b/crates/ra_analysis/tests/tests.rs
index 72b2ebf97..fbe89f444 100644
--- a/crates/ra_analysis/tests/tests.rs
+++ b/crates/ra_analysis/tests/tests.rs
@@ -10,10 +10,10 @@ use test_utils::assert_eq_dbg;
10 10
11use ra_analysis::{ 11use ra_analysis::{
12 mock_analysis::{analysis_and_position, single_file, single_file_with_position, MockAnalysis}, 12 mock_analysis::{analysis_and_position, single_file, single_file_with_position, MockAnalysis},
13 AnalysisChange, CrateGraph, FileId, FnDescriptor, 13 AnalysisChange, CrateGraph, FileId, FnSignatureInfo,
14}; 14};
15 15
16fn get_signature(text: &str) -> (FnDescriptor, Option<usize>) { 16fn get_signature(text: &str) -> (FnSignatureInfo, Option<usize>) {
17 let (analysis, position) = single_file_with_position(text); 17 let (analysis, position) = single_file_with_position(text);
18 analysis.resolve_callable(position).unwrap().unwrap() 18 analysis.resolve_callable(position).unwrap().unwrap()
19} 19}
@@ -126,7 +126,7 @@ fn test_resolve_crate_root() {
126 let mut host = mock.analysis_host(); 126 let mut host = mock.analysis_host();
127 assert!(host.analysis().crate_for(mod_file).unwrap().is_empty()); 127 assert!(host.analysis().crate_for(mod_file).unwrap().is_empty());
128 128
129 let mut crate_graph = CrateGraph::new(); 129 let mut crate_graph = CrateGraph::default();
130 let crate_id = crate_graph.add_crate_root(root_file); 130 let crate_id = crate_graph.add_crate_root(root_file);
131 let mut change = AnalysisChange::new(); 131 let mut change = AnalysisChange::new();
132 change.set_crate_graph(crate_graph); 132 change.set_crate_graph(crate_graph);
diff --git a/crates/ra_db/Cargo.toml b/crates/ra_db/Cargo.toml
new file mode 100644
index 000000000..f316c0ab2
--- /dev/null
+++ b/crates/ra_db/Cargo.toml
@@ -0,0 +1,14 @@
1[package]
2edition = "2018"
3name = "ra_db"
4version = "0.1.0"
5authors = ["Aleksey Kladov <[email protected]>"]
6
7[dependencies]
8relative-path = "0.4.0"
9salsa = "0.8.0"
10rustc-hash = "1.0"
11parking_lot = "0.6.4"
12ra_syntax = { path = "../ra_syntax" }
13ra_editor = { path = "../ra_editor" }
14test_utils = { path = "../test_utils" }
diff --git a/crates/ra_db/src/file_resolver.rs b/crates/ra_db/src/file_resolver.rs
new file mode 100644
index 000000000..f849ac752
--- /dev/null
+++ b/crates/ra_db/src/file_resolver.rs
@@ -0,0 +1,76 @@
1use std::{
2 sync::Arc,
3 hash::{Hash, Hasher},
4 fmt,
5};
6
7use relative_path::RelativePath;
8
9use crate::input::FileId;
10
11pub trait FileResolver: fmt::Debug + Send + Sync + 'static {
12 fn file_stem(&self, file_id: FileId) -> String;
13 fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option<FileId>;
14 fn debug_path(&self, _1file_id: FileId) -> Option<std::path::PathBuf> {
15 None
16 }
17}
18
19#[derive(Clone, Debug)]
20pub struct FileResolverImp {
21 inner: Arc<FileResolver>,
22}
23
24impl PartialEq for FileResolverImp {
25 fn eq(&self, other: &FileResolverImp) -> bool {
26 self.inner() == other.inner()
27 }
28}
29
30impl Eq for FileResolverImp {}
31
32impl Hash for FileResolverImp {
33 fn hash<H: Hasher>(&self, hasher: &mut H) {
34 self.inner().hash(hasher);
35 }
36}
37
38impl FileResolverImp {
39 pub fn new(inner: Arc<FileResolver>) -> FileResolverImp {
40 FileResolverImp { inner }
41 }
42 pub fn file_stem(&self, file_id: FileId) -> String {
43 self.inner.file_stem(file_id)
44 }
45 pub fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option<FileId> {
46 self.inner.resolve(file_id, path)
47 }
48 pub fn debug_path(&self, file_id: FileId) -> Option<std::path::PathBuf> {
49 self.inner.debug_path(file_id)
50 }
51 fn inner(&self) -> *const FileResolver {
52 &*self.inner
53 }
54}
55
56impl Default for FileResolverImp {
57 fn default() -> FileResolverImp {
58 #[derive(Debug)]
59 struct DummyResolver;
60 impl FileResolver for DummyResolver {
61 fn file_stem(&self, _file_: FileId) -> String {
62 panic!("file resolver not set")
63 }
64 fn resolve(
65 &self,
66 _file_id: FileId,
67 _path: &::relative_path::RelativePath,
68 ) -> Option<FileId> {
69 panic!("file resolver not set")
70 }
71 }
72 FileResolverImp {
73 inner: Arc::new(DummyResolver),
74 }
75 }
76}
diff --git a/crates/ra_analysis/src/input.rs b/crates/ra_db/src/input.rs
index a78b6e397..9101ac7a8 100644
--- a/crates/ra_analysis/src/input.rs
+++ b/crates/ra_db/src/input.rs
@@ -1,11 +1,10 @@
1use std::{fmt, sync::Arc}; 1use std::sync::Arc;
2 2
3use relative_path::RelativePath;
4use rustc_hash::FxHashMap; 3use rustc_hash::FxHashMap;
5use rustc_hash::FxHashSet; 4use rustc_hash::FxHashSet;
6use salsa; 5use salsa;
7 6
8use crate::{symbol_index::SymbolIndex, FileResolverImp}; 7use crate::file_resolver::FileResolverImp;
9 8
10#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 9#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
11pub struct FileId(pub u32); 10pub struct FileId(pub u32);
@@ -19,8 +18,8 @@ pub struct CrateGraph {
19} 18}
20 19
21impl CrateGraph { 20impl CrateGraph {
22 pub fn new() -> CrateGraph { 21 pub fn crate_root(&self, crate_id: CrateId) -> FileId {
23 CrateGraph::default() 22 self.crate_roots[&crate_id]
24 } 23 }
25 pub fn add_crate_root(&mut self, file_id: FileId) -> CrateId { 24 pub fn add_crate_root(&mut self, file_id: FileId) -> CrateId {
26 let crate_id = CrateId(self.crate_roots.len() as u32); 25 let crate_id = CrateId(self.crate_roots.len() as u32);
@@ -28,18 +27,17 @@ impl CrateGraph {
28 assert!(prev.is_none()); 27 assert!(prev.is_none());
29 crate_id 28 crate_id
30 } 29 }
31} 30 pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> {
32 31 let (&crate_id, _) = self
33pub trait FileResolver: fmt::Debug + Send + Sync + 'static { 32 .crate_roots
34 fn file_stem(&self, file_id: FileId) -> String; 33 .iter()
35 fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option<FileId>; 34 .find(|(_crate_id, &root_id)| root_id == file_id)?;
36 fn debug_path(&self, _file_id: FileId) -> Option<std::path::PathBuf> { 35 Some(crate_id)
37 None
38 } 36 }
39} 37}
40 38
41salsa::query_group! { 39salsa::query_group! {
42 pub(crate) trait FilesDatabase: salsa::Database { 40 pub trait FilesDatabase: salsa::Database {
43 fn file_text(file_id: FileId) -> Arc<String> { 41 fn file_text(file_id: FileId) -> Arc<String> {
44 type FileTextQuery; 42 type FileTextQuery;
45 storage input; 43 storage input;
@@ -56,10 +54,6 @@ salsa::query_group! {
56 type LibrariesQuery; 54 type LibrariesQuery;
57 storage input; 55 storage input;
58 } 56 }
59 fn library_symbols(id: SourceRootId) -> Arc<SymbolIndex> {
60 type LibrarySymbolsQuery;
61 storage input;
62 }
63 fn crate_graph() -> Arc<CrateGraph> { 57 fn crate_graph() -> Arc<CrateGraph> {
64 type CrateGraphQuery; 58 type CrateGraphQuery;
65 storage input; 59 storage input;
@@ -68,12 +62,12 @@ salsa::query_group! {
68} 62}
69 63
70#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 64#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
71pub(crate) struct SourceRootId(pub(crate) u32); 65pub struct SourceRootId(pub u32);
72 66
73#[derive(Default, Clone, Debug, PartialEq, Eq)] 67#[derive(Default, Clone, Debug, PartialEq, Eq)]
74pub(crate) struct SourceRoot { 68pub struct SourceRoot {
75 pub(crate) file_resolver: FileResolverImp, 69 pub file_resolver: FileResolverImp,
76 pub(crate) files: FxHashSet<FileId>, 70 pub files: FxHashSet<FileId>,
77} 71}
78 72
79pub(crate) const WORKSPACE: SourceRootId = SourceRootId(0); 73pub const WORKSPACE: SourceRootId = SourceRootId(0);
diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs
new file mode 100644
index 000000000..53805aada
--- /dev/null
+++ b/crates/ra_db/src/lib.rs
@@ -0,0 +1,83 @@
1//! ra_db defines basic database traits. Concrete DB is defined by ra_analysis.
2mod syntax_ptr;
3mod file_resolver;
4mod input;
5mod loc2id;
6pub mod mock;
7
8use std::sync::Arc;
9use ra_editor::LineIndex;
10use ra_syntax::{TextUnit, SourceFileNode};
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
13pub struct Canceled;
14
15pub type Cancelable<T> = Result<T, Canceled>;
16
17impl std::fmt::Display for Canceled {
18 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19 fmt.write_str("Canceled")
20 }
21}
22
23impl std::error::Error for Canceled {}
24
25pub use crate::{
26 syntax_ptr::LocalSyntaxPtr,
27 file_resolver::{FileResolver, FileResolverImp},
28 input::{
29 FilesDatabase, FileId, CrateId, SourceRoot, SourceRootId, CrateGraph, WORKSPACE,
30 FileTextQuery, FileSourceRootQuery, SourceRootQuery, LibrariesQuery, CrateGraphQuery,
31 },
32 loc2id::{LocationIntener, NumericId},
33};
34
35#[macro_export]
36macro_rules! impl_numeric_id {
37 ($id:ident) => {
38 impl $crate::NumericId for $id {
39 fn from_u32(id: u32) -> Self {
40 $id(id)
41 }
42 fn to_u32(self) -> u32 {
43 self.0
44 }
45 }
46 };
47}
48
49pub trait BaseDatabase: salsa::Database {
50 fn check_canceled(&self) -> Cancelable<()> {
51 if self.salsa_runtime().is_current_revision_canceled() {
52 Err(Canceled)
53 } else {
54 Ok(())
55 }
56 }
57}
58
59salsa::query_group! {
60 pub trait SyntaxDatabase: crate::input::FilesDatabase + BaseDatabase {
61 fn source_file(file_id: FileId) -> SourceFileNode {
62 type SourceFileQuery;
63 }
64 fn file_lines(file_id: FileId) -> Arc<LineIndex> {
65 type FileLinesQuery;
66 }
67 }
68}
69
70fn source_file(db: &impl SyntaxDatabase, file_id: FileId) -> SourceFileNode {
71 let text = db.file_text(file_id);
72 SourceFileNode::parse(&*text)
73}
74fn file_lines(db: &impl SyntaxDatabase, file_id: FileId) -> Arc<LineIndex> {
75 let text = db.file_text(file_id);
76 Arc::new(LineIndex::new(&*text))
77}
78
79#[derive(Clone, Copy, Debug)]
80pub struct FilePosition {
81 pub file_id: FileId,
82 pub offset: TextUnit,
83}
diff --git a/crates/ra_db/src/loc2id.rs b/crates/ra_db/src/loc2id.rs
new file mode 100644
index 000000000..69ba43d0f
--- /dev/null
+++ b/crates/ra_db/src/loc2id.rs
@@ -0,0 +1,100 @@
1use parking_lot::Mutex;
2
3use std::hash::Hash;
4
5use rustc_hash::FxHashMap;
6
7/// There are two principle ways to refer to things:
8/// - by their locatinon (module in foo/bar/baz.rs at line 42)
9/// - by their numeric id (module `ModuleId(42)`)
10///
11/// The first one is more powerful (you can actually find the thing in question
12/// by id), but the second one is so much more compact.
13///
14/// `Loc2IdMap` allows us to have a cake an eat it as well: by maintaining a
15/// bidirectional mapping between positional and numeric ids, we can use compact
16/// representation wich still allows us to get the actual item
17#[derive(Debug)]
18struct Loc2IdMap<LOC, ID>
19where
20 ID: NumericId,
21 LOC: Clone + Eq + Hash,
22{
23 loc2id: FxHashMap<LOC, ID>,
24 id2loc: FxHashMap<ID, LOC>,
25}
26
27impl<LOC, ID> Default for Loc2IdMap<LOC, ID>
28where
29 ID: NumericId,
30 LOC: Clone + Eq + Hash,
31{
32 fn default() -> Self {
33 Loc2IdMap {
34 loc2id: FxHashMap::default(),
35 id2loc: FxHashMap::default(),
36 }
37 }
38}
39
40impl<LOC, ID> Loc2IdMap<LOC, ID>
41where
42 ID: NumericId,
43 LOC: Clone + Eq + Hash,
44{
45 pub fn loc2id(&mut self, loc: &LOC) -> ID {
46 match self.loc2id.get(loc) {
47 Some(id) => return id.clone(),
48 None => (),
49 }
50 let id = self.loc2id.len();
51 assert!(id < u32::max_value() as usize);
52 let id = ID::from_u32(id as u32);
53 self.loc2id.insert(loc.clone(), id.clone());
54 self.id2loc.insert(id.clone(), loc.clone());
55 id
56 }
57
58 pub fn id2loc(&self, id: ID) -> LOC {
59 self.id2loc[&id].clone()
60 }
61}
62
63pub trait NumericId: Clone + Eq + Hash {
64 fn from_u32(id: u32) -> Self;
65 fn to_u32(self) -> u32;
66}
67
68#[derive(Debug)]
69pub struct LocationIntener<LOC, ID>
70where
71 ID: NumericId,
72 LOC: Clone + Eq + Hash,
73{
74 map: Mutex<Loc2IdMap<LOC, ID>>,
75}
76
77impl<LOC, ID> Default for LocationIntener<LOC, ID>
78where
79 ID: NumericId,
80 LOC: Clone + Eq + Hash,
81{
82 fn default() -> Self {
83 LocationIntener {
84 map: Default::default(),
85 }
86 }
87}
88
89impl<LOC, ID> LocationIntener<LOC, ID>
90where
91 ID: NumericId,
92 LOC: Clone + Eq + Hash,
93{
94 pub fn loc2id(&self, loc: &LOC) -> ID {
95 self.map.lock().loc2id(loc)
96 }
97 pub fn id2loc(&self, id: ID) -> LOC {
98 self.map.lock().id2loc(id)
99 }
100}
diff --git a/crates/ra_db/src/mock.rs b/crates/ra_db/src/mock.rs
new file mode 100644
index 000000000..2840f9655
--- /dev/null
+++ b/crates/ra_db/src/mock.rs
@@ -0,0 +1,51 @@
1use std::sync::Arc;
2
3use rustc_hash::FxHashSet;
4use relative_path::{RelativePath, RelativePathBuf};
5
6use crate::{FileId, FileResolver, SourceRoot, FileResolverImp};
7
8#[derive(Default, Debug)]
9pub struct FileMap(Vec<(FileId, RelativePathBuf)>);
10
11impl FileMap {
12 pub fn add(&mut self, path: RelativePathBuf) -> FileId {
13 let file_id = FileId((self.0.len() + 1) as u32);
14 self.0.push((file_id, path));
15 file_id
16 }
17
18 pub fn into_source_root(self) -> SourceRoot {
19 let files = self.files();
20 let file_resolver = FileResolverImp::new(Arc::new(self));
21 SourceRoot {
22 file_resolver,
23 files,
24 }
25 }
26
27 pub fn files(&self) -> FxHashSet<FileId> {
28 self.iter().map(|(id, _)| id).collect()
29 }
30
31 fn iter<'a>(&'a self) -> impl Iterator<Item = (FileId, &'a RelativePath)> + 'a {
32 self.0
33 .iter()
34 .map(|(id, path)| (*id, path.as_relative_path()))
35 }
36
37 fn path(&self, id: FileId) -> &RelativePath {
38 self.iter().find(|&(it, _)| it == id).unwrap().1
39 }
40}
41
42impl FileResolver for FileMap {
43 fn file_stem(&self, id: FileId) -> String {
44 self.path(id).file_stem().unwrap().to_string()
45 }
46 fn resolve(&self, id: FileId, rel: &RelativePath) -> Option<FileId> {
47 let path = self.path(id).join(rel).normalize();
48 let id = self.iter().find(|&(_, p)| path == p)?.0;
49 Some(id)
50 }
51}
diff --git a/crates/ra_db/src/syntax_ptr.rs b/crates/ra_db/src/syntax_ptr.rs
new file mode 100644
index 000000000..dac94dd36
--- /dev/null
+++ b/crates/ra_db/src/syntax_ptr.rs
@@ -0,0 +1,48 @@
1use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, TextRange};
2
3/// A pionter to a syntax node inside a file.
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
5pub struct LocalSyntaxPtr {
6 range: TextRange,
7 kind: SyntaxKind,
8}
9
10impl LocalSyntaxPtr {
11 pub fn new(node: SyntaxNodeRef) -> LocalSyntaxPtr {
12 LocalSyntaxPtr {
13 range: node.range(),
14 kind: node.kind(),
15 }
16 }
17
18 pub fn resolve(self, file: &SourceFileNode) -> SyntaxNode {
19 let mut curr = file.syntax();
20 loop {
21 if curr.range() == self.range && curr.kind() == self.kind {
22 return curr.owned();
23 }
24 curr = curr
25 .children()
26 .find(|it| self.range.is_subrange(&it.range()))
27 .unwrap_or_else(|| panic!("can't resolve local ptr to SyntaxNode: {:?}", self))
28 }
29 }
30
31 pub fn range(self) -> TextRange {
32 self.range
33 }
34}
35
36#[test]
37fn test_local_syntax_ptr() {
38 use ra_syntax::{ast, AstNode};
39 let file = SourceFileNode::parse("struct Foo { f: u32, }");
40 let field = file
41 .syntax()
42 .descendants()
43 .find_map(ast::NamedFieldDef::cast)
44 .unwrap();
45 let ptr = LocalSyntaxPtr::new(field.syntax());
46 let field_syntax = ptr.resolve(&file);
47 assert_eq!(field.syntax(), field_syntax);
48}
diff --git a/crates/ra_editor/src/code_actions.rs b/crates/ra_editor/src/code_actions.rs
index 0139b19d3..6979251d1 100644
--- a/crates/ra_editor/src/code_actions.rs
+++ b/crates/ra_editor/src/code_actions.rs
@@ -4,7 +4,7 @@ use ra_syntax::{
4 algo::{find_covering_node, find_leaf_at_offset}, 4 algo::{find_covering_node, find_leaf_at_offset},
5 ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner}, 5 ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner},
6 Direction, SourceFileNode, 6 Direction, SourceFileNode,
7 SyntaxKind::{COMMA, WHITESPACE}, 7 SyntaxKind::{COMMA, WHITESPACE, COMMENT},
8 SyntaxNodeRef, TextRange, TextUnit, 8 SyntaxNodeRef, TextRange, TextUnit,
9}; 9};
10 10
@@ -41,7 +41,8 @@ pub fn add_derive<'a>(
41 offset: TextUnit, 41 offset: TextUnit,
42) -> Option<impl FnOnce() -> LocalEdit + 'a> { 42) -> Option<impl FnOnce() -> LocalEdit + 'a> {
43 let nominal = find_node_at_offset::<ast::NominalDef>(file.syntax(), offset)?; 43 let nominal = find_node_at_offset::<ast::NominalDef>(file.syntax(), offset)?;
44 Some(move || { 44 let node_start = derive_insertion_offset(nominal)?;
45 return Some(move || {
45 let derive_attr = nominal 46 let derive_attr = nominal
46 .attrs() 47 .attrs()
47 .filter_map(|x| x.as_call()) 48 .filter_map(|x| x.as_call())
@@ -51,7 +52,6 @@ pub fn add_derive<'a>(
51 let mut edit = EditBuilder::new(); 52 let mut edit = EditBuilder::new();
52 let offset = match derive_attr { 53 let offset = match derive_attr {
53 None => { 54 None => {
54 let node_start = nominal.syntax().range().start();
55 edit.insert(node_start, "#[derive()]\n".to_string()); 55 edit.insert(node_start, "#[derive()]\n".to_string());
56 node_start + TextUnit::of_str("#[derive(") 56 node_start + TextUnit::of_str("#[derive(")
57 } 57 }
@@ -61,7 +61,16 @@ pub fn add_derive<'a>(
61 edit: edit.finish(), 61 edit: edit.finish(),
62 cursor_position: Some(offset), 62 cursor_position: Some(offset),
63 } 63 }
64 }) 64 });
65
66 // Insert `derive` after doc comments.
67 fn derive_insertion_offset(nominal: ast::NominalDef) -> Option<TextUnit> {
68 let non_ws_child = nominal
69 .syntax()
70 .children()
71 .find(|it| it.kind() != COMMENT && it.kind() != WHITESPACE)?;
72 Some(non_ws_child.range().start())
73 }
65} 74}
66 75
67pub fn add_impl<'a>( 76pub fn add_impl<'a>(
@@ -113,7 +122,7 @@ pub fn introduce_variable<'a>(
113 let node = find_covering_node(file.syntax(), range); 122 let node = find_covering_node(file.syntax(), range);
114 let expr = node.ancestors().filter_map(ast::Expr::cast).next()?; 123 let expr = node.ancestors().filter_map(ast::Expr::cast).next()?;
115 124
116 let anchor_stmt = ahchor_stmt(expr)?; 125 let anchor_stmt = anchor_stmt(expr)?;
117 let indent = anchor_stmt.prev_sibling()?; 126 let indent = anchor_stmt.prev_sibling()?;
118 if indent.kind() != WHITESPACE { 127 if indent.kind() != WHITESPACE {
119 return None; 128 return None;
@@ -124,7 +133,12 @@ pub fn introduce_variable<'a>(
124 133
125 buf.push_str("let var_name = "); 134 buf.push_str("let var_name = ");
126 expr.syntax().text().push_to(&mut buf); 135 expr.syntax().text().push_to(&mut buf);
127 if expr.syntax().range().start() == anchor_stmt.range().start() { 136 let is_full_stmt = if let Some(expr_stmt) = ast::ExprStmt::cast(anchor_stmt) {
137 Some(expr.syntax()) == expr_stmt.expr().map(|e| e.syntax())
138 } else {
139 false
140 };
141 if is_full_stmt {
128 edit.replace(expr.syntax().range(), buf); 142 edit.replace(expr.syntax().range(), buf);
129 } else { 143 } else {
130 buf.push_str(";"); 144 buf.push_str(";");
@@ -141,7 +155,7 @@ pub fn introduce_variable<'a>(
141 155
142 /// Statement or last in the block expression, which will follow 156 /// Statement or last in the block expression, which will follow
143 /// the freshly introduced var. 157 /// the freshly introduced var.
144 fn ahchor_stmt(expr: ast::Expr) -> Option<SyntaxNodeRef> { 158 fn anchor_stmt(expr: ast::Expr) -> Option<SyntaxNodeRef> {
145 expr.syntax().ancestors().find(|&node| { 159 expr.syntax().ancestors().find(|&node| {
146 if ast::Stmt::cast(node).is_some() { 160 if ast::Stmt::cast(node).is_some() {
147 return true; 161 return true;
@@ -181,7 +195,7 @@ mod tests {
181 } 195 }
182 196
183 #[test] 197 #[test]
184 fn test_add_derive() { 198 fn add_derive_new() {
185 check_action( 199 check_action(
186 "struct Foo { a: i32, <|>}", 200 "struct Foo { a: i32, <|>}",
187 "#[derive(<|>)]\nstruct Foo { a: i32, }", 201 "#[derive(<|>)]\nstruct Foo { a: i32, }",
@@ -192,6 +206,10 @@ mod tests {
192 "#[derive(<|>)]\nstruct Foo { a: i32, }", 206 "#[derive(<|>)]\nstruct Foo { a: i32, }",
193 |file, off| add_derive(file, off).map(|f| f()), 207 |file, off| add_derive(file, off).map(|f| f()),
194 ); 208 );
209 }
210
211 #[test]
212 fn add_derive_existing() {
195 check_action( 213 check_action(
196 "#[derive(Clone)]\nstruct Foo { a: i32<|>, }", 214 "#[derive(Clone)]\nstruct Foo { a: i32<|>, }",
197 "#[derive(Clone<|>)]\nstruct Foo { a: i32, }", 215 "#[derive(Clone<|>)]\nstruct Foo { a: i32, }",
@@ -200,6 +218,24 @@ mod tests {
200 } 218 }
201 219
202 #[test] 220 #[test]
221 fn add_derive_new_with_doc_comment() {
222 check_action(
223 "
224/// `Foo` is a pretty important struct.
225/// It does stuff.
226struct Foo { a: i32<|>, }
227 ",
228 "
229/// `Foo` is a pretty important struct.
230/// It does stuff.
231#[derive(<|>)]
232struct Foo { a: i32, }
233 ",
234 |file, off| add_derive(file, off).map(|f| f()),
235 );
236 }
237
238 #[test]
203 fn test_add_impl() { 239 fn test_add_impl() {
204 check_action( 240 check_action(
205 "struct Foo {<|>}\n", 241 "struct Foo {<|>}\n",
@@ -219,7 +255,7 @@ mod tests {
219 } 255 }
220 256
221 #[test] 257 #[test]
222 fn test_intrdoduce_var_simple() { 258 fn test_introduce_var_simple() {
223 check_action_range( 259 check_action_range(
224 " 260 "
225fn foo() { 261fn foo() {
@@ -235,7 +271,7 @@ fn foo() {
235 } 271 }
236 272
237 #[test] 273 #[test]
238 fn test_intrdoduce_var_expr_stmt() { 274 fn test_introduce_var_expr_stmt() {
239 check_action_range( 275 check_action_range(
240 " 276 "
241fn foo() { 277fn foo() {
@@ -250,7 +286,23 @@ fn foo() {
250 } 286 }
251 287
252 #[test] 288 #[test]
253 fn test_intrdoduce_var_last_expr() { 289 fn test_introduce_var_part_of_expr_stmt() {
290 check_action_range(
291 "
292fn foo() {
293 <|>1<|> + 1;
294}",
295 "
296fn foo() {
297 let <|>var_name = 1;
298 var_name + 1;
299}",
300 |file, range| introduce_variable(file, range).map(|f| f()),
301 );
302 }
303
304 #[test]
305 fn test_introduce_var_last_expr() {
254 check_action_range( 306 check_action_range(
255 " 307 "
256fn foo() { 308fn foo() {
@@ -265,4 +317,20 @@ fn foo() {
265 ); 317 );
266 } 318 }
267 319
320 #[test]
321 fn test_introduce_var_last_full_expr() {
322 check_action_range(
323 "
324fn foo() {
325 <|>bar(1 + 1)<|>
326}",
327 "
328fn foo() {
329 let <|>var_name = bar(1 + 1);
330 var_name
331}",
332 |file, range| introduce_variable(file, range).map(|f| f()),
333 );
334 }
335
268} 336}
diff --git a/crates/ra_editor/src/edit.rs b/crates/ra_editor/src/edit.rs
index c3149ec54..372b8d14c 100644
--- a/crates/ra_editor/src/edit.rs
+++ b/crates/ra_editor/src/edit.rs
@@ -26,7 +26,7 @@ impl EditBuilder {
26 } 26 }
27 pub fn finish(self) -> Edit { 27 pub fn finish(self) -> Edit {
28 let mut atoms = self.atoms; 28 let mut atoms = self.atoms;
29 atoms.sort_by_key(|a| a.delete.start()); 29 atoms.sort_by_key(|a| (a.delete.start(), a.delete.end()));
30 for (a1, a2) in atoms.iter().zip(atoms.iter().skip(1)) { 30 for (a1, a2) in atoms.iter().zip(atoms.iter().skip(1)) {
31 assert!(a1.delete.end() <= a2.delete.start()) 31 assert!(a1.delete.end() <= a2.delete.start())
32 } 32 }
diff --git a/crates/ra_hir/Cargo.toml b/crates/ra_hir/Cargo.toml
new file mode 100644
index 000000000..1b9e148b2
--- /dev/null
+++ b/crates/ra_hir/Cargo.toml
@@ -0,0 +1,17 @@
1[package]
2edition = "2018"
3name = "ra_hir"
4version = "0.1.0"
5authors = ["Aleksey Kladov <[email protected]>"]
6
7[dependencies]
8log = "0.4.5"
9relative-path = "0.4.0"
10salsa = "0.8.0"
11rustc-hash = "1.0"
12parking_lot = "0.6.4"
13id-arena = "2.0"
14ra_syntax = { path = "../ra_syntax" }
15ra_editor = { path = "../ra_editor" }
16ra_db = { path = "../ra_db" }
17test_utils = { path = "../test_utils" }
diff --git a/crates/ra_hir/src/arena.rs b/crates/ra_hir/src/arena.rs
new file mode 100644
index 000000000..d4f9d9cb9
--- /dev/null
+++ b/crates/ra_hir/src/arena.rs
@@ -0,0 +1,66 @@
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 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() -> u32 {
50 0
51 }
52 fn new_id(_arena_id: u32, 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>) -> u32 {
62 0
63 }
64}
65
66pub(crate) type Arena<T> = id_arena::Arena<T, ArenaBehavior<T>>;
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
new file mode 100644
index 000000000..2f01bae6d
--- /dev/null
+++ b/crates/ra_hir/src/db.rs
@@ -0,0 +1,66 @@
1use std::sync::Arc;
2
3use ra_syntax::{
4 SyntaxNode,
5 ast::FnDefNode,
6};
7use ra_db::{SourceRootId, LocationIntener, SyntaxDatabase, FileId, Cancelable};
8
9use crate::{
10 DefLoc, DefId, FnId,
11 SourceFileItems, SourceItemId,
12 query_definitions,
13 FnScopes,
14 module::{ModuleId, ModuleTree, ModuleSource,
15 nameres::{ItemMap, InputModuleItems}},
16};
17
18salsa::query_group! {
19
20pub trait HirDatabase: SyntaxDatabase
21 + AsRef<LocationIntener<DefLoc, DefId>>
22 + AsRef<LocationIntener<SourceItemId, FnId>>
23{
24 fn fn_scopes(fn_id: FnId) -> Arc<FnScopes> {
25 type FnScopesQuery;
26 use fn query_definitions::fn_scopes;
27 }
28 fn fn_syntax(fn_id: FnId) -> FnDefNode {
29 type FnSyntaxQuery;
30 // Don't retain syntax trees in memory
31 storage dependencies;
32 use fn query_definitions::fn_syntax;
33 }
34
35 fn file_items(file_id: FileId) -> Arc<SourceFileItems> {
36 type SourceFileItemsQuery;
37 storage dependencies;
38 use fn query_definitions::file_items;
39 }
40
41 fn file_item(source_item_id: SourceItemId) -> SyntaxNode {
42 type FileItemQuery;
43 storage dependencies;
44 use fn query_definitions::file_item;
45 }
46
47 fn submodules(source: ModuleSource) -> Cancelable<Arc<Vec<crate::module::imp::Submodule>>> {
48 type SubmodulesQuery;
49 use fn query_definitions::submodules;
50 }
51
52 fn input_module_items(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable<Arc<InputModuleItems>> {
53 type InputModuleItemsQuery;
54 use fn query_definitions::input_module_items;
55 }
56 fn item_map(source_root_id: SourceRootId) -> Cancelable<Arc<ItemMap>> {
57 type ItemMapQuery;
58 use fn query_definitions::item_map;
59 }
60 fn module_tree(source_root_id: SourceRootId) -> Cancelable<Arc<ModuleTree>> {
61 type ModuleTreeQuery;
62 use fn crate::module::imp::module_tree;
63 }
64}
65
66}
diff --git a/crates/ra_analysis/src/descriptors/function/mod.rs b/crates/ra_hir/src/function/mod.rs
index 86eee5e93..c8af2e54f 100644
--- a/crates/ra_analysis/src/descriptors/function/mod.rs
+++ b/crates/ra_hir/src/function/mod.rs
@@ -1,30 +1,83 @@
1pub(super) mod imp;
2mod scope; 1mod scope;
3 2
4use std::cmp::{max, min}; 3use std::{
4 cmp::{max, min},
5 sync::Arc,
6};
5 7
6use ra_syntax::{ 8use ra_syntax::{
9 TextRange, TextUnit, SyntaxNodeRef,
7 ast::{self, AstNode, DocCommentsOwner, NameOwner}, 10 ast::{self, AstNode, DocCommentsOwner, NameOwner},
8 TextRange, TextUnit,
9}; 11};
12use ra_db::FileId;
10 13
11use crate::{ 14use crate::{
12 syntax_ptr::SyntaxPtr, FileId, 15 FnId, HirDatabase, SourceItemId,
13 loc2id::IdDatabase,
14}; 16};
15 17
16pub(crate) use self::scope::{resolve_local_name, FnScopes}; 18pub use self::scope::FnScopes;
17pub(crate) use crate::loc2id::FnId;
18 19
19impl FnId { 20impl FnId {
20 pub(crate) fn get(db: &impl IdDatabase, file_id: FileId, fn_def: ast::FnDef) -> FnId { 21 pub fn get(db: &impl HirDatabase, file_id: FileId, fn_def: ast::FnDef) -> FnId {
21 let ptr = SyntaxPtr::new(file_id, fn_def.syntax()); 22 let file_items = db.file_items(file_id);
22 db.id_maps().fn_id(ptr) 23 let item_id = file_items.id_of(fn_def.syntax());
24 let item_id = SourceItemId { file_id, item_id };
25 FnId::from_loc(db, &item_id)
26 }
27}
28
29pub struct Function {
30 fn_id: FnId,
31}
32
33impl Function {
34 pub fn guess_from_source(
35 db: &impl HirDatabase,
36 file_id: FileId,
37 fn_def: ast::FnDef,
38 ) -> Function {
39 let fn_id = FnId::get(db, file_id, fn_def);
40 Function { fn_id }
41 }
42
43 pub fn guess_for_name_ref(
44 db: &impl HirDatabase,
45 file_id: FileId,
46 name_ref: ast::NameRef,
47 ) -> Option<Function> {
48 Function::guess_for_node(db, file_id, name_ref.syntax())
49 }
50
51 pub fn guess_for_bind_pat(
52 db: &impl HirDatabase,
53 file_id: FileId,
54 bind_pat: ast::BindPat,
55 ) -> Option<Function> {
56 Function::guess_for_node(db, file_id, bind_pat.syntax())
57 }
58
59 fn guess_for_node(
60 db: &impl HirDatabase,
61 file_id: FileId,
62 node: SyntaxNodeRef,
63 ) -> Option<Function> {
64 let fn_def = node.ancestors().find_map(ast::FnDef::cast)?;
65 let res = Function::guess_from_source(db, file_id, fn_def);
66 Some(res)
67 }
68
69 pub fn scope(&self, db: &impl HirDatabase) -> Arc<FnScopes> {
70 db.fn_scopes(self.fn_id)
71 }
72
73 pub fn signature_info(&self, db: &impl HirDatabase) -> Option<FnSignatureInfo> {
74 let syntax = db.fn_syntax(self.fn_id);
75 FnSignatureInfo::new(syntax.borrowed())
23 } 76 }
24} 77}
25 78
26#[derive(Debug, Clone)] 79#[derive(Debug, Clone)]
27pub struct FnDescriptor { 80pub struct FnSignatureInfo {
28 pub name: String, 81 pub name: String,
29 pub label: String, 82 pub label: String,
30 pub ret_type: Option<String>, 83 pub ret_type: Option<String>,
@@ -32,8 +85,8 @@ pub struct FnDescriptor {
32 pub doc: Option<String>, 85 pub doc: Option<String>,
33} 86}
34 87
35impl FnDescriptor { 88impl FnSignatureInfo {
36 pub fn new(node: ast::FnDef) -> Option<Self> { 89 fn new(node: ast::FnDef) -> Option<Self> {
37 let name = node.name()?.text().to_string(); 90 let name = node.name()?.text().to_string();
38 91
39 let mut doc = None; 92 let mut doc = None;
@@ -52,7 +105,7 @@ impl FnDescriptor {
52 node.syntax().text().to_string() 105 node.syntax().text().to_string()
53 }; 106 };
54 107
55 if let Some((comment_range, docs)) = FnDescriptor::extract_doc_comments(node) { 108 if let Some((comment_range, docs)) = FnSignatureInfo::extract_doc_comments(node) {
56 let comment_range = comment_range 109 let comment_range = comment_range
57 .checked_sub(node.syntax().range().start()) 110 .checked_sub(node.syntax().range().start())
58 .unwrap(); 111 .unwrap();
@@ -84,10 +137,10 @@ impl FnDescriptor {
84 } 137 }
85 } 138 }
86 139
87 let params = FnDescriptor::param_list(node); 140 let params = FnSignatureInfo::param_list(node);
88 let ret_type = node.ret_type().map(|r| r.syntax().text().to_string()); 141 let ret_type = node.ret_type().map(|r| r.syntax().text().to_string());
89 142
90 Some(FnDescriptor { 143 Some(FnSignatureInfo {
91 name, 144 name,
92 ret_type, 145 ret_type,
93 params, 146 params,
diff --git a/crates/ra_analysis/src/descriptors/function/scope.rs b/crates/ra_hir/src/function/scope.rs
index 54d7fa456..863453291 100644
--- a/crates/ra_analysis/src/descriptors/function/scope.rs
+++ b/crates/ra_hir/src/function/scope.rs
@@ -1,13 +1,13 @@
1use rustc_hash::{FxHashMap, FxHashSet}; 1use rustc_hash::{FxHashMap, FxHashSet};
2 2
3use ra_syntax::{ 3use ra_syntax::{
4 AstNode, SmolStr, SyntaxNodeRef, TextRange,
4 algo::generate, 5 algo::generate,
5 ast::{self, ArgListOwner, LoopBodyOwner, NameOwner}, 6 ast::{self, ArgListOwner, LoopBodyOwner, NameOwner},
6 AstNode, SmolStr, SyntaxNodeRef,
7}; 7};
8use ra_db::LocalSyntaxPtr;
8 9
9use crate::{ 10use crate::{
10 syntax_ptr::LocalSyntaxPtr,
11 arena::{Arena, Id}, 11 arena::{Arena, Id},
12}; 12};
13 13
@@ -15,7 +15,7 @@ pub(crate) type ScopeId = Id<ScopeData>;
15 15
16#[derive(Debug, PartialEq, Eq)] 16#[derive(Debug, PartialEq, Eq)]
17pub struct FnScopes { 17pub struct FnScopes {
18 pub(crate) self_param: Option<LocalSyntaxPtr>, 18 pub self_param: Option<LocalSyntaxPtr>,
19 scopes: Arena<ScopeData>, 19 scopes: Arena<ScopeData>,
20 scope_for: FxHashMap<LocalSyntaxPtr, ScopeId>, 20 scope_for: FxHashMap<LocalSyntaxPtr, ScopeId>,
21} 21}
@@ -27,13 +27,13 @@ pub struct ScopeEntry {
27} 27}
28 28
29#[derive(Debug, PartialEq, Eq)] 29#[derive(Debug, PartialEq, Eq)]
30pub(crate) struct ScopeData { 30pub struct ScopeData {
31 parent: Option<ScopeId>, 31 parent: Option<ScopeId>,
32 entries: Vec<ScopeEntry>, 32 entries: Vec<ScopeEntry>,
33} 33}
34 34
35impl FnScopes { 35impl FnScopes {
36 pub(crate) fn new(fn_def: ast::FnDef) -> FnScopes { 36 pub fn new(fn_def: ast::FnDef) -> FnScopes {
37 let mut scopes = FnScopes { 37 let mut scopes = FnScopes {
38 self_param: fn_def 38 self_param: fn_def
39 .param_list() 39 .param_list()
@@ -49,7 +49,7 @@ impl FnScopes {
49 } 49 }
50 scopes 50 scopes
51 } 51 }
52 pub(crate) fn entries(&self, scope: ScopeId) -> &[ScopeEntry] { 52 pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
53 &self.scopes[scope].entries 53 &self.scopes[scope].entries
54 } 54 }
55 pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item = ScopeId> + 'a { 55 pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item = ScopeId> + 'a {
@@ -57,14 +57,45 @@ impl FnScopes {
57 self.scopes[scope].parent 57 self.scopes[scope].parent
58 }) 58 })
59 } 59 }
60 pub fn resolve_local_name<'a>(&'a self, name_ref: ast::NameRef) -> Option<&'a ScopeEntry> {
61 let mut shadowed = FxHashSet::default();
62 let ret = self
63 .scope_chain(name_ref.syntax())
64 .flat_map(|scope| self.entries(scope).iter())
65 .filter(|entry| shadowed.insert(entry.name()))
66 .filter(|entry| entry.name() == &name_ref.text())
67 .nth(0);
68 ret
69 }
70
71 pub fn find_all_refs(&self, pat: ast::BindPat) -> Vec<ReferenceDescriptor> {
72 let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap();
73 let name_ptr = LocalSyntaxPtr::new(pat.syntax());
74 let refs: Vec<_> = fn_def
75 .syntax()
76 .descendants()
77 .filter_map(ast::NameRef::cast)
78 .filter(|name_ref| match self.resolve_local_name(*name_ref) {
79 None => false,
80 Some(entry) => entry.ptr() == name_ptr,
81 })
82 .map(|name_ref| ReferenceDescriptor {
83 name: name_ref.syntax().text().to_string(),
84 range: name_ref.syntax().range(),
85 })
86 .collect();
87
88 refs
89 }
90
60 fn root_scope(&mut self) -> ScopeId { 91 fn root_scope(&mut self) -> ScopeId {
61 self.scopes.push(ScopeData { 92 self.scopes.alloc(ScopeData {
62 parent: None, 93 parent: None,
63 entries: vec![], 94 entries: vec![],
64 }) 95 })
65 } 96 }
66 fn new_scope(&mut self, parent: ScopeId) -> ScopeId { 97 fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
67 self.scopes.push(ScopeData { 98 self.scopes.alloc(ScopeData {
68 parent: Some(parent), 99 parent: Some(parent),
69 entries: vec![], 100 entries: vec![],
70 }) 101 })
@@ -104,10 +135,10 @@ impl ScopeEntry {
104 }; 135 };
105 Some(res) 136 Some(res)
106 } 137 }
107 pub(crate) fn name(&self) -> &SmolStr { 138 pub fn name(&self) -> &SmolStr {
108 &self.name 139 &self.name
109 } 140 }
110 pub(crate) fn ptr(&self) -> LocalSyntaxPtr { 141 pub fn ptr(&self) -> LocalSyntaxPtr {
111 self.ptr 142 self.ptr
112 } 143 }
113} 144}
@@ -249,18 +280,10 @@ fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) {
249 } 280 }
250} 281}
251 282
252pub fn resolve_local_name<'a>( 283#[derive(Debug)]
253 name_ref: ast::NameRef, 284pub struct ReferenceDescriptor {
254 scopes: &'a FnScopes, 285 pub range: TextRange,
255) -> Option<&'a ScopeEntry> { 286 pub name: String,
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} 287}
265 288
266#[cfg(test)] 289#[cfg(test)]
@@ -376,7 +399,7 @@ mod tests {
376 399
377 let scopes = FnScopes::new(fn_def); 400 let scopes = FnScopes::new(fn_def);
378 401
379 let local_name_entry = resolve_local_name(name_ref, &scopes).unwrap(); 402 let local_name_entry = scopes.resolve_local_name(name_ref).unwrap();
380 let local_name = local_name_entry.ptr().resolve(&file); 403 let local_name = local_name_entry.ptr().resolve(&file);
381 let expected_name = 404 let expected_name =
382 find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()).unwrap(); 405 find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()).unwrap();
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
new file mode 100644
index 000000000..e7b6a81f4
--- /dev/null
+++ b/crates/ra_hir/src/lib.rs
@@ -0,0 +1,141 @@
1//! HIR (previsouly known as descriptors) provides a high-level OO acess to Rust
2//! code.
3//!
4//! The principal difference between HIR and syntax trees is that HIR is bound
5//! to a particular crate instance. That is, it has cfg flags and features
6//! applied. So, there relation between syntax and HIR is many-to-one.
7
8macro_rules! ctry {
9 ($expr:expr) => {
10 match $expr {
11 None => return Ok(None),
12 Some(it) => it,
13 }
14 };
15}
16
17pub mod db;
18#[cfg(test)]
19mod mock;
20mod query_definitions;
21mod function;
22mod module;
23mod path;
24mod arena;
25
26use std::ops::Index;
27
28use ra_syntax::{SyntaxNodeRef, SyntaxNode};
29use ra_db::{LocationIntener, SourceRootId, FileId, Cancelable};
30
31use crate::{
32 db::HirDatabase,
33 arena::{Arena, Id},
34};
35
36pub use self::{
37 path::{Path, PathKind},
38 module::{Module, ModuleId, Problem, nameres::ItemMap},
39 function::{Function, FnScopes},
40};
41
42pub use self::function::FnSignatureInfo;
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
45pub struct FnId(u32);
46ra_db::impl_numeric_id!(FnId);
47
48impl FnId {
49 pub fn from_loc(
50 db: &impl AsRef<LocationIntener<SourceItemId, FnId>>,
51 loc: &SourceItemId,
52 ) -> FnId {
53 db.as_ref().loc2id(loc)
54 }
55 pub fn loc(self, db: &impl AsRef<LocationIntener<SourceItemId, FnId>>) -> SourceItemId {
56 db.as_ref().id2loc(self)
57 }
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
61pub struct DefId(u32);
62ra_db::impl_numeric_id!(DefId);
63
64#[derive(Clone, Debug, PartialEq, Eq, Hash)]
65pub enum DefLoc {
66 Module {
67 id: ModuleId,
68 source_root: SourceRootId,
69 },
70 Item {
71 source_item_id: SourceItemId,
72 },
73}
74
75impl DefId {
76 pub fn loc(self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefLoc {
77 db.as_ref().id2loc(self)
78 }
79}
80
81impl DefLoc {
82 pub fn id(&self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefId {
83 db.as_ref().loc2id(&self)
84 }
85}
86
87pub enum Def {
88 Module(Module),
89 Item,
90}
91
92impl DefId {
93 pub fn resolve(self, db: &impl HirDatabase) -> Cancelable<Def> {
94 let loc = self.loc(db);
95 let res = match loc {
96 DefLoc::Module { id, source_root } => {
97 let descr = Module::new(db, source_root, id)?;
98 Def::Module(descr)
99 }
100 DefLoc::Item { .. } => Def::Item,
101 };
102 Ok(res)
103 }
104}
105
106/// Identifier of item within a specific file. This is stable over reparses, so
107/// it's OK to use it as a salsa key/value.
108pub(crate) type SourceFileItemId = Id<SyntaxNode>;
109
110#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
111pub struct SourceItemId {
112 file_id: FileId,
113 item_id: SourceFileItemId,
114}
115
116/// Maps item's `SyntaxNode`s to `SourceFileItemId` and back.
117#[derive(Debug, PartialEq, Eq, Default)]
118pub struct SourceFileItems {
119 arena: Arena<SyntaxNode>,
120}
121
122impl SourceFileItems {
123 fn alloc(&mut self, item: SyntaxNode) -> SourceFileItemId {
124 self.arena.alloc(item)
125 }
126 pub fn id_of(&self, item: SyntaxNodeRef) -> SourceFileItemId {
127 let (id, _item) = self
128 .arena
129 .iter()
130 .find(|(_id, i)| i.borrowed() == item)
131 .unwrap();
132 id
133 }
134}
135
136impl Index<SourceFileItemId> for SourceFileItems {
137 type Output = SyntaxNode;
138 fn index(&self, idx: SourceFileItemId) -> &SyntaxNode {
139 &self.arena[idx]
140 }
141}
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs
new file mode 100644
index 000000000..8e256b89f
--- /dev/null
+++ b/crates/ra_hir/src/mock.rs
@@ -0,0 +1,172 @@
1use std::sync::Arc;
2
3use parking_lot::Mutex;
4use salsa::{self, Database};
5use ra_db::{LocationIntener, BaseDatabase, FilePosition, mock::FileMap, FileId, WORKSPACE};
6use relative_path::RelativePathBuf;
7use test_utils::{parse_fixture, CURSOR_MARKER, extract_offset};
8
9use crate::{db, DefId, DefLoc, FnId, SourceItemId};
10
11#[derive(Debug)]
12pub(crate) struct MockDatabase {
13 events: Mutex<Option<Vec<salsa::Event<MockDatabase>>>>,
14 runtime: salsa::Runtime<MockDatabase>,
15 id_maps: Arc<IdMaps>,
16}
17
18impl MockDatabase {
19 pub(crate) fn with_position(fixture: &str) -> (MockDatabase, FilePosition) {
20 let mut db = MockDatabase::default();
21
22 let mut position = None;
23 let mut file_map = FileMap::default();
24 for entry in parse_fixture(fixture) {
25 if entry.text.contains(CURSOR_MARKER) {
26 assert!(
27 position.is_none(),
28 "only one marker (<|>) per fixture is allowed"
29 );
30 position = Some(db.add_file_with_position(&mut file_map, &entry.meta, &entry.text));
31 } else {
32 db.add_file(&mut file_map, &entry.meta, &entry.text);
33 }
34 }
35 let position = position.expect("expected a marker (<|>)");
36 let source_root = file_map.into_source_root();
37 db.query_mut(ra_db::SourceRootQuery)
38 .set(WORKSPACE, Arc::new(source_root));
39 (db, position)
40 }
41
42 fn add_file(&mut self, file_map: &mut FileMap, path: &str, text: &str) -> FileId {
43 assert!(path.starts_with('/'));
44 let path = RelativePathBuf::from_path(&path[1..]).unwrap();
45
46 let file_id = file_map.add(path);
47 let text = Arc::new(text.to_string());
48 self.query_mut(ra_db::FileTextQuery).set(file_id, text);
49 self.query_mut(ra_db::FileSourceRootQuery)
50 .set(file_id, WORKSPACE);
51 file_id
52 }
53
54 fn add_file_with_position(
55 &mut self,
56 file_map: &mut FileMap,
57 path: &str,
58 text: &str,
59 ) -> FilePosition {
60 let (offset, text) = extract_offset(text);
61 let file_id = self.add_file(file_map, path, &text);
62 FilePosition { file_id, offset }
63 }
64}
65
66#[derive(Debug, Default)]
67struct IdMaps {
68 fns: LocationIntener<SourceItemId, FnId>,
69 defs: LocationIntener<DefLoc, DefId>,
70}
71
72impl salsa::Database for MockDatabase {
73 fn salsa_runtime(&self) -> &salsa::Runtime<MockDatabase> {
74 &self.runtime
75 }
76
77 fn salsa_event(&self, event: impl Fn() -> salsa::Event<MockDatabase>) {
78 let mut events = self.events.lock();
79 if let Some(events) = &mut *events {
80 events.push(event());
81 }
82 }
83}
84
85impl Default for MockDatabase {
86 fn default() -> MockDatabase {
87 let mut db = MockDatabase {
88 events: Default::default(),
89 runtime: salsa::Runtime::default(),
90 id_maps: Default::default(),
91 };
92 db.query_mut(ra_db::SourceRootQuery)
93 .set(ra_db::WORKSPACE, Default::default());
94 db.query_mut(ra_db::CrateGraphQuery)
95 .set((), Default::default());
96 db.query_mut(ra_db::LibrariesQuery)
97 .set((), Default::default());
98 db
99 }
100}
101
102impl salsa::ParallelDatabase for MockDatabase {
103 fn snapshot(&self) -> salsa::Snapshot<MockDatabase> {
104 salsa::Snapshot::new(MockDatabase {
105 events: Default::default(),
106 runtime: self.runtime.snapshot(self),
107 id_maps: self.id_maps.clone(),
108 })
109 }
110}
111
112impl BaseDatabase for MockDatabase {}
113
114impl AsRef<LocationIntener<DefLoc, DefId>> for MockDatabase {
115 fn as_ref(&self) -> &LocationIntener<DefLoc, DefId> {
116 &self.id_maps.defs
117 }
118}
119
120impl AsRef<LocationIntener<SourceItemId, FnId>> for MockDatabase {
121 fn as_ref(&self) -> &LocationIntener<SourceItemId, FnId> {
122 &self.id_maps.fns
123 }
124}
125
126impl MockDatabase {
127 pub(crate) fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event<MockDatabase>> {
128 *self.events.lock() = Some(Vec::new());
129 f();
130 let events = self.events.lock().take().unwrap();
131 events
132 }
133
134 pub(crate) fn log_executed(&self, f: impl FnOnce()) -> Vec<String> {
135 let events = self.log(f);
136 events
137 .into_iter()
138 .filter_map(|e| match e.kind {
139 // This pretty horrible, but `Debug` is the only way to inspect
140 // QueryDescriptor at the moment.
141 salsa::EventKind::WillExecute { descriptor } => Some(format!("{:?}", descriptor)),
142 _ => None,
143 })
144 .collect()
145 }
146}
147
148salsa::database_storage! {
149 pub(crate) struct MockDatabaseStorage for MockDatabase {
150 impl ra_db::FilesDatabase {
151 fn file_text() for ra_db::FileTextQuery;
152 fn file_source_root() for ra_db::FileSourceRootQuery;
153 fn source_root() for ra_db::SourceRootQuery;
154 fn libraries() for ra_db::LibrariesQuery;
155 fn crate_graph() for ra_db::CrateGraphQuery;
156 }
157 impl ra_db::SyntaxDatabase {
158 fn source_file() for ra_db::SourceFileQuery;
159 fn file_lines() for ra_db::FileLinesQuery;
160 }
161 impl db::HirDatabase {
162 fn module_tree() for db::ModuleTreeQuery;
163 fn fn_scopes() for db::FnScopesQuery;
164 fn file_items() for db::SourceFileItemsQuery;
165 fn file_item() for db::FileItemQuery;
166 fn input_module_items() for db::InputModuleItemsQuery;
167 fn item_map() for db::ItemMapQuery;
168 fn fn_syntax() for db::FnSyntaxQuery;
169 fn submodules() for db::SubmodulesQuery;
170 }
171 }
172}
diff --git a/crates/ra_analysis/src/descriptors/module/imp.rs b/crates/ra_hir/src/module/imp.rs
index 80892acb7..76ea129a7 100644
--- a/crates/ra_analysis/src/descriptors/module/imp.rs
+++ b/crates/ra_hir/src/module/imp.rs
@@ -6,21 +6,19 @@ use ra_syntax::{
6}; 6};
7use relative_path::RelativePathBuf; 7use relative_path::RelativePathBuf;
8use rustc_hash::{FxHashMap, FxHashSet}; 8use rustc_hash::{FxHashMap, FxHashSet};
9use ra_db::{SourceRoot, SourceRootId, FileResolverImp, Cancelable, FileId,};
9 10
10use crate::{ 11use crate::{
11 db, 12 HirDatabase,
12 descriptors::DescriptorDatabase,
13 input::{SourceRoot, SourceRootId},
14 Cancelable, FileId, FileResolverImp,
15}; 13};
16 14
17use super::{ 15use super::{
18 LinkData, LinkId, ModuleData, ModuleId, ModuleSource, ModuleSourceNode, 16 LinkData, LinkId, ModuleData, ModuleId, ModuleSource,
19 ModuleTree, Problem, 17 ModuleTree, Problem,
20}; 18};
21 19
22#[derive(Clone, Hash, PartialEq, Eq, Debug)] 20#[derive(Clone, Hash, PartialEq, Eq, Debug)]
23pub(crate) enum Submodule { 21pub enum Submodule {
24 Declaration(SmolStr), 22 Declaration(SmolStr),
25 Definition(SmolStr, ModuleSource), 23 Definition(SmolStr, ModuleSource),
26} 24}
@@ -34,39 +32,6 @@ impl Submodule {
34 } 32 }
35} 33}
36 34
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>( 35pub(crate) fn modules<'a>(
71 root: impl ast::ModuleItemOwner<'a>, 36 root: impl ast::ModuleItemOwner<'a>,
72) -> impl Iterator<Item = (SmolStr, ast::Module<'a>)> { 37) -> impl Iterator<Item = (SmolStr, ast::Module<'a>)> {
@@ -82,16 +47,16 @@ pub(crate) fn modules<'a>(
82} 47}
83 48
84pub(crate) fn module_tree( 49pub(crate) fn module_tree(
85 db: &impl DescriptorDatabase, 50 db: &impl HirDatabase,
86 source_root: SourceRootId, 51 source_root: SourceRootId,
87) -> Cancelable<Arc<ModuleTree>> { 52) -> Cancelable<Arc<ModuleTree>> {
88 db::check_canceled(db)?; 53 db.check_canceled()?;
89 let res = create_module_tree(db, source_root)?; 54 let res = create_module_tree(db, source_root)?;
90 Ok(Arc::new(res)) 55 Ok(Arc::new(res))
91} 56}
92 57
93fn create_module_tree<'a>( 58fn create_module_tree<'a>(
94 db: &impl DescriptorDatabase, 59 db: &impl HirDatabase,
95 source_root: SourceRootId, 60 source_root: SourceRootId,
96) -> Cancelable<ModuleTree> { 61) -> Cancelable<ModuleTree> {
97 let mut tree = ModuleTree::default(); 62 let mut tree = ModuleTree::default();
@@ -121,7 +86,7 @@ fn create_module_tree<'a>(
121} 86}
122 87
123fn build_subtree( 88fn build_subtree(
124 db: &impl DescriptorDatabase, 89 db: &impl HirDatabase,
125 source_root: &SourceRoot, 90 source_root: &SourceRoot,
126 tree: &mut ModuleTree, 91 tree: &mut ModuleTree,
127 visited: &mut FxHashSet<ModuleSource>, 92 visited: &mut FxHashSet<ModuleSource>,
@@ -135,7 +100,7 @@ fn build_subtree(
135 parent, 100 parent,
136 children: Vec::new(), 101 children: Vec::new(),
137 }); 102 });
138 for sub in db._submodules(source)?.iter() { 103 for sub in db.submodules(source)?.iter() {
139 let link = tree.push_link(LinkData { 104 let link = tree.push_link(LinkData {
140 name: sub.name().clone(), 105 name: sub.name().clone(),
141 owner: id, 106 owner: id,
diff --git a/crates/ra_analysis/src/descriptors/module/mod.rs b/crates/ra_hir/src/module/mod.rs
index 54ff95b66..3ae83d8cb 100644
--- a/crates/ra_analysis/src/descriptors/module/mod.rs
+++ b/crates/ra_hir/src/module/mod.rs
@@ -10,64 +10,63 @@ use ra_syntax::{
10 ast::{self, AstNode, NameOwner}, 10 ast::{self, AstNode, NameOwner},
11 SmolStr, SyntaxNode, 11 SmolStr, SyntaxNode,
12}; 12};
13use ra_db::{SourceRootId, FileId, FilePosition, Cancelable};
13use relative_path::RelativePathBuf; 14use relative_path::RelativePathBuf;
14 15
15use crate::{ 16use crate::{
16 db::SyntaxDatabase, syntax_ptr::SyntaxPtr, FileId, FilePosition, Cancelable, 17 DefLoc, DefId, Path, PathKind, HirDatabase, SourceItemId,
17 descriptors::{Path, PathKind, DescriptorDatabase},
18 input::SourceRootId,
19 arena::{Arena, Id}, 18 arena::{Arena, Id},
20}; 19};
21 20
22pub(crate) use self::nameres::ModuleScope; 21pub use self::nameres::ModuleScope;
23 22
24/// `ModuleDescriptor` is API entry point to get all the information 23/// `Module` is API entry point to get all the information
25/// about a particular module. 24/// about a particular module.
26#[derive(Debug, Clone)] 25#[derive(Debug, Clone)]
27pub(crate) struct ModuleDescriptor { 26pub struct Module {
28 tree: Arc<ModuleTree>, 27 tree: Arc<ModuleTree>,
29 source_root_id: SourceRootId, 28 source_root_id: SourceRootId,
30 module_id: ModuleId, 29 module_id: ModuleId,
31} 30}
32 31
33impl ModuleDescriptor { 32impl Module {
34 /// Lookup `ModuleDescriptor` by `FileId`. Note that this is inherently 33 /// Lookup `Module` by `FileId`. Note that this is inherently
35 /// lossy transformation: in general, a single source might correspond to 34 /// lossy transformation: in general, a single source might correspond to
36 /// several modules. 35 /// several modules.
37 pub fn guess_from_file_id( 36 pub fn guess_from_file_id(
38 db: &impl DescriptorDatabase, 37 db: &impl HirDatabase,
39 file_id: FileId, 38 file_id: FileId,
40 ) -> Cancelable<Option<ModuleDescriptor>> { 39 ) -> Cancelable<Option<Module>> {
41 ModuleDescriptor::guess_from_source(db, file_id, ModuleSource::SourceFile(file_id)) 40 Module::guess_from_source(db, file_id, ModuleSource::SourceFile(file_id))
42 } 41 }
43 42
44 /// Lookup `ModuleDescriptor` by position in the source code. Note that this 43 /// Lookup `Module` by position in the source code. Note that this
45 /// is inherently lossy transformation: in general, a single source might 44 /// is inherently lossy transformation: in general, a single source might
46 /// correspond to several modules. 45 /// correspond to several modules.
47 pub fn guess_from_position( 46 pub fn guess_from_position(
48 db: &impl DescriptorDatabase, 47 db: &impl HirDatabase,
49 position: FilePosition, 48 position: FilePosition,
50 ) -> Cancelable<Option<ModuleDescriptor>> { 49 ) -> Cancelable<Option<Module>> {
51 let file = db.file_syntax(position.file_id); 50 let file = db.source_file(position.file_id);
52 let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset) 51 let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset)
53 { 52 {
54 Some(m) if !m.has_semi() => ModuleSource::new_inline(position.file_id, m), 53 Some(m) if !m.has_semi() => ModuleSource::new_inline(db, position.file_id, m),
55 _ => ModuleSource::SourceFile(position.file_id), 54 _ => ModuleSource::SourceFile(position.file_id),
56 }; 55 };
57 ModuleDescriptor::guess_from_source(db, position.file_id, module_source) 56 Module::guess_from_source(db, position.file_id, module_source)
58 } 57 }
59 58
60 fn guess_from_source( 59 fn guess_from_source(
61 db: &impl DescriptorDatabase, 60 db: &impl HirDatabase,
62 file_id: FileId, 61 file_id: FileId,
63 module_source: ModuleSource, 62 module_source: ModuleSource,
64 ) -> Cancelable<Option<ModuleDescriptor>> { 63 ) -> Cancelable<Option<Module>> {
65 let source_root_id = db.file_source_root(file_id); 64 let source_root_id = db.file_source_root(file_id);
66 let module_tree = db._module_tree(source_root_id)?; 65 let module_tree = db.module_tree(source_root_id)?;
67 66
68 let res = match module_tree.any_module_for_source(module_source) { 67 let res = match module_tree.any_module_for_source(module_source) {
69 None => None, 68 None => None,
70 Some(module_id) => Some(ModuleDescriptor { 69 Some(module_id) => Some(Module {
71 tree: module_tree, 70 tree: module_tree,
72 source_root_id, 71 source_root_id,
73 module_id, 72 module_id,
@@ -76,12 +75,23 @@ impl ModuleDescriptor {
76 Ok(res) 75 Ok(res)
77 } 76 }
78 77
78 pub(super) fn new(
79 db: &impl HirDatabase,
80 source_root_id: SourceRootId,
81 module_id: ModuleId,
82 ) -> Cancelable<Module> {
83 let module_tree = db.module_tree(source_root_id)?;
84 let res = Module {
85 tree: module_tree,
86 source_root_id,
87 module_id,
88 };
89 Ok(res)
90 }
91
79 /// Returns `mod foo;` or `mod foo {}` node whihc declared this module. 92 /// Returns `mod foo;` or `mod foo {}` node whihc declared this module.
80 /// Returns `None` for the root module 93 /// Returns `None` for the root module
81 pub fn parent_link_source( 94 pub fn parent_link_source(&self, db: &impl HirDatabase) -> Option<(FileId, ast::ModuleNode)> {
82 &self,
83 db: &impl DescriptorDatabase,
84 ) -> Option<(FileId, ast::ModuleNode)> {
85 let link = self.module_id.parent_link(&self.tree)?; 95 let link = self.module_id.parent_link(&self.tree)?;
86 let file_id = link.owner(&self.tree).source(&self.tree).file_id(); 96 let file_id = link.owner(&self.tree).source(&self.tree).file_id();
87 let src = link.bind_source(&self.tree, db); 97 let src = link.bind_source(&self.tree, db);
@@ -93,60 +103,74 @@ impl ModuleDescriptor {
93 } 103 }
94 104
95 /// Parent module. Returns `None` if this is a root module. 105 /// Parent module. Returns `None` if this is a root module.
96 pub fn parent(&self) -> Option<ModuleDescriptor> { 106 pub fn parent(&self) -> Option<Module> {
97 let parent_id = self.module_id.parent(&self.tree)?; 107 let parent_id = self.module_id.parent(&self.tree)?;
98 Some(ModuleDescriptor { 108 Some(Module {
99 module_id: parent_id, 109 module_id: parent_id,
100 ..self.clone() 110 ..self.clone()
101 }) 111 })
102 } 112 }
103 113
104 /// The root of the tree this module is part of 114 /// The root of the tree this module is part of
105 pub fn crate_root(&self) -> ModuleDescriptor { 115 pub fn crate_root(&self) -> Module {
106 let root_id = self.module_id.crate_root(&self.tree); 116 let root_id = self.module_id.crate_root(&self.tree);
107 ModuleDescriptor { 117 Module {
108 module_id: root_id, 118 module_id: root_id,
109 ..self.clone() 119 ..self.clone()
110 } 120 }
111 } 121 }
112 122
113 /// `name` is `None` for the crate's root module 123 /// `name` is `None` for the crate's root module
114 #[allow(unused)]
115 pub fn name(&self) -> Option<SmolStr> { 124 pub fn name(&self) -> Option<SmolStr> {
116 let link = self.module_id.parent_link(&self.tree)?; 125 let link = self.module_id.parent_link(&self.tree)?;
117 Some(link.name(&self.tree)) 126 Some(link.name(&self.tree))
118 } 127 }
119 128
129 pub fn def_id(&self, db: &impl HirDatabase) -> DefId {
130 let def_loc = DefLoc::Module {
131 id: self.module_id,
132 source_root: self.source_root_id,
133 };
134 def_loc.id(db)
135 }
136
120 /// Finds a child module with the specified name. 137 /// Finds a child module with the specified name.
121 pub fn child(&self, name: &str) -> Option<ModuleDescriptor> { 138 pub fn child(&self, name: &str) -> Option<Module> {
122 let child_id = self.module_id.child(&self.tree, name)?; 139 let child_id = self.module_id.child(&self.tree, name)?;
123 Some(ModuleDescriptor { 140 Some(Module {
124 module_id: child_id, 141 module_id: child_id,
125 ..self.clone() 142 ..self.clone()
126 }) 143 })
127 } 144 }
128 145
129 /// Returns a `ModuleScope`: a set of items, visible in this module. 146 /// Returns a `ModuleScope`: a set of items, visible in this module.
130 pub(crate) fn scope(&self, db: &impl DescriptorDatabase) -> Cancelable<ModuleScope> { 147 pub fn scope(&self, db: &impl HirDatabase) -> Cancelable<ModuleScope> {
131 let item_map = db._item_map(self.source_root_id)?; 148 let item_map = db.item_map(self.source_root_id)?;
132 let res = item_map.per_module[&self.module_id].clone(); 149 let res = item_map.per_module[&self.module_id].clone();
133 Ok(res) 150 Ok(res)
134 } 151 }
135 152
136 pub(crate) fn resolve_path(&self, path: Path) -> Option<ModuleDescriptor> { 153 pub fn resolve_path(&self, db: &impl HirDatabase, path: Path) -> Cancelable<Option<DefId>> {
137 let mut curr = match path.kind { 154 let mut curr = match path.kind {
138 PathKind::Crate => self.crate_root(), 155 PathKind::Crate => self.crate_root(),
139 PathKind::Self_ | PathKind::Plain => self.clone(), 156 PathKind::Self_ | PathKind::Plain => self.clone(),
140 PathKind::Super => self.parent()?, 157 PathKind::Super => ctry!(self.parent()),
141 }; 158 }
159 .def_id(db);
160
142 let segments = path.segments; 161 let segments = path.segments;
143 for name in segments { 162 for name in segments.iter() {
144 curr = curr.child(&name)?; 163 let module = match curr.loc(db) {
164 DefLoc::Module { id, source_root } => Module::new(db, source_root, id)?,
165 _ => return Ok(None),
166 };
167 let scope = module.scope(db)?;
168 curr = ctry!(ctry!(scope.get(&name)).def_id);
145 } 169 }
146 Some(curr) 170 Ok(Some(curr))
147 } 171 }
148 172
149 pub fn problems(&self, db: &impl DescriptorDatabase) -> Vec<(SyntaxNode, Problem)> { 173 pub fn problems(&self, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> {
150 self.module_id.problems(&self.tree, db) 174 self.module_id.problems(&self.tree, db)
151 } 175 }
152} 176}
@@ -159,19 +183,19 @@ impl ModuleDescriptor {
159/// (which can have multiple parents) to the precise world of modules (which 183/// (which can have multiple parents) to the precise world of modules (which
160/// always have one parent). 184/// always have one parent).
161#[derive(Default, Debug, PartialEq, Eq)] 185#[derive(Default, Debug, PartialEq, Eq)]
162pub(crate) struct ModuleTree { 186pub struct ModuleTree {
163 mods: Arena<ModuleData>, 187 mods: Arena<ModuleData>,
164 links: Arena<LinkData>, 188 links: Arena<LinkData>,
165} 189}
166 190
167impl ModuleTree { 191impl ModuleTree {
168 fn modules<'a>(&'a self) -> impl Iterator<Item = ModuleId> + 'a { 192 pub(crate) fn modules<'a>(&'a self) -> impl Iterator<Item = ModuleId> + 'a {
169 self.mods.keys() 193 self.mods.iter().map(|(id, _)| id)
170 } 194 }
171 195
172 fn modules_for_source(&self, source: ModuleSource) -> Vec<ModuleId> { 196 fn modules_for_source(&self, source: ModuleSource) -> Vec<ModuleId> {
173 self.mods 197 self.mods
174 .items() 198 .iter()
175 .filter(|(_idx, it)| it.source == source) 199 .filter(|(_idx, it)| it.source == source)
176 .map(|(idx, _)| idx) 200 .map(|(idx, _)| idx)
177 .collect() 201 .collect()
@@ -185,20 +209,19 @@ impl ModuleTree {
185/// `ModuleSource` is the syntax tree element that produced this module: 209/// `ModuleSource` is the syntax tree element that produced this module:
186/// either a file, or an inlinde module. 210/// either a file, or an inlinde module.
187#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 211#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
188pub(crate) enum ModuleSource { 212pub enum ModuleSource {
189 SourceFile(FileId), 213 SourceFile(FileId),
190 #[allow(dead_code)] 214 Module(SourceItemId),
191 Module(SyntaxPtr),
192} 215}
193 216
194/// An owned syntax node for a module. Unlike `ModuleSource`, 217/// An owned syntax node for a module. Unlike `ModuleSource`,
195/// this holds onto the AST for the whole file. 218/// this holds onto the AST for the whole file.
196enum ModuleSourceNode { 219pub(crate) enum ModuleSourceNode {
197 SourceFile(ast::SourceFileNode), 220 SourceFile(ast::SourceFileNode),
198 Module(ast::ModuleNode), 221 Module(ast::ModuleNode),
199} 222}
200 223
201pub(crate) type ModuleId = Id<ModuleData>; 224pub type ModuleId = Id<ModuleData>;
202type LinkId = Id<LinkData>; 225type LinkId = Id<LinkData>;
203 226
204#[derive(Clone, Debug, Hash, PartialEq, Eq)] 227#[derive(Clone, Debug, Hash, PartialEq, Eq)]
@@ -213,7 +236,7 @@ pub enum Problem {
213} 236}
214 237
215impl ModuleId { 238impl ModuleId {
216 fn source(self, tree: &ModuleTree) -> ModuleSource { 239 pub(crate) fn source(self, tree: &ModuleTree) -> ModuleSource {
217 tree.mods[self].source 240 tree.mods[self].source
218 } 241 }
219 fn parent_link(self, tree: &ModuleTree) -> Option<LinkId> { 242 fn parent_link(self, tree: &ModuleTree) -> Option<LinkId> {
@@ -243,7 +266,7 @@ impl ModuleId {
243 Some((link.name.clone(), module)) 266 Some((link.name.clone(), module))
244 }) 267 })
245 } 268 }
246 fn problems(self, tree: &ModuleTree, db: &impl SyntaxDatabase) -> Vec<(SyntaxNode, Problem)> { 269 fn problems(self, tree: &ModuleTree, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> {
247 tree.mods[self] 270 tree.mods[self]
248 .children 271 .children
249 .iter() 272 .iter()
@@ -264,7 +287,7 @@ impl LinkId {
264 fn name(self, tree: &ModuleTree) -> SmolStr { 287 fn name(self, tree: &ModuleTree) -> SmolStr {
265 tree.links[self].name.clone() 288 tree.links[self].name.clone()
266 } 289 }
267 fn bind_source<'a>(self, tree: &ModuleTree, db: &impl SyntaxDatabase) -> ast::ModuleNode { 290 fn bind_source<'a>(self, tree: &ModuleTree, db: &impl HirDatabase) -> ast::ModuleNode {
268 let owner = self.owner(tree); 291 let owner = self.owner(tree);
269 match owner.source(tree).resolve(db) { 292 match owner.source(tree).resolve(db) {
270 ModuleSourceNode::SourceFile(root) => { 293 ModuleSourceNode::SourceFile(root) => {
@@ -280,41 +303,47 @@ impl LinkId {
280} 303}
281 304
282#[derive(Debug, PartialEq, Eq, Hash)] 305#[derive(Debug, PartialEq, Eq, Hash)]
283pub(crate) struct ModuleData { 306pub struct ModuleData {
284 source: ModuleSource, 307 source: ModuleSource,
285 parent: Option<LinkId>, 308 parent: Option<LinkId>,
286 children: Vec<LinkId>, 309 children: Vec<LinkId>,
287} 310}
288 311
289impl ModuleSource { 312impl ModuleSource {
290 fn new_inline(file_id: FileId, module: ast::Module) -> ModuleSource { 313 pub(crate) fn new_inline(
314 db: &impl HirDatabase,
315 file_id: FileId,
316 module: ast::Module,
317 ) -> ModuleSource {
291 assert!(!module.has_semi()); 318 assert!(!module.has_semi());
292 let ptr = SyntaxPtr::new(file_id, module.syntax()); 319 let items = db.file_items(file_id);
293 ModuleSource::Module(ptr) 320 let item_id = items.id_of(module.syntax());
321 let id = SourceItemId { file_id, item_id };
322 ModuleSource::Module(id)
294 } 323 }
295 324
296 pub(crate) fn as_file(self) -> Option<FileId> { 325 pub fn as_file(self) -> Option<FileId> {
297 match self { 326 match self {
298 ModuleSource::SourceFile(f) => Some(f), 327 ModuleSource::SourceFile(f) => Some(f),
299 ModuleSource::Module(..) => None, 328 ModuleSource::Module(..) => None,
300 } 329 }
301 } 330 }
302 331
303 pub(crate) fn file_id(self) -> FileId { 332 pub fn file_id(self) -> FileId {
304 match self { 333 match self {
305 ModuleSource::SourceFile(f) => f, 334 ModuleSource::SourceFile(f) => f,
306 ModuleSource::Module(ptr) => ptr.file_id(), 335 ModuleSource::Module(source_item_id) => source_item_id.file_id,
307 } 336 }
308 } 337 }
309 338
310 fn resolve(self, db: &impl SyntaxDatabase) -> ModuleSourceNode { 339 pub(crate) fn resolve(self, db: &impl HirDatabase) -> ModuleSourceNode {
311 match self { 340 match self {
312 ModuleSource::SourceFile(file_id) => { 341 ModuleSource::SourceFile(file_id) => {
313 let syntax = db.file_syntax(file_id); 342 let syntax = db.source_file(file_id);
314 ModuleSourceNode::SourceFile(syntax.ast().owned()) 343 ModuleSourceNode::SourceFile(syntax.ast().owned())
315 } 344 }
316 ModuleSource::Module(ptr) => { 345 ModuleSource::Module(item_id) => {
317 let syntax = db.resolve_syntax_ptr(ptr); 346 let syntax = db.file_item(item_id);
318 let syntax = syntax.borrowed(); 347 let syntax = syntax.borrowed();
319 let module = ast::Module::cast(syntax).unwrap(); 348 let module = ast::Module::cast(syntax).unwrap();
320 ModuleSourceNode::Module(module.owned()) 349 ModuleSourceNode::Module(module.owned())
@@ -333,11 +362,11 @@ struct LinkData {
333 362
334impl ModuleTree { 363impl ModuleTree {
335 fn push_mod(&mut self, data: ModuleData) -> ModuleId { 364 fn push_mod(&mut self, data: ModuleData) -> ModuleId {
336 self.mods.push(data) 365 self.mods.alloc(data)
337 } 366 }
338 fn push_link(&mut self, data: LinkData) -> LinkId { 367 fn push_link(&mut self, data: LinkData) -> LinkId {
339 let owner = data.owner; 368 let owner = data.owner;
340 let id = self.links.push(data); 369 let id = self.links.alloc(data);
341 self.mods[owner].children.push(id); 370 self.mods[owner].children.push(id);
342 id 371 id
343 } 372 }
diff --git a/crates/ra_analysis/src/descriptors/module/nameres.rs b/crates/ra_hir/src/module/nameres.rs
index 648ec5e43..8529e16b3 100644
--- a/crates/ra_analysis/src/descriptors/module/nameres.rs
+++ b/crates/ra_hir/src/module/nameres.rs
@@ -16,38 +16,44 @@
16//! structure itself is modified. 16//! structure itself is modified.
17use std::{ 17use std::{
18 sync::Arc, 18 sync::Arc,
19 time::Instant,
20}; 19};
21 20
22use rustc_hash::FxHashMap; 21use rustc_hash::FxHashMap;
23
24use ra_syntax::{ 22use ra_syntax::{
23 TextRange,
25 SmolStr, SyntaxKind::{self, *}, 24 SmolStr, SyntaxKind::{self, *},
26 ast::{self, ModuleItemOwner} 25 ast::{self, AstNode}
27}; 26};
27use ra_db::SourceRootId;
28 28
29use crate::{ 29use crate::{
30 Cancelable, 30 Cancelable, FileId,
31 loc2id::{DefId, DefLoc}, 31 DefId, DefLoc,
32 descriptors::{ 32 SourceItemId, SourceFileItemId, SourceFileItems,
33 Path, PathKind, 33 Path, PathKind,
34 DescriptorDatabase, 34 HirDatabase,
35 module::{ModuleId, ModuleTree, ModuleSourceNode}, 35 module::{ModuleId, ModuleTree},
36 },
37 syntax_ptr::{LocalSyntaxPtr},
38 input::SourceRootId,
39}; 36};
40 37
41/// Item map is the result of the name resolution. Item map contains, for each 38/// Item map is the result of the name resolution. Item map contains, for each
42/// module, the set of visible items. 39/// module, the set of visible items.
43#[derive(Default, Debug, PartialEq, Eq)] 40#[derive(Default, Debug, PartialEq, Eq)]
44pub(crate) struct ItemMap { 41pub struct ItemMap {
45 pub(crate) per_module: FxHashMap<ModuleId, ModuleScope>, 42 pub per_module: FxHashMap<ModuleId, ModuleScope>,
46} 43}
47 44
48#[derive(Debug, Default, PartialEq, Eq, Clone)] 45#[derive(Debug, Default, PartialEq, Eq, Clone)]
49pub(crate) struct ModuleScope { 46pub struct ModuleScope {
50 pub(crate) items: FxHashMap<SmolStr, Resolution>, 47 items: FxHashMap<SmolStr, Resolution>,
48}
49
50impl ModuleScope {
51 pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a SmolStr, &Resolution)> + 'a {
52 self.items.iter()
53 }
54 pub fn get(&self, name: &SmolStr) -> Option<&Resolution> {
55 self.items.get(name)
56 }
51} 57}
52 58
53/// A set of items and imports declared inside a module, without relation to 59/// A set of items and imports declared inside a module, without relation to
@@ -57,85 +63,63 @@ pub(crate) struct ModuleScope {
57/// recomputing name res: if `InputModuleItems` are the same, we can avoid 63/// recomputing name res: if `InputModuleItems` are the same, we can avoid
58/// running name resolution. 64/// running name resolution.
59#[derive(Debug, Default, PartialEq, Eq)] 65#[derive(Debug, Default, PartialEq, Eq)]
60pub(crate) struct InputModuleItems { 66pub struct InputModuleItems {
61 items: Vec<ModuleItem>, 67 items: Vec<ModuleItem>,
62 imports: Vec<Import>, 68 imports: Vec<Import>,
63} 69}
64 70
71#[derive(Debug, PartialEq, Eq)]
72struct ModuleItem {
73 id: SourceFileItemId,
74 name: SmolStr,
75 kind: SyntaxKind,
76 vis: Vis,
77}
78
79#[derive(Debug, PartialEq, Eq)]
80enum Vis {
81 // Priv,
82 Other,
83}
84
65#[derive(Debug, Clone, PartialEq, Eq)] 85#[derive(Debug, Clone, PartialEq, Eq)]
66struct Import { 86struct Import {
67 path: Path, 87 path: Path,
68 kind: ImportKind, 88 kind: ImportKind,
69} 89}
70 90
71#[derive(Debug, Clone, PartialEq, Eq)] 91#[derive(Debug, Clone, Copy, PartialEq, Eq)]
72enum ImportKind { 92pub struct NamedImport {
73 Glob, 93 pub file_item_id: SourceFileItemId,
74 // TODO: make offset independent 94 pub relative_range: TextRange,
75 Named(LocalSyntaxPtr),
76} 95}
77 96
78pub(crate) fn input_module_items( 97impl NamedImport {
79 db: &impl DescriptorDatabase, 98 pub fn range(&self, db: &impl HirDatabase, file_id: FileId) -> TextRange {
80 source_root: SourceRootId, 99 let source_item_id = SourceItemId {
81 module_id: ModuleId, 100 file_id,
82) -> Cancelable<Arc<InputModuleItems>> { 101 item_id: self.file_item_id,
83 let module_tree = db._module_tree(source_root)?; 102 };
84 let source = module_id.source(&module_tree); 103 let syntax = db.file_item(source_item_id);
85 let res = match source.resolve(db) { 104 let offset = syntax.borrowed().range().start();
86 ModuleSourceNode::SourceFile(it) => { 105 self.relative_range + offset
87 let items = it.borrowed().items(); 106 }
88 InputModuleItems::new(items)
89 }
90 ModuleSourceNode::Module(it) => {
91 let items = it
92 .borrowed()
93 .item_list()
94 .into_iter()
95 .flat_map(|it| it.items());
96 InputModuleItems::new(items)
97 }
98 };
99 Ok(Arc::new(res))
100} 107}
101 108
102pub(crate) fn item_map( 109#[derive(Debug, Clone, PartialEq, Eq)]
103 db: &impl DescriptorDatabase, 110enum ImportKind {
104 source_root: SourceRootId, 111 Glob,
105) -> Cancelable<Arc<ItemMap>> { 112 Named(NamedImport),
106 let start = Instant::now();
107 let module_tree = db._module_tree(source_root)?;
108 let input = module_tree
109 .modules()
110 .map(|id| {
111 let items = db._input_module_items(source_root, id)?;
112 Ok((id, items))
113 })
114 .collect::<Cancelable<FxHashMap<_, _>>>()?;
115
116 let mut resolver = Resolver {
117 db: db,
118 input: &input,
119 source_root,
120 module_tree,
121 result: ItemMap::default(),
122 };
123 resolver.resolve()?;
124 let res = resolver.result;
125 let elapsed = start.elapsed();
126 log::info!("item_map: {:?}", elapsed);
127 Ok(Arc::new(res))
128} 113}
129 114
130/// Resolution is basically `DefId` atm, but it should account for stuff like 115/// Resolution is basically `DefId` atm, but it should account for stuff like
131/// multiple namespaces, ambiguity and errors. 116/// multiple namespaces, ambiguity and errors.
132#[derive(Debug, Clone, PartialEq, Eq)] 117#[derive(Debug, Clone, PartialEq, Eq)]
133pub(crate) struct Resolution { 118pub struct Resolution {
134 /// None for unresolved 119 /// None for unresolved
135 pub(crate) def_id: Option<DefId>, 120 pub def_id: Option<DefId>,
136 /// ident by whitch this is imported into local scope. 121 /// ident by whitch this is imported into local scope.
137 /// TODO: make this offset-independent. 122 pub import: Option<NamedImport>,
138 pub(crate) import_name: Option<LocalSyntaxPtr>,
139} 123}
140 124
141// #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 125// #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -150,55 +134,49 @@ pub(crate) struct Resolution {
150// values: Option<T>, 134// values: Option<T>,
151// } 135// }
152 136
153#[derive(Debug, PartialEq, Eq)]
154struct ModuleItem {
155 ptr: LocalSyntaxPtr,
156 name: SmolStr,
157 kind: SyntaxKind,
158 vis: Vis,
159}
160
161#[derive(Debug, PartialEq, Eq)]
162enum Vis {
163 // Priv,
164 Other,
165}
166
167impl InputModuleItems { 137impl InputModuleItems {
168 fn new<'a>(items: impl Iterator<Item = ast::ModuleItem<'a>>) -> InputModuleItems { 138 pub(crate) fn new<'a>(
139 file_items: &SourceFileItems,
140 items: impl Iterator<Item = ast::ModuleItem<'a>>,
141 ) -> InputModuleItems {
169 let mut res = InputModuleItems::default(); 142 let mut res = InputModuleItems::default();
170 for item in items { 143 for item in items {
171 res.add_item(item); 144 res.add_item(file_items, item);
172 } 145 }
173 res 146 res
174 } 147 }
175 148
176 fn add_item(&mut self, item: ast::ModuleItem) -> Option<()> { 149 fn add_item(&mut self, file_items: &SourceFileItems, item: ast::ModuleItem) -> Option<()> {
177 match item { 150 match item {
178 ast::ModuleItem::StructDef(it) => self.items.push(ModuleItem::new(it)?), 151 ast::ModuleItem::StructDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
179 ast::ModuleItem::EnumDef(it) => self.items.push(ModuleItem::new(it)?), 152 ast::ModuleItem::EnumDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
180 ast::ModuleItem::FnDef(it) => self.items.push(ModuleItem::new(it)?), 153 ast::ModuleItem::FnDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
181 ast::ModuleItem::TraitDef(it) => self.items.push(ModuleItem::new(it)?), 154 ast::ModuleItem::TraitDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
182 ast::ModuleItem::TypeDef(it) => self.items.push(ModuleItem::new(it)?), 155 ast::ModuleItem::TypeDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
183 ast::ModuleItem::ImplItem(_) => { 156 ast::ModuleItem::ImplItem(_) => {
184 // impls don't define items 157 // impls don't define items
185 } 158 }
186 ast::ModuleItem::UseItem(it) => self.add_use_item(it), 159 ast::ModuleItem::UseItem(it) => self.add_use_item(file_items, it),
187 ast::ModuleItem::ExternCrateItem(_) => { 160 ast::ModuleItem::ExternCrateItem(_) => {
188 // TODO 161 // TODO
189 } 162 }
190 ast::ModuleItem::ConstDef(it) => self.items.push(ModuleItem::new(it)?), 163 ast::ModuleItem::ConstDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
191 ast::ModuleItem::StaticDef(it) => self.items.push(ModuleItem::new(it)?), 164 ast::ModuleItem::StaticDef(it) => self.items.push(ModuleItem::new(file_items, it)?),
192 ast::ModuleItem::Module(it) => self.items.push(ModuleItem::new(it)?), 165 ast::ModuleItem::Module(it) => self.items.push(ModuleItem::new(file_items, it)?),
193 } 166 }
194 Some(()) 167 Some(())
195 } 168 }
196 169
197 fn add_use_item(&mut self, item: ast::UseItem) { 170 fn add_use_item(&mut self, file_items: &SourceFileItems, item: ast::UseItem) {
198 Path::expand_use_item(item, |path, ptr| { 171 let file_item_id = file_items.id_of(item.syntax());
199 let kind = match ptr { 172 let start_offset = item.syntax().range().start();
173 Path::expand_use_item(item, |path, range| {
174 let kind = match range {
200 None => ImportKind::Glob, 175 None => ImportKind::Glob,
201 Some(ptr) => ImportKind::Named(ptr), 176 Some(range) => ImportKind::Named(NamedImport {
177 file_item_id,
178 relative_range: range - start_offset,
179 }),
202 }; 180 };
203 self.imports.push(Import { kind, path }) 181 self.imports.push(Import { kind, path })
204 }) 182 })
@@ -206,13 +184,13 @@ impl InputModuleItems {
206} 184}
207 185
208impl ModuleItem { 186impl ModuleItem {
209 fn new<'a>(item: impl ast::NameOwner<'a>) -> Option<ModuleItem> { 187 fn new<'a>(file_items: &SourceFileItems, item: impl ast::NameOwner<'a>) -> Option<ModuleItem> {
210 let name = item.name()?.text(); 188 let name = item.name()?.text();
211 let ptr = LocalSyntaxPtr::new(item.syntax());
212 let kind = item.syntax().kind(); 189 let kind = item.syntax().kind();
213 let vis = Vis::Other; 190 let vis = Vis::Other;
191 let id = file_items.id_of(item.syntax());
214 let res = ModuleItem { 192 let res = ModuleItem {
215 ptr, 193 id,
216 name, 194 name,
217 kind, 195 kind,
218 vis, 196 vis,
@@ -221,28 +199,28 @@ impl ModuleItem {
221 } 199 }
222} 200}
223 201
224struct Resolver<'a, DB> { 202pub(crate) struct Resolver<'a, DB> {
225 db: &'a DB, 203 pub(crate) db: &'a DB,
226 input: &'a FxHashMap<ModuleId, Arc<InputModuleItems>>, 204 pub(crate) input: &'a FxHashMap<ModuleId, Arc<InputModuleItems>>,
227 source_root: SourceRootId, 205 pub(crate) source_root: SourceRootId,
228 module_tree: Arc<ModuleTree>, 206 pub(crate) module_tree: Arc<ModuleTree>,
229 result: ItemMap, 207 pub(crate) result: ItemMap,
230} 208}
231 209
232impl<'a, DB> Resolver<'a, DB> 210impl<'a, DB> Resolver<'a, DB>
233where 211where
234 DB: DescriptorDatabase, 212 DB: HirDatabase,
235{ 213{
236 fn resolve(&mut self) -> Cancelable<()> { 214 pub(crate) fn resolve(mut self) -> Cancelable<ItemMap> {
237 for (&module_id, items) in self.input.iter() { 215 for (&module_id, items) in self.input.iter() {
238 self.populate_module(module_id, items) 216 self.populate_module(module_id, items)
239 } 217 }
240 218
241 for &module_id in self.input.keys() { 219 for &module_id in self.input.keys() {
242 crate::db::check_canceled(self.db)?; 220 self.db.check_canceled()?;
243 self.resolve_imports(module_id); 221 self.resolve_imports(module_id);
244 } 222 }
245 Ok(()) 223 Ok(self.result)
246 } 224 }
247 225
248 fn populate_module(&mut self, module_id: ModuleId, input: &InputModuleItems) { 226 fn populate_module(&mut self, module_id: ModuleId, input: &InputModuleItems) {
@@ -252,12 +230,12 @@ where
252 230
253 for import in input.imports.iter() { 231 for import in input.imports.iter() {
254 if let Some(name) = import.path.segments.iter().last() { 232 if let Some(name) = import.path.segments.iter().last() {
255 if let ImportKind::Named(ptr) = import.kind { 233 if let ImportKind::Named(import) = import.kind {
256 module_items.items.insert( 234 module_items.items.insert(
257 name.clone(), 235 name.clone(),
258 Resolution { 236 Resolution {
259 def_id: None, 237 def_id: None,
260 import_name: Some(ptr), 238 import: Some(import),
261 }, 239 },
262 ); 240 );
263 } 241 }
@@ -269,12 +247,16 @@ where
269 // handle submodules separatelly 247 // handle submodules separatelly
270 continue; 248 continue;
271 } 249 }
272 let ptr = item.ptr.into_global(file_id); 250 let def_loc = DefLoc::Item {
273 let def_loc = DefLoc::Item { ptr }; 251 source_item_id: SourceItemId {
274 let def_id = self.db.id_maps().def_id(def_loc); 252 file_id,
253 item_id: item.id,
254 },
255 };
256 let def_id = def_loc.id(self.db);
275 let resolution = Resolution { 257 let resolution = Resolution {
276 def_id: Some(def_id), 258 def_id: Some(def_id),
277 import_name: None, 259 import: None,
278 }; 260 };
279 module_items.items.insert(item.name.clone(), resolution); 261 module_items.items.insert(item.name.clone(), resolution);
280 } 262 }
@@ -284,10 +266,10 @@ where
284 id: mod_id, 266 id: mod_id,
285 source_root: self.source_root, 267 source_root: self.source_root,
286 }; 268 };
287 let def_id = self.db.id_maps().def_id(def_loc); 269 let def_id = def_loc.id(self.db);
288 let resolution = Resolution { 270 let resolution = Resolution {
289 def_id: Some(def_id), 271 def_id: Some(def_id),
290 import_name: None, 272 import: None,
291 }; 273 };
292 module_items.items.insert(name, resolution); 274 module_items.items.insert(name, resolution);
293 } 275 }
@@ -333,7 +315,7 @@ where
333 }; 315 };
334 316
335 if !is_last { 317 if !is_last {
336 curr = match self.db.id_maps().def_loc(def_id) { 318 curr = match def_id.loc(self.db) {
337 DefLoc::Module { id, .. } => id, 319 DefLoc::Module { id, .. } => id,
338 _ => return, 320 _ => return,
339 } 321 }
@@ -341,7 +323,7 @@ where
341 self.update(module_id, |items| { 323 self.update(module_id, |items| {
342 let res = Resolution { 324 let res = Resolution {
343 def_id: Some(def_id), 325 def_id: Some(def_id),
344 import_name: Some(ptr), 326 import: Some(ptr),
345 }; 327 };
346 items.items.insert(name.clone(), res); 328 items.items.insert(name.clone(), res);
347 }) 329 })
@@ -357,23 +339,24 @@ where
357 339
358#[cfg(test)] 340#[cfg(test)]
359mod tests { 341mod tests {
342 use std::sync::Arc;
343
344 use salsa::Database;
345 use ra_db::FilesDatabase;
346 use ra_syntax::SmolStr;
347
360 use crate::{ 348 use crate::{
361 AnalysisChange, 349 self as hir,
362 mock_analysis::{MockAnalysis, analysis_and_position}, 350 db::HirDatabase,
363 descriptors::{DescriptorDatabase, module::ModuleDescriptor}, 351 mock::MockDatabase,
364 input::FilesDatabase,
365}; 352};
366 use super::*;
367 353
368 fn item_map(fixture: &str) -> (Arc<ItemMap>, ModuleId) { 354 fn item_map(fixture: &str) -> (Arc<hir::ItemMap>, hir::ModuleId) {
369 let (analysis, pos) = analysis_and_position(fixture); 355 let (db, pos) = MockDatabase::with_position(fixture);
370 let db = analysis.imp.db;
371 let source_root = db.file_source_root(pos.file_id); 356 let source_root = db.file_source_root(pos.file_id);
372 let descr = ModuleDescriptor::guess_from_position(&*db, pos) 357 let module = hir::Module::guess_from_position(&db, pos).unwrap().unwrap();
373 .unwrap() 358 let module_id = module.module_id;
374 .unwrap(); 359 (db.item_map(source_root).unwrap(), module_id)
375 let module_id = descr.module_id;
376 (db._item_map(source_root).unwrap(), module_id)
377 } 360 }
378 361
379 #[test] 362 #[test]
@@ -400,10 +383,10 @@ mod tests {
400 383
401 #[test] 384 #[test]
402 fn typing_inside_a_function_should_not_invalidate_item_map() { 385 fn typing_inside_a_function_should_not_invalidate_item_map() {
403 let mock_analysis = MockAnalysis::with_files( 386 let (mut db, pos) = MockDatabase::with_position(
404 " 387 "
405 //- /lib.rs 388 //- /lib.rs
406 mod foo; 389 mod foo;<|>
407 390
408 use crate::foo::bar::Baz; 391 use crate::foo::bar::Baz;
409 392
@@ -417,45 +400,35 @@ mod tests {
417 pub struct Baz; 400 pub struct Baz;
418 ", 401 ",
419 ); 402 );
420 403 let source_root = db.file_source_root(pos.file_id);
421 let file_id = mock_analysis.id_of("/lib.rs");
422 let mut host = mock_analysis.analysis_host();
423
424 let source_root = host.analysis().imp.db.file_source_root(file_id);
425
426 { 404 {
427 let db = host.analysis().imp.db;
428 let events = db.log_executed(|| { 405 let events = db.log_executed(|| {
429 db._item_map(source_root).unwrap(); 406 db.item_map(source_root).unwrap();
430 }); 407 });
431 assert!(format!("{:?}", events).contains("_item_map")) 408 assert!(format!("{:?}", events).contains("item_map"))
432 } 409 }
433 410
434 let mut change = AnalysisChange::new(); 411 let new_text = "
435
436 change.change_file(
437 file_id,
438 "
439 mod foo; 412 mod foo;
440 413
441 use crate::foo::bar::Baz; 414 use crate::foo::bar::Baz;
442 415
443 fn foo() -> i32 { 92 } 416 fn foo() -> i32 { 92 }
444 " 417 "
445 .to_string(), 418 .to_string();
446 );
447 419
448 host.apply_change(change); 420 db.query_mut(ra_db::FileTextQuery)
421 .set(pos.file_id, Arc::new(new_text));
449 422
450 { 423 {
451 let db = host.analysis().imp.db;
452 let events = db.log_executed(|| { 424 let events = db.log_executed(|| {
453 db._item_map(source_root).unwrap(); 425 db.item_map(source_root).unwrap();
454 }); 426 });
455 // assert!( 427 assert!(
456 // !format!("{:?}", events).contains("_item_map"), 428 !format!("{:?}", events).contains("_item_map"),
457 // "{:#?}", events 429 "{:#?}",
458 // ) 430 events
431 )
459 } 432 }
460 } 433 }
461} 434}
diff --git a/crates/ra_analysis/src/descriptors/path.rs b/crates/ra_hir/src/path.rs
index 99fca18b1..4a2e427cd 100644
--- a/crates/ra_analysis/src/descriptors/path.rs
+++ b/crates/ra_hir/src/path.rs
@@ -1,15 +1,13 @@
1use ra_syntax::{SmolStr, ast, AstNode}; 1use ra_syntax::{SmolStr, ast, AstNode, TextRange};
2
3use crate::syntax_ptr::LocalSyntaxPtr;
4 2
5#[derive(Debug, Clone, PartialEq, Eq)] 3#[derive(Debug, Clone, PartialEq, Eq)]
6pub(crate) struct Path { 4pub struct Path {
7 pub(crate) kind: PathKind, 5 pub kind: PathKind,
8 pub(crate) segments: Vec<SmolStr>, 6 pub segments: Vec<SmolStr>,
9} 7}
10 8
11#[derive(Debug, Clone, Copy, PartialEq, Eq)] 9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub(crate) enum PathKind { 10pub enum PathKind {
13 Plain, 11 Plain,
14 Self_, 12 Self_,
15 Super, 13 Super,
@@ -18,17 +16,14 @@ pub(crate) enum PathKind {
18 16
19impl Path { 17impl Path {
20 /// Calls `cb` with all paths, represented by this use item. 18 /// Calls `cb` with all paths, represented by this use item.
21 pub(crate) fn expand_use_item( 19 pub fn expand_use_item(item: ast::UseItem, mut cb: impl FnMut(Path, Option<TextRange>)) {
22 item: ast::UseItem,
23 mut cb: impl FnMut(Path, Option<LocalSyntaxPtr>),
24 ) {
25 if let Some(tree) = item.use_tree() { 20 if let Some(tree) = item.use_tree() {
26 expand_use_tree(None, tree, &mut cb); 21 expand_use_tree(None, tree, &mut cb);
27 } 22 }
28 } 23 }
29 24
30 /// Converts an `ast::Path` to `Path`. Works with use trees. 25 /// Converts an `ast::Path` to `Path`. Works with use trees.
31 pub(crate) fn from_ast(mut path: ast::Path) -> Option<Path> { 26 pub fn from_ast(mut path: ast::Path) -> Option<Path> {
32 let mut kind = PathKind::Plain; 27 let mut kind = PathKind::Plain;
33 let mut segments = Vec::new(); 28 let mut segments = Vec::new();
34 loop { 29 loop {
@@ -69,7 +64,7 @@ impl Path {
69 } 64 }
70 65
71 /// `true` is this path is a single identifier, like `foo` 66 /// `true` is this path is a single identifier, like `foo`
72 pub(crate) fn is_ident(&self) -> bool { 67 pub fn is_ident(&self) -> bool {
73 self.kind == PathKind::Plain && self.segments.len() == 1 68 self.kind == PathKind::Plain && self.segments.len() == 1
74 } 69 }
75} 70}
@@ -77,7 +72,7 @@ impl Path {
77fn expand_use_tree( 72fn expand_use_tree(
78 prefix: Option<Path>, 73 prefix: Option<Path>,
79 tree: ast::UseTree, 74 tree: ast::UseTree,
80 cb: &mut impl FnMut(Path, Option<LocalSyntaxPtr>), 75 cb: &mut impl FnMut(Path, Option<TextRange>),
81) { 76) {
82 if let Some(use_tree_list) = tree.use_tree_list() { 77 if let Some(use_tree_list) = tree.use_tree_list() {
83 let prefix = match tree.path() { 78 let prefix = match tree.path() {
@@ -93,13 +88,13 @@ fn expand_use_tree(
93 } else { 88 } else {
94 if let Some(ast_path) = tree.path() { 89 if let Some(ast_path) = tree.path() {
95 if let Some(path) = convert_path(prefix, ast_path) { 90 if let Some(path) = convert_path(prefix, ast_path) {
96 let ptr = if tree.has_star() { 91 let range = if tree.has_star() {
97 None 92 None
98 } else { 93 } else {
99 let ptr = LocalSyntaxPtr::new(ast_path.segment().unwrap().syntax()); 94 let range = ast_path.segment().unwrap().syntax().range();
100 Some(ptr) 95 Some(range)
101 }; 96 };
102 cb(path, ptr) 97 cb(path, range)
103 } 98 }
104 } 99 }
105 } 100 }
diff --git a/crates/ra_hir/src/query_definitions.rs b/crates/ra_hir/src/query_definitions.rs
new file mode 100644
index 000000000..6f602878c
--- /dev/null
+++ b/crates/ra_hir/src/query_definitions.rs
@@ -0,0 +1,154 @@
1use std::{
2 sync::Arc,
3 time::Instant,
4};
5
6use rustc_hash::FxHashMap;
7use ra_syntax::{
8 AstNode, SyntaxNode, SmolStr,
9 ast::{self, FnDef, FnDefNode, NameOwner, ModuleItemOwner}
10};
11use ra_db::{SourceRootId, FileId, Cancelable,};
12
13use crate::{
14 FnId,
15 SourceFileItems, SourceItemId,
16 db::HirDatabase,
17 function::FnScopes,
18 module::{
19 ModuleSource, ModuleSourceNode, ModuleId,
20 imp::Submodule,
21 nameres::{InputModuleItems, ItemMap, Resolver},
22 },
23};
24
25/// Resolve `FnId` to the corresponding `SyntaxNode`
26pub(super) fn fn_syntax(db: &impl HirDatabase, fn_id: FnId) -> FnDefNode {
27 let item_id = fn_id.loc(db);
28 let syntax = db.file_item(item_id);
29 FnDef::cast(syntax.borrowed()).unwrap().owned()
30}
31
32pub(super) fn fn_scopes(db: &impl HirDatabase, fn_id: FnId) -> Arc<FnScopes> {
33 let syntax = db.fn_syntax(fn_id);
34 let res = FnScopes::new(syntax.borrowed());
35 Arc::new(res)
36}
37
38pub(super) fn file_items(db: &impl HirDatabase, file_id: FileId) -> Arc<SourceFileItems> {
39 let source_file = db.source_file(file_id);
40 let source_file = source_file.borrowed();
41 let mut res = SourceFileItems::default();
42 source_file
43 .syntax()
44 .descendants()
45 .filter_map(ast::ModuleItem::cast)
46 .map(|it| it.syntax().owned())
47 .for_each(|it| {
48 res.alloc(it);
49 });
50 Arc::new(res)
51}
52
53pub(super) fn file_item(db: &impl HirDatabase, source_item_id: SourceItemId) -> SyntaxNode {
54 db.file_items(source_item_id.file_id)[source_item_id.item_id].clone()
55}
56
57pub(crate) fn submodules(
58 db: &impl HirDatabase,
59 source: ModuleSource,
60) -> Cancelable<Arc<Vec<Submodule>>> {
61 db.check_canceled()?;
62 let file_id = source.file_id();
63 let submodules = match source.resolve(db) {
64 ModuleSourceNode::SourceFile(it) => collect_submodules(db, file_id, it.borrowed()),
65 ModuleSourceNode::Module(it) => it
66 .borrowed()
67 .item_list()
68 .map(|it| collect_submodules(db, file_id, it))
69 .unwrap_or_else(Vec::new),
70 };
71 return Ok(Arc::new(submodules));
72
73 fn collect_submodules<'a>(
74 db: &impl HirDatabase,
75 file_id: FileId,
76 root: impl ast::ModuleItemOwner<'a>,
77 ) -> Vec<Submodule> {
78 modules(root)
79 .map(|(name, m)| {
80 if m.has_semi() {
81 Submodule::Declaration(name)
82 } else {
83 let src = ModuleSource::new_inline(db, file_id, m);
84 Submodule::Definition(name, src)
85 }
86 })
87 .collect()
88 }
89}
90
91pub(crate) fn modules<'a>(
92 root: impl ast::ModuleItemOwner<'a>,
93) -> impl Iterator<Item = (SmolStr, ast::Module<'a>)> {
94 root.items()
95 .filter_map(|item| match item {
96 ast::ModuleItem::Module(m) => Some(m),
97 _ => None,
98 })
99 .filter_map(|module| {
100 let name = module.name()?.text();
101 Some((name, module))
102 })
103}
104
105pub(super) fn input_module_items(
106 db: &impl HirDatabase,
107 source_root: SourceRootId,
108 module_id: ModuleId,
109) -> Cancelable<Arc<InputModuleItems>> {
110 let module_tree = db.module_tree(source_root)?;
111 let source = module_id.source(&module_tree);
112 let file_items = db.file_items(source.file_id());
113 let res = match source.resolve(db) {
114 ModuleSourceNode::SourceFile(it) => {
115 let items = it.borrowed().items();
116 InputModuleItems::new(&file_items, items)
117 }
118 ModuleSourceNode::Module(it) => {
119 let items = it
120 .borrowed()
121 .item_list()
122 .into_iter()
123 .flat_map(|it| it.items());
124 InputModuleItems::new(&file_items, items)
125 }
126 };
127 Ok(Arc::new(res))
128}
129
130pub(super) fn item_map(
131 db: &impl HirDatabase,
132 source_root: SourceRootId,
133) -> Cancelable<Arc<ItemMap>> {
134 let start = Instant::now();
135 let module_tree = db.module_tree(source_root)?;
136 let input = module_tree
137 .modules()
138 .map(|id| {
139 let items = db.input_module_items(source_root, id)?;
140 Ok((id, items))
141 })
142 .collect::<Cancelable<FxHashMap<_, _>>>()?;
143 let resolver = Resolver {
144 db: db,
145 input: &input,
146 source_root,
147 module_tree,
148 result: ItemMap::default(),
149 };
150 let res = resolver.resolve()?;
151 let elapsed = start.elapsed();
152 log::info!("item_map: {:?}", elapsed);
153 Ok(Arc::new(res))
154}
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs
index 5d5a0c55e..28368787c 100644
--- a/crates/ra_lsp_server/src/conv.rs
+++ b/crates/ra_lsp_server/src/conv.rs
@@ -63,10 +63,7 @@ impl ConvWith for TextUnit {
63 63
64 fn conv_with(self, line_index: &LineIndex) -> Position { 64 fn conv_with(self, line_index: &LineIndex) -> Position {
65 let line_col = line_index.line_col(self); 65 let line_col = line_index.line_col(self);
66 Position::new( 66 Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16))
67 u64::from(line_col.line),
68 u64::from(u32::from(line_col.col_utf16)),
69 )
70 } 67 }
71} 68}
72 69
@@ -204,10 +201,8 @@ impl TryConvWith for SourceChange {
204 .map(|it| it.edits.as_slice()) 201 .map(|it| it.edits.as_slice())
205 .unwrap_or(&[]); 202 .unwrap_or(&[]);
206 let line_col = translate_offset_with_edit(&*line_index, pos.offset, edits); 203 let line_col = translate_offset_with_edit(&*line_index, pos.offset, edits);
207 let position = Position::new( 204 let position =
208 u64::from(line_col.line), 205 Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16));
209 u64::from(u32::from(line_col.col_utf16)),
210 );
211 Some(TextDocumentPositionParams { 206 Some(TextDocumentPositionParams {
212 text_document: TextDocumentIdentifier::new(pos.file_id.try_conv_with(world)?), 207 text_document: TextDocumentIdentifier::new(pos.file_id.try_conv_with(world)?),
213 position, 208 position,
diff --git a/crates/ra_lsp_server/src/main.rs b/crates/ra_lsp_server/src/main.rs
index 26bcddd8e..8301a1044 100644
--- a/crates/ra_lsp_server/src/main.rs
+++ b/crates/ra_lsp_server/src/main.rs
@@ -65,27 +65,3 @@ fn main_inner() -> Result<()> {
65 info!("... IO is down"); 65 info!("... IO is down");
66 Ok(()) 66 Ok(())
67} 67}
68
69/*
70 (let ((backend (eglot-xref-backend)))
71 (mapcar
72 (lambda (xref)
73 (let ((loc (xref-item-location xref)))
74 (propertize
75 (concat
76 (when (xref-file-location-p loc)
77 (with-slots (file line column) loc
78 (format "%s:%s:%s:"
79 (propertize (file-relative-name file)
80 'face 'compilation-info)
81 (propertize (format "%s" line)
82 'face 'compilation-line
83 )
84 column)))
85 (xref-item-summary xref))
86 'xref xref)))
87 (xref-backend-apropos backend "Analysis"))
88 )
89
90
91*/
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index 27933a7ae..6d5622b15 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -384,7 +384,7 @@ pub fn handle_completion(
384 let completion_triggered_after_single_colon = { 384 let completion_triggered_after_single_colon = {
385 let mut res = false; 385 let mut res = false;
386 if let Some(ctx) = params.context { 386 if let Some(ctx) = params.context {
387 if ctx.trigger_character.unwrap_or(String::new()) == ":" { 387 if ctx.trigger_character.unwrap_or_default() == ":" {
388 let source_file = world.analysis().file_syntax(position.file_id); 388 let source_file = world.analysis().file_syntax(position.file_id);
389 let syntax = source_file.syntax(); 389 let syntax = source_file.syntax();
390 let text = syntax.text(); 390 let text = syntax.text();
@@ -567,10 +567,13 @@ pub fn handle_rename(world: ServerWorld, params: RenameParams) -> Result<Option<
567 let mut changes = HashMap::new(); 567 let mut changes = HashMap::new();
568 for r in refs { 568 for r in refs {
569 if let Ok(loc) = to_location(r.0, r.1, &world, &line_index) { 569 if let Ok(loc) = to_location(r.0, r.1, &world, &line_index) {
570 changes.entry(loc.uri).or_insert(Vec::new()).push(TextEdit { 570 changes
571 range: loc.range, 571 .entry(loc.uri)
572 new_text: params.new_name.clone(), 572 .or_insert_with(Vec::new)
573 }); 573 .push(TextEdit {
574 range: loc.range,
575 new_text: params.new_name.clone(),
576 });
574 } 577 }
575 } 578 }
576 579
diff --git a/crates/ra_lsp_server/src/path_map.rs b/crates/ra_lsp_server/src/path_map.rs
index a624043d8..02e54629c 100644
--- a/crates/ra_lsp_server/src/path_map.rs
+++ b/crates/ra_lsp_server/src/path_map.rs
@@ -43,7 +43,7 @@ impl PathMap {
43 (inserted, file_id) 43 (inserted, file_id)
44 } 44 }
45 pub fn get_id(&self, path: &Path) -> Option<FileId> { 45 pub fn get_id(&self, path: &Path) -> Option<FileId> {
46 self.path2id.get(path).map(|&id| id) 46 self.path2id.get(path).cloned()
47 } 47 }
48 pub fn get_path(&self, file_id: FileId) -> &Path { 48 pub fn get_path(&self, file_id: FileId) -> &Path {
49 self.id2path.get(&file_id).unwrap().as_path() 49 self.id2path.get(&file_id).unwrap().as_path()
diff --git a/crates/ra_lsp_server/src/server_world.rs b/crates/ra_lsp_server/src/server_world.rs
index 3e7670fcc..12faeb93a 100644
--- a/crates/ra_lsp_server/src/server_world.rs
+++ b/crates/ra_lsp_server/src/server_world.rs
@@ -140,7 +140,7 @@ impl ServerWorldState {
140 Ok(file_id) 140 Ok(file_id)
141 } 141 }
142 pub fn set_workspaces(&mut self, ws: Vec<CargoWorkspace>) { 142 pub fn set_workspaces(&mut self, ws: Vec<CargoWorkspace>) {
143 let mut crate_graph = CrateGraph::new(); 143 let mut crate_graph = CrateGraph::default();
144 ws.iter() 144 ws.iter()
145 .flat_map(|ws| { 145 .flat_map(|ws| {
146 ws.packages() 146 ws.packages()