rrubocop 0.8.15

10x faster RuboCop drop-in. Same output format and configs, without maintaining RuboCop gems.
use super::directives::{
    collect_directives, cop_highlight, cop_names, disable_marker, redundant_col, DisableDirective,
};
use super::removal::push_removal;
use super::RedundantCopDisableDirective;
use crate::cop::{cop_ran_in_lint, Cop, CopConfig};
use crate::correction::Correction;
use crate::diagnostic::Diagnostic;
use crate::parse::source::SourceFile;

fn cop_matches_disabled(offense_cop: &str, disabled: &str) -> bool {
    if disabled.eq_ignore_ascii_case("all") {
        return true;
    }
    if disabled.eq_ignore_ascii_case(offense_cop) {
        return true;
    }
    offense_cop
        .split_once('/')
        .is_some_and(|(dept, _)| disabled.eq_ignore_ascii_case(dept))
}

fn offense_in_range(offense: &Diagnostic, range: (usize, usize)) -> bool {
    offense.location.line >= range.0 && offense.location.line <= range.1
}

fn directive_needed(cop: &str, range: (usize, usize), offenses: &[Diagnostic]) -> bool {
    offenses.iter().any(|o| {
        cop_matches_disabled(&o.cop_name, cop) && offense_in_range(o, range)
    })
}

fn active_for_redundant_audit(c: &dyn Cop) -> bool {
    cop_ran_in_lint(c) && c.redundant_disable_audit()
}

fn cop_in_department(c: &dyn Cop, dept: &str) -> bool {
    c.name()
        .split_once('/')
        .is_some_and(|(d, _)| d.eq_ignore_ascii_case(dept))
}

fn any_active_cop(active: &[(&dyn Cop, &CopConfig)], pred: impl Fn(&dyn Cop) -> bool) -> bool {
    active.iter().any(|(c, _)| pred(*c))
}

fn cop_auditable(name: &str, active: &[(&dyn Cop, &CopConfig)]) -> bool {
    if name.eq_ignore_ascii_case("all") {
        return any_active_cop(active, active_for_redundant_audit);
    }
    if name.contains('/') {
        return any_active_cop(active, |c| {
            c.name().eq_ignore_ascii_case(name) && active_for_redundant_audit(c)
        });
    }
    any_active_cop(active, |c| active_for_redundant_audit(c) && cop_in_department(c, name))
}

fn cop_is_redundant(
    name: &str,
    dir: &DisableDirective,
    offenses: &[Diagnostic],
    active: &[(&dyn Cop, &CopConfig)],
) -> bool {
    cop_auditable(name, active) && !directive_needed(name, dir.range, offenses)
}

fn redundant_offense(
    cop: &RedundantCopDisableDirective,
    source: &SourceFile,
    line: &str,
    dir: &DisableDirective,
    name: &str,
) -> Diagnostic {
    let col = redundant_col(line, name, dir.column);
    let mut diag = cop.diagnostic(
        source,
        dir.line,
        col,
        format!("Unnecessary disabling of `{name}`."),
    );
    diag.highlight_length = cop_highlight(line, col, name);
    diag
}

fn block_disable_cops(source: &SourceFile, line_no: usize) -> Option<Vec<String>> {
    let line = source.line_text(line_no)?;
    if !line.trim_start().starts_with('#') {
        return None;
    }
    let (_, rest) = disable_marker(&line)?;
    Some(cop_names(rest))
}

fn all_block_disable_cops_redundant(
    source: &SourceFile,
    dir: &DisableDirective,
    offenses: &[Diagnostic],
    active: &[(&dyn Cop, &CopConfig)],
) -> bool {
    block_disable_cops(source, dir.line).is_some_and(|cops| {
        cops.iter()
            .all(|name| cop_is_redundant(name, dir, offenses, active))
    })
}

fn remove_entire_disable_line(
    source: &SourceFile,
    dir: &DisableDirective,
    offenses: &[Diagnostic],
    active: &[(&dyn Cop, &CopConfig)],
    remove_entire: bool,
) -> bool {
    if !remove_entire {
        return false;
    }
    if dir.range.0 == dir.range.1 {
        return true;
    }
    all_block_disable_cops_redundant(source, dir, offenses, active)
}

fn apply_redundant_fix(
    source: &SourceFile,
    dir: &DisableDirective,
    name: &str,
    remove_entire: bool,
    entire_fix: &mut bool,
    offenses: &[Diagnostic],
    active: &[(&dyn Cop, &CopConfig)],
    mut corrections: Option<&mut Vec<Correction>>,
    diag: &mut Diagnostic,
) {
    if remove_entire_disable_line(source, dir, offenses, active, remove_entire) {
        if !*entire_fix {
            push_removal(
                source,
                dir.line,
                name,
                None,
                corrections.as_deref_mut(),
                diag,
            );
            *entire_fix = true;
        } else if corrections.is_some() {
            diag.corrected = true;
        }
        return;
    }
    push_removal(
        source,
        dir.line,
        name,
        Some(1),
        corrections.as_deref_mut(),
        diag,
    );
}

fn report_directive_redundancies(
    cop: &RedundantCopDisableDirective,
    source: &SourceFile,
    line: &str,
    dir: &DisableDirective,
    offenses: &[Diagnostic],
    active: &[(&dyn Cop, &CopConfig)],
    diagnostics: &mut Vec<Diagnostic>,
    mut corrections: Option<&mut Vec<Correction>>,
) {
    let redundant: Vec<&String> = dir
        .cops
        .iter()
        .filter(|name| cop_is_redundant(name, dir, offenses, active))
        .collect();
    if redundant.is_empty() {
        return;
    }
    let remove_entire = redundant.len() == dir.cops.len();
    let mut entire_fix = false;
    for name in redundant {
        let mut diag = redundant_offense(cop, source, line, dir, name);
        apply_redundant_fix(
            source,
            dir,
            name,
            remove_entire,
            &mut entire_fix,
            offenses,
            active,
            corrections.as_deref_mut(),
            &mut diag,
        );
        diagnostics.push(diag);
    }
}

/// Post-pass: compare disable directives against offenses found before filtering.
pub fn audit_redundant_disables(
    cop: &RedundantCopDisableDirective,
    source: &SourceFile,
    offenses: &[Diagnostic],
    active: &[(&dyn Cop, &CopConfig)],
    diagnostics: &mut Vec<Diagnostic>,
    mut corrections: Option<&mut Vec<Correction>>,
) {
    for dir in collect_directives(source) {
        let line = source.line_text(dir.line).unwrap_or("").to_string();
        report_directive_redundancies(
            cop,
            source,
            &line,
            &dir,
            offenses,
            active,
            diagnostics,
            corrections.as_deref_mut(),
        );
    }
}