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