lisette-semantics 0.4.2

Little language inspired by Rust that compiles to Go
Documentation
use crate::passes::walk::NodeCtx;
use syntax::ast::{Expression, MatchArm, MatchOrigin, Span};

use super::helpers::{
    enum_variant_binding, is_bare_identifier, is_none_pattern, span_text, wraps_binding,
};

pub fn check_needless_match(expression: &Expression, ctx: &NodeCtx) {
    let Expression::Match {
        subject,
        arms,
        origin,
        span,
        ..
    } = expression
    else {
        return;
    };

    if matches!(origin, MatchOrigin::IfLet { .. }) {
        return;
    }

    if arms.len() != 2 || arms.iter().any(MatchArm::has_guard) {
        return;
    }

    let subject_ty = subject.get_type();
    let result_ty = expression.get_type();
    let reconstructs = if subject_ty.is_option() && result_ty.is_option() {
        option_passthrough(&arms[0], &arms[1])
    } else if subject_ty.is_result() && result_ty.is_result() {
        result_passthrough(&arms[0], &arms[1])
    } else {
        return;
    };

    if !reconstructs {
        return;
    }

    let Some(subject_text) = span_text(ctx.source, subject) else {
        return;
    };

    let match_keyword_span = Span::new(span.file_id, span.byte_offset, 5);
    ctx.sink.push(diagnostics::lint::needless_match(
        &match_keyword_span,
        subject_text,
    ));
}

fn option_passthrough(a: &MatchArm, b: &MatchArm) -> bool {
    let pair = |some_arm: &MatchArm, none_arm: &MatchArm| {
        rewraps_self(some_arm, "Some")
            && is_none_pattern(&none_arm.pattern)
            && is_bare_identifier(&none_arm.expression, "None")
    };
    pair(a, b) || pair(b, a)
}

fn result_passthrough(a: &MatchArm, b: &MatchArm) -> bool {
    let pair = |ok_arm: &MatchArm, err_arm: &MatchArm| {
        rewraps_self(ok_arm, "Ok") && rewraps_self(err_arm, "Err")
    };
    pair(a, b) || pair(b, a)
}

fn rewraps_self(arm: &MatchArm, variant: &str) -> bool {
    enum_variant_binding(&arm.pattern, variant)
        .is_some_and(|binding| wraps_binding(&arm.expression, variant, binding))
}