#![cfg_attr(coverage_nightly, coverage(off))]
mod advanced_routes;
mod core_routes;
mod entropy_semantic;
pub mod perf_report;
mod platform_routes;
use crate::cli::{self, AnalyzeCommands};
use anyhow::Result;
pub(super) fn reject_unimplemented_ml(ml: bool, command: &str, scores: &str) -> Result<()> {
if !ml {
return Ok(());
}
anyhow::bail!(
"--ml is not implemented: {scores} are still computed by the heuristic formulas, \
so this flag would relabel them without changing them. \
Re-run `{command}` without --ml (see GH-97)."
)
}
#[cfg(test)]
pub(crate) use advanced_routes::{convert_cache_strategy, convert_deep_context_dag_type};
#[cfg(test)]
pub(crate) use entropy_semantic::{
create_entropy_config, format_markdown_violations, format_violation_list, get_top_violations,
output_entropy_results,
};
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub async fn route_analyze_command(cmd: AnalyzeCommands) -> Result<()> {
let perf_label = perf_report::perf_command_label(&cmd);
let started = std::time::Instant::now();
let result = dispatch_analyze_command(cmd).await;
if let Some(label) = perf_label {
perf_report::emit(label, started.elapsed());
}
result
}
async fn dispatch_analyze_command(cmd: AnalyzeCommands) -> Result<()> {
use cli::AnalyzeCommands;
match cmd {
AnalyzeCommands::Bottleneck { .. }
| AnalyzeCommands::Complexity { .. }
| AnalyzeCommands::Churn { .. }
| AnalyzeCommands::DeadCode { .. }
| AnalyzeCommands::Defects { .. }
| AnalyzeCommands::Dag { .. }
| AnalyzeCommands::Satd { .. } => route_core_analysis(cmd).await,
AnalyzeCommands::DeepContext { .. }
| AnalyzeCommands::Tdg { .. }
| AnalyzeCommands::BuildTdg { .. }
| AnalyzeCommands::LintHotspot { .. }
| AnalyzeCommands::Comprehensive { .. } => route_advanced_analysis(cmd).await,
AnalyzeCommands::Duplicates { .. }
| AnalyzeCommands::DefectPrediction { .. }
| AnalyzeCommands::Provability { .. }
| AnalyzeCommands::Clippy { .. }
| AnalyzeCommands::Entropy { .. } => route_quality_analysis(cmd).await,
AnalyzeCommands::GraphMetrics { .. }
| AnalyzeCommands::NameSimilarity { .. }
| AnalyzeCommands::ProofAnnotations { .. }
| AnalyzeCommands::IncrementalCoverage { .. }
| AnalyzeCommands::CoverageImprove { .. }
| AnalyzeCommands::SymbolTable { .. }
| AnalyzeCommands::BigO { .. } => route_specialized_analysis(cmd).await,
AnalyzeCommands::AssemblyScript { .. }
| AnalyzeCommands::WebAssembly { .. }
| AnalyzeCommands::Wasm { .. } => route_language_specific_analysis(cmd).await,
#[cfg(feature = "deep-wasm")]
AnalyzeCommands::DeepWasm { .. } => platform_routes::route_deep_wasm_analysis(cmd).await,
#[cfg(feature = "mutation-testing")]
AnalyzeCommands::Mutate { .. } => platform_routes::route_mutation_testing(cmd).await,
AnalyzeCommands::Makefile { .. } => route_system_analysis(cmd).await,
AnalyzeCommands::Cluster { .. } | AnalyzeCommands::Topics { .. } => {
entropy_semantic::route_semantic_analysis(cmd).await
}
AnalyzeCommands::Models { .. } => platform_routes::route_model_analysis(cmd).await,
}
}
async fn route_core_analysis(cmd: AnalyzeCommands) -> Result<()> {
match cmd {
AnalyzeCommands::Bottleneck { .. } => core_routes::route_bottleneck_analysis(cmd).await,
AnalyzeCommands::Complexity { .. } => core_routes::route_complexity_analysis(cmd).await,
AnalyzeCommands::Churn { .. } => core_routes::route_churn_analysis(cmd).await,
AnalyzeCommands::DeadCode { .. } => core_routes::route_dead_code_analysis(cmd).await,
AnalyzeCommands::Defects { .. } => core_routes::route_defects_analysis(cmd).await,
AnalyzeCommands::Dag { .. } => core_routes::route_dag_analysis(cmd).await,
AnalyzeCommands::Satd { .. } => core_routes::route_satd_analysis(cmd).await,
_ => unreachable!("Expected core analysis command"),
}
}
async fn route_advanced_analysis(cmd: AnalyzeCommands) -> Result<()> {
match cmd {
AnalyzeCommands::DeepContext { .. } => {
advanced_routes::route_deep_context_analysis(cmd).await
}
AnalyzeCommands::Tdg { .. } => advanced_routes::route_tdg_analysis(cmd).await,
AnalyzeCommands::BuildTdg { .. } => advanced_routes::route_build_tdg_analysis(cmd).await,
AnalyzeCommands::LintHotspot { .. } => {
advanced_routes::route_lint_hotspot_analysis(cmd).await
}
AnalyzeCommands::Comprehensive { .. } => {
advanced_routes::route_comprehensive_analysis(cmd).await
}
_ => unreachable!("Expected advanced analysis command"),
}
}
async fn route_quality_analysis(cmd: AnalyzeCommands) -> Result<()> {
match cmd {
AnalyzeCommands::Duplicates { .. } => advanced_routes::route_duplicates_analysis(cmd).await,
AnalyzeCommands::DefectPrediction { .. } => {
advanced_routes::route_defect_prediction_analysis(cmd).await
}
AnalyzeCommands::Provability { .. } => {
advanced_routes::route_provability_analysis(cmd).await
}
AnalyzeCommands::Clippy { .. } => advanced_routes::route_clippy_analysis(cmd).await,
AnalyzeCommands::Entropy { .. } => entropy_semantic::route_entropy_analysis(cmd).await,
_ => unreachable!("Expected quality analysis command"),
}
}
async fn route_specialized_analysis(cmd: AnalyzeCommands) -> Result<()> {
match cmd {
AnalyzeCommands::GraphMetrics { .. } => {
platform_routes::route_graph_metrics_analysis(cmd).await
}
AnalyzeCommands::NameSimilarity { .. } => {
platform_routes::route_name_similarity_analysis(cmd).await
}
AnalyzeCommands::ProofAnnotations { .. } => {
platform_routes::route_proof_annotations_analysis(cmd).await
}
AnalyzeCommands::IncrementalCoverage { .. } => {
platform_routes::route_incremental_coverage_analysis(cmd).await
}
AnalyzeCommands::CoverageImprove {
path,
project_path,
target,
max_iterations,
fast,
mutation_threshold,
focus,
exclude,
output,
format,
} => {
let path = project_path.unwrap_or(path);
crate::cli::handlers::coverage_improve_handler::handle_coverage_improve(
path,
target,
max_iterations,
fast,
mutation_threshold,
focus,
exclude,
output,
format,
)
.await
}
AnalyzeCommands::SymbolTable { .. } => {
platform_routes::route_symbol_table_analysis(cmd).await
}
AnalyzeCommands::BigO { .. } => platform_routes::route_big_o_analysis(cmd).await,
_ => unreachable!("Expected specialized analysis command"),
}
}
async fn route_language_specific_analysis(cmd: AnalyzeCommands) -> Result<()> {
match cmd {
AnalyzeCommands::AssemblyScript { .. } => {
platform_routes::route_assemblyscript_analysis(cmd).await
}
AnalyzeCommands::WebAssembly { .. } => {
platform_routes::route_webassembly_analysis(cmd).await
}
#[cfg(feature = "wasm-ast")]
AnalyzeCommands::Wasm { .. } => platform_routes::route_wasm_analysis(cmd).await,
#[cfg(not(feature = "wasm-ast"))]
AnalyzeCommands::Wasm { .. } => {
anyhow::bail!(
"WASM analysis requires the 'wasm-ast' feature. Build with --features wasm-ast"
)
}
_ => unreachable!("Expected language-specific analysis command"),
}
}
async fn route_system_analysis(cmd: AnalyzeCommands) -> Result<()> {
match cmd {
AnalyzeCommands::Makefile { .. } => platform_routes::route_makefile_analysis(cmd).await,
_ => unreachable!("Expected system analysis command"),
}
}
#[cfg(test)]
#[path = "../analysis_handlers_tests.rs"]
mod tests;
#[cfg(test)]
mod ml_refusal_tests {
use super::reject_unimplemented_ml;
#[test]
fn an_unset_flag_is_not_refused() {
assert!(reject_unimplemented_ml(false, "analyze tdg", "TDG scores").is_ok());
}
#[test]
fn tdg_ml_is_refused_rather_than_relabelled() {
let err = reject_unimplemented_ml(true, "analyze tdg", "TDG scores")
.expect_err("--ml returned heuristic scores, so it must not be accepted");
let err = err.to_string();
assert!(err.contains("--ml is not implemented"), "{err}");
assert!(err.contains("TDG scores"), "{err}");
assert!(err.contains("analyze tdg"), "{err}");
}
#[test]
fn complexity_ml_keeps_its_own_wording() {
let err = reject_unimplemented_ml(true, "analyze complexity", "complexity scores")
.unwrap_err()
.to_string();
assert!(err.contains("complexity scores"), "{err}");
assert!(err.contains("analyze complexity"), "{err}");
}
}