big-code-analysis 2.0.0

Tool to compute and export code metrics
Documentation
//! `Npm` implementation for Go.
#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
#![allow(
    clippy::cast_precision_loss,
    clippy::cast_possible_truncation,
    clippy::cast_sign_loss
)]

use super::*;

impl Npm for GoCode {
    fn compute<'a>(node: &Node<'a>, code: &'a [u8], stats: &mut Stats) {
        use Go as G;

        match node.kind_id().into() {
            // First-visit pass on the file root: enable npm output if
            // the file declares any receiver methods. Walks only the
            // direct children — Go always declares methods at file
            // scope, so deeper recursion is unnecessary.
            G::SourceFile
                if stats.is_disabled()
                    && node
                        .children()
                        .any(|c| matches!(c.kind_id().into(), G::MethodDeclaration)) =>
            {
                stats.is_class_space = true;
            }
            // Each receiver method contributes one to the per-space
            // count; `compute_sum` rolls it into `class_nm_sum`, and
            // the parent's merge bubbles it up to the Unit. The
            // method's own space is left unmarked so its
            // per-function npm block stays suppressed.
            G::MethodDeclaration => {
                stats.class_nm += 1;
                // Go's export rule is lexical (issue #458): the method
                // is public iff its own name's first character is an
                // uppercase Unicode letter — the receiver's visibility
                // is irrelevant. The method name is the
                // `FieldIdentifier` child of the declaration.
                let exported = node
                    .children()
                    .find(|c| matches!(c.kind_id().into(), G::FieldIdentifier))
                    .and_then(|name| name.utf8_text(code))
                    .is_some_and(super::npa::go_is_exported);
                if exported {
                    stats.class_npm += 1;
                }
            }
            // `interface { Foo(); Bar() int }` declares method
            // signatures via `MethodElem` children of an
            // `InterfaceType`. Interfaces have no func_space of
            // their own, so the count lands on the enclosing space
            // (typically Unit). Embedded interfaces (`io.Reader`)
            // parse as `TypeElem`, not `MethodElem`, so they are not
            // counted here. Go's export rule is lexical and applies
            // to interface method names too (issue #471, the
            // interface-side analog of #458): a signature is public
            // iff its name's first character is an uppercase Unicode
            // letter. `interface_nm` counts every method element;
            // `interface_npm` only the exported ones.
            G::InterfaceType => {
                let mut methods = 0_usize;
                let mut exported = 0_usize;
                for method in node
                    .children()
                    .filter(|c| matches!(c.kind_id().into(), G::MethodElem))
                {
                    methods += 1;
                    // The method name is the `FieldIdentifier` child,
                    // mirroring the `MethodDeclaration` arm; a missing
                    // name is treated as not-exported.
                    let is_exported = method
                        .children()
                        .find(|c| matches!(c.kind_id().into(), G::FieldIdentifier))
                        .and_then(|name| name.utf8_text(code))
                        .is_some_and(super::npa::go_is_exported);
                    if is_exported {
                        exported += 1;
                    }
                }
                if methods == 0 {
                    return;
                }
                if stats.is_disabled() {
                    stats.is_class_space = true;
                }
                stats.interface_nm += methods;
                stats.interface_npm += exported;
            }
            _ => {}
        }
    }
}