pilota_build/
db.rs

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] 宏
34#[salsa::db]
35#[derive(Clone)]
36pub struct RootDatabase {
37    storage: salsa::Storage<Self>,
38    // 直接在数据库中存储数据
39    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::Database trait
270#[salsa::db]
271impl salsa::Database for RootDatabase {}
272
273// 定义 RirDatabase trait
274pub trait RirDatabase: salsa::Database {
275    // 访问数据库中的数据
276    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    // 查询方法
287    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
312// 为 RootDatabase 实现 RirDatabase trait
313impl 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    // 使用缓存实现查询方法
351    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}