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
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
//! 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.
//
// The wrapper-plus-leaf shape this and the sibling below assume is the
// only one that reaches them. Two other rules alias a bare keyword to
// `modifier`, which makes those childless (#1418):
// `_parameter_type_with_modifiers` (six keywords, under `parameter`)
// and `_lambda_expression_init` / `anonymous_method_expression`
// (`static` / `async`, under a lambda). Neither is a direct child of a
// declaration — `parameter_list` intervenes for the first, and a lambda
// is an expression — and both helpers are handed a class member or an
// `accessor_declaration` only. They also use `first_child`, so a
// childless node would answer `false` rather than mis-answer even if
// one did arrive.
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
// Named arguments of a Ruby `Call`, in source order. The grammar hangs
// them off an `argument_list` — aliased to two visible ids — whose
// children include the `(`, `)` and `,` tokens of the parenthesised
// spelling, so anonymous nodes are filtered out. A call written without
// an argument list (`private`, spelled as a `Call` only when it has a
// receiver) yields nothing.
pub
// How many attributes / methods one argument to an `attr_*` or
// visibility macro names. A `%i[a b]` word array is one argument naming
// several; every other symbol spelling names exactly one. `HashKeySymbol`
// (`a:`) reaches no argument list at the pinned grammar — it is only ever
// a `pair` or `keyword_pattern` key — but is kept as a defensive arm
// (grammar-dispatch rule 2), pinned by
// `ruby_hash_key_symbol_declares_no_attribute`.
// 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
// The method name a `:foo` argument to `private` / `public` / … names.
// `simple_symbol` carries the whole literal including the leading colon
// (`:ok?`, `:==`); `delimited_symbol` (`:"val="`) and the `bare_symbol`
// elements of a `%i[…]` array both wrap their text in a `string_content`
// child. A `%i[…]` array names several methods rather than one, so its
// callers expand it into these elements first. Every other argument
// shape — a splat (`private *SYMS`), a bare identifier (`private foo`),
// a string (`private "foo"`) — names nothing this walk can resolve and
// yields `None` (#1399).
//
// A delimited symbol is resolvable only when `string_content` is its
// *whole* content: `:"get_#{suffix}"` also carries an `interpolation`
// child, and returning the literal half of it would name — and demote —
// a different method called `get_`.
pub
// The declared name of a Ruby `method` / `singleton_method`. Read from
// the grammar's required `name` field rather than by child index: the
// name is the *second* identifier of `def obj.x`, and it is a `setter`
// (`val=`) or an `operator` (`==`) node rather than an `identifier` for
// those two spellings (grammar-dispatch rule 3).
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
// A Ruby visibility keyword used in call position. `targets_singleton`
// records *which* method family the keyword names: the bare keywords
// govern instance methods only, and demoting a `def self.x` takes the
// dedicated `private_class_method` / `public_class_method` pair (#1255).
pub
// What a visibility-keyword `Call` in a Ruby class body does. Shared by
// `Npm` and `Npa` so the two walkers cannot drift on the same Ruby rule
// (grammar-dispatch rule 7).
pub
// Classifies a Ruby class-body `Call` whose callee is a visibility
// keyword.
//
// An argument-less `private_class_method` is an ArgumentError in Ruby
// rather than a flag flip, so it yields no effect at all.
pub
// Whether a declaration counts as public when no visibility keyword
// names it: an instance method or attribute takes the body-wide flag,
// while a singleton method (`def self.x`) ignores that flag entirely —
// Ruby's `private` governs instance methods only (#1255).
pub
// Whether a declaration nested in a visibility call's argument list is
// public. The keyword governs it only when it names that method family,
// so `private def self.x` leaves the singleton alone and
// `private_class_method` never reaches an instance method or an
// attribute; an ungoverned declaration falls back to its own default.
// The single source of the rule for both `Npm` and `Npa` (#1255).
pub
// The bare name a Ruby `Call` invokes on the enclosing class body.
//
// A receiver other than `self` puts the call on another object, so
// `Other.private :x` and `@cfg.attr_accessor :y` name nothing in *this*
// class and yield `None`. Both halves are read from the grammar's
// `receiver` / `method` fields rather than by scanning for the leading
// `identifier` (grammar-dispatch rule 3): the scan answers correctly
// only when the receiver happens to be a local variable, and reports
// the method name as the callee for a `constant`, an instance variable,
// a global, or a chained call — all of which are `_primary` receivers.
// Whether a Ruby `Call` node invokes the `attr_*` macro family on the
// enclosing class. Each macro takes a list of attribute symbols and
// synthesises the matching reader / writer / accessor methods.
pub
// Books `count` attributes against the class tallies, all of them
// public or none. Kept as a helper so the three declaration shapes
// `ruby_walk_class_body` recognises cannot disagree on the rule.
// Attributes declared by an `attr_*` macro nested in a visibility call's
// argument list: `private attr_accessor :b` parses as a `private` call
// whose sole argument is the `attr_accessor` call, so the macro is never
// a direct child of the body (#1255).
// 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 attr_accessor :b`) does not flip
// the body-wide flag — matching Ruby's runtime behaviour — but it does
// govern the declarations it wraps, so an `attr_*` macro nested in a
// visibility call takes the keyword's visibility (#1255).
//
// The retroactive symbol form (`private :b` after `attr_accessor :b`)
// is deliberately not modelled here. It renames the visibility of the
// generated *reader* only, leaving `b=` public, so there is no single
// answer for the attribute — unlike `Npm`, where the symbol names one
// method (see #1399).
//
// 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
// 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