1use std::sync::Arc;
6
7use salsa::Database;
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
26crate::impl_arc_update!(IndexArc);
30
31#[salsa::tracked(no_eq)]
34pub fn file_index(db: &dyn Database, file: SourceFile) -> IndexArc {
35 let doc = parsed_doc(db, file);
36 IndexArc(Arc::new(FileIndex::extract(doc.get())))
37}
38
39#[cfg(test)]
40mod tests {
41 use std::sync::Arc;
42 use std::sync::atomic::{AtomicUsize, Ordering};
43
44 use super::*;
45 use crate::db::analysis::AnalysisHost;
46 use crate::db::input::{FileId, SourceFile};
47 use crate::db::parse::parsed_doc;
48 use salsa::Setter;
49
50 static CALLS: AtomicUsize = AtomicUsize::new(0);
51
52 #[salsa::tracked]
55 fn counted_index_len(db: &dyn Database, file: SourceFile) -> usize {
56 CALLS.fetch_add(1, Ordering::SeqCst);
57 file_index(db, file).get().classes.len()
58 }
59
60 #[test]
61 fn file_index_extracts_class() {
62 let host = AnalysisHost::new();
63 let file = SourceFile::new(
64 host.db(),
65 FileId(0),
66 Arc::<str>::from("file:///t.php"),
67 Arc::<str>::from("<?php\nclass Foo { public function bar() {} }"),
68 None,
69 );
70 let idx = file_index(host.db(), file);
71 assert_eq!(idx.get().classes.len(), 1);
72 assert_eq!(idx.get().classes[0].name, "Foo".into());
73 }
74
75 #[test]
76 fn file_index_memoizes_and_shares_parse_with_downstream() {
77 CALLS.store(0, Ordering::SeqCst);
78 let mut host = AnalysisHost::new();
79 let file = SourceFile::new(
80 host.db(),
81 FileId(1),
82 Arc::<str>::from("file:///t.php"),
83 Arc::<str>::from("<?php\nclass A {} class B {}"),
84 None,
85 );
86
87 let _ = parsed_doc(host.db(), file);
89 let _ = counted_index_len(host.db(), file);
90 let _ = counted_index_len(host.db(), file);
91 assert_eq!(
92 CALLS.load(Ordering::SeqCst),
93 1,
94 "index query should memoize within a revision"
95 );
96
97 file.set_text(host.db_mut())
99 .to(Arc::<str>::from("<?php\nclass A {}"));
100 let _ = counted_index_len(host.db(), file);
101 assert_eq!(CALLS.load(Ordering::SeqCst), 2);
102
103 let idx = file_index(host.db(), file);
104 assert_eq!(idx.get().classes.len(), 1);
105 }
106}