perl-pragma 0.15.2

Perl pragma extraction and analysis primitives
Documentation
use super::directives::{apply_no_directive, apply_use_directive};
use crate::PragmaState;
use perl_ast::ast::{Node, NodeKind};
use std::ops::Range;

pub(crate) fn build_ranges(
    node: &Node,
    current_state: &mut PragmaState,
    ranges: &mut Vec<(Range<usize>, PragmaState)>,
) {
    match &node.kind {
        NodeKind::Use { module, args, .. } => {
            apply_use_directive(
                node.location.start..node.location.end,
                module,
                args,
                current_state,
                ranges,
            );
        }
        NodeKind::No { module, args, .. } => {
            apply_no_directive(
                node.location.start..node.location.end,
                module,
                args,
                current_state,
                ranges,
            );
        }
        NodeKind::Block { statements } => {
            build_statement_block(statements, node.location.end, current_state, ranges);
        }
        NodeKind::Program { statements } => {
            for stmt in statements {
                build_ranges(stmt, current_state, ranges);
            }
        }
        NodeKind::Subroutine { body, .. }
        | NodeKind::Method { body, .. }
        | NodeKind::Class { body, .. } => {
            build_scoped_body(body, current_state, ranges);
        }
        NodeKind::If { then_branch, elsif_branches, else_branch, .. } => {
            build_scoped_body(then_branch, current_state, ranges);
            for (_, elsif_body) in elsif_branches {
                build_scoped_body(elsif_body, current_state, ranges);
            }
            if let Some(else_b) = else_branch {
                build_scoped_body(else_b, current_state, ranges);
            }
        }
        NodeKind::While { body, continue_block, .. }
        | NodeKind::For { body, continue_block, .. }
        | NodeKind::Foreach { body, continue_block, .. } => {
            build_scoped_body(body, current_state, ranges);
            if let Some(continue_block) = continue_block {
                build_scoped_body(continue_block, current_state, ranges);
            }
        }
        NodeKind::Eval { block } => {
            if matches!(block.kind, NodeKind::Block { .. }) {
                build_scoped_body(block, current_state, ranges);
            }
        }
        NodeKind::Do { block } | NodeKind::Defer { block } | NodeKind::PhaseBlock { block, .. } => {
            build_scoped_body(block, current_state, ranges);
        }
        NodeKind::Given { body, .. } | NodeKind::When { body, .. } | NodeKind::Default { body } => {
            build_scoped_body(body, current_state, ranges);
        }
        NodeKind::Try { body, catch_blocks, finally_block } => {
            build_scoped_body(body, current_state, ranges);
            for (_, catch_body) in catch_blocks {
                build_scoped_body(catch_body, current_state, ranges);
            }
            if let Some(finally_block) = finally_block {
                build_scoped_body(finally_block, current_state, ranges);
            }
        }
        NodeKind::LabeledStatement { statement, .. } => {
            build_ranges(statement, current_state, ranges);
        }
        NodeKind::StatementModifier { statement, condition, .. } => {
            build_ranges(statement, current_state, ranges);
            build_ranges(condition, current_state, ranges);
        }
        NodeKind::Package { block: Some(pkg_block), .. } => {
            build_scoped_body(pkg_block, current_state, ranges);
        }
        _ => {}
    }
}

fn build_scoped_body(
    body: &Node,
    current_state: &mut PragmaState,
    ranges: &mut Vec<(Range<usize>, PragmaState)>,
) {
    let saved_state = current_state.clone();
    build_ranges(body, current_state, ranges);
    *current_state = saved_state;
    ranges.push((body.location.end..body.location.end, current_state.clone()));
}

fn build_statement_block(
    statements: &[Node],
    end: usize,
    current_state: &mut PragmaState,
    ranges: &mut Vec<(Range<usize>, PragmaState)>,
) {
    let saved_state = current_state.clone();
    for stmt in statements {
        build_ranges(stmt, current_state, ranges);
    }
    *current_state = saved_state;
    ranges.push((end..end, current_state.clone()));
}