aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_analysis/Cargo.toml2
-rw-r--r--crates/ra_analysis/src/db.rs117
-rw-r--r--crates/ra_analysis/src/db/imp.rs153
-rw-r--r--crates/ra_analysis/src/db/mod.rs85
-rw-r--r--crates/ra_analysis/src/descriptors.rs10
-rw-r--r--crates/ra_analysis/src/imp.rs55
-rw-r--r--crates/ra_analysis/src/lib.rs9
-rw-r--r--crates/ra_analysis/src/module_map.rs163
-rw-r--r--crates/ra_analysis/src/queries.rs39
-rw-r--r--crates/ra_analysis/src/roots.rs70
-rw-r--r--crates/ra_analysis/src/symbol_index.rs9
-rw-r--r--crates/ra_analysis/tests/tests.rs2
-rw-r--r--crates/ra_editor/src/line_index.rs2
-rw-r--r--crates/ra_editor/src/symbols.rs2
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs4
-rw-r--r--crates/ra_lsp_server/src/main_loop/mod.rs4
-rw-r--r--crates/ra_lsp_server/src/server_world.rs1
-rw-r--r--crates/ra_syntax/src/lib.rs2
-rw-r--r--crates/salsa/Cargo.toml8
-rw-r--r--crates/salsa/src/lib.rs293
-rw-r--r--crates/salsa/tests/integration.rs170
21 files changed, 236 insertions, 964 deletions
diff --git a/crates/ra_analysis/Cargo.toml b/crates/ra_analysis/Cargo.toml
index 28d95c581..17b04182f 100644
--- a/crates/ra_analysis/Cargo.toml
+++ b/crates/ra_analysis/Cargo.toml
@@ -15,7 +15,7 @@ fst = "0.3.1"
15im = "12.0.0" 15im = "12.0.0"
16ra_syntax = { path = "../ra_syntax" } 16ra_syntax = { path = "../ra_syntax" }
17ra_editor = { path = "../ra_editor" } 17ra_editor = { path = "../ra_editor" }
18salsa = { path = "../salsa" } 18salsa = "0.5.0"
19rustc-hash = "1.0" 19rustc-hash = "1.0"
20 20
21[dev-dependencies] 21[dev-dependencies]
diff --git a/crates/ra_analysis/src/db.rs b/crates/ra_analysis/src/db.rs
new file mode 100644
index 000000000..c69577233
--- /dev/null
+++ b/crates/ra_analysis/src/db.rs
@@ -0,0 +1,117 @@
1use std::{
2 fmt,
3 sync::Arc,
4 hash::{Hash, Hasher},
5};
6use salsa;
7use rustc_hash::FxHashSet;
8use ra_syntax::File;
9use ra_editor::{LineIndex};
10use crate::{
11 symbol_index::SymbolIndex,
12 module_map::{ModulesDatabase, ModuleTreeQuery, ModuleDescriptorQuery},
13 FileId, FileResolverImp,
14};
15
16#[derive(Default)]
17pub(crate) struct RootDatabase {
18 runtime: salsa::runtime::Runtime<RootDatabase>,
19}
20
21impl fmt::Debug for RootDatabase {
22 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
23 fmt.write_str("RootDatabase { ... }")
24 }
25}
26
27impl salsa::Database for RootDatabase {
28 fn salsa_runtime(&self) -> &salsa::runtime::Runtime<RootDatabase> {
29 &self.runtime
30 }
31}
32
33impl salsa::ParallelDatabase for RootDatabase {
34 fn fork(&self) -> Self {
35 RootDatabase {
36 runtime: self.runtime.fork(),
37 }
38 }
39}
40
41impl Clone for RootDatabase {
42 fn clone(&self) -> RootDatabase {
43 salsa::ParallelDatabase::fork(self)
44 }
45}
46
47salsa::database_storage! {
48 pub(crate) struct RootDatabaseStorage for RootDatabase {
49 impl FilesDatabase {
50 fn file_text() for FileTextQuery;
51 fn file_set() for FileSetQuery;
52 }
53 impl SyntaxDatabase {
54 fn file_syntax() for FileSyntaxQuery;
55 fn file_lines() for FileLinesQuery;
56 fn file_symbols() for FileSymbolsQuery;
57 }
58 impl ModulesDatabase {
59 fn module_tree() for ModuleTreeQuery;
60 fn module_descriptor() for ModuleDescriptorQuery;
61 }
62 }
63}
64
65salsa::query_group! {
66 pub(crate) trait FilesDatabase: salsa::Database {
67 fn file_text(file_id: FileId) -> Arc<String> {
68 type FileTextQuery;
69 storage input;
70 }
71 fn file_set(key: ()) -> Arc<FileSet> {
72 type FileSetQuery;
73 storage input;
74 }
75 }
76}
77
78#[derive(Default, Debug, PartialEq, Eq)]
79pub(crate) struct FileSet {
80 pub(crate) files: FxHashSet<FileId>,
81 pub(crate) resolver: FileResolverImp,
82}
83
84impl Hash for FileSet {
85 fn hash<H: Hasher>(&self, hasher: &mut H) {
86 let mut files = self.files.iter().cloned().collect::<Vec<_>>();
87 files.sort();
88 files.hash(hasher);
89 }
90}
91
92salsa::query_group! {
93 pub(crate) trait SyntaxDatabase: FilesDatabase {
94 fn file_syntax(file_id: FileId) -> File {
95 type FileSyntaxQuery;
96 }
97 fn file_lines(file_id: FileId) -> Arc<LineIndex> {
98 type FileLinesQuery;
99 }
100 fn file_symbols(file_id: FileId) -> Arc<SymbolIndex> {
101 type FileSymbolsQuery;
102 }
103 }
104}
105
106fn file_syntax(db: &impl SyntaxDatabase, file_id: FileId) -> File {
107 let text = db.file_text(file_id);
108 File::parse(&*text)
109}
110fn file_lines(db: &impl SyntaxDatabase, file_id: FileId) -> Arc<LineIndex> {
111 let text = db.file_text(file_id);
112 Arc::new(LineIndex::new(&*text))
113}
114fn file_symbols(db: &impl SyntaxDatabase, file_id: FileId) -> Arc<SymbolIndex> {
115 let syntax = db.file_syntax(file_id);
116 Arc::new(SymbolIndex::for_file(file_id, syntax))
117}
diff --git a/crates/ra_analysis/src/db/imp.rs b/crates/ra_analysis/src/db/imp.rs
deleted file mode 100644
index 7669b6184..000000000
--- a/crates/ra_analysis/src/db/imp.rs
+++ /dev/null
@@ -1,153 +0,0 @@
1use std::{
2 sync::Arc,
3 any::Any,
4 hash::{Hash, Hasher},
5 collections::hash_map::{DefaultHasher},
6 iter,
7};
8use rustc_hash::FxHashMap;
9use salsa;
10use crate::{FileId, imp::FileResolverImp};
11use super::{State, Query, QueryCtx};
12
13pub(super) type Data = Arc<Any + Send + Sync + 'static>;
14
15#[derive(Debug)]
16pub(super) struct Db {
17 names: Arc<FxHashMap<salsa::QueryTypeId, &'static str>>,
18 pub(super) imp: salsa::Db<State, Data>,
19}
20
21impl Db {
22 pub(super) fn new(mut reg: QueryRegistry) -> Db {
23 let config = reg.config.take().unwrap();
24 Db {
25 names: Arc::new(reg.names),
26 imp: salsa::Db::new(config, State::default())
27 }
28 }
29 pub(crate) fn with_changes(&self, new_state: State, changed_files: &[FileId], resolver_changed: bool) -> Db {
30 let names = self.names.clone();
31 let mut invalidations = salsa::Invalidations::new();
32 invalidations.invalidate(FILE_TEXT, changed_files.iter().map(hash).map(salsa::InputFingerprint));
33 if resolver_changed {
34 invalidations.invalidate(FILE_SET, iter::once(salsa::InputFingerprint(hash(&()))));
35 } else {
36 invalidations.invalidate(FILE_SET, iter::empty());
37 }
38 let imp = self.imp.with_ground_data(
39 new_state,
40 invalidations,
41 );
42 Db { names, imp }
43 }
44 pub(super) fn extract_trace(&self, ctx: &salsa::QueryCtx<State, Data>) -> Vec<&'static str> {
45 ctx.trace().into_iter().map(|it| self.names[&it]).collect()
46 }
47}
48
49pub(crate) trait EvalQuery {
50 type Params;
51 type Output;
52 fn query_type(&self) -> salsa::QueryTypeId;
53 fn f(&self) -> salsa::QueryFn<State, Data>;
54 fn get(&self, ctx: &QueryCtx, params: Self::Params) -> Arc<Self::Output>;
55}
56
57impl<T, R> EvalQuery for Query<T, R>
58where
59 T: Hash + Send + Sync + 'static,
60 R: Hash + Send + Sync + 'static,
61{
62 type Params = T;
63 type Output = R;
64 fn query_type(&self) -> salsa::QueryTypeId {
65 salsa::QueryTypeId(self.0)
66 }
67 fn f(&self) -> salsa::QueryFn<State, Data> {
68 let f = self.1;
69 Box::new(move |ctx, data| {
70 let ctx = QueryCtx { imp: ctx };
71 let data: &T = data.downcast_ref().unwrap();
72 let res = f(ctx, data);
73 let h = hash(&res);
74 (Arc::new(res), salsa::OutputFingerprint(h))
75 })
76 }
77 fn get(&self, ctx: &QueryCtx, params: Self::Params) -> Arc<Self::Output> {
78 let query_id = salsa::QueryId(
79 self.query_type(),
80 salsa::InputFingerprint(hash(&params)),
81 );
82 let res = ctx.imp.get(query_id, Arc::new(params));
83 res.downcast().unwrap()
84 }
85}
86
87pub(super) struct QueryRegistry {
88 config: Option<salsa::QueryConfig<State, Data>>,
89 names: FxHashMap<salsa::QueryTypeId, &'static str>,
90}
91
92impl QueryRegistry {
93 pub(super) fn new() -> QueryRegistry {
94 let mut config = salsa::QueryConfig::<State, Data>::new();
95 config = config.with_ground_query(
96 FILE_TEXT, Box::new(|state, params| {
97 let file_id: &FileId = params.downcast_ref().unwrap();
98 let res = state.file_map[file_id].clone();
99 let fingerprint = salsa::OutputFingerprint(hash(&res));
100 (res, fingerprint)
101 })
102 );
103 config = config.with_ground_query(
104 FILE_SET, Box::new(|state, _params| {
105 let file_ids: Vec<FileId> = state.file_map.keys().cloned().collect();
106 let hash = hash(&file_ids);
107 let file_resolver = state.file_resolver.clone();
108 let res = (file_ids, file_resolver);
109 let fingerprint = salsa::OutputFingerprint(hash);
110 (Arc::new(res), fingerprint)
111 })
112 );
113 let mut names = FxHashMap::default();
114 names.insert(FILE_TEXT, "FILE_TEXT");
115 names.insert(FILE_SET, "FILE_SET");
116 QueryRegistry { config: Some(config), names }
117 }
118 pub(super) fn add<Q: EvalQuery>(&mut self, q: Q, name: &'static str) {
119 let id = q.query_type();
120 let prev = self.names.insert(id, name);
121 assert!(prev.is_none(), "duplicate query: {:?}", id);
122 let config = self.config.take().unwrap();
123 let config = config.with_query(id, q.f());
124 self.config= Some(config);
125 }
126}
127
128fn hash<T: Hash>(x: &T) -> u64 {
129 let mut hasher = DefaultHasher::new();
130 x.hash(&mut hasher);
131 hasher.finish()
132}
133
134const FILE_TEXT: salsa::QueryTypeId = salsa::QueryTypeId(0);
135pub(super) fn file_text(ctx: QueryCtx, file_id: FileId) -> Arc<String> {
136 let query_id = salsa::QueryId(
137 FILE_TEXT,
138 salsa::InputFingerprint(hash(&file_id)),
139 );
140 let res = ctx.imp.get(query_id, Arc::new(file_id));
141 res.downcast().unwrap()
142}
143
144const FILE_SET: salsa::QueryTypeId = salsa::QueryTypeId(1);
145pub(super) fn file_set(ctx: QueryCtx) -> Arc<(Vec<FileId>, FileResolverImp)> {
146 let query_id = salsa::QueryId(
147 FILE_SET,
148 salsa::InputFingerprint(hash(&())),
149 );
150 let res = ctx.imp.get(query_id, Arc::new(()));
151 res.downcast().unwrap()
152}
153
diff --git a/crates/ra_analysis/src/db/mod.rs b/crates/ra_analysis/src/db/mod.rs
deleted file mode 100644
index 4eb7d922d..000000000
--- a/crates/ra_analysis/src/db/mod.rs
+++ /dev/null
@@ -1,85 +0,0 @@
1mod imp;
2
3use std::{
4 sync::Arc,
5};
6use im;
7use salsa;
8use crate::{FileId, imp::FileResolverImp};
9
10#[derive(Debug, Default, Clone)]
11pub(crate) struct State {
12 pub(crate) file_map: im::HashMap<FileId, Arc<String>>,
13 pub(crate) file_resolver: FileResolverImp
14}
15
16#[derive(Debug)]
17pub(crate) struct Db {
18 imp: imp::Db,
19}
20
21#[derive(Clone, Copy)]
22pub(crate) struct QueryCtx<'a> {
23 imp: &'a salsa::QueryCtx<State, imp::Data>,
24}
25
26pub(crate) struct Query<T, R>(pub(crate) u16, pub(crate) fn(QueryCtx, &T) -> R);
27
28pub(crate) struct QueryRegistry {
29 imp: imp::QueryRegistry,
30}
31
32impl Default for Db {
33 fn default() -> Db {
34 Db::new()
35 }
36}
37
38impl Db {
39 pub(crate) fn new() -> Db {
40 let reg = QueryRegistry::new();
41 Db { imp: imp::Db::new(reg.imp) }
42 }
43 pub(crate) fn state(&self) -> &State {
44 self.imp.imp.ground_data()
45 }
46 pub(crate) fn with_changes(&self, new_state: State, changed_files: &[FileId], resolver_changed: bool) -> Db {
47 Db { imp: self.imp.with_changes(new_state, changed_files, resolver_changed) }
48 }
49 pub(crate) fn make_query<F: FnOnce(QueryCtx) -> R, R>(&self, f: F) -> R {
50 let ctx = QueryCtx { imp: &self.imp.imp.query_ctx() };
51 f(ctx)
52 }
53 #[allow(unused)]
54 pub(crate) fn trace_query<F: FnOnce(QueryCtx) -> R, R>(&self, f: F) -> (R, Vec<&'static str>) {
55 let ctx = QueryCtx { imp: &self.imp.imp.query_ctx() };
56 let res = f(ctx);
57 let trace = self.imp.extract_trace(ctx.imp);
58 (res, trace)
59 }
60}
61
62impl<'a> QueryCtx<'a> {
63 pub(crate) fn get<Q: imp::EvalQuery>(&self, q: Q, params: Q::Params) -> Arc<Q::Output> {
64 q.get(self, params)
65 }
66}
67
68pub(crate) fn file_text(ctx: QueryCtx, file_id: FileId) -> Arc<String> {
69 imp::file_text(ctx, file_id)
70}
71
72pub(crate) fn file_set(ctx: QueryCtx) -> Arc<(Vec<FileId>, FileResolverImp)> {
73 imp::file_set(ctx)
74}
75impl QueryRegistry {
76 fn new() -> QueryRegistry {
77 let mut reg = QueryRegistry { imp: imp::QueryRegistry::new() };
78 crate::queries::register_queries(&mut reg);
79 crate::module_map::register_queries(&mut reg);
80 reg
81 }
82 pub(crate) fn add<Q: imp::EvalQuery>(&mut self, q: Q, name: &'static str) {
83 self.imp.add(q, name)
84 }
85}
diff --git a/crates/ra_analysis/src/descriptors.rs b/crates/ra_analysis/src/descriptors.rs
index f26dac875..8d9f38ca5 100644
--- a/crates/ra_analysis/src/descriptors.rs
+++ b/crates/ra_analysis/src/descriptors.rs
@@ -12,7 +12,7 @@ use crate::{
12 imp::FileResolverImp, 12 imp::FileResolverImp,
13}; 13};
14 14
15#[derive(Debug, Hash)] 15#[derive(Debug, PartialEq, Eq, Hash)]
16pub struct ModuleDescriptor { 16pub struct ModuleDescriptor {
17 pub submodules: Vec<Submodule> 17 pub submodules: Vec<Submodule>
18} 18}
@@ -43,7 +43,7 @@ pub struct Submodule {
43 pub name: SmolStr, 43 pub name: SmolStr,
44} 44}
45 45
46#[derive(Hash, Debug)] 46#[derive(Debug, PartialEq, Eq, Hash)]
47pub(crate) struct ModuleTreeDescriptor { 47pub(crate) struct ModuleTreeDescriptor {
48 nodes: Vec<NodeData>, 48 nodes: Vec<NodeData>,
49 links: Vec<LinkData>, 49 links: Vec<LinkData>,
@@ -52,7 +52,7 @@ pub(crate) struct ModuleTreeDescriptor {
52 52
53#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] 53#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
54struct Node(usize); 54struct Node(usize);
55#[derive(Hash, Debug)] 55#[derive(Hash, Debug, PartialEq, Eq)]
56struct NodeData { 56struct NodeData {
57 file_id: FileId, 57 file_id: FileId,
58 links: Vec<Link>, 58 links: Vec<Link>,
@@ -61,7 +61,7 @@ struct NodeData {
61 61
62#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] 62#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
63pub(crate) struct Link(usize); 63pub(crate) struct Link(usize);
64#[derive(Hash, Debug)] 64#[derive(Hash, Debug, PartialEq, Eq)]
65struct LinkData { 65struct LinkData {
66 owner: Node, 66 owner: Node,
67 name: SmolStr, 67 name: SmolStr,
@@ -70,7 +70,7 @@ struct LinkData {
70} 70}
71 71
72 72
73#[derive(Clone, Debug, Hash)] 73#[derive(Clone, Debug, Hash, PartialEq, Eq)]
74pub enum Problem { 74pub enum Problem {
75 UnresolvedModule { 75 UnresolvedModule {
76 candidate: RelativePathBuf, 76 candidate: RelativePathBuf,
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs
index 26f5e175b..5efcaeca0 100644
--- a/crates/ra_analysis/src/imp.rs
+++ b/crates/ra_analysis/src/imp.rs
@@ -1,8 +1,8 @@
1use std::{ 1use std::{
2 sync::{ 2 sync::{
3 Arc, 3 Arc,
4 atomic::{AtomicBool, Ordering::SeqCst},
5 }, 4 },
5 hash::{Hash, Hasher},
6 fmt, 6 fmt,
7 collections::VecDeque, 7 collections::VecDeque,
8 iter, 8 iter,
@@ -29,6 +29,21 @@ pub(crate) struct FileResolverImp {
29 inner: Arc<FileResolver> 29 inner: Arc<FileResolver>
30} 30}
31 31
32impl PartialEq for FileResolverImp {
33 fn eq(&self, other: &FileResolverImp) -> bool {
34 self.inner() == other.inner()
35 }
36}
37
38impl Eq for FileResolverImp {
39}
40
41impl Hash for FileResolverImp {
42 fn hash<H: Hasher>(&self, hasher: &mut H) {
43 self.inner().hash(hasher);
44 }
45}
46
32impl FileResolverImp { 47impl FileResolverImp {
33 pub(crate) fn new(inner: Arc<FileResolver>) -> FileResolverImp { 48 pub(crate) fn new(inner: Arc<FileResolver>) -> FileResolverImp {
34 FileResolverImp { inner } 49 FileResolverImp { inner }
@@ -39,6 +54,9 @@ impl FileResolverImp {
39 pub(crate) fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option<FileId> { 54 pub(crate) fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option<FileId> {
40 self.inner.resolve(file_id, path) 55 self.inner.resolve(file_id, path)
41 } 56 }
57 fn inner(&self) -> *const FileResolver {
58 &*self.inner
59 }
42} 60}
43 61
44impl Default for FileResolverImp { 62impl Default for FileResolverImp {
@@ -59,29 +77,27 @@ impl Default for FileResolverImp {
59 77
60#[derive(Debug)] 78#[derive(Debug)]
61pub(crate) struct AnalysisHostImpl { 79pub(crate) struct AnalysisHostImpl {
62 data: Arc<WorldData> 80 data: WorldData
63} 81}
64 82
65impl AnalysisHostImpl { 83impl AnalysisHostImpl {
66 pub fn new() -> AnalysisHostImpl { 84 pub fn new() -> AnalysisHostImpl {
67 AnalysisHostImpl { 85 AnalysisHostImpl {
68 data: Arc::new(WorldData::default()), 86 data: WorldData::default(),
69 } 87 }
70 } 88 }
71 pub fn analysis(&self) -> AnalysisImpl { 89 pub fn analysis(&self) -> AnalysisImpl {
72 AnalysisImpl { 90 AnalysisImpl {
73 needs_reindex: AtomicBool::new(false),
74 data: self.data.clone(), 91 data: self.data.clone(),
75 } 92 }
76 } 93 }
77 pub fn change_files(&mut self, changes: &mut dyn Iterator<Item=(FileId, Option<String>)>) { 94 pub fn change_files(&mut self, changes: &mut dyn Iterator<Item=(FileId, Option<String>)>) {
78 let data = self.data_mut(); 95 self.data_mut()
79 data.root = Arc::new(data.root.apply_changes(changes, None)); 96 .root.apply_changes(changes, None);
80 } 97 }
81 pub fn set_file_resolver(&mut self, resolver: FileResolverImp) { 98 pub fn set_file_resolver(&mut self, resolver: FileResolverImp) {
82 let data = self.data_mut(); 99 self.data_mut()
83 data.file_resolver = resolver.clone(); 100 .root.apply_changes(&mut iter::empty(), Some(resolver));
84 data.root = Arc::new(data.root.apply_changes(&mut iter::empty(), Some(resolver)));
85 } 101 }
86 pub fn set_crate_graph(&mut self, graph: CrateGraph) { 102 pub fn set_crate_graph(&mut self, graph: CrateGraph) {
87 let mut visited = FxHashSet::default(); 103 let mut visited = FxHashSet::default();
@@ -96,34 +112,24 @@ impl AnalysisHostImpl {
96 self.data_mut().libs.push(Arc::new(root)); 112 self.data_mut().libs.push(Arc::new(root));
97 } 113 }
98 fn data_mut(&mut self) -> &mut WorldData { 114 fn data_mut(&mut self) -> &mut WorldData {
99 Arc::make_mut(&mut self.data) 115 &mut self.data
100 } 116 }
101} 117}
102 118
103pub(crate) struct AnalysisImpl { 119pub(crate) struct AnalysisImpl {
104 needs_reindex: AtomicBool, 120 data: WorldData,
105 data: Arc<WorldData>,
106} 121}
107 122
108impl fmt::Debug for AnalysisImpl { 123impl fmt::Debug for AnalysisImpl {
109 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 124 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
110 (&*self.data).fmt(f) 125 self.data.fmt(f)
111 }
112}
113
114impl Clone for AnalysisImpl {
115 fn clone(&self) -> AnalysisImpl {
116 AnalysisImpl {
117 needs_reindex: AtomicBool::new(self.needs_reindex.load(SeqCst)),
118 data: Arc::clone(&self.data),
119 }
120 } 126 }
121} 127}
122 128
123impl AnalysisImpl { 129impl AnalysisImpl {
124 fn root(&self, file_id: FileId) -> &SourceRoot { 130 fn root(&self, file_id: FileId) -> &SourceRoot {
125 if self.data.root.contains(file_id) { 131 if self.data.root.contains(file_id) {
126 return &*self.data.root; 132 return &self.data.root;
127 } 133 }
128 &**self.data.libs.iter().find(|it| it.contains(file_id)).unwrap() 134 &**self.data.libs.iter().find(|it| it.contains(file_id)).unwrap()
129 } 135 }
@@ -386,9 +392,8 @@ impl AnalysisImpl {
386 392
387#[derive(Default, Clone, Debug)] 393#[derive(Default, Clone, Debug)]
388struct WorldData { 394struct WorldData {
389 file_resolver: FileResolverImp,
390 crate_graph: CrateGraph, 395 crate_graph: CrateGraph,
391 root: Arc<WritableSourceRoot>, 396 root: WritableSourceRoot,
392 libs: Vec<Arc<ReadonlySourceRoot>>, 397 libs: Vec<Arc<ReadonlySourceRoot>>,
393} 398}
394 399
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs
index 86c66236c..d8b355a81 100644
--- a/crates/ra_analysis/src/lib.rs
+++ b/crates/ra_analysis/src/lib.rs
@@ -19,7 +19,6 @@ mod imp;
19mod job; 19mod job;
20mod roots; 20mod roots;
21mod db; 21mod db;
22mod queries;
23mod descriptors; 22mod descriptors;
24 23
25use std::{ 24use std::{
@@ -162,7 +161,7 @@ impl Query {
162 } 161 }
163} 162}
164 163
165#[derive(Clone, Debug)] 164#[derive(Debug)]
166pub struct Analysis { 165pub struct Analysis {
167 imp: AnalysisImpl 166 imp: AnalysisImpl
168} 167}
@@ -258,3 +257,9 @@ impl LibraryData {
258 LibraryData { root } 257 LibraryData { root }
259 } 258 }
260} 259}
260
261#[test]
262fn analysis_is_send() {
263 fn is_send<T: Send>() {}
264 is_send::<Analysis>();
265}
diff --git a/crates/ra_analysis/src/module_map.rs b/crates/ra_analysis/src/module_map.rs
index c77c5cec6..c1799e3d4 100644
--- a/crates/ra_analysis/src/module_map.rs
+++ b/crates/ra_analysis/src/module_map.rs
@@ -1,157 +1,34 @@
1use std::sync::Arc; 1use std::sync::Arc;
2use crate::{ 2use crate::{
3 FileId, 3 FileId,
4 db::{ 4 db::{SyntaxDatabase},
5 Query, QueryRegistry, QueryCtx,
6 file_set
7 },
8 queries::file_syntax,
9 descriptors::{ModuleDescriptor, ModuleTreeDescriptor}, 5 descriptors::{ModuleDescriptor, ModuleTreeDescriptor},
10}; 6};
11 7
12pub(crate) fn register_queries(reg: &mut QueryRegistry) { 8salsa::query_group! {
13 reg.add(MODULE_DESCR, "MODULE_DESCR"); 9 pub(crate) trait ModulesDatabase: SyntaxDatabase {
14 reg.add(MODULE_TREE, "MODULE_TREE"); 10 fn module_tree(key: ()) -> Arc<ModuleTreeDescriptor> {
15} 11 type ModuleTreeQuery;
16
17pub(crate) fn module_tree(ctx: QueryCtx) -> Arc<ModuleTreeDescriptor> {
18 ctx.get(MODULE_TREE, ())
19}
20
21const MODULE_DESCR: Query<FileId, ModuleDescriptor> = Query(30, |ctx, &file_id| {
22 let file = file_syntax(ctx, file_id);
23 ModuleDescriptor::new(file.ast())
24});
25
26const MODULE_TREE: Query<(), ModuleTreeDescriptor> = Query(31, |ctx, _| {
27 let file_set = file_set(ctx);
28 let mut files = Vec::new();
29 for &file_id in file_set.0.iter() {
30 let module_descr = ctx.get(MODULE_DESCR, file_id);
31 files.push((file_id, module_descr));
32 }
33 ModuleTreeDescriptor::new(files.iter().map(|(file_id, descr)| (*file_id, &**descr)), &file_set.1)
34});
35
36#[cfg(test)]
37mod tests {
38 use std::collections::HashMap;
39 use im;
40 use relative_path::{RelativePath, RelativePathBuf};
41 use crate::{
42 db::{Db},
43 imp::FileResolverImp,
44 FileId, FileResolver,
45 };
46 use super::*;
47
48 #[derive(Debug)]
49 struct FileMap(im::HashMap<FileId, RelativePathBuf>);
50
51 impl FileResolver for FileMap {
52 fn file_stem(&self, file_id: FileId) -> String {
53 self.0[&file_id].file_stem().unwrap().to_string()
54 } 12 }
55 fn resolve(&self, file_id: FileId, rel: &RelativePath) -> Option<FileId> { 13 fn module_descriptor(file_id: FileId) -> Arc<ModuleDescriptor> {
56 let path = self.0[&file_id].join(rel).normalize(); 14 type ModuleDescriptorQuery;
57 self.0.iter()
58 .filter_map(|&(id, ref p)| Some(id).filter(|_| p == &path))
59 .next()
60 } 15 }
61 } 16 }
17}
62 18
63 struct Fixture {
64 next_file_id: u32,
65 fm: im::HashMap<FileId, RelativePathBuf>,
66 db: Db,
67 }
68
69 impl Fixture {
70 fn new() -> Fixture {
71 Fixture {
72 next_file_id: 1,
73 fm: im::HashMap::new(),
74 db: Db::new(),
75 }
76 }
77 fn add_file(&mut self, path: &str, text: &str) -> FileId {
78 assert!(path.starts_with("/"));
79 let file_id = FileId(self.next_file_id);
80 self.next_file_id += 1;
81 self.fm.insert(file_id, RelativePathBuf::from(&path[1..]));
82 let mut new_state = self.db.state().clone();
83 new_state.file_map.insert(file_id, Arc::new(text.to_string()));
84 new_state.file_resolver = FileResolverImp::new(
85 Arc::new(FileMap(self.fm.clone()))
86 );
87 self.db = self.db.with_changes(new_state, &[file_id], true);
88 file_id
89 }
90 fn remove_file(&mut self, file_id: FileId) {
91 self.fm.remove(&file_id);
92 let mut new_state = self.db.state().clone();
93 new_state.file_map.remove(&file_id);
94 new_state.file_resolver = FileResolverImp::new(
95 Arc::new(FileMap(self.fm.clone()))
96 );
97 self.db = self.db.with_changes(new_state, &[file_id], true);
98 }
99 fn change_file(&mut self, file_id: FileId, new_text: &str) {
100 let mut new_state = self.db.state().clone();
101 new_state.file_map.insert(file_id, Arc::new(new_text.to_string()));
102 self.db = self.db.with_changes(new_state, &[file_id], false);
103 }
104 fn check_parent_modules(
105 &self,
106 file_id: FileId,
107 expected: &[FileId],
108 queries: &[(&'static str, u64)]
109 ) {
110 let (tree, events) = self.db.trace_query(|ctx| module_tree(ctx));
111 let actual = tree.parent_modules(file_id)
112 .into_iter()
113 .map(|link| link.owner(&tree))
114 .collect::<Vec<_>>();
115 assert_eq!(actual.as_slice(), expected);
116 let mut counts = HashMap::new();
117 events.into_iter()
118 .for_each(|event| *counts.entry(event).or_insert(0) += 1);
119 for &(query_id, expected_count) in queries.iter() {
120 let actual_count = *counts.get(&query_id).unwrap_or(&0);
121 assert_eq!(
122 actual_count,
123 expected_count,
124 "counts for {} differ",
125 query_id,
126 )
127 }
128
129 }
130 }
131
132 #[test]
133 fn test_parent_module() {
134 let mut f = Fixture::new();
135 let foo = f.add_file("/foo.rs", "");
136 f.check_parent_modules(foo, &[], &[("MODULE_DESCR", 1)]);
137
138 let lib = f.add_file("/lib.rs", "mod foo;");
139 f.check_parent_modules(foo, &[lib], &[("MODULE_DESCR", 1)]);
140 f.check_parent_modules(foo, &[lib], &[("MODULE_DESCR", 0)]);
141
142 f.change_file(lib, "");
143 f.check_parent_modules(foo, &[], &[("MODULE_DESCR", 1)]);
144
145 f.change_file(lib, "mod foo;");
146 f.check_parent_modules(foo, &[lib], &[("MODULE_DESCR", 1)]);
147
148 f.change_file(lib, "mod bar;");
149 f.check_parent_modules(foo, &[], &[("MODULE_DESCR", 1)]);
150 19
151 f.change_file(lib, "mod foo;"); 20fn module_descriptor(db: &impl ModulesDatabase, file_id: FileId) -> Arc<ModuleDescriptor> {
152 f.check_parent_modules(foo, &[lib], &[("MODULE_DESCR", 1)]); 21 let file = db.file_syntax(file_id);
22 Arc::new(ModuleDescriptor::new(file.ast()))
23}
153 24
154 f.remove_file(lib); 25fn module_tree(db: &impl ModulesDatabase, (): ()) -> Arc<ModuleTreeDescriptor> {
155 f.check_parent_modules(foo, &[], &[("MODULE_DESCR", 0)]); 26 let file_set = db.file_set(());
27 let mut files = Vec::new();
28 for &file_id in file_set.files.iter() {
29 let module_descr = db.module_descriptor(file_id);
30 files.push((file_id, module_descr));
156 } 31 }
32 let res = ModuleTreeDescriptor::new(files.iter().map(|(file_id, descr)| (*file_id, &**descr)), &file_set.resolver);
33 Arc::new(res)
157} 34}
diff --git a/crates/ra_analysis/src/queries.rs b/crates/ra_analysis/src/queries.rs
deleted file mode 100644
index 613bf1e61..000000000
--- a/crates/ra_analysis/src/queries.rs
+++ /dev/null
@@ -1,39 +0,0 @@
1use std::sync::Arc;
2use ra_syntax::File;
3use ra_editor::LineIndex;
4use crate::{
5 FileId,
6 db::{Query, QueryCtx, QueryRegistry},
7 symbol_index::SymbolIndex,
8};
9
10pub(crate) use crate::db::{file_text, file_set};
11
12pub(crate) fn file_syntax(ctx: QueryCtx, file_id: FileId) -> File {
13 (&*ctx.get(FILE_SYNTAX, file_id)).clone()
14}
15pub(crate) fn file_lines(ctx: QueryCtx, file_id: FileId) -> Arc<LineIndex> {
16 ctx.get(FILE_LINES, file_id)
17}
18pub(crate) fn file_symbols(ctx: QueryCtx, file_id: FileId) -> Arc<SymbolIndex> {
19 ctx.get(FILE_SYMBOLS, file_id)
20}
21
22const FILE_SYNTAX: Query<FileId, File> = Query(16, |ctx, file_id: &FileId| {
23 let text = file_text(ctx, *file_id);
24 File::parse(&*text)
25});
26const FILE_LINES: Query<FileId, LineIndex> = Query(17, |ctx, file_id: &FileId| {
27 let text = file_text(ctx, *file_id);
28 LineIndex::new(&*text)
29});
30const FILE_SYMBOLS: Query<FileId, SymbolIndex> = Query(18, |ctx, file_id: &FileId| {
31 let syntax = file_syntax(ctx, *file_id);
32 SymbolIndex::for_file(*file_id, syntax)
33});
34
35pub(crate) fn register_queries(reg: &mut QueryRegistry) {
36 reg.add(FILE_SYNTAX, "FILE_SYNTAX");
37 reg.add(FILE_LINES, "FILE_LINES");
38 reg.add(FILE_SYMBOLS, "FILE_SYMBOLS");
39}
diff --git a/crates/ra_analysis/src/roots.rs b/crates/ra_analysis/src/roots.rs
index 208acc4c2..76bcecd38 100644
--- a/crates/ra_analysis/src/roots.rs
+++ b/crates/ra_analysis/src/roots.rs
@@ -5,7 +5,8 @@ use std::{
5 5
6use once_cell::sync::OnceCell; 6use once_cell::sync::OnceCell;
7use rayon::prelude::*; 7use rayon::prelude::*;
8use rustc_hash::FxHashMap; 8use salsa::Database;
9use rustc_hash::{FxHashMap, FxHashSet};
9use ra_editor::LineIndex; 10use ra_editor::LineIndex;
10use ra_syntax::File; 11use ra_syntax::File;
11 12
@@ -14,7 +15,8 @@ use crate::{
14 imp::FileResolverImp, 15 imp::FileResolverImp,
15 symbol_index::SymbolIndex, 16 symbol_index::SymbolIndex,
16 descriptors::{ModuleDescriptor, ModuleTreeDescriptor}, 17 descriptors::{ModuleDescriptor, ModuleTreeDescriptor},
17 db::Db, 18 db::{self, FilesDatabase, SyntaxDatabase},
19 module_map::ModulesDatabase,
18}; 20};
19 21
20pub(crate) trait SourceRoot { 22pub(crate) trait SourceRoot {
@@ -25,62 +27,68 @@ pub(crate) trait SourceRoot {
25 fn symbols(&self, acc: &mut Vec<Arc<SymbolIndex>>); 27 fn symbols(&self, acc: &mut Vec<Arc<SymbolIndex>>);
26} 28}
27 29
28#[derive(Default, Debug)] 30#[derive(Default, Debug, Clone)]
29pub(crate) struct WritableSourceRoot { 31pub(crate) struct WritableSourceRoot {
30 db: Db, 32 db: db::RootDatabase,
31} 33}
32 34
33impl WritableSourceRoot { 35impl WritableSourceRoot {
34 pub fn apply_changes( 36 pub fn apply_changes(
35 &self, 37 &mut self,
36 changes: &mut dyn Iterator<Item=(FileId, Option<String>)>, 38 changes: &mut dyn Iterator<Item=(FileId, Option<String>)>,
37 file_resolver: Option<FileResolverImp>, 39 file_resolver: Option<FileResolverImp>,
38 ) -> WritableSourceRoot { 40 ) {
39 let resolver_changed = file_resolver.is_some(); 41 let mut changed = FxHashSet::default();
40 let mut changed_files = Vec::new(); 42 let mut removed = FxHashSet::default();
41 let mut new_state = self.db.state().clone();
42
43 for (file_id, text) in changes { 43 for (file_id, text) in changes {
44 changed_files.push(file_id);
45 match text { 44 match text {
46 Some(text) => {
47 new_state.file_map.insert(file_id, Arc::new(text));
48 },
49 None => { 45 None => {
50 new_state.file_map.remove(&file_id); 46 removed.insert(file_id);
47 }
48 Some(text) => {
49 self.db.query(db::FileTextQuery)
50 .set(file_id, Arc::new(text));
51 changed.insert(file_id);
51 } 52 }
52 } 53 }
53 } 54 }
54 if let Some(file_resolver) = file_resolver { 55 let file_set = self.db.file_set(());
55 new_state.file_resolver = file_resolver 56 let mut files: FxHashSet<FileId> = file_set
56 } 57 .files
57 WritableSourceRoot { 58 .clone();
58 db: self.db.with_changes(new_state, &changed_files, resolver_changed) 59 for file_id in removed {
60 files.remove(&file_id);
59 } 61 }
62 files.extend(changed);
63 let resolver = file_resolver.unwrap_or_else(|| file_set.resolver.clone());
64 self.db.query(db::FileSetQuery)
65 .set((), Arc::new(db::FileSet { files, resolver }));
60 } 66 }
61} 67}
62 68
63impl SourceRoot for WritableSourceRoot { 69impl SourceRoot for WritableSourceRoot {
64 fn module_tree(&self) -> Arc<ModuleTreeDescriptor> { 70 fn module_tree(&self) -> Arc<ModuleTreeDescriptor> {
65 self.db.make_query(crate::module_map::module_tree) 71 self.db.module_tree(())
66 } 72 }
67
68 fn contains(&self, file_id: FileId) -> bool { 73 fn contains(&self, file_id: FileId) -> bool {
69 self.db.state().file_map.contains_key(&file_id) 74 self.db.file_set(())
75 .files
76 .contains(&file_id)
70 } 77 }
71 fn lines(&self, file_id: FileId) -> Arc<LineIndex> { 78 fn lines(&self, file_id: FileId) -> Arc<LineIndex> {
72 self.db.make_query(|ctx| crate::queries::file_lines(ctx, file_id)) 79 self.db.file_lines(file_id)
73 } 80 }
74 fn syntax(&self, file_id: FileId) -> File { 81 fn syntax(&self, file_id: FileId) -> File {
75 self.db.make_query(|ctx| crate::queries::file_syntax(ctx, file_id)) 82 self.db.file_syntax(file_id)
76 } 83 }
77 fn symbols<'a>(&'a self, acc: &mut Vec<Arc<SymbolIndex>>) { 84 fn symbols<'a>(&'a self, acc: &mut Vec<Arc<SymbolIndex>>) {
78 self.db.make_query(|ctx| { 85 let db = &self.db;
79 let file_set = crate::queries::file_set(ctx); 86 let symbols = db.file_set(());
80 let syms = file_set.0.iter() 87 let symbols = symbols
81 .map(|file_id| crate::queries::file_symbols(ctx, *file_id)); 88 .files
82 acc.extend(syms); 89 .iter()
83 }); 90 .map(|&file_id| db.file_symbols(file_id));
91 acc.extend(symbols);
84 } 92 }
85} 93}
86 94
diff --git a/crates/ra_analysis/src/symbol_index.rs b/crates/ra_analysis/src/symbol_index.rs
index e5d83d365..54672fde4 100644
--- a/crates/ra_analysis/src/symbol_index.rs
+++ b/crates/ra_analysis/src/symbol_index.rs
@@ -17,6 +17,15 @@ pub(crate) struct SymbolIndex {
17 map: fst::Map, 17 map: fst::Map,
18} 18}
19 19
20impl PartialEq for SymbolIndex {
21 fn eq(&self, other: &SymbolIndex) -> bool {
22 self.symbols == other.symbols
23 }
24}
25
26impl Eq for SymbolIndex {
27}
28
20impl Hash for SymbolIndex { 29impl Hash for SymbolIndex {
21 fn hash<H: Hasher>(&self, hasher: &mut H) { 30 fn hash<H: Hasher>(&self, hasher: &mut H) {
22 self.symbols.hash(hasher) 31 self.symbols.hash(hasher)
diff --git a/crates/ra_analysis/tests/tests.rs b/crates/ra_analysis/tests/tests.rs
index 755640fb4..2d3679fa9 100644
--- a/crates/ra_analysis/tests/tests.rs
+++ b/crates/ra_analysis/tests/tests.rs
@@ -238,4 +238,4 @@ fn bar() {
238 assert_eq!(desc.params, vec!["&self".to_string(), "x".to_string()]); 238 assert_eq!(desc.params, vec!["&self".to_string(), "x".to_string()]);
239 assert_eq!(desc.ret_type, None); 239 assert_eq!(desc.ret_type, None);
240 assert_eq!(param, Some(1)); 240 assert_eq!(param, Some(1));
241} \ No newline at end of file 241}
diff --git a/crates/ra_editor/src/line_index.rs b/crates/ra_editor/src/line_index.rs
index 6ccfdbd83..95d64b8a8 100644
--- a/crates/ra_editor/src/line_index.rs
+++ b/crates/ra_editor/src/line_index.rs
@@ -1,7 +1,7 @@
1use superslice::Ext; 1use superslice::Ext;
2use crate::TextUnit; 2use crate::TextUnit;
3 3
4#[derive(Clone, Debug, Hash)] 4#[derive(Clone, Debug, Hash, PartialEq, Eq)]
5pub struct LineIndex { 5pub struct LineIndex {
6 newlines: Vec<TextUnit>, 6 newlines: Vec<TextUnit>,
7} 7}
diff --git a/crates/ra_editor/src/symbols.rs b/crates/ra_editor/src/symbols.rs
index 6211ed547..d9e4b2df7 100644
--- a/crates/ra_editor/src/symbols.rs
+++ b/crates/ra_editor/src/symbols.rs
@@ -17,7 +17,7 @@ pub struct StructureNode {
17 pub kind: SyntaxKind, 17 pub kind: SyntaxKind,
18} 18}
19 19
20#[derive(Debug, Clone, Hash)] 20#[derive(Debug, Clone, PartialEq, Eq, Hash)]
21pub struct FileSymbol { 21pub struct FileSymbol {
22 pub name: SmolStr, 22 pub name: SmolStr,
23 pub node_range: TextRange, 23 pub node_range: TextRange,
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index 1fe78f3d0..5acb39b60 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -478,7 +478,7 @@ pub fn handle_code_action(
478} 478}
479 479
480pub fn publish_diagnostics( 480pub fn publish_diagnostics(
481 world: ServerWorld, 481 world: &ServerWorld,
482 file_id: FileId, 482 file_id: FileId,
483) -> Result<req::PublishDiagnosticsParams> { 483) -> Result<req::PublishDiagnosticsParams> {
484 let uri = world.file_id_to_uri(file_id)?; 484 let uri = world.file_id_to_uri(file_id)?;
@@ -497,7 +497,7 @@ pub fn publish_diagnostics(
497} 497}
498 498
499pub fn publish_decorations( 499pub fn publish_decorations(
500 world: ServerWorld, 500 world: &ServerWorld,
501 file_id: FileId, 501 file_id: FileId,
502) -> Result<req::PublishDecorationsParams> { 502) -> Result<req::PublishDecorationsParams> {
503 let uri = world.file_id_to_uri(file_id)?; 503 let uri = world.file_id_to_uri(file_id)?;
diff --git a/crates/ra_lsp_server/src/main_loop/mod.rs b/crates/ra_lsp_server/src/main_loop/mod.rs
index 773501efe..cf2477cb5 100644
--- a/crates/ra_lsp_server/src/main_loop/mod.rs
+++ b/crates/ra_lsp_server/src/main_loop/mod.rs
@@ -391,7 +391,7 @@ fn update_file_notifications_on_threadpool(
391) { 391) {
392 pool.spawn(move || { 392 pool.spawn(move || {
393 for file_id in subscriptions { 393 for file_id in subscriptions {
394 match handlers::publish_diagnostics(world.clone(), file_id) { 394 match handlers::publish_diagnostics(&world, file_id) {
395 Err(e) => { 395 Err(e) => {
396 error!("failed to compute diagnostics: {:?}", e) 396 error!("failed to compute diagnostics: {:?}", e)
397 } 397 }
@@ -400,7 +400,7 @@ fn update_file_notifications_on_threadpool(
400 sender.send(Task::Notify(not)); 400 sender.send(Task::Notify(not));
401 } 401 }
402 } 402 }
403 match handlers::publish_decorations(world.clone(), file_id) { 403 match handlers::publish_decorations(&world, file_id) {
404 Err(e) => { 404 Err(e) => {
405 error!("failed to compute decorations: {:?}", e) 405 error!("failed to compute decorations: {:?}", e)
406 } 406 }
diff --git a/crates/ra_lsp_server/src/server_world.rs b/crates/ra_lsp_server/src/server_world.rs
index c931afa02..9b3013ae8 100644
--- a/crates/ra_lsp_server/src/server_world.rs
+++ b/crates/ra_lsp_server/src/server_world.rs
@@ -23,7 +23,6 @@ pub struct ServerWorldState {
23 pub mem_map: FxHashMap<FileId, Option<String>>, 23 pub mem_map: FxHashMap<FileId, Option<String>>,
24} 24}
25 25
26#[derive(Clone)]
27pub struct ServerWorld { 26pub struct ServerWorld {
28 pub workspaces: Arc<Vec<CargoWorkspace>>, 27 pub workspaces: Arc<Vec<CargoWorkspace>>,
29 pub analysis: Analysis, 28 pub analysis: Analysis,
diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs
index 5bb54aba1..7eba5ee61 100644
--- a/crates/ra_syntax/src/lib.rs
+++ b/crates/ra_syntax/src/lib.rs
@@ -59,7 +59,7 @@ use crate::{
59 yellow::{GreenNode}, 59 yellow::{GreenNode},
60}; 60};
61 61
62#[derive(Clone, Debug, Hash)] 62#[derive(Clone, Debug, Hash, PartialEq, Eq)]
63pub struct File { 63pub struct File {
64 root: SyntaxNode 64 root: SyntaxNode
65} 65}
diff --git a/crates/salsa/Cargo.toml b/crates/salsa/Cargo.toml
deleted file mode 100644
index 9eb83234f..000000000
--- a/crates/salsa/Cargo.toml
+++ /dev/null
@@ -1,8 +0,0 @@
1[package]
2name = "salsa"
3version = "0.1.0"
4authors = ["Aleksey Kladov <[email protected]>"]
5
6[dependencies]
7parking_lot = "0.6.3"
8im = "12.0.0"
diff --git a/crates/salsa/src/lib.rs b/crates/salsa/src/lib.rs
deleted file mode 100644
index 35deed374..000000000
--- a/crates/salsa/src/lib.rs
+++ /dev/null
@@ -1,293 +0,0 @@
1extern crate im;
2extern crate parking_lot;
3
4use std::{
5 sync::Arc,
6 collections::{HashSet, HashMap},
7 cell::RefCell,
8};
9use parking_lot::Mutex;
10
11pub type GroundQueryFn<T, D> = Box<Fn(&T, &D) -> (D, OutputFingerprint) + Send + Sync + 'static>;
12pub type QueryFn<T, D> = Box<Fn(&QueryCtx<T, D>, &D) -> (D, OutputFingerprint) + Send + Sync + 'static>;
13
14#[derive(Debug)]
15pub struct Db<T, D> {
16 db: Arc<DbState<T, D>>,
17 query_config: Arc<QueryConfig<T, D>>,
18}
19
20pub struct QueryConfig<T, D> {
21 ground_fn: HashMap<QueryTypeId, GroundQueryFn<T, D>>,
22 query_fn: HashMap<QueryTypeId, QueryFn<T, D>>,
23}
24
25impl<T, D> ::std::fmt::Debug for QueryConfig<T, D> {
26 fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
27 ::std::fmt::Display::fmt("QueryConfig { ... }", f)
28 }
29}
30
31#[derive(Debug)]
32struct DbState<T, D> {
33 ground_data: T,
34 gen: Gen,
35 graph: Mutex<im::HashMap<QueryId, (Gen, Arc<QueryRecord<D>>)>>,
36}
37
38#[derive(Debug)]
39struct QueryRecord<D> {
40 params: D,
41 output: D,
42 output_fingerprint: OutputFingerprint,
43 deps: Vec<(QueryId, OutputFingerprint)>,
44}
45
46impl<T, D> DbState<T, D> {
47 fn record(
48 &self,
49 query_id: QueryId,
50 params: D,
51 output: D,
52 output_fingerprint: OutputFingerprint,
53 deps: Vec<(QueryId, OutputFingerprint)>,
54 ) {
55 let gen = self.gen;
56 let record = QueryRecord {
57 params,
58 output,
59 output_fingerprint,
60 deps,
61 };
62 self.graph.lock().insert(query_id, (gen, Arc::new(record)));
63 }
64}
65
66impl<T, D> QueryConfig<T, D> {
67 pub fn new() -> Self {
68 QueryConfig {
69 ground_fn: HashMap::new(),
70 query_fn: HashMap::new(),
71 }
72 }
73 pub fn with_ground_query(
74 mut self,
75 query_type: QueryTypeId,
76 query_fn: GroundQueryFn<T, D>
77 ) -> Self {
78 let prev = self.ground_fn.insert(query_type, query_fn);
79 assert!(prev.is_none());
80 self
81 }
82 pub fn with_query(
83 mut self,
84 query_type: QueryTypeId,
85 query_fn: QueryFn<T, D>,
86 ) -> Self {
87 let prev = self.query_fn.insert(query_type, query_fn);
88 assert!(prev.is_none());
89 self
90 }
91}
92
93pub struct QueryCtx<T, D> {
94 db: Arc<DbState<T, D>>,
95 query_config: Arc<QueryConfig<T, D>>,
96 stack: RefCell<Vec<Vec<(QueryId, OutputFingerprint)>>>,
97 executed: RefCell<Vec<QueryTypeId>>,
98}
99
100impl<T, D> QueryCtx<T, D>
101where
102 D: Clone
103{
104 fn new(db: &Db<T, D>) -> QueryCtx<T, D> {
105 QueryCtx {
106 db: Arc::clone(&db.db),
107 query_config: Arc::clone(&db.query_config),
108 stack: RefCell::new(vec![Vec::new()]),
109 executed: RefCell::new(Vec::new()),
110 }
111 }
112 pub fn get(
113 &self,
114 query_id: QueryId,
115 params: D,
116 ) -> D {
117 let (res, output_fingerprint) = self.get_inner(query_id, params);
118 self.record_dep(query_id, output_fingerprint);
119 res
120 }
121 pub fn trace(&self) -> Vec<QueryTypeId> {
122 ::std::mem::replace(&mut *self.executed.borrow_mut(), Vec::new())
123 }
124
125 fn get_inner(
126 &self,
127 query_id: QueryId,
128 params: D,
129 ) -> (D, OutputFingerprint) {
130 let (gen, record) = {
131 let guard = self.db.graph.lock();
132 match guard.get(&query_id).map(|it| it.clone()){
133 None => {
134 drop(guard);
135 return self.force(query_id, params);
136 },
137 Some(it) => it,
138 }
139 };
140 if gen == self.db.gen {
141 return (record.output.clone(), record.output_fingerprint)
142 }
143 if self.query_config.ground_fn.contains_key(&query_id.0) {
144 let (invalidated, record) = {
145 let guard = self.db.graph.lock();
146 let (gen, ref record) = guard[&query_id];
147 (gen == INVALIDATED, record.clone())
148 };
149 if invalidated {
150 return self.force(query_id, params);
151 } else {
152 return (record.output.clone(), record.output_fingerprint);
153 }
154 }
155 for (dep_query_id, prev_fingerprint) in record.deps.iter().cloned() {
156 let dep_params: D = {
157 let guard = self.db.graph.lock();
158 guard[&dep_query_id]
159 .1
160 .params
161 .clone()
162 };
163 if prev_fingerprint != self.get_inner(dep_query_id, dep_params).1 {
164 return self.force(query_id, params)
165 }
166 }
167 let gen = self.db.gen;
168 {
169 let mut guard = self.db.graph.lock();
170 guard[&query_id].0 = gen;
171 }
172 (record.output.clone(), record.output_fingerprint)
173 }
174 fn force(
175 &self,
176 query_id: QueryId,
177 params: D,
178 ) -> (D, OutputFingerprint) {
179 self.executed.borrow_mut().push(query_id.0);
180 self.stack.borrow_mut().push(Vec::new());
181
182 let (res, output_fingerprint) = if let Some(f) = self.query_config.ground_fn.get(&query_id.0) {
183 f(&self.db.ground_data, &params)
184 } else if let Some(f) = self.query_config.query_fn.get(&query_id.0) {
185 f(self, &params)
186 } else {
187 panic!("unknown query type: {:?}", query_id.0);
188 };
189
190 let res: D = res.into();
191
192 let deps = self.stack.borrow_mut().pop().unwrap();
193 self.db.record(query_id, params, res.clone(), output_fingerprint, deps);
194 (res, output_fingerprint)
195 }
196 fn record_dep(
197 &self,
198 query_id: QueryId,
199 output_fingerprint: OutputFingerprint,
200 ) -> () {
201 let mut stack = self.stack.borrow_mut();
202 let deps = stack.last_mut().unwrap();
203 deps.push((query_id, output_fingerprint))
204 }
205}
206
207pub struct Invalidations {
208 types: HashSet<QueryTypeId>,
209 ids: Vec<QueryId>,
210}
211
212impl Invalidations {
213 pub fn new() -> Invalidations {
214 Invalidations {
215 types: HashSet::new(),
216 ids: Vec::new(),
217 }
218 }
219 pub fn invalidate(
220 &mut self,
221 query_type: QueryTypeId,
222 params: impl Iterator<Item=InputFingerprint>,
223 ) {
224 self.types.insert(query_type);
225 self.ids.extend(params.map(|it| QueryId(query_type, it)))
226 }
227}
228
229impl<T, D> Db<T, D>
230where
231 D: Clone
232{
233 pub fn new(query_config: QueryConfig<T, D>, ground_data: T) -> Db<T, D> {
234 Db {
235 db: Arc::new(DbState { ground_data, gen: Gen(0), graph: Default::default() }),
236 query_config: Arc::new(query_config),
237 }
238 }
239 pub fn ground_data(&self) -> &T {
240 &self.db.ground_data
241 }
242 pub fn with_ground_data(
243 &self,
244 ground_data: T,
245 invalidations: Invalidations,
246 ) -> Db<T, D> {
247 for id in self.query_config.ground_fn.keys() {
248 assert!(
249 invalidations.types.contains(id),
250 "all ground queries must be invalidated"
251 );
252 }
253
254 let gen = Gen(self.db.gen.0 + 1);
255 let mut graph = self.db.graph.lock().clone();
256 for id in invalidations.ids {
257 if let Some((gen, _)) = graph.get_mut(&id) {
258 *gen = INVALIDATED;
259 }
260 }
261 let graph = Mutex::new(graph);
262 Db {
263 db: Arc::new(DbState { ground_data, gen, graph }),
264 query_config: Arc::clone(&self.query_config)
265 }
266 }
267 pub fn query_ctx(&self) -> QueryCtx<T, D> {
268 QueryCtx::new(self)
269 }
270 pub fn get(
271 &self,
272 query_id: QueryId,
273 params: D,
274 ) -> (D, Vec<QueryTypeId>) {
275 let ctx = self.query_ctx();
276 let res = ctx.get(query_id, params.into());
277 let executed = ::std::mem::replace(&mut *ctx.executed.borrow_mut(), Vec::new());
278 (res, executed)
279 }
280}
281
282#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
283struct Gen(u64);
284const INVALIDATED: Gen = Gen(!0);
285#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
286pub struct InputFingerprint(pub u64);
287#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
288pub struct OutputFingerprint(pub u64);
289#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
290pub struct QueryTypeId(pub u16);
291#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
292pub struct QueryId(pub QueryTypeId, pub InputFingerprint);
293
diff --git a/crates/salsa/tests/integration.rs b/crates/salsa/tests/integration.rs
deleted file mode 100644
index aed9219be..000000000
--- a/crates/salsa/tests/integration.rs
+++ /dev/null
@@ -1,170 +0,0 @@
1extern crate salsa;
2use std::{
3 iter::once,
4 sync::Arc,
5 collections::hash_map::{HashMap, DefaultHasher},
6 any::Any,
7 hash::{Hash, Hasher},
8};
9
10type State = HashMap<u32, String>;
11type Data = Arc<Any + Send + Sync + 'static>;
12const GET_TEXT: salsa::QueryTypeId = salsa::QueryTypeId(1);
13const GET_FILES: salsa::QueryTypeId = salsa::QueryTypeId(2);
14const FILE_NEWLINES: salsa::QueryTypeId = salsa::QueryTypeId(3);
15const TOTAL_NEWLINES: salsa::QueryTypeId = salsa::QueryTypeId(4);
16
17fn mk_ground_query<T, R>(
18 state: &State,
19 params: &Data,
20 f: fn(&State, &T) -> R,
21) -> (Data, salsa::OutputFingerprint)
22where
23 T: 'static,
24 R: Hash + Send + Sync + 'static,
25{
26 let params = params.downcast_ref().unwrap();
27 let result = f(state, params);
28 let fingerprint = o_print(&result);
29 (Arc::new(result), fingerprint)
30}
31
32fn get<T, R>(db: &salsa::Db<State, Data>, query_type: salsa::QueryTypeId, param: T) -> (Arc<R>, Vec<salsa::QueryTypeId>)
33where
34 T: Hash + Send + Sync + 'static,
35 R: Send + Sync + 'static,
36{
37 let i_print = i_print(&param);
38 let param = Arc::new(param);
39 let (res, trace) = db.get(salsa::QueryId(query_type, i_print), param);
40 (res.downcast().unwrap(), trace)
41}
42
43struct QueryCtx<'a>(&'a salsa::QueryCtx<State, Data>);
44
45impl<'a> QueryCtx<'a> {
46 fn get_text(&self, id: u32) -> Arc<String> {
47 let i_print = i_print(&id);
48 let text = self.0.get(salsa::QueryId(GET_TEXT, i_print), Arc::new(id));
49 text.downcast().unwrap()
50 }
51 fn get_files(&self) -> Arc<Vec<u32>> {
52 let i_print = i_print(&());
53 let files = self.0.get(salsa::QueryId(GET_FILES, i_print), Arc::new(()));
54 let res = files.downcast().unwrap();
55 res
56 }
57 fn get_n_lines(&self, id: u32) -> usize {
58 let i_print = i_print(&id);
59 let n_lines = self.0.get(salsa::QueryId(FILE_NEWLINES, i_print), Arc::new(id));
60 *n_lines.downcast().unwrap()
61 }
62}
63
64fn mk_query<T, R>(
65 query_ctx: &salsa::QueryCtx<State, Data>,
66 params: &Data,
67 f: fn(QueryCtx, &T) -> R,
68) -> (Data, salsa::OutputFingerprint)
69where
70 T: 'static,
71 R: Hash + Send + Sync + 'static,
72{
73 let params: &T = params.downcast_ref().unwrap();
74 let query_ctx = QueryCtx(query_ctx);
75 let result = f(query_ctx, params);
76 let fingerprint = o_print(&result);
77 (Arc::new(result), fingerprint)
78}
79
80fn mk_queries() -> salsa::QueryConfig<State, Data> {
81 salsa::QueryConfig::<State, Data>::new()
82 .with_ground_query(GET_TEXT, Box::new(|state, id| {
83 mk_ground_query::<u32, String>(state, id, |state, id| state[id].clone())
84 }))
85 .with_ground_query(GET_FILES, Box::new(|state, id| {
86 mk_ground_query::<(), Vec<u32>>(state, id, |state, &()| state.keys().cloned().collect())
87 }))
88 .with_query(FILE_NEWLINES, Box::new(|query_ctx, id| {
89 mk_query(query_ctx, id, |query_ctx, &id| {
90 let text = query_ctx.get_text(id);
91 text.lines().count()
92 })
93 }))
94 .with_query(TOTAL_NEWLINES, Box::new(|query_ctx, id| {
95 mk_query(query_ctx, id, |query_ctx, &()| {
96 let mut total = 0;
97 for &id in query_ctx.get_files().iter() {
98 total += query_ctx.get_n_lines(id)
99 }
100 total
101 })
102 }))
103}
104
105#[test]
106fn test_number_of_lines() {
107 let mut state = State::new();
108 let db = salsa::Db::new(mk_queries(), state.clone());
109 let (newlines, trace) = get::<(), usize>(&db, TOTAL_NEWLINES, ());
110 assert_eq!(*newlines, 0);
111 assert_eq!(trace.len(), 2);
112 let (newlines, trace) = get::<(), usize>(&db, TOTAL_NEWLINES, ());
113 assert_eq!(*newlines, 0);
114 assert_eq!(trace.len(), 0);
115
116 state.insert(1, "hello\nworld".to_string());
117 let mut inv = salsa::Invalidations::new();
118 inv.invalidate(GET_TEXT, once(i_print(&1u32)));
119 inv.invalidate(GET_FILES, once(i_print(&())));
120 let db = db.with_ground_data(state.clone(), inv);
121 let (newlines, trace) = get::<(), usize>(&db, TOTAL_NEWLINES, ());
122 assert_eq!(*newlines, 2);
123 assert_eq!(trace.len(), 4);
124
125 state.insert(2, "spam\neggs".to_string());
126 let mut inv = salsa::Invalidations::new();
127 inv.invalidate(GET_TEXT, once(i_print(&2u32)));
128 inv.invalidate(GET_FILES, once(i_print(&())));
129 let db = db.with_ground_data(state.clone(), inv);
130 let (newlines, trace) = get::<(), usize>(&db, TOTAL_NEWLINES, ());
131 assert_eq!(*newlines, 4);
132 assert_eq!(trace.len(), 4);
133
134 let mut invs = vec![];
135 for i in 0..10 {
136 let id = i + 10;
137 invs.push(i_print(&id));
138 state.insert(id, "spam".to_string());
139 }
140 let mut inv = salsa::Invalidations::new();
141 inv.invalidate(GET_TEXT, invs.into_iter());
142 inv.invalidate(GET_FILES, once(i_print(&())));
143 let db = db.with_ground_data(state.clone(), inv);
144 let (newlines, trace) = get::<(), usize>(&db, TOTAL_NEWLINES, ());
145 assert_eq!(*newlines, 14);
146 assert_eq!(trace.len(), 22);
147
148 state.insert(15, String::new());
149 let mut inv = salsa::Invalidations::new();
150 inv.invalidate(GET_TEXT, once(i_print(&15u32)));
151 inv.invalidate(GET_FILES, once(i_print(&())));
152 let db = db.with_ground_data(state.clone(), inv);
153 let (newlines, trace) = get::<(), usize>(&db, TOTAL_NEWLINES, ());
154 assert_eq!(*newlines, 13);
155 assert_eq!(trace.len(), 4);
156}
157
158fn o_print<T: Hash>(x: &T) -> salsa::OutputFingerprint {
159 let mut hasher = DefaultHasher::new();
160 x.hash(&mut hasher);
161 let hash = hasher.finish();
162 salsa::OutputFingerprint(hash)
163}
164
165fn i_print<T: Hash>(x: &T) -> salsa::InputFingerprint {
166 let mut hasher = DefaultHasher::new();
167 x.hash(&mut hasher);
168 let hash = hasher.finish();
169 salsa::InputFingerprint(hash)
170}