use std::sync::Arc;
use mir_issues::Issue;
use php_ast::ast::Program;
use php_rs_parser::source_map::SourceMap;
use rayon::prelude::*;
use crate::db::MirDatabase;
use crate::pass2::Pass2Driver;
use crate::session::AnalysisSession;
use crate::symbol::ResolvedSymbol;
pub struct FileAnalysis {
pub issues: Vec<Issue>,
pub symbols: Vec<ResolvedSymbol>,
}
impl FileAnalysis {
pub fn symbol_at(&self, byte_offset: u32) -> Option<&ResolvedSymbol> {
self.symbols
.iter()
.filter(|s| s.span.start <= byte_offset && byte_offset < s.span.end)
.min_by_key(|s| s.span.end - s.span.start)
}
}
pub struct FileAnalyzer<'a> {
session: &'a AnalysisSession,
}
impl<'a> FileAnalyzer<'a> {
pub fn new(session: &'a AnalysisSession) -> Self {
Self { session }
}
pub fn analyze(
&self,
file: Arc<str>,
source: &str,
program: &Program<'_, '_>,
source_map: &SourceMap,
) -> FileAnalysis {
self.session.ensure_essential_stubs_loaded();
self.session.ensure_stubs_for_ast(program);
let db = self.session.snapshot_db();
let driver = Pass2Driver::new(&db, self.session.php_version());
let (issues, symbols) = driver.analyze_bodies(program, file, source, source_map);
FileAnalysis { issues, symbols }
}
}
pub struct BatchFileAnalyzer<'a> {
session: &'a AnalysisSession,
}
pub struct ParsedFile {
pub(crate) file: Arc<str>,
pub(crate) source: Arc<str>,
pub(crate) program: *const Program<'static, 'static>,
pub(crate) source_map: *const SourceMap,
}
impl ParsedFile {
pub fn file(&self) -> &Arc<str> {
&self.file
}
pub fn source(&self) -> &Arc<str> {
&self.source
}
}
unsafe impl Send for ParsedFile {}
unsafe impl Sync for ParsedFile {}
impl ParsedFile {
pub unsafe fn new(
file: Arc<str>,
source: Arc<str>,
program: *const Program<'static, 'static>,
source_map: *const SourceMap,
) -> Self {
Self {
file,
source,
program,
source_map,
}
}
}
impl<'a> BatchFileAnalyzer<'a> {
pub fn new(session: &'a AnalysisSession) -> Self {
Self { session }
}
pub fn analyze_batch(&self, files: Vec<ParsedFile>) -> Vec<(Arc<str>, FileAnalysis)> {
self.session.ensure_essential_stubs_loaded();
files.iter().for_each(|file| {
let program = unsafe { &*file.program };
self.session.ensure_stubs_for_ast(program);
});
let db = self.session.snapshot_db();
files
.into_par_iter()
.map_with(db, |db, file| {
let program = unsafe { &*file.program };
let source_map = unsafe { &*file.source_map };
let driver = Pass2Driver::new(db as &dyn MirDatabase, self.session.php_version());
let (issues, symbols) =
driver.analyze_bodies(program, file.file.clone(), &file.source, source_map);
let analysis = FileAnalysis { issues, symbols };
(file.file, analysis)
})
.collect()
}
}