use std::sync::Arc;
use mir_issues::Issue;
use php_ast::owned::Program;
use php_rs_parser::source_map::SourceMap;
use rayon::prelude::*;
use crate::body_analysis::BodyAnalyzer;
use crate::db::MirDatabase;
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 {
crate::metrics::record_file_analysis();
self.session
.prepare_ast_for_analysis(program, file.as_ref());
let _scope = crate::metrics::BodyAnalysisScope::new();
let db = self.session.snapshot_db();
let driver = BodyAnalyzer::new(&db, self.session.php_version());
let (issues, symbols) = driver.analyze_bodies(program, file, source, source_map);
self.session
.commit_ref_locs_batch(db.take_pending_ref_locs());
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: Program,
pub(crate) source_map: SourceMap,
}
impl ParsedFile {
pub fn file(&self) -> &Arc<str> {
&self.file
}
pub fn source(&self) -> &Arc<str> {
&self.source
}
pub fn new(file: Arc<str>, source: Arc<str>, program: Program, source_map: 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)> {
files.iter().for_each(|file| {
self.session.ensure_stubs_for_ast(&file.program);
});
let db = self.session.snapshot_db();
let results: Vec<(Arc<str>, FileAnalysis, Vec<crate::db::RefLoc>)> = files
.into_par_iter()
.map_with(db, |db, file| {
let driver = BodyAnalyzer::new(db as &dyn MirDatabase, self.session.php_version());
let (issues, symbols) = driver.analyze_bodies(
&file.program,
file.file.clone(),
&file.source,
&file.source_map,
);
let pending = db.take_pending_ref_locs();
let analysis = FileAnalysis { issues, symbols };
(file.file, analysis, pending)
})
.collect();
let mut all_ref_locs = Vec::new();
let mut out = Vec::with_capacity(results.len());
for (file, analysis, ref_locs) in results {
all_ref_locs.extend(ref_locs);
out.push((file, analysis));
}
self.session.commit_ref_locs_batch(all_ref_locs);
out
}
}