lisette-semantics 0.4.1

Little language inspired by Rust that compiles to Go
Documentation
use crate::passes::walk::NodeCtx;
use syntax::ast::Expression;

pub fn check_exit_after_defer(expression: &Expression, ctx: &NodeCtx) {
    let body = match expression {
        Expression::Function { body, .. } | Expression::Lambda { body, .. } => body,
        _ => return,
    };

    scan(body, false, ctx);
}

fn scan(expression: &Expression, pending: bool, ctx: &NodeCtx) {
    match expression {
        Expression::Function { .. } | Expression::Lambda { .. } => {}
        Expression::Block { items, .. } => {
            let mut pending = pending;
            for item in items {
                scan(item, pending, ctx);
                if matches!(item, Expression::Defer { .. }) {
                    pending = true;
                }
            }
        }
        Expression::Call {
            expression: callee,
            span,
            ..
        } => {
            if pending && is_os_exit(callee) {
                ctx.sink.push(diagnostics::lint::exit_after_defer(span));
            }
            for child in expression.children() {
                scan(child, pending, ctx);
            }
        }
        _ => {
            for child in expression.children() {
                scan(child, pending, ctx);
            }
        }
    }
}

fn is_os_exit(callee: &Expression) -> bool {
    let Expression::DotAccess {
        expression: receiver,
        member,
        ..
    } = callee.unwrap_parens()
    else {
        return false;
    };
    member.as_str() == "Exit" && receiver.get_type().as_import_namespace() == Some("go:os")
}