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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
//! Cross-language `Npa` helper functions shared by the per-language
//! submodules and by sibling metrics (`npm`, `checker`).
//!
//! The public metric `Stats`, the `Npa` trait, and the
//! `impl_npa_java_like!` / `ts_npa_compute!` / `ts_member_is_public!` /
//! `js_npa_compute!` macros stay in the parent module; only the
//! cross-language predicate helpers live here so the parent clears the
//! 800-SLOC self-scan limit (#976).
use *;
// Distinguishes interface-like containers (interface, trait, annotation
// type) — whose members are implicitly public — from class-like
// containers (class, enum, record) that need an explicit `public`
// modifier. The dekobon grammar models all of these bodies as
// `class_body`, so the discriminant lives on the parent. Shared with
// `impl Npm for GroovyCode` (`metrics::npm`).
pub
// Detects an explicit `public` modifier on a class member declaration.
// The dekobon grammar flattens the `_modifier` rule, so modifier
// tokens appear as direct children of the declaration — no `Modifiers`
// wrapper to descend into. Shared with `impl Npm for GroovyCode`.
pub
// C# uses individual `Modifier` nodes (not wrapped under a single
// `modifiers` node like Java); detecting `public` requires scanning
// every Modifier child of the declaration for a `public` keyword.
pub
// A C# member or accessor node carries an explicit visibility-narrowing
// modifier (`private` / `protected`). The single source of truth for the
// C# narrowing rule, shared by the npa interface-member gate below and the
// npm accessor gate (`csharp_member_public_method_count`).
pub
// C# 8+ interface members default to public, so a field is a public
// attribute UNLESS it carries an explicit `private` or `protected`
// modifier. This is the inverse of `csharp_is_explicit_public` (the
// class rule, which requires an explicit `public`): missing modifier
// means public here, matching `ts_member_is_public!`'s default-public
// gate for TypeScript class members.
pub
// PHP's strict-explicit visibility rule (mirroring Java's pattern): a
// declaration is treated as public only when it carries an explicit
// `public` modifier. Modifier-less declarations — deprecated for
// properties since PHP 8 and merely conventional for methods — are NOT
// counted, even though PHP semantically defaults methods to public.
pub
// Counts the number of symbol arguments passed to an `attr_accessor` /
// `attr_reader` / `attr_writer` macro `Call` node. `attr_accessor :a,
// :b, :c` exposes three attributes; an `attr_*` call with no arguments
// is ill-formed Ruby but defensively returns zero rather than one.
pub
// Ruby class-body visibility state. `private` / `public` / `protected`
// keywords flip this flag for every subsequent declaration in the same
// body until another marker overrides them. The default at the top of
// every class body is `Public`.
pub
// Recognises a bare visibility-keyword `identifier` child of a Ruby
// class body (`private` / `public` / `protected` with no arguments).
// tree-sitter-ruby emits the keyword-form as a literal `identifier`
// token; the argument-form (`private :foo`, `private def bar`) is a
// `Call` node instead and does NOT flip the body-wide flag.
pub
// Identifies the `attr_*` macro family on a Ruby `Call` node. Each
// macro takes a list of attribute symbols and synthesises the matching
// reader / writer / accessor methods on the enclosing class.
pub
// Walks the direct children of a Ruby class / singleton-class body
// (`BodyStatement` under `Class` / `SingletonClass`) tallying:
// - class-scope assignments to `@var` (`InstanceVariable`) and
// `@@var` (`ClassVariable`) — one attribute per assignment, regardless
// of whether the RHS is a constant or another expression.
// - `attr_accessor` / `attr_reader` / `attr_writer` macros — one
// attribute per symbol argument.
//
// Visibility flags follow Ruby's keyword-marker convention: a bare
// `private` / `public` / `protected` identifier flips the default for
// every subsequent declaration in the body. The default visibility at
// the top of every class body is `public`. The argument-form of those
// keywords (`private :foo`, `private def x`) does not flip the body-
// wide flag — matching Ruby's runtime behaviour.
//
// Attribute assignments to instance/class variables are visible only
// via the methods that wrap them, so the visibility flag at the point
// of declaration is what `npa` should reflect.
pub
// Go's lexical export rule: an identifier names an exported (public)
// member iff its first character is an uppercase Unicode letter
// (`unicode.IsUpper`). Uses `char::is_uppercase` on the first `char`
// (not `is_ascii_uppercase`) so non-ASCII exports such as `Ärger`
// are recognised. The blank identifier `_` and lowercase names are
// unexported. Shared by `Npa` (field names) and `Npm` (method
// names); see issue #458.
pub
// Extracts the declared name of a Go struct field for visibility
// purposes. A named field carries one or more `FieldIdentifier`
// children; an embedded field has none and instead embeds a type,
// whose base identifier (`TypeIdentifier`) is the field's name —
// `io.Reader` embeds as `Reader`, `*Foo` as `Foo`. Returns the
// embedded type's name node so callers can read its text from
// `code`.
pub
// Name-based for cross-grammar correctness (see `cpp_count_field_identifiers`).
//
// A `function_definition` child is accepted outright rather than
// recursed into: it *is* a function, whatever declarator it carries.
// Recursing would miss a conversion operator with an inline body,
// whose declarator is an `operator_cast` and not a
// `function_declarator` (#1258).
//
// A conversion operator declared *without* a body has no
// `function_definition` to accept either — it parses as a bare
// `declaration > operator_cast`, or `template_declaration >
// declaration > operator_cast` in the templated form, with only an
// `abstract_function_declarator` underneath. `operator_cast` is
// therefore matched in its own right; matching the
// `abstract_function_declarator` instead would also claim ordinary
// function-pointer type positions (#1298).
//
// A `function_declarator`, by contrast, is *not* accepted outright:
// the same node spells two opposite things, and only its `declarator`
// field tells them apart. A name (`realMethod()`) or an
// `operator_name` (`operator->()`) is a member function; a
// `parenthesized_declarator` may be either, so it is handed to
// `cpp_parenthesized_declares_function` (#1300).
//
// Neither `function_definition` nor `operator_cast` can be reached
// from the `Npa` call sites, which only ever pass a
// `field_declaration`: per both grammars' `node-types.json`, neither
// `field_declaration` nor the `pointer_declarator` /
// `reference_declarator` wrappers this recurses through can carry a
// `function_definition`, an `operator_cast`, or a `declaration` — so
// there is no path to either at any depth. Both are live only for
// `Npm`'s `declaration` / `template_declaration` children.
pub
// Given the `parenthesized_declarator` in a `function_declarator`'s
// `declarator` field, says whether the enclosing declaration is still
// a function. Parentheses alone do not make it a function *pointer* —
// what does is an indirection interposed inside them:
//
// - `void (f)();` and `int (operator+)(int);` are ordinary member
// functions written with redundant parentheses. This is the
// macro-defence idiom (`int (max)(int, int);`), which suppresses
// expansion of a function-like macro of the same name.
// - `int (*fp)(int);` interposes a `*`, so the parameter list belongs
// to the pointee and the member is data.
// - `int (*getFp(int))(int);` interposes a `*` too, but wraps a
// further `function_declarator`, so it is a function *returning* a
// function pointer. Neither language lets a function return a
// function type, so that inner nesting is the only reading — the
// same grammar fact `crate::c_declarator` relies on.
//
// Hence: every child must still read as a function. An indirection
// qualifies only when `cpp_declares_function` finds a function under
// it; a bare name, an `operator_name`, or an `ms_call_modifier`
// (`int (__cdecl *f)(int)`) never disqualifies on its own.
// Matches on node-kind NAMES, not one grammar's enum discriminants, so it
// is correct for every C-family grammar: upstream `Cpp` and the Mozilla
// `Mozcpp` fork (#720) assign *different* kind_ids to the same node kinds,
// and this helper is shared by both impls. (A `kind_id().into()` against a
// fixed `Cpp` enum silently miscounted Mozcpp nodes, zeroing its `npa`.)
// Each declarator's aliases all render to the one base name, so a single
// string arm covers the family.
pub
// Returns `true` if `pat` contains exactly one `UNDERSCORE` token
// (identified by `underscore_id`) and no other named children.
// Anonymous tokens such as a leading `|` in a Rust or-pattern
// (`| _ => ...`) are skipped — they do not change the semantic
// meaning of the pattern.
//
// Shared between languages whose `default:`-equivalent wildcard
// pattern is a single `_`:
// - Rust `match_pattern` (`Cyclomatic` and `Abc` for `RustCode`)
// - Python `case_pattern` (`Abc` for `PythonCode`)
//
// The Rust caller passes its grammar's `UNDERSCORE` kind id; Python
// passes its own. Guard handling is the caller's responsibility —
// in Rust the guard is a sibling inside `match_pattern` and so adds
// a named child here (this helper returns `false`); in Python the
// guard is an `if_clause` sibling on the enclosing `case_clause`,
// so the caller must check the surrounding node separately.
pub
// Returns `true` iff a Python `case_clause` should count as a
// non-trivial decision: either the pattern is not a bare `_`, or
// the clause carries an `if`-guard (`case _ if g:`).
//
// Shared between the `Cyclomatic` and `Abc` implementations for
// `PythonCode`. The bare wildcard without a guard is Python's
// `default:`-equivalent and is filtered out, matching Rust's bare-`_`
// MatchArm rule and Java/C#'s `default:` rule.
//
// `underscore_id` is the grammar's `Python::UNDERSCORE` kind id,
// passed in so the helper does not assume a particular module-path
// to the language enum.
pub
// Returns `true` iff a Ruby `in_clause` pattern-match arm (`case … in`)
// counts as a non-trivial decision: either its pattern is not a bare
// `_`, or the arm carries an `if` / `unless` guard. A bare wildcard
// `in _` with no guard is Ruby's `case … in` default arm and is
// filtered out, mirroring Rust's bare-`_` `MatchArm` rule and Python's
// `case _:` rule (#977).
//
// Shared between the `Cyclomatic` and `Abc` implementations for
// `RubyCode`. Ruby surfaces the wildcard as an `identifier` whose
// source text is `_` (there is no dedicated underscore token, unlike
// Rust / Python), so the pattern check reads the byte slice rather than
// matching a kind id. The pattern is the first *named* child — the `in`
// keyword token is anonymous and the guard / body follow the pattern.
pub
// A `visibility_modifier` node counts as public unless it has a direct
// `Zelf` child — the structural signature of `pub(self)` / `pub(in self)`,
// which restrict visibility to the current module (semantically private,
// issue #460). `pub(in self::inner)` nests `self` inside a
// `scoped_identifier` (not a direct child) and stays public. Shared by
// `rust_item_is_public` and the tuple-struct positional-field path so the
// two never drift.
pub
// Returns true if `node` carries a `visibility_modifier` that makes the
// item public to its definition. Matches Rust's "public-only-when-`pub`"
// model: bare `pub`, `pub(crate)`, `pub(super)`, and `pub(in <path>)`
// widen visibility beyond the current module and count as public.
//
// The exceptions are `pub(self)` and `pub(in self)`, which restrict
// visibility to the current module — semantically identical to no
// modifier (private), so they must NOT count toward npm/npa (issue
// #460). The grammar emits the restriction keyword as a dedicated
// child of the `visibility_modifier`: `self` is a `Zelf` node, `crate`
// a `Crate` node, `super` a `Super` node. `pub(self)` and `pub(in self)`
// are exactly the forms whose `visibility_modifier` has a direct `Zelf`
// child; `pub(in self::inner)` nests `self` inside a `scoped_identifier`
// (not a direct child) and is treated as public, matching the issue's
// "only `self` / `in self` is private" scope. No source-text inspection
// is needed — the structural check is precise.
pub
// Single normalization point for Python's aliased `block` kind_ids.
//
// tree-sitter-python lists two `kind_id`s that both stringify to
// `"block"`: `Block` (135, the hidden `_block` supertype) and
// `Block2` (160, the concrete production). Empirically only `Block2`
// is ever emitted for real block bodies (function, class, if/for,
// while/try/with), so `Block` is dead today — but a future grammar
// bump could promote the supertype to a concrete node. Routing every
// "is this a block?" check through here means such a bump is handled
// at one site instead of silently undercounting at several (issue
// #419; lesson 2 / 34 / 56 in docs/development/lessons_learned.md).
pub
// Kotlin's grammar models classes and interfaces under a single
// `class_declaration` node; the `class` / `interface` keyword child
// disambiguates. A `ClassBody` belongs to an interface iff its parent
// `class_declaration` has an `interface` keyword child.
pub
// Kotlin's default visibility is `public`. A declaration is non-public
// only when it carries an explicit `private` / `protected` / `internal`
// modifier under its `Modifiers` child. Returns `true` for missing
// `Modifiers`, missing `VisibilityModifier`, or an explicit `public`
// modifier.
pub