#![cfg_attr(coverage_nightly, coverage(off))]
pub mod clippy;
pub mod metrics;
pub mod output;
pub mod types;
pub use types::{
EnforcementMetadata, FileSummary, LintHotspot, LintHotspotParams, LintHotspotResult,
QualityGateStatus, QualityViolation, RefactorChain, RefactorStep, SeverityDistribution,
ViolationDetail,
};
pub use output::format_summary;
use crate::cli::LintHotspotOutputFormat;
use anyhow::Result;
use metrics::{calculate_enforcement_metadata, check_quality_gates, generate_refactor_chain};
use output::format_output;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
#[allow(clippy::too_many_arguments)]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub async fn handle_analyze_lint_hotspot(
project_path: PathBuf,
file: Option<PathBuf>,
format: LintHotspotOutputFormat,
max_density: f64,
min_confidence: f64,
enforce: bool,
dry_run: bool,
enforcement_metadata: bool,
output: Option<PathBuf>,
perf: bool,
clippy_flags: String,
top_files: usize,
include: Vec<String>,
exclude: Vec<String>,
) -> Result<()> {
if !include.is_empty() || !exclude.is_empty() {
eprintln!("🔍 Applying file filters...");
if !include.is_empty() {
eprintln!(" Include patterns: {include:?}");
}
if !exclude.is_empty() {
eprintln!(" Exclude patterns: {exclude:?}");
}
}
let params = LintHotspotParams {
project_path,
file,
format,
max_density,
min_confidence,
enforce,
dry_run,
enforcement_metadata,
output,
perf,
clippy_flags,
top_files,
include,
exclude,
};
handle_analyze_lint_hotspot_with_params(params).await
}
async fn handle_analyze_lint_hotspot_with_params(params: LintHotspotParams) -> Result<()> {
let start_time = std::time::Instant::now();
log_analysis_start(¶ms.format);
let result = run_analysis_by_mode(¶ms).await;
let mut result = match result {
Ok(r) => r,
Err(e) if e.to_string().contains("No lint violations found") => {
use crate::cli::colors as c;
eprintln!("{}", c::pass("No lint violations found — project is clean"));
return Ok(());
}
Err(e) => return Err(e),
};
apply_file_filters(&mut result, ¶ms)?;
let final_result = build_final_result(result, ¶ms)?;
output_results(&final_result, ¶ms, start_time.elapsed()).await?;
execute_enforcement_if_needed(&final_result, ¶ms);
check_exit_conditions(&final_result, ¶ms);
Ok(())
}
fn log_analysis_start(format: &LintHotspotOutputFormat) {
if *format != LintHotspotOutputFormat::Json {
eprintln!("🔍 Running Clippy analysis...");
}
}
async fn run_analysis_by_mode(params: &LintHotspotParams) -> Result<LintHotspotResult> {
if let Some(ref file_path) = params.file {
log_single_file_mode(file_path, ¶ms.format);
clippy::run_clippy_analysis_single_file(
¶ms.project_path,
file_path,
¶ms.clippy_flags,
)
.await
} else {
clippy::run_clippy_analysis(¶ms.project_path, ¶ms.clippy_flags).await
}
}
fn log_single_file_mode(file_path: &Path, format: &LintHotspotOutputFormat) {
if *format != LintHotspotOutputFormat::Json {
eprintln!("📄 Analyzing single file: {}", file_path.display());
}
}
fn apply_file_filters(result: &mut LintHotspotResult, params: &LintHotspotParams) -> Result<()> {
if params.include.is_empty() && params.exclude.is_empty() {
return Ok(());
}
use crate::utils::file_filter::FileFilter;
let filter = FileFilter::new(params.include.clone(), params.exclude.clone())?;
if !filter.has_filters() {
return Ok(());
}
filter_violations(result, &filter);
recalculate_hotspot_metrics(result);
Ok(())
}
fn filter_violations(
result: &mut LintHotspotResult,
filter: &crate::utils::file_filter::FileFilter,
) {
result.hotspot.detailed_violations.retain(|violation| {
let path = std::path::Path::new(&violation.file);
filter.should_include(path)
});
result.all_violations.retain(|violation| {
let path = std::path::Path::new(&violation.file);
filter.should_include(path)
});
let filtered_summary: HashMap<PathBuf, FileSummary> = result
.summary_by_file
.drain()
.filter(|(path, _summary)| filter.should_include(path))
.collect();
result.summary_by_file = filtered_summary;
}
fn recalculate_hotspot_metrics(result: &mut LintHotspotResult) {
result.hotspot.total_violations = result.hotspot.detailed_violations.len();
if result.hotspot.sloc > 0 {
result.hotspot.defect_density =
result.hotspot.total_violations as f64 / result.hotspot.sloc as f64;
}
}
fn build_final_result(
mut result: LintHotspotResult,
params: &LintHotspotParams,
) -> Result<LintHotspotResult> {
let enforcement = generate_enforcement_metadata_if_needed(&result.hotspot, params);
let refactor_chain = generate_refactor_chain_if_needed(&result.hotspot, params, &enforcement);
let quality_gate = check_quality_gates(&result.hotspot, params.max_density);
result.enforcement = enforcement;
result.refactor_chain = refactor_chain;
result.quality_gate = quality_gate;
Ok(result)
}
fn generate_enforcement_metadata_if_needed(
hotspot: &LintHotspot,
params: &LintHotspotParams,
) -> Option<EnforcementMetadata> {
if params.enforcement_metadata || params.enforce {
Some(calculate_enforcement_metadata(
hotspot,
params.min_confidence,
))
} else {
None
}
}
fn generate_refactor_chain_if_needed(
hotspot: &LintHotspot,
params: &LintHotspotParams,
enforcement: &Option<EnforcementMetadata>,
) -> Option<RefactorChain> {
if params.enforce || enforcement.as_ref().is_some_and(|e| e.requires_enforcement) {
Some(generate_refactor_chain(hotspot, params.min_confidence))
} else {
None
}
}
async fn output_results(
final_result: &LintHotspotResult,
params: &LintHotspotParams,
elapsed: std::time::Duration,
) -> Result<()> {
let output_content = format_output(
final_result,
params.format.clone(),
params.perf,
elapsed,
params.top_files,
)?;
if let Some(output_path) = ¶ms.output {
tokio::fs::write(output_path, &output_content).await?;
} else {
println!("{output_content}");
}
Ok(())
}
fn execute_enforcement_if_needed(final_result: &LintHotspotResult, params: &LintHotspotParams) {
if params.enforce && !params.dry_run && final_result.quality_gate.blocking {
eprintln!("🚨 Enforcement required - executing refactor chain...");
eprintln!("⚠️ Enforcement execution not yet implemented");
}
}
fn check_exit_conditions(final_result: &LintHotspotResult, params: &LintHotspotParams) {
if should_exit_with_error(final_result, params) {
log_enforcement_failure_if_needed(final_result, params);
std::process::exit(1);
}
}
fn should_exit_with_error(final_result: &LintHotspotResult, params: &LintHotspotParams) -> bool {
!final_result.quality_gate.passed
|| (params.enforce && final_result.total_project_violations > 0)
}
fn log_enforcement_failure_if_needed(final_result: &LintHotspotResult, params: &LintHotspotParams) {
if params.enforce
&& final_result.total_project_violations > 0
&& final_result.quality_gate.passed
{
eprintln!(
"\n❌ Enforcement failed: {} violations found",
final_result.total_project_violations
);
}
}
#[cfg(all(test, feature = "broken-tests"))]
#[path = "../lint_hotspot_handlers_tests.rs"]
mod tests;