use crate::*;
use super::combined;
pub(crate) struct CollectedFindings {
pub(crate) findings: Vec<Finding>,
pub(crate) analysis_errors: Vec<String>,
pub(crate) rule_revisions: std::collections::HashMap<String, u32>,
pub(crate) boundary_rules_checked: usize,
pub(crate) boundaries_config_path: PathBuf,
pub(crate) suppressed_inline: usize,
}
pub(crate) fn collect_findings(
workspace: &judge::ingest::Workspace,
) -> Result<CollectedFindings, CliError> {
collect_findings_with_progress(workspace, &mut |_| Ok(()))
}
pub(crate) fn collect_findings_with_progress(
workspace: &judge::ingest::Workspace,
progress: &mut dyn FnMut(combined::ProgressEvent) -> Result<(), CliError>,
) -> Result<CollectedFindings, CliError> {
let mut findings = Vec::new();
let mut analysis_errors = Vec::new();
let mut rule_revisions = default_rule_revisions();
progress(combined::ProgressEvent::started("complexity"))?;
collect_complexity_and_history(workspace, &mut findings, &mut analysis_errors);
progress(combined::ProgressEvent::completed("complexity"))?;
progress(combined::ProgressEvent::started("slop"))?;
collect_slop(workspace, &mut findings, &mut analysis_errors)?;
progress(combined::ProgressEvent::completed("slop"))?;
progress(combined::ProgressEvent::started("duplication"))?;
collect_duplication(workspace, &mut findings, &mut analysis_errors);
progress(combined::ProgressEvent::completed("duplication"))?;
progress(combined::ProgressEvent::started("structural"))?;
collect_structural(workspace, &mut findings);
progress(combined::ProgressEvent::completed("structural"))?;
progress(combined::ProgressEvent::started("security"))?;
collect_security(workspace, &mut findings, &mut analysis_errors, false);
progress(combined::ProgressEvent::completed("security"))?;
progress(combined::ProgressEvent::started("dependencies"))?;
collect_dependencies(workspace, &mut findings, &mut analysis_errors);
progress(combined::ProgressEvent::completed("dependencies"))?;
progress(combined::ProgressEvent::started("boundaries"))?;
let (boundaries_config_path, boundary_rules_checked) = collect_boundaries(
workspace,
&mut findings,
&mut analysis_errors,
&mut rule_revisions,
)?;
progress(combined::ProgressEvent::completed("boundaries"))?;
progress(combined::ProgressEvent::started("suppression"))?;
let (findings, suppressed_inline) = collect_suppression(workspace, findings)?;
progress(combined::ProgressEvent::completed("suppression"))?;
Ok(CollectedFindings {
findings,
analysis_errors,
rule_revisions,
boundary_rules_checked,
boundaries_config_path,
suppressed_inline,
})
}
fn default_rule_revisions() -> std::collections::HashMap<String, u32> {
let mut revisions = deps_rule_revisions();
revisions.insert(
judge::rules::duplication::DUPLICATE_RULE.to_string(),
judge::rules::duplication::DUPLICATE_RULE_REVISION,
);
revisions.extend(slop_structural_security_rule_revisions());
revisions.insert(
judge::rules::complexity::MAINTAINABILITY_INDEX_RULE.to_string(),
judge::rules::complexity::MAINTAINABILITY_INDEX_RULE_REVISION,
);
revisions
}
pub(crate) fn boundaries_rule_revisions() -> std::collections::HashMap<String, u32> {
std::collections::HashMap::from([
(
judge::rules::boundaries::BOUNDARY_VIOLATION_RULE.to_string(),
judge::rules::boundaries::BOUNDARY_VIOLATION_RULE_REVISION,
),
(
judge::rules::boundaries::DEPENDENCY_CYCLE_RULE.to_string(),
judge::rules::boundaries::DEPENDENCY_CYCLE_RULE_REVISION,
),
])
}
pub(crate) fn deps_rule_revisions() -> std::collections::HashMap<String, u32> {
std::collections::HashMap::from([
(
judge::rules::deps::MISPLACED_DEPENDENCY_KIND_RULE.to_string(),
judge::rules::deps::MISPLACED_DEPENDENCY_KIND_RULE_REVISION,
),
(
judge::rules::deps::UNUSED_DEV_DEPENDENCY_RULE.to_string(),
judge::rules::deps::UNUSED_DEV_DEPENDENCY_RULE_REVISION,
),
(
judge::rules::deps::HEAVY_DEPENDENCY_RULE.to_string(),
judge::rules::deps::HEAVY_DEPENDENCY_RULE_REVISION,
),
(
judge::rules::deps::UNUSED_FEATURE_FLAG_RULE.to_string(),
judge::rules::deps::UNUSED_FEATURE_FLAG_RULE_REVISION,
),
(
judge::rules::deps::DEFAULT_FEATURES_UNUSED_RULE.to_string(),
judge::rules::deps::DEFAULT_FEATURES_UNUSED_RULE_REVISION,
),
(
judge::rules::deps::UNUSED_FEATURE_RULE.to_string(),
judge::rules::deps::UNUSED_FEATURE_RULE_REVISION,
),
(
judge::rules::deps::DEP_WITHOUT_REPO_RULE.to_string(),
judge::rules::deps::DEP_WITHOUT_REPO_RULE_REVISION,
),
(
judge::rules::dep_graph::DUPLICATE_CRATE_VERSIONS_RULE.to_string(),
judge::rules::dep_graph::DUPLICATE_CRATE_VERSIONS_RULE_REVISION,
),
(
judge::rules::dep_graph::MSRV_DRIFT_RULE.to_string(),
judge::rules::dep_graph::MSRV_DRIFT_RULE_REVISION,
),
(
judge::rules::dep_graph::WORKSPACE_DEP_DRIFT_RULE.to_string(),
judge::rules::dep_graph::WORKSPACE_DEP_DRIFT_RULE_REVISION,
),
(
judge::rules::slopsquat::NAME_COLLISION_RISK_RULE.to_string(),
judge::rules::slopsquat::NAME_COLLISION_RISK_RULE_REVISION,
),
])
}
pub(crate) fn slop_structural_security_rule_revisions() -> std::collections::HashMap<String, u32> {
std::collections::HashMap::from([
(
judge::rules::slop::SWALLOWED_RESULT_RULE.to_string(),
judge::rules::slop::SWALLOWED_RESULT_RULE_REVISION,
),
(
judge::rules::slop::EMPTY_ERROR_ARM_RULE.to_string(),
judge::rules::slop::EMPTY_ERROR_ARM_RULE_REVISION,
),
(
judge::rules::slop::CATCH_ALL_ERROR_RULE.to_string(),
judge::rules::slop::CATCH_ALL_ERROR_RULE_REVISION,
),
(
judge::rules::slop::SUPPRESSION_DEBT_RULE.to_string(),
judge::rules::slop::SUPPRESSION_DEBT_RULE_REVISION,
),
(
judge::rules::slop::MERGED_STUB_RULE.to_string(),
judge::rules::slop::MERGED_STUB_RULE_REVISION,
),
(
judge::rules::slop::EMPTY_IMPL_RULE.to_string(),
judge::rules::slop::EMPTY_IMPL_RULE_REVISION,
),
(
judge::rules::slop::ASSERTION_FREE_TEST_RULE.to_string(),
judge::rules::slop::ASSERTION_FREE_TEST_RULE_REVISION,
),
(
judge::rules::slop::TAUTOLOGICAL_TEST_RULE.to_string(),
judge::rules::slop::TAUTOLOGICAL_TEST_RULE_REVISION,
),
(
judge::rules::slop::IGNORED_TEST_ACCUMULATION_RULE.to_string(),
judge::rules::slop::IGNORED_TEST_ACCUMULATION_RULE_REVISION,
),
(
judge::rules::slop::CONVERSATIONAL_ARTIFACT_RULE.to_string(),
judge::rules::slop::CONVERSATIONAL_ARTIFACT_RULE_REVISION,
),
(
judge::rules::slop::RESTATING_COMMENT_RULE.to_string(),
judge::rules::slop::RESTATING_COMMENT_RULE_REVISION,
),
(
judge::rules::slop::STEP_COMMENT_INFLATION_RULE.to_string(),
judge::rules::slop::STEP_COMMENT_INFLATION_RULE_REVISION,
),
(
judge::rules::slop::GENERIC_NAMING_RULE.to_string(),
judge::rules::slop::GENERIC_NAMING_RULE_REVISION,
),
(
judge::rules::slop::DOC_RESTATES_SIGNATURE_RULE.to_string(),
judge::rules::slop::DOC_RESTATES_SIGNATURE_RULE_REVISION,
),
(
judge::rules::slop_structural::COMPLEXITY_INFLATION_RULE.to_string(),
judge::rules::slop_structural::COMPLEXITY_INFLATION_RULE_REVISION,
),
(
judge::rules::complexity::SIGNATURE_COMPLEXITY_RULE.to_string(),
judge::rules::complexity::SIGNATURE_COMPLEXITY_RULE_REVISION,
),
(
judge::rules::slop_structural::ABSTRACTION_INFLATION_RULE.to_string(),
judge::rules::slop_structural::ABSTRACTION_INFLATION_RULE_REVISION,
),
(
judge::rules::slop_structural::FRAGILE_SUBSTRING_CLASSIFICATION_RULE.to_string(),
judge::rules::slop_structural::FRAGILE_SUBSTRING_CLASSIFICATION_RULE_REVISION,
),
(
judge::rules::security::UNSAFE_SURFACE_RULE.to_string(),
judge::rules::security::UNSAFE_SURFACE_RULE_REVISION,
),
(
judge::rules::security::UNSAFE_DENSITY_RULE.to_string(),
judge::rules::security::UNSAFE_DENSITY_RULE_REVISION,
),
(
judge::rules::security::INTEGER_CAST_RISK_RULE.to_string(),
judge::rules::security::INTEGER_CAST_RISK_RULE_REVISION,
),
(
judge::rules::security::PANIC_IN_LIB_RULE.to_string(),
judge::rules::security::PANIC_IN_LIB_RULE_REVISION,
),
(
judge::rules::security::HARDCODED_SECRET_RULE.to_string(),
judge::rules::security::HARDCODED_SECRET_RULE_REVISION,
),
])
}
fn collect_complexity_and_history(
workspace: &judge::ingest::Workspace,
findings: &mut Vec<Finding>,
analysis_errors: &mut Vec<String>,
) {
let complexity_source_files = super::analysis_commands::workspace_source_files(workspace);
let complexity = judge::rules::complexity::analyze_workspace(complexity_source_files, false);
append_analysis_errors(analysis_errors, &complexity.errors);
findings.extend(judge::rules::slop_structural::complexity_inflation(
&complexity.functions,
));
findings.extend(judge::rules::complexity::signature_complexity(
&complexity.functions,
));
findings.extend(judge::rules::complexity::maintainability_index(
&complexity.functions,
));
}
fn collect_slop(
workspace: &judge::ingest::Workspace,
findings: &mut Vec<Finding>,
analysis_errors: &mut Vec<String>,
) -> Result<(), CliError> {
let slop = super::analysis_commands::analyze_slop_workspace(workspace, false)?;
append_analysis_errors(analysis_errors, &slop.errors);
findings.extend(slop.findings);
Ok(())
}
fn collect_duplication(
workspace: &judge::ingest::Workspace,
findings: &mut Vec<Finding>,
analysis_errors: &mut Vec<String>,
) {
let dupes_source_files = super::analysis_commands::workspace_source_files(workspace);
let dupes = judge::rules::duplication::analyze_workspace_with_options(
dupes_source_files,
DupeMode::Mild,
judge::rules::duplication::DEFAULT_MIN_TOKENS,
false,
false,
);
append_analysis_errors(analysis_errors, &dupes.errors);
findings.extend(dupes.to_findings());
}
pub(crate) fn collect_structural(
workspace: &judge::ingest::Workspace,
findings: &mut Vec<Finding>,
) {
let abstraction_source_files = super::analysis_commands::workspace_source_files(workspace);
findings.extend(judge::rules::slop_structural::analyze_workspace_structural(
abstraction_source_files,
));
let fragile_substring_source_files =
super::analysis_commands::workspace_source_files(workspace);
findings.extend(
judge::rules::slop_structural::fragile_substring_classification(
fragile_substring_source_files,
),
);
}
pub(crate) fn collect_security(
workspace: &judge::ingest::Workspace,
findings: &mut Vec<Finding>,
analysis_errors: &mut Vec<String>,
include_generated: bool,
) -> usize {
let security_source_files = super::analysis_commands::workspace_source_files(workspace);
let security =
judge::rules::security::analyze_workspace(security_source_files, include_generated);
append_analysis_errors(analysis_errors, &security.errors);
findings.extend(security.findings);
security.excluded_generated
}
fn collect_dependencies(
workspace: &judge::ingest::Workspace,
findings: &mut Vec<Finding>,
analysis_errors: &mut Vec<String>,
) {
let deps = judge::rules::deps::analyze_workspace(workspace);
append_analysis_errors(analysis_errors, &deps.errors);
findings.extend(deps.findings);
let dep_graph = judge::rules::dep_graph::analyze_workspace(workspace);
append_analysis_errors(analysis_errors, &dep_graph.errors);
findings.extend(dep_graph.findings);
findings.extend(judge::rules::slopsquat::analyze_name_collision(workspace));
}
fn collect_boundaries(
workspace: &judge::ingest::Workspace,
findings: &mut Vec<Finding>,
analysis_errors: &mut Vec<String>,
rule_revisions: &mut std::collections::HashMap<String, u32>,
) -> Result<(PathBuf, usize), CliError> {
let boundaries_config_path = workspace.root.join("judge.toml");
let mut boundary_rules_checked = 0;
if boundaries_config_path.exists() {
let config = parse_boundary_config(&boundaries_config_path)?;
boundary_rules_checked = config.boundaries.len();
let evaluated = judge::rules::boundaries::evaluate(workspace, &config)?;
findings.extend(evaluated.findings);
rule_revisions.extend(boundaries_rule_revisions());
}
let feature_graph_manifest = workspace.root.join("Cargo.toml");
match judge::rules::boundaries::feature_graph_cycles(Some(&feature_graph_manifest)) {
Ok(cycle_findings) => {
findings.extend(cycle_findings);
rule_revisions.insert(
judge::rules::boundaries::FEATURE_GRAPH_CYCLE_RULE.to_string(),
judge::rules::boundaries::FEATURE_GRAPH_CYCLE_RULE_REVISION,
);
}
Err(err) => analysis_errors.push(err.to_string()),
}
Ok((boundaries_config_path, boundary_rules_checked))
}
fn collect_suppression(
workspace: &judge::ingest::Workspace,
findings: Vec<Finding>,
) -> Result<(Vec<Finding>, usize), CliError> {
let (findings, suppressed_inline) =
judge::suppression::apply_inline_suppressions(findings, &workspace.root)?;
Ok((findings, suppressed_inline))
}