#![cfg_attr(coverage_nightly, coverage(off))]
use std::path::PathBuf;
use std::sync::Arc;
use tracing::debug;
use crate::services::cache::{CacheConfig, SessionCacheManager};
use crate::services::deep_context::analyzer_core::types::{
AnalysisResult, ParallelAnalysisResults,
};
use crate::services::deep_context::AnalysisType;
use crate::services::deep_context::DeepContextAnalyzer;
impl DeepContextAnalyzer {
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub(crate) async fn execute_parallel_analyses_with_progress(
&self,
project_path: &std::path::Path,
progress: &crate::services::progress::ProgressTracker,
) -> anyhow::Result<ParallelAnalysisResults> {
let analysis_count = self.config.include_analyses.len() as u64;
let analysis_progress = progress.create_sub_progress("Running analyses", analysis_count);
let mut results = ParallelAnalysisResults::default();
let mut prebuilt_context: Option<Arc<crate::services::context::ProjectContext>> = None;
if self.config.include_analyses.contains(&AnalysisType::Ast) {
let file_classifier_config = self.config.file_classifier_config.clone();
let path = project_path.to_path_buf();
let ast_result = tokio::spawn(async move {
AnalysisResult::Ast(
crate::services::deep_context::analyze_ast_contexts(
&path,
file_classifier_config,
)
.await,
)
})
.await?;
if let AnalysisResult::Ast(Ok(ref ast_contexts)) = ast_result {
let files: Vec<crate::services::context::FileContext> =
ast_contexts.iter().map(|efc| efc.base.clone()).collect();
let summary = crate::services::context::ProjectSummary {
total_files: files.len(),
total_functions: files
.iter()
.flat_map(|f| f.items.iter())
.filter(|i| matches!(i, crate::services::context::AstItem::Function { .. }))
.count(),
total_structs: 0,
total_enums: 0,
total_traits: 0,
total_impls: 0,
dependencies: vec![],
};
prebuilt_context = Some(Arc::new(crate::services::context::ProjectContext {
project_type: "rust".to_string(),
files,
summary,
graph: None,
}));
}
self.integrate_analysis_result(&mut results, ast_result);
analysis_progress.inc(1);
}
let mut join_set = self.spawn_remaining_tasks(project_path, prebuilt_context)?;
let remaining = self
.collect_analysis_results_with_progress(&mut join_set, &analysis_progress)
.await?;
self.merge_results(&mut results, remaining);
analysis_progress.finish_with_message("Analyses complete");
Ok(results)
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub(crate) fn spawn_remaining_tasks(
&self,
project_path: &std::path::Path,
prebuilt_context: Option<Arc<crate::services::context::ProjectContext>>,
) -> anyhow::Result<tokio::task::JoinSet<AnalysisResult>> {
let mut join_set = tokio::task::JoinSet::new();
let shared_cache = Arc::new(SessionCacheManager::new(CacheConfig::default()));
for analysis_type in &self.config.include_analyses {
if matches!(analysis_type, AnalysisType::Ast) {
continue;
}
self.spawn_analysis_task(
&mut join_set,
project_path,
analysis_type,
&shared_cache,
&prebuilt_context,
)?;
}
Ok(join_set)
}
fn merge_results(&self, target: &mut ParallelAnalysisResults, source: ParallelAnalysisResults) {
if source.ast_contexts.is_some() {
target.ast_contexts = source.ast_contexts;
}
if source.complexity_report.is_some() {
target.complexity_report = source.complexity_report;
}
if source.churn_analysis.is_some() {
target.churn_analysis = source.churn_analysis;
}
if source.dead_code_results.is_some() {
target.dead_code_results = source.dead_code_results;
}
if source.duplicate_code_results.is_some() {
target.duplicate_code_results = source.duplicate_code_results;
}
if source.satd_results.is_some() {
target.satd_results = source.satd_results;
}
if source.provability_results.is_some() {
target.provability_results = source.provability_results;
}
if source.dependency_graph.is_some() {
target.dependency_graph = source.dependency_graph;
}
if source.big_o_analysis.is_some() {
target.big_o_analysis = source.big_o_analysis;
}
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub(crate) fn spawn_analysis_task(
&self,
join_set: &mut tokio::task::JoinSet<AnalysisResult>,
project_path: &std::path::Path,
analysis_type: &AnalysisType,
shared_cache: &Arc<SessionCacheManager>,
prebuilt_context: &Option<Arc<crate::services::context::ProjectContext>>,
) -> anyhow::Result<()> {
let path = project_path.to_path_buf();
match analysis_type {
AnalysisType::Ast => self.spawn_ast_analysis(join_set, path),
AnalysisType::Complexity => self.spawn_complexity_analysis(join_set, path),
AnalysisType::Churn => self.spawn_churn_analysis(join_set, path),
AnalysisType::DeadCode => self.spawn_dead_code_analysis(join_set, path),
AnalysisType::DuplicateCode => self.spawn_duplicate_analysis(join_set, path),
AnalysisType::Satd => self.spawn_satd_analysis(join_set, path),
AnalysisType::Provability => self.spawn_provability_analysis(
join_set,
path,
shared_cache.clone(),
prebuilt_context.clone(),
),
AnalysisType::Dag => self.spawn_dag_analysis(
join_set,
path,
shared_cache.clone(),
prebuilt_context.clone(),
),
AnalysisType::TechnicalDebtGradient => Ok(()), AnalysisType::BigO => self.spawn_big_o_analysis(join_set, path),
}
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub(crate) fn spawn_ast_analysis(
&self,
join_set: &mut tokio::task::JoinSet<AnalysisResult>,
path: PathBuf,
) -> anyhow::Result<()> {
let file_classifier_config = self.config.file_classifier_config.clone();
join_set.spawn(async move {
AnalysisResult::Ast(
crate::services::deep_context::analyze_ast_contexts(&path, file_classifier_config)
.await,
)
});
Ok(())
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub(crate) fn spawn_complexity_analysis(
&self,
join_set: &mut tokio::task::JoinSet<AnalysisResult>,
path: PathBuf,
) -> anyhow::Result<()> {
join_set.spawn(async move {
AnalysisResult::Complexity(
crate::services::deep_context::analysis_functions::analyze_complexity(&path).await,
)
});
Ok(())
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub(crate) fn spawn_churn_analysis(
&self,
join_set: &mut tokio::task::JoinSet<AnalysisResult>,
path: PathBuf,
) -> anyhow::Result<()> {
let days = self.config.period_days;
join_set.spawn(async move {
AnalysisResult::Churn(
crate::services::deep_context::analysis_functions::analyze_churn(&path, days).await,
)
});
Ok(())
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub(crate) fn spawn_dead_code_analysis(
&self,
join_set: &mut tokio::task::JoinSet<AnalysisResult>,
path: PathBuf,
) -> anyhow::Result<()> {
join_set.spawn(async move {
AnalysisResult::DeadCode(
crate::services::deep_context::analysis_functions::analyze_dead_code(&path).await,
)
});
Ok(())
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub(crate) fn spawn_duplicate_analysis(
&self,
join_set: &mut tokio::task::JoinSet<AnalysisResult>,
path: PathBuf,
) -> anyhow::Result<()> {
join_set.spawn(async move {
AnalysisResult::DuplicateCode(
crate::services::deep_context::analysis_functions::analyze_duplicate_code(&path)
.await,
)
});
Ok(())
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub(crate) fn spawn_satd_analysis(
&self,
join_set: &mut tokio::task::JoinSet<AnalysisResult>,
path: PathBuf,
) -> anyhow::Result<()> {
join_set.spawn(async move {
let result = tokio::task::spawn_blocking(move || {
tokio::runtime::Handle::current().block_on(async {
crate::services::deep_context::analysis_functions::analyze_satd(&path).await
})
})
.await
.unwrap_or_else(|_| Err(anyhow::anyhow!("SATD analysis failed")));
AnalysisResult::Satd(result)
});
Ok(())
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub(crate) fn spawn_provability_analysis(
&self,
join_set: &mut tokio::task::JoinSet<AnalysisResult>,
path: PathBuf,
cache: Arc<SessionCacheManager>,
prebuilt: Option<Arc<crate::services::context::ProjectContext>>,
) -> anyhow::Result<()> {
join_set.spawn(async move {
AnalysisResult::Provability(
crate::services::deep_context::analysis_functions::analyze_provability_with_context(
&path,
Some(cache),
prebuilt,
)
.await,
)
});
Ok(())
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub(crate) fn spawn_dag_analysis(
&self,
join_set: &mut tokio::task::JoinSet<AnalysisResult>,
path: PathBuf,
cache: Arc<SessionCacheManager>,
prebuilt: Option<Arc<crate::services::context::ProjectContext>>,
) -> anyhow::Result<()> {
let dag_type = self.config.dag_type.clone();
join_set.spawn(async move {
AnalysisResult::Dag(
crate::services::deep_context::analysis_functions::analyze_dag_with_context(
&path,
dag_type,
Some(cache),
prebuilt,
)
.await,
)
});
Ok(())
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub(crate) fn spawn_big_o_analysis(
&self,
join_set: &mut tokio::task::JoinSet<AnalysisResult>,
path: PathBuf,
) -> anyhow::Result<()> {
join_set.spawn(async move {
AnalysisResult::BigO(
crate::services::deep_context::analysis_functions::analyze_big_o(&path).await,
)
});
Ok(())
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub(crate) async fn collect_analysis_results_with_progress(
&self,
join_set: &mut tokio::task::JoinSet<AnalysisResult>,
progress: &crate::services::progress::ProgressBar,
) -> anyhow::Result<ParallelAnalysisResults> {
let results = self
.process_analysis_results_with_progress(join_set, progress)
.await?;
debug!("Parallel analysis collection completed successfully");
Ok(results)
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub(crate) async fn process_analysis_results_with_progress(
&self,
join_set: &mut tokio::task::JoinSet<AnalysisResult>,
progress: &crate::services::progress::ProgressBar,
) -> anyhow::Result<ParallelAnalysisResults> {
let mut pending_results = Vec::new();
while let Some(result) = join_set.join_next().await {
pending_results.push(result?);
progress.inc(1);
}
let result_processors: Vec<_> = pending_results
.into_iter()
.map(|result| tokio::spawn(async move { result }))
.collect();
let mut results = ParallelAnalysisResults::default();
for processor in result_processors {
if let Ok(processed) = processor.await {
self.integrate_analysis_result(&mut results, processed);
}
}
Ok(results)
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub(crate) fn integrate_analysis_result(
&self,
results: &mut ParallelAnalysisResults,
result: AnalysisResult,
) {
match result {
AnalysisResult::Ast(Ok(data)) => {
results.ast_contexts = Some(data);
}
AnalysisResult::Complexity(Ok(data)) => {
results.complexity_report = Some(data);
}
AnalysisResult::Churn(Ok(data)) => {
results.churn_analysis = Some(data);
}
AnalysisResult::DeadCode(Ok(data)) => {
results.dead_code_results = Some(data);
}
AnalysisResult::DuplicateCode(Ok(data)) => {
results.duplicate_code_results = Some(data);
}
AnalysisResult::Satd(Ok(data)) => {
results.satd_results = Some(data);
}
AnalysisResult::Provability(Ok(data)) => {
results.provability_results = Some(data);
}
AnalysisResult::Dag(Ok(data)) => {
results.dependency_graph = Some(data);
}
AnalysisResult::BigO(Ok(data)) => {
results.big_o_analysis = Some(data);
}
other => self.log_integration_error(&other),
}
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub(crate) fn log_integration_error(&self, result: &AnalysisResult) {
match result {
AnalysisResult::Ast(Err(e))
| AnalysisResult::Complexity(Err(e))
| AnalysisResult::Churn(Err(e))
| AnalysisResult::DeadCode(Err(e))
| AnalysisResult::DuplicateCode(Err(e))
| AnalysisResult::Satd(Err(e))
| AnalysisResult::Provability(Err(e))
| AnalysisResult::Dag(Err(e))
| AnalysisResult::BigO(Err(e)) => {
debug!("{} analysis failed: {}", self.get_analysis_name(result), e);
}
_ => {}
}
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub(crate) fn get_analysis_name(&self, result: &AnalysisResult) -> &'static str {
match result {
AnalysisResult::Ast(_) => "AST",
AnalysisResult::Complexity(_) => "Complexity",
AnalysisResult::Churn(_) => "Churn",
AnalysisResult::DeadCode(_) => "Dead code",
AnalysisResult::DuplicateCode(_) => "Duplicate code",
AnalysisResult::Satd(_) => "SATD",
AnalysisResult::Provability(_) => "Provability",
AnalysisResult::Dag(_) => "DAG",
AnalysisResult::BigO(_) => "Big-O",
}
}
}