1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
//! `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;
}
_ => {}
}
}
}