big-code-analysis 2.3.1

Tool to compute and export code metrics
Documentation
#![allow(
    clippy::enum_glob_use,
    clippy::too_many_lines,
    clippy::wildcard_imports
)]
#![allow(
    clippy::cast_precision_loss,
    clippy::cast_possible_truncation,
    clippy::cast_sign_loss
)]

use super::cpp::{
    cpp_count_arguments, cpp_count_chain_operands, cpp_count_condition_slot, cpp_count_return,
    cpp_walk_for_statement, cpp_walk_ternary,
};
use super::{Abc, Stats};
use crate::metrics::cyclomatic::cpp_operator_is_applied;
use crate::*;

impl Abc for MozcppCode {
    fn compute<'a>(
        node: &Node<'a>,
        _code: &'a [u8],
        ancestors: Ancestors<'a, '_>,
        stats: &mut Stats,
    ) {
        // bca: suppress(cyclomatic)
        // Exhaustive one-arm-per-grammar-kind dispatch table; see the
        // rationale on `CppCode::compute`, of which this is the
        // Mozilla-fork clone.
        use Mozcpp::*;

        // A requires clause pays no condition — see the Cpp twin (#1533).
        let opens = matches!(node.kind_id().into(), RequiresClause | RequiresExpression);
        let in_constraint = stats.constraint.covers(node, opens);

        match node.kind_id().into() {
            // `assignment_expression` covers both plain `=` and every
            // compound form (`+=`, `-=`, `*=`, `/=`, `%=`, `&=`, `|=`,
            // `^=`, `<<=`, `>>=`); the grammar lifts them all into a
            // single named node so we count once per
            // `assignment_expression`. `update_expression` covers both
            // prefix and postfix `++` / `--`.
            AssignmentExpression | AssignmentExpression2 | UpdateExpression => {
                stats.assignments += 1.;
            }
            // `int x = expr;` parses as a `declaration` carrying an
            // `init_declarator` of the form `declarator = value`. Per
            // Fitzpatrick (1997), every `=` operator increments A; the
            // JS impl already counts `let x = 5;` (and excludes
            // `const`). We follow the literal reading for C++ too and
            // count every `init_declarator` whose body contains an
            // explicit `=` token. `const int x = 5;` is counted along
            // with `int x = 5;` — distinguishing them would diverge
            // from the JS rule's "let counted, const not" mapping
            // because C++ `const` semantics are unlike JS `const` (a
            // C++ `const int x` binding is the canonical "one
            // assignment to initialise" — closer to Rust's
            // non-`mut` `let` than to JS's hoisted reference binding).
            // `int x;` parses as a plain declarator inside the
            // `declaration`, not an `init_declarator`, so this arm
            // never fires for un-initialised declarations. The second
            // `init_declarator` grammar form `int x(5);` / `int x{5};`
            // (paren / brace init) carries no `=` token and stays out
            // — only the `=` operator counts.
            InitDeclarator if node.first_child(|id| id == EQ as u16).is_some() => {
                stats.assignments += 1.;
            }
            // Every call counts (method calls fold in as
            // `call_expression` with a `field_expression` callee). The
            // C++ grammar exposes two aliased `call_expression` ids.
            // `new T(...)` allocations count as a branch — they invoke
            // a constructor, mirroring Java's `New` and C#'s
            // `ObjectCreationExpression` rule.
            CallExpression | CallExpression2 | NewExpression => {
                stats.branches += 1.;
            }
            // Only the A and B arms above apply in a constraint.
            _ if in_constraint => {}
            // `else` opens an alternative branch path; `case`
            // (non-default) adds one per switch arm; `?` opens a
            // ternary; `try` / `catch` count per Fitzpatrick (and
            // Java's rule). `Try2` is the second token-id alias the
            // C++ grammar emits for `try` (it appears under
            // structured-exception forms).
            //
            // `&&` / `||` are deliberately NOT counted (Fitzpatrick
            // Rule 7 in Figure 3 for C++; the unary-conditional
            // counterpart is Rule 9). See the module-level `Stats`
            // doc-comment for the cross-language policy (issue
            // #395, walker tracked in #403).
            Else | Case | QMARK | Try | Try2 | Catch => {
                stats.conditions += 1.;
            }
            // The seven comparison tokens count only when applied. The
            // vendored grammar has the same productions for them as
            // tree-sitter-cpp; the `grammar.json` sweep, the fold and
            // `#if` decisions, and the operator-overload defect (#1448)
            // are recorded on the matching arm of `CppCode::compute`.
            LT | GT | LTEQ | GTEQ | EQEQ | BANGEQ | NotEq | LTEQGT
                if cpp_operator_is_applied(node, ancestors) =>
            {
                stats.conditions += 1.;
            }
            // Fitzpatrick Rule 9 (C++ in Figure 3): each operand of a
            // `&&` / `||` chain is one condition (issue #403). `and` /
            // `or` are the same operators spelled as ISO alternative
            // tokens, which the grammar gives kinds of their own.
            //
            // Gated like the comparisons, so a `constraint_conjunction`
            // and a binary fold's second
            // operator spelling score nothing; the derivation is on the
            // Cpp twin.
            AMPAMP | PIPEPIPE | And | Or if cpp_operator_is_applied(node, ancestors) => {
                if let Some(parent) = ancestors.parent(node) {
                    cpp_count_chain_operands(&parent, &mut stats.conditions);
                }
            }
            // Phase-2B (issue #403): condition slots, read by grammar field
            // in `cpp_count_condition_slot`. The slot pays for any
            // predicate no other arm counts (#1526), so `if (true)`,
            // `if (-x)` and `return !x` each count one condition; bare
            // `return x` reports zero.
            IfStatement | WhileStatement | DoStatement => {
                cpp_count_condition_slot(node, &mut stats.conditions);
            }
            // `co_return` reads its value as `return` does; the
            // derivation is on the Cpp twin (#1547).
            ReturnStatement | CoReturnStatement => {
                cpp_count_return(node, &mut stats.conditions);
            }
            // `f(!a, !b)` — argument list walker. Two aliases —
            // `argument_list` is emitted as ArgumentList or
            // ArgumentList2 depending on production rule path.
            ArgumentList | ArgumentList2 => {
                cpp_count_arguments(node, &mut stats.conditions);
            }
            // `a ? !b : !c` — the ternary's own `?` token is already
            // counted by the condition arm above; this walks the three
            // operand slots (issue #1102).
            ConditionalExpression => {
                cpp_walk_ternary(node, &mut stats.conditions);
            }
            // `for (init; cond; update)` — the condition slot, read by
            // grammar field (issue #1276). `for (;;)` has no condition
            // field and counts nothing.
            ForStatement => {
                cpp_walk_for_statement(node, &mut stats.conditions);
            }
            _ => {}
        }
    }
}