use std::sync::Arc;
use salsa::Database;
use crate::ast::ParsedDoc;
use crate::db::input::SourceFile;
#[derive(Clone)]
pub struct ParsedArc(pub Arc<ParsedDoc>);
impl ParsedArc {
pub fn get(&self) -> &ParsedDoc {
&self.0
}
}
crate::impl_arc_update!(ParsedArc);
#[salsa::tracked(no_eq, lru = 2048)]
pub fn parsed_doc(db: &dyn Database, file: SourceFile<'_>) -> ParsedArc {
let doc = ParsedDoc::parse(file.text_input(db).text(db));
ParsedArc(Arc::new(doc))
}
#[salsa::tracked(lru = 2048)]
pub fn parse_error_count(db: &dyn Database, file: SourceFile<'_>) -> usize {
parsed_doc(db, file).get().errors.len()
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use super::*;
use crate::db::analysis::AnalysisHost;
use crate::db::input::{FileText, Workspace, workspace_files};
use salsa::Setter;
static CALLS: AtomicUsize = AtomicUsize::new(0);
#[salsa::tracked]
fn counted_parse(db: &dyn Database, file: SourceFile<'_>) -> usize {
CALLS.fetch_add(1, Ordering::SeqCst);
parsed_doc(db, file).get().errors.len()
}
fn make_ws(host: &AnalysisHost, uri: &str, ft: FileText) -> Workspace {
Workspace::new(
host.db(),
std::sync::Arc::from([(Arc::<str>::from(uri), ft)]),
mir_analyzer::PhpVersion::LATEST,
)
}
#[test]
fn parsed_doc_returns_ast() {
let host = AnalysisHost::new();
let ft = FileText::new(
host.db(),
Arc::<str>::from("<?php\nfunction greet() {}"),
None,
);
let ws = make_ws(&host, "file:///t.php", ft);
let files = workspace_files(host.db(), ws);
let arc = parsed_doc(host.db(), files[0]);
assert!(arc.get().errors.is_empty());
assert!(!arc.get().program().stmts.is_empty());
}
#[test]
fn parsed_doc_memoizes_and_invalidates() {
CALLS.store(0, Ordering::SeqCst);
let mut host = AnalysisHost::new();
let ft = FileText::new(host.db(), Arc::<str>::from("<?php\nfunction a() {}"), None);
let ws = make_ws(&host, "file:///t.php", ft);
{
let files = workspace_files(host.db(), ws);
let _ = counted_parse(host.db(), files[0]);
let _ = counted_parse(host.db(), files[0]);
assert_eq!(
CALLS.load(Ordering::SeqCst),
1,
"salsa should memoize the second call with unchanged input"
);
}
ft.set_text(host.db_mut())
.to(Arc::<str>::from("<?php\nclass {"));
{
let files = workspace_files(host.db(), ws);
let _ = counted_parse(host.db(), files[0]);
assert_eq!(
CALLS.load(Ordering::SeqCst),
2,
"downstream query should re-run after input text changes"
);
}
}
#[test]
fn parse_error_count_reflects_diagnostics() {
let host = AnalysisHost::new();
let ft = FileText::new(host.db(), Arc::<str>::from("<?php\nclass {"), None);
let ws = make_ws(&host, "file:///t.php", ft);
let files = workspace_files(host.db(), ws);
assert!(parse_error_count(host.db(), files[0]) > 0);
}
}