1use std::sync::Arc;
6
7use salsa::{Database, Update};
8
9use crate::db::input::SourceFile;
10use crate::db::parse::parsed_doc;
11use crate::file_index::FileIndex;
12
13#[derive(Clone)]
18pub struct IndexArc(pub Arc<FileIndex>);
19
20impl IndexArc {
21 pub fn get(&self) -> &FileIndex {
22 &self.0
23 }
24}
25
26unsafe impl Update for IndexArc {
30 unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
31 let old_ref = unsafe { &mut *old_pointer };
32 if Arc::ptr_eq(&old_ref.0, &new_value.0) {
33 false
34 } else {
35 *old_ref = new_value;
36 true
37 }
38 }
39}
40
41#[salsa::tracked(no_eq)]
44pub fn file_index(db: &dyn Database, file: SourceFile) -> IndexArc {
45 let doc = parsed_doc(db, file);
46 IndexArc(Arc::new(FileIndex::extract(doc.get())))
47}
48
49#[cfg(test)]
50mod tests {
51 use std::sync::Arc;
52 use std::sync::atomic::{AtomicUsize, Ordering};
53
54 use super::*;
55 use crate::db::analysis::AnalysisHost;
56 use crate::db::input::{FileId, SourceFile};
57 use crate::db::parse::parsed_doc;
58 use salsa::Setter;
59
60 static CALLS: AtomicUsize = AtomicUsize::new(0);
61
62 #[salsa::tracked]
65 fn counted_index_len(db: &dyn Database, file: SourceFile) -> usize {
66 CALLS.fetch_add(1, Ordering::SeqCst);
67 file_index(db, file).get().classes.len()
68 }
69
70 #[test]
71 fn file_index_extracts_class() {
72 let host = AnalysisHost::new();
73 let file = SourceFile::new(
74 host.db(),
75 FileId(0),
76 Arc::<str>::from("file:///t.php"),
77 Arc::<str>::from("<?php\nclass Foo { public function bar() {} }"),
78 None,
79 );
80 let idx = file_index(host.db(), file);
81 assert_eq!(idx.get().classes.len(), 1);
82 assert_eq!(idx.get().classes[0].name, "Foo");
83 }
84
85 #[test]
86 fn file_index_memoizes_and_shares_parse_with_downstream() {
87 CALLS.store(0, Ordering::SeqCst);
88 let mut host = AnalysisHost::new();
89 let file = SourceFile::new(
90 host.db(),
91 FileId(1),
92 Arc::<str>::from("file:///t.php"),
93 Arc::<str>::from("<?php\nclass A {} class B {}"),
94 None,
95 );
96
97 let _ = parsed_doc(host.db(), file);
99 let _ = counted_index_len(host.db(), file);
100 let _ = counted_index_len(host.db(), file);
101 assert_eq!(
102 CALLS.load(Ordering::SeqCst),
103 1,
104 "index query should memoize within a revision"
105 );
106
107 file.set_text(host.db_mut())
109 .to(Arc::<str>::from("<?php\nclass A {}"));
110 let _ = counted_index_len(host.db(), file);
111 assert_eq!(CALLS.load(Ordering::SeqCst), 2);
112
113 let idx = file_index(host.db(), file);
114 assert_eq!(idx.get().classes.len(), 1);
115 }
116}