1pub mod cached_queries;
2mod salsa_ids;
3
4use std::{fmt::Debug, path::PathBuf, sync::Arc};
5
6pub use cached_queries::CachedQueries;
7use faststr::FastStr;
8use rustc_hash::{FxHashMap, FxHashSet};
9pub use salsa_ids::{IntoSalsa, SalsaDefId, SalsaFileId, SalsaTyKind};
10
11use crate::{
12 middle::{
13 context::{CrateId, DefLocation},
14 ty::{CodegenTy, TyKind},
15 },
16 rir::{self, File, Item, Node},
17 symbol::{DefId, FileId},
18 tags::{TagId, Tags},
19};
20
21pub type ItemPath = Arc<Vec<FastStr>>;
22pub type TypeGraph = crate::middle::type_graph::TypeGraph;
23pub type WorkspaceGraph = crate::middle::workspace_graph::WorkspaceGraph;
24
25fn empty_type_graph() -> TypeGraph {
26 TypeGraph::from_items(std::iter::empty::<(DefId, Arc<Item>)>())
27}
28
29fn empty_workspace_graph() -> WorkspaceGraph {
30 WorkspaceGraph::from_items(std::iter::empty::<(DefId, Arc<Item>)>())
31}
32
33#[salsa::db]
35#[derive(Clone)]
36pub struct RootDatabase {
37 storage: salsa::Storage<Self>,
38 nodes: Arc<FxHashMap<DefId, rir::Node>>,
40 files: Arc<FxHashMap<FileId, Arc<rir::File>>>,
41 file_ids_map: Arc<FxHashMap<Arc<PathBuf>, FileId>>,
42 type_graph: Arc<TypeGraph>,
43 tags_map: Arc<FxHashMap<TagId, Arc<Tags>>>,
44 input_files: Arc<Vec<FileId>>,
45 args: Arc<FxHashSet<DefId>>,
46 workspace_graph: Arc<WorkspaceGraph>,
47 file_paths: Arc<FxHashMap<FileId, Arc<PathBuf>>>,
48}
49
50impl Default for RootDatabase {
51 fn default() -> Self {
52 RootDatabase {
53 storage: salsa::Storage::new(None),
54 nodes: Arc::new(FxHashMap::default()),
55 files: Arc::new(FxHashMap::default()),
56 file_ids_map: Arc::new(FxHashMap::default()),
57 type_graph: Arc::new(empty_type_graph()),
58 tags_map: Arc::new(FxHashMap::default()),
59 input_files: Arc::new(Vec::new()),
60 args: Arc::new(FxHashSet::default()),
61 workspace_graph: Arc::new(empty_workspace_graph()),
62 file_paths: Arc::new(FxHashMap::default()),
63 }
64 }
65}
66
67impl RootDatabase {
68 pub fn with_nodes(mut self, nodes: FxHashMap<DefId, rir::Node>) -> Self {
69 self.nodes = Arc::new(nodes);
70 self
71 }
72
73 pub fn with_workspace_graph(mut self, g: WorkspaceGraph) -> Self {
74 self.workspace_graph = Arc::new(g);
75 self
76 }
77
78 pub fn with_input_files(mut self, input_files: Vec<FileId>) -> Self {
79 self.input_files = Arc::new(input_files);
80 self
81 }
82
83 pub fn with_files(mut self, files: impl Iterator<Item = (FileId, Arc<File>)>) -> Self {
84 self.files = Arc::new(files.collect());
85 self
86 }
87
88 pub fn with_file_ids_map(mut self, file_ids_map: FxHashMap<Arc<PathBuf>, FileId>) -> Self {
89 self.file_ids_map = Arc::new(file_ids_map);
90 self
91 }
92
93 pub fn with_file_paths(mut self, file_paths: FxHashMap<FileId, Arc<PathBuf>>) -> Self {
94 self.file_paths = Arc::new(file_paths);
95 self
96 }
97
98 pub fn with_tags(
99 mut self,
100 tags_map: FxHashMap<TagId, Arc<Tags>>,
101 type_graph: TypeGraph,
102 ) -> Self {
103 self.tags_map = Arc::new(tags_map);
104 self.type_graph = Arc::new(type_graph);
105 self
106 }
107
108 pub fn with_args(mut self, args: FxHashSet<DefId>) -> Self {
109 self.args = Arc::new(args);
110 self
111 }
112
113 pub fn collect_def_ids(
114 &self,
115 input: &[DefId],
116 locations: Option<&FxHashMap<DefId, DefLocation>>,
117 ) -> FxHashMap<DefId, DefLocation> {
118 use crate::middle::ty::Visitor;
119 struct PathCollector<'a> {
120 map: &'a mut FxHashMap<DefId, DefLocation>,
121 visiting: &'a mut FxHashSet<DefId>,
122 db: &'a RootDatabase,
123 locations: Option<&'a FxHashMap<DefId, DefLocation>>,
124 }
125
126 impl crate::ty::Visitor for PathCollector<'_> {
127 fn visit_path(&mut self, path: &crate::rir::Path) {
128 collect(self.db, path.did, self.map, self.visiting, self.locations)
129 }
130 }
131
132 fn collect(
133 db: &RootDatabase,
134 def_id: DefId,
135 map: &mut FxHashMap<DefId, DefLocation>,
136 visiting: &mut FxHashSet<DefId>,
137 locations: Option<&FxHashMap<DefId, DefLocation>>,
138 ) {
139 if map.contains_key(&def_id) {
140 return;
141 }
142 if let Some(locations) = locations {
143 map.insert(def_id, locations[&def_id].clone());
144 } else if !matches!(&*db.item(def_id).unwrap(), rir::Item::Mod(_)) {
145 let file_id = db.node(def_id).unwrap().file_id;
146
147 if db.input_files().contains(&file_id) {
148 let type_graph = db.workspace_graph();
149 let node = type_graph.node_map[&def_id];
150 for from in type_graph
151 .graph
152 .neighbors_directed(node, petgraph::Direction::Incoming)
153 {
154 let from_def_id = type_graph.id_map[&from];
155 let from_file_id = db.node(from_def_id).unwrap().file_id;
156
157 if from_file_id != file_id {
158 map.insert(def_id, DefLocation::Dynamic);
159 break;
160 } else {
161 if !map.contains_key(&from_def_id) && !visiting.contains(&from_def_id) {
162 visiting.insert(from_def_id);
163 collect(db, from_def_id, map, visiting, locations);
164 visiting.remove(&from_def_id);
165 }
166 if map
167 .get(&from_def_id)
168 .map(|v| match v {
169 DefLocation::Fixed(_, _) => false,
170 DefLocation::Dynamic => true,
171 })
172 .unwrap_or(true)
173 {
174 map.insert(def_id, DefLocation::Dynamic);
175 break;
176 }
177 }
178 }
179 map.entry(def_id).or_insert_with(|| {
180 let file = db.file(file_id).unwrap();
181 DefLocation::Fixed(CrateId { main_file: file_id }, file.package.clone())
182 });
183 } else {
184 map.insert(def_id, DefLocation::Dynamic);
185 }
186 }
187
188 let node = db.node(def_id).unwrap();
189 tracing::trace!("collecting {:?}", node.expect_item().symbol_name());
190
191 node.related_nodes
192 .iter()
193 .for_each(|def_id| collect(db, *def_id, map, visiting, locations));
194
195 let item = node.expect_item();
196
197 match item {
198 rir::Item::Message(m) => m.fields.iter().for_each(|f| {
199 PathCollector {
200 db,
201 map,
202 visiting,
203 locations,
204 }
205 .visit(&f.ty)
206 }),
207 rir::Item::Enum(e) => e.variants.iter().flat_map(|v| &v.fields).for_each(|ty| {
208 PathCollector {
209 db,
210 map,
211 visiting,
212 locations,
213 }
214 .visit(ty)
215 }),
216 rir::Item::Service(s) => {
217 s.extend
218 .iter()
219 .for_each(|p| collect(db, p.did, map, visiting, locations));
220 s.methods
221 .iter()
222 .flat_map(|m| m.args.iter().map(|f| &f.ty).chain(std::iter::once(&m.ret)))
223 .for_each(|ty| {
224 PathCollector {
225 db,
226 map,
227 visiting,
228 locations,
229 }
230 .visit(ty)
231 });
232 }
233 rir::Item::NewType(n) => PathCollector {
234 db,
235 map,
236 visiting,
237 locations,
238 }
239 .visit(&n.ty),
240 rir::Item::Const(c) => {
241 PathCollector {
242 db,
243 map,
244 visiting,
245 locations,
246 }
247 .visit(&c.ty);
248 }
249 rir::Item::Mod(m) => {
250 m.items
251 .iter()
252 .for_each(|i| collect(db, *i, map, visiting, locations));
253 }
254 }
255 }
256 let mut map = FxHashMap::default();
257 let mut visiting = FxHashSet::default();
258
259 input.iter().for_each(|def_id| {
260 visiting.insert(*def_id);
261 collect(self, *def_id, &mut map, &mut visiting, locations);
262 visiting.remove(def_id);
263 });
264
265 map
266 }
267}
268
269#[salsa::db]
271impl salsa::Database for RootDatabase {}
272
273pub trait RirDatabase: salsa::Database {
275 fn nodes(&self) -> &Arc<FxHashMap<DefId, rir::Node>>;
277 fn files(&self) -> &Arc<FxHashMap<FileId, Arc<rir::File>>>;
278 fn file_ids_map(&self) -> &Arc<FxHashMap<Arc<PathBuf>, FileId>>;
279 fn file_paths(&self) -> &Arc<FxHashMap<FileId, Arc<PathBuf>>>;
280 fn type_graph(&self) -> &Arc<TypeGraph>;
281 fn tags_map(&self) -> &Arc<FxHashMap<TagId, Arc<Tags>>>;
282 fn input_files(&self) -> &Arc<Vec<FileId>>;
283 fn args(&self) -> &Arc<FxHashSet<DefId>>;
284 fn workspace_graph(&self) -> &Arc<WorkspaceGraph>;
285
286 fn node(&self, def_id: DefId) -> Option<Node>;
288
289 fn file(&self, file_id: FileId) -> Option<Arc<File>>;
290
291 fn file_id(&self, path: PathBuf) -> Option<FileId> {
292 self.file_ids_map().get(&path).cloned()
293 }
294
295 fn item(&self, def_id: DefId) -> Option<Arc<Item>>;
296
297 fn expect_item(&self, def_id: DefId) -> Arc<Item> {
298 self.item(def_id).unwrap()
299 }
300
301 fn codegen_item_ty(&self, ty: TyKind) -> CodegenTy;
302
303 fn codegen_const_ty(&self, ty: TyKind) -> CodegenTy;
304
305 fn codegen_ty(&self, def_id: DefId) -> CodegenTy;
306
307 fn service_methods(&self, def_id: DefId) -> Arc<[Arc<rir::Method>]>;
308
309 fn is_arg(&self, def_id: DefId) -> bool;
310}
311
312impl RirDatabase for RootDatabase {
314 fn nodes(&self) -> &Arc<FxHashMap<DefId, rir::Node>> {
315 &self.nodes
316 }
317
318 fn files(&self) -> &Arc<FxHashMap<FileId, Arc<rir::File>>> {
319 &self.files
320 }
321
322 fn file_ids_map(&self) -> &Arc<FxHashMap<Arc<PathBuf>, FileId>> {
323 &self.file_ids_map
324 }
325
326 fn file_paths(&self) -> &Arc<FxHashMap<FileId, Arc<PathBuf>>> {
327 &self.file_paths
328 }
329
330 fn type_graph(&self) -> &Arc<TypeGraph> {
331 &self.type_graph
332 }
333
334 fn tags_map(&self) -> &Arc<FxHashMap<TagId, Arc<Tags>>> {
335 &self.tags_map
336 }
337
338 fn input_files(&self) -> &Arc<Vec<FileId>> {
339 &self.input_files
340 }
341
342 fn args(&self) -> &Arc<FxHashSet<DefId>> {
343 &self.args
344 }
345
346 fn workspace_graph(&self) -> &Arc<WorkspaceGraph> {
347 &self.workspace_graph
348 }
349
350 fn node(&self, def_id: DefId) -> Option<Node> {
352 use cached_queries::{CachedQueries, get_node};
353 let salsa_id = def_id.into_salsa(self as &dyn CachedQueries);
354 get_node(self as &dyn CachedQueries, salsa_id)
355 }
356
357 fn file(&self, file_id: FileId) -> Option<Arc<File>> {
358 use cached_queries::{CachedQueries, get_file};
359 let salsa_id = file_id.into_salsa(self as &dyn CachedQueries);
360 get_file(self as &dyn CachedQueries, salsa_id)
361 }
362
363 fn item(&self, def_id: DefId) -> Option<Arc<Item>> {
364 use cached_queries::{CachedQueries, get_item};
365 let salsa_id = def_id.into_salsa(self as &dyn CachedQueries);
366 get_item(self as &dyn CachedQueries, salsa_id)
367 }
368
369 fn service_methods(&self, def_id: DefId) -> Arc<[Arc<rir::Method>]> {
370 use cached_queries::{CachedQueries, get_service_methods};
371 let salsa_id = def_id.into_salsa(self as &dyn CachedQueries);
372 get_service_methods(self as &dyn CachedQueries, salsa_id)
373 }
374
375 fn is_arg(&self, def_id: DefId) -> bool {
376 use cached_queries::{CachedQueries, is_arg_cached};
377 let salsa_id = def_id.into_salsa(self as &dyn CachedQueries);
378 is_arg_cached(self as &dyn CachedQueries, salsa_id)
379 }
380
381 fn codegen_item_ty(&self, ty: TyKind) -> CodegenTy {
382 use cached_queries::{CachedQueries, codegen_item_ty_cached};
383 let salsa_ty = ty.into_salsa(self as &dyn CachedQueries);
384 codegen_item_ty_cached(self as &dyn CachedQueries, salsa_ty)
385 }
386
387 fn codegen_const_ty(&self, ty: TyKind) -> CodegenTy {
388 use cached_queries::{CachedQueries, codegen_const_ty_cached};
389 let salsa_ty = ty.into_salsa(self as &dyn CachedQueries);
390 codegen_const_ty_cached(self as &dyn CachedQueries, salsa_ty)
391 }
392
393 fn codegen_ty(&self, def_id: DefId) -> CodegenTy {
394 use cached_queries::{CachedQueries, codegen_ty_cached};
395 let salsa_id = def_id.into_salsa(self as &dyn CachedQueries);
396 codegen_ty_cached(self as &dyn CachedQueries, salsa_id)
397 }
398}
399
400impl Debug for RootDatabase {
401 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
402 write!(f, "RootDatabase {{ .. }}")
403 }
404}