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
//! The `get_op_type` template the four JS-family grammars share
//! (JavaScript, MozJS, TypeScript, TSX), kept apart from the trait so
//! the per-language deltas and their rationale read as one unit.
// Emit a `Getter::get_op_type` body for a JS-family language. The four
// JS-family grammars (JavaScript, MozJS, TypeScript, TSX) share most of
// their Halstead operator/operand kind classifications; per-language
// deltas are passed as bracketed extras so all four impls stay in
// lockstep when a kind is added or removed (issue #299).
//
// `$op_extras` per language:
// * JavaScript / MozJS: `OptionalChain` — the bare `?.` token (these
// grammars expose no `optional_chain` wrapper).
// * TypeScript / TSX: `QMARKDOT`, `PredefinedType` — `QMARKDOT` is
// the bare `?.` token under the `optional_chain` wrapper (issue
// #281); `PredefinedType` is the TS type keyword set (`string`,
// `number`, `boolean`, …).
// * JavaScript / MozJS / TSX: `LTSLASH`, `SLASHGT` — the JSX closing
// and self-closing tag delimiters, which these grammars spell as
// their own two-character tokens rather than as a `<` / `>` pair.
// Until #1395 neither was in either arm, so a JSX element billed
// its opening `<` and its `>`s but nothing for `</`, and `<br />`
// reported one bracket operator where the source spells two. They
// are extras rather than members of the shared arm because
// TypeScript is the one grammar of the four with no JSX and
// therefore no such variants to name.
//
// `$operand_extras` per language:
// * JavaScript / MozJS / TSX: `Identifier2`, `String2` — anonymous
// keyword aliases the JS grammar exposes for `Identifier` and
// `String`. TSX's `String2` (kind_id 261) is its string-literal
// alias, so it stays an operand like JS's.
// * TypeScript: none. TS's own aliases are either operators or
// deliberately unclassified (below).
// The `: string` type-keyword aliases (TS `String2`, kind_id 135;
// TSX `String3`, kind_id 141) are deliberately absent: they are
// emitted only as the child of a `predefined_type` wrapper, which
// already contributes the text-keyed `"string"` operator, and
// #313's listing of the keyword child as an operand counted one
// source token twice (#1261, which also narrowed
// `Checker::is_string` to match).
//
// **A member access contributes its leaves, never the composite.**
// `a.b` is the operands `a` and `b` plus the `.` operator — three
// vocabulary entries, not four. Until #1263 the operand arm listed the
// `member_expression` wrapper *and* the walker descended into its
// `object` / `property` children, so every access billed one extra
// `N2` entry keyed on the whole `a.b` text (and `a.b.c` billed two:
// `a.b` and `a.b.c`). The composite is what the rest of the workspace
// already excludes — C, C++, Java, Rust, Python, Go, Kotlin, Ruby, Lua
// and PHP were all measured leaves-only for the identical shape — so
// identical code scored differently by language. The same call applies
// to TS's `nested_identifier` (`namespace N.M`), C#'s
// `qualified_name` / `generic_name` / `alias_qualified_name`, and
// Groovy's `qualified_name` / `qualified_type`.
//
// This is grammar-dispatch section 5's "any predicate listing both a
// container and a kind it can contain double-counts", and its section
// 6 corollary is why `PrivatePropertyIdentifier` joins the operand arm
// in the same change: `this.#x`'s `#x` leaf was in no operand list, so
// the composite had been its only count, and a bare deletion would
// have regressed private-field access to zero operands. (The `#x` in
// the *field definition* `#x = 1` had never been counted at all — no
// wrapper covered it — so this fixes that half too.)
//
// `jsx: [<text kind>, <entity kind>]` (JavaScript, MozJS, TSX) bills
// the content of a JSX element (#1483). A `jsx_text` node is an
// operand, the way a string's contents are; it is childless and no
// node containing it (`jsx_element`) is classified, so nothing bills it
// twice. Two refinements need the source bytes and so live in the
// `get_op_type_with_code` / `get_operand_id` overrides the parameter
// also emits:
//
// * A whitespace-only `jsx_text` is `Unknown`. The scanner emits no
// token for whitespace that starts with a newline, but same-line
// spaces between tags (`<a/> <b/>`) are a node of their own, and
// billing them would make `N2` follow the source's layout.
// * The operand is keyed on the text with its surrounding whitespace
// trimmed. A token keeps the newlines and indentation around it, so
// `Start` at two indentation depths would otherwise be two distinct
// operands. This is the same shape as Kotlin's #454 narrowing.
// Only the ends are trimmed: a multi-line text keeps its inner
// newlines and indentation, so the same lines at two depths, or
// wrapped differently, stay distinct operands although JSX joins
// them into one string. Folding them needs an owned key, and
// `get_operand_id` returns a slice of the source.
//
// An `html_character_reference` (`&`) splits the text around it
// into siblings, so `a & b` is three operands: `a`, `&` and
// `b`. Each node is one vocabulary entry, and the entity denotes a
// character the surrounding text does not, so it is not folded into
// either neighbour. The same kind also sits inside a JSX attribute's
// `string`, which is already the operand, so it is billed only when
// its parent is not a string.
//
// `Checker::is_string` deliberately does not list `jsx_text`: `find
// string` reports delimited string literals, the set the alterator
// flattens (#283), and element text is undelimited markup content.
//
// The `TemplateString` interpolation guard is shared verbatim (issue
// #192): a bare `` `...` `` mirrors a `"..."` operand, but an
// interpolated template must yield `Unknown` because its inner
// `TemplateSubstitution` expressions are walked separately.
//
// The `Regex` arms are shared the same way (issue #1314), and for the
// same reason: all four grammars spell a regex literal identically —
// a `regex` wrapper whose two delimiters are `SLASH`, the kind id real
// division uses — so neither arm has a per-language delta to hoist to
// a call site. Only the ids differ (`SLASH` 87/81/90/87, `Regex`
// 224/250/264/225) and the macro names both through the enum, so no
// invocation mentions either. See the arms themselves for the
// fabrication and the missing-operand halves.
pub use impl_js_family_get_op_type;