rrubocop 0.8.15

10x faster RuboCop drop-in. Same output format and configs, without maintaining RuboCop gems.
use tree_sitter::Node;

use crate::cop::shared::{call_method_name, call_receiver};
use crate::parse::source::SourceFile;

use super::common::{binary_op, literal_or_const, same_target, sides, unwrap};

fn match_side<'a>(
    source: &'a SourceFile,
    l: Node<'a>,
    r: Node<'a>,
    target: Node<'a>,
) -> Option<Node<'a>> {
    let l = unwrap(l);
    let r = unwrap(r);
    if same_target(source, l, target) && r.kind() == "regex" {
        return Some(r);
    }
    if same_target(source, r, target) && l.kind() == "regex" {
        return Some(l);
    }
    None
}

fn equality_side<'a>(
    source: &'a SourceFile,
    l: Node<'a>,
    r: Node<'a>,
    target: Node<'a>,
) -> Option<Node<'a>> {
    let l = unwrap(l);
    let r = unwrap(r);
    if same_target(source, l, target) && literal_or_const(source, r) {
        return Some(r);
    }
    if same_target(source, r, target) && literal_or_const(source, l) {
        return Some(l);
    }
    None
}

fn collect_binary_condition(source: &SourceFile, n: Node<'_>, target: Node<'_>) -> bool {
    let op = binary_op(source, n).unwrap_or(b"");
    let (l, r) = sides(n).unwrap();
    match op {
        b"=~" => match_side(source, l, r, target).is_some(),
        b"==" | b"eql?" | b"equal?" | b"===" => equality_side(source, l, r, target).is_some(),
        _ => false,
    }
}

fn collect_call_condition(source: &SourceFile, n: Node<'_>, target: Node<'_>) -> bool {
    let meth = call_method_name(source, n).unwrap_or(b"");
    let recv = call_receiver(n);
    match meth {
        b"=~" | b"match" | b"match?" => match (
            recv,
            crate::cop::shared::argument_nodes(n).first().copied(),
        ) {
            (Some(r), Some(a)) => match_side(source, r, a, target).is_some(),
            _ => false,
        },
        b"is_a?" => recv.is_some_and(|r| same_target(source, r, target)),
        _ => false,
    }
}

pub(super) fn collect_condition(source: &SourceFile, node: Node<'_>, target: Node<'_>) -> bool {
    let n = unwrap(node);
    if n.kind() == "binary" && binary_op(source, n) == Some(b"||") {
        let (l, r) = sides(n).unwrap();
        return collect_condition(source, l, target) && collect_condition(source, r, target);
    }
    if binary_op(source, n).is_some() {
        return collect_binary_condition(source, n, target);
    }
    if n.kind() == "call" {
        return collect_call_condition(source, n, target);
    }
    false
}