ucn 3.8.11 → 3.8.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/languages/go.js CHANGED
@@ -7,6 +7,7 @@
7
7
 
8
8
  const {
9
9
  traverseTree,
10
+ traverseTreeCached,
10
11
  nodeToLocation,
11
12
  parseStructuredParams,
12
13
  extractGoDocstring
@@ -54,88 +55,242 @@ function extractReceiver(receiverNode) {
54
55
  return text.replace(/^\(|\)$/g, '').trim();
55
56
  }
56
57
 
58
+ // --- Single-pass helpers: extracted from find* callbacks ---
59
+
57
60
  /**
58
- * Find all functions in Go code using tree-sitter
61
+ * Process a node for function extraction (single-pass helper)
62
+ * Returns true if node was matched, false otherwise
59
63
  */
60
- function findFunctions(code, parser) {
61
- const tree = parseTree(parser, code);
62
- const functions = [];
63
- const processedRanges = new Set();
64
+ function _processFunction(node, functions, processedRanges, lines) {
65
+ if (node.type === 'function_declaration') {
66
+ const rangeKey = `${node.startIndex}-${node.endIndex}`;
67
+ if (processedRanges.has(rangeKey)) return false;
68
+ processedRanges.add(rangeKey);
64
69
 
65
- traverseTree(tree.rootNode, (node) => {
70
+ const nameNode = node.childForFieldName('name');
71
+ const paramsNode = node.childForFieldName('parameters');
72
+
73
+ if (nameNode) {
74
+ const { startLine, endLine, indent } = nodeToLocation(node, lines);
75
+ const returnType = extractReturnType(node);
76
+ const docstring = extractGoDocstring(lines, startLine);
77
+ const typeParams = extractTypeParams(node);
78
+ const isExported = /^[A-Z]/.test(nameNode.text);
79
+
80
+ functions.push({
81
+ name: nameNode.text,
82
+ params: extractGoParams(paramsNode),
83
+ paramsStructured: parseStructuredParams(paramsNode, 'go'),
84
+ startLine,
85
+ endLine,
86
+ indent,
87
+ modifiers: isExported ? ['export'] : [],
88
+ ...(returnType && { returnType }),
89
+ ...(docstring && { docstring }),
90
+ ...(typeParams && { generics: typeParams })
91
+ });
92
+ }
93
+ return true;
94
+ }
95
+
96
+ if (node.type === 'method_declaration') {
66
97
  const rangeKey = `${node.startIndex}-${node.endIndex}`;
98
+ if (processedRanges.has(rangeKey)) return false;
99
+ processedRanges.add(rangeKey);
67
100
 
68
- // Function declarations
69
- if (node.type === 'function_declaration') {
70
- if (processedRanges.has(rangeKey)) return true;
71
- processedRanges.add(rangeKey);
101
+ const nameNode = node.childForFieldName('name');
102
+ const paramsNode = node.childForFieldName('parameters');
103
+ const receiverNode = node.childForFieldName('receiver');
104
+
105
+ if (nameNode) {
106
+ const { startLine, endLine, indent } = nodeToLocation(node, lines);
107
+ const receiver = extractReceiver(receiverNode);
108
+ const returnType = extractReturnType(node);
109
+ const docstring = extractGoDocstring(lines, startLine);
110
+ const isExported = /^[A-Z]/.test(nameNode.text);
111
+
112
+ functions.push({
113
+ name: nameNode.text,
114
+ params: extractGoParams(paramsNode),
115
+ paramsStructured: parseStructuredParams(paramsNode, 'go'),
116
+ startLine,
117
+ endLine,
118
+ indent,
119
+ isMethod: true,
120
+ receiver,
121
+ modifiers: isExported ? ['export'] : [],
122
+ ...(returnType && { returnType }),
123
+ ...(docstring && { docstring })
124
+ });
125
+ }
126
+ return true;
127
+ }
72
128
 
73
- const nameNode = node.childForFieldName('name');
74
- const paramsNode = node.childForFieldName('parameters');
129
+ return false;
130
+ }
75
131
 
76
- if (nameNode) {
77
- const { startLine, endLine, indent } = nodeToLocation(node, code);
78
- const returnType = extractReturnType(node);
79
- const docstring = extractGoDocstring(code, startLine);
80
- const typeParams = extractTypeParams(node);
132
+ /**
133
+ * Process a node for type/class extraction (single-pass helper)
134
+ * Returns true if node was matched, false otherwise
135
+ */
136
+ function _processClass(node, types, processedRanges, lines) {
137
+ if (node.type !== 'type_declaration') return false;
138
+
139
+ const rangeKey = `${node.startIndex}-${node.endIndex}`;
140
+ if (processedRanges.has(rangeKey)) return false;
141
+ processedRanges.add(rangeKey);
142
+
143
+ for (let i = 0; i < node.namedChildCount; i++) {
144
+ const spec = node.namedChild(i);
145
+ if (spec.type === 'type_spec') {
146
+ const nameNode = spec.childForFieldName('name');
147
+ const typeNode = spec.childForFieldName('type');
148
+
149
+ if (nameNode && typeNode) {
150
+ const { startLine, endLine } = nodeToLocation(node, lines);
151
+ const name = nameNode.text;
152
+ const docstring = extractGoDocstring(lines, startLine);
153
+ const typeParams = extractTypeParams(spec);
154
+
155
+ let typeKind = 'type';
156
+ if (typeNode.type === 'struct_type') {
157
+ typeKind = 'struct';
158
+ } else if (typeNode.type === 'interface_type') {
159
+ typeKind = 'interface';
160
+ }
81
161
 
82
- // Check if exported (capitalized)
83
- const isExported = /^[A-Z]/.test(nameNode.text);
162
+ const isExported = /^[A-Z]/.test(name);
84
163
 
85
- functions.push({
86
- name: nameNode.text,
87
- params: extractGoParams(paramsNode),
88
- paramsStructured: parseStructuredParams(paramsNode, 'go'),
164
+ const members = typeKind === 'struct' ? extractStructFields(typeNode, lines)
165
+ : typeKind === 'interface' ? extractInterfaceMembers(typeNode, lines)
166
+ : [];
167
+
168
+ const embeddedBases = members
169
+ .filter(m => m.embedded)
170
+ .map(m => m.name);
171
+
172
+ types.push({
173
+ name,
89
174
  startLine,
90
175
  endLine,
91
- indent,
176
+ type: typeKind,
177
+ members,
92
178
  modifiers: isExported ? ['export'] : [],
93
- ...(returnType && { returnType }),
94
179
  ...(docstring && { docstring }),
95
- ...(typeParams && { generics: typeParams })
180
+ ...(typeParams && { generics: typeParams }),
181
+ ...(embeddedBases.length > 0 && { extends: embeddedBases.join(', ') })
96
182
  });
97
183
  }
98
- return true;
99
184
  }
185
+ }
186
+ return true;
187
+ }
100
188
 
101
- // Method declarations (with receivers)
102
- if (node.type === 'method_declaration') {
103
- if (processedRanges.has(rangeKey)) return true;
104
- processedRanges.add(rangeKey);
189
+ // Module-level state detection helpers
190
+ const GO_STATE_PATTERN = /^(CONFIG|SETTINGS|[A-Z][A-Z0-9_]+|Default[A-Z][a-zA-Z]*|[A-Z][a-zA-Z]*(?:Config|Settings|Options))$/;
105
191
 
106
- const nameNode = node.childForFieldName('name');
107
- const paramsNode = node.childForFieldName('parameters');
108
- const receiverNode = node.childForFieldName('receiver');
192
+ function _isGoExportedName(name) {
193
+ return /^[A-Z]/.test(name);
194
+ }
109
195
 
110
- if (nameNode) {
111
- const { startLine, endLine, indent } = nodeToLocation(node, code);
112
- const receiver = extractReceiver(receiverNode);
113
- const returnType = extractReturnType(node);
114
- const docstring = extractGoDocstring(code, startLine);
196
+ function _isCompositeLiteral(valueNode) {
197
+ if (!valueNode) return false;
198
+ if (valueNode.type === 'composite_literal') return true;
199
+ for (let i = 0; i < valueNode.namedChildCount; i++) {
200
+ if (valueNode.namedChild(i).type === 'composite_literal') return true;
201
+ }
202
+ return false;
203
+ }
115
204
 
116
- // Check if exported
117
- const isExported = /^[A-Z]/.test(nameNode.text);
205
+ function _blockHasIota(constDecl) {
206
+ for (let i = 0; i < constDecl.namedChildCount; i++) {
207
+ const spec = constDecl.namedChild(i);
208
+ if (spec.type === 'const_spec') {
209
+ const valueNode = spec.childForFieldName('value');
210
+ if (valueNode) {
211
+ const checkIota = (n) => {
212
+ if (n.type === 'iota') return true;
213
+ for (let j = 0; j < n.childCount; j++) {
214
+ if (checkIota(n.child(j))) return true;
215
+ }
216
+ return false;
217
+ };
218
+ if (checkIota(valueNode)) return true;
219
+ }
220
+ }
221
+ }
222
+ return false;
223
+ }
118
224
 
119
- functions.push({
120
- name: nameNode.text,
121
- params: extractGoParams(paramsNode),
122
- paramsStructured: parseStructuredParams(paramsNode, 'go'),
123
- startLine,
124
- endLine,
125
- indent,
126
- isMethod: true,
127
- receiver,
128
- modifiers: isExported ? ['export'] : [],
129
- ...(returnType && { returnType }),
130
- ...(docstring && { docstring })
131
- });
225
+ /**
226
+ * Process a node for state object extraction (single-pass helper)
227
+ * Returns true if node was matched, false otherwise
228
+ */
229
+ function _processState(node, objects, lines) {
230
+ if (node.type === 'const_declaration') {
231
+ const isIotaBlock = _blockHasIota(node);
232
+ for (let i = 0; i < node.namedChildCount; i++) {
233
+ const spec = node.namedChild(i);
234
+ if (spec.type === 'const_spec') {
235
+ const nameNode = spec.childForFieldName('name');
236
+ const valueNode = spec.childForFieldName('value');
237
+ if (!nameNode) continue;
238
+ const name = nameNode.text;
239
+
240
+ if (valueNode && _isCompositeLiteral(valueNode) && GO_STATE_PATTERN.test(name)) {
241
+ const { startLine, endLine } = nodeToLocation(spec, lines);
242
+ objects.push({ name, startLine, endLine });
243
+ } else if (isIotaBlock && /^[A-Z]/.test(name)) {
244
+ const { startLine, endLine } = nodeToLocation(spec, lines);
245
+ objects.push({ name, startLine, endLine, isConst: true });
246
+ } else if (_isGoExportedName(name)) {
247
+ const { startLine, endLine } = nodeToLocation(spec, lines);
248
+ objects.push({ name, startLine, endLine, isConst: true });
249
+ }
132
250
  }
133
- return true;
134
251
  }
252
+ return true;
253
+ }
254
+
255
+ if (node.type === 'var_declaration') {
256
+ for (let i = 0; i < node.namedChildCount; i++) {
257
+ const spec = node.namedChild(i);
258
+ if (spec.type === 'var_spec') {
259
+ const nameNode = spec.childForFieldName('name');
260
+ const valueNode = spec.childForFieldName('value');
135
261
 
262
+ if (nameNode) {
263
+ const name = nameNode.text;
264
+ if (valueNode && _isCompositeLiteral(valueNode) && GO_STATE_PATTERN.test(name)) {
265
+ const { startLine, endLine } = nodeToLocation(spec, lines);
266
+ objects.push({ name, startLine, endLine });
267
+ } else if (_isGoExportedName(name)) {
268
+ const { startLine, endLine } = nodeToLocation(spec, lines);
269
+ objects.push({ name, startLine, endLine });
270
+ }
271
+ }
272
+ }
273
+ }
136
274
  return true;
137
- });
275
+ }
138
276
 
277
+ return false;
278
+ }
279
+
280
+ // --- End single-pass helpers ---
281
+
282
+ /**
283
+ * Find all functions in Go code using tree-sitter
284
+ */
285
+ function findFunctions(code, parser) {
286
+ const tree = parseTree(parser, code);
287
+ const lines = code.split('\n');
288
+ const functions = [];
289
+ const processedRanges = new Set();
290
+ traverseTreeCached(tree.rootNode, (node) => {
291
+ _processFunction(node, functions, processedRanges, lines);
292
+ return true;
293
+ });
139
294
  functions.sort((a, b) => a.startLine - b.startLine);
140
295
  return functions;
141
296
  }
@@ -156,67 +311,13 @@ function extractTypeParams(node) {
156
311
  */
157
312
  function findClasses(code, parser) {
158
313
  const tree = parseTree(parser, code);
314
+ const lines = code.split('\n');
159
315
  const types = [];
160
316
  const processedRanges = new Set();
161
-
162
- traverseTree(tree.rootNode, (node) => {
163
- const rangeKey = `${node.startIndex}-${node.endIndex}`;
164
-
165
- if (node.type === 'type_declaration') {
166
- if (processedRanges.has(rangeKey)) return true;
167
- processedRanges.add(rangeKey);
168
-
169
- for (let i = 0; i < node.namedChildCount; i++) {
170
- const spec = node.namedChild(i);
171
- if (spec.type === 'type_spec') {
172
- const nameNode = spec.childForFieldName('name');
173
- const typeNode = spec.childForFieldName('type');
174
-
175
- if (nameNode && typeNode) {
176
- const { startLine, endLine } = nodeToLocation(node, code);
177
- const name = nameNode.text;
178
- const docstring = extractGoDocstring(code, startLine);
179
- const typeParams = extractTypeParams(spec);
180
-
181
- let typeKind = 'type';
182
- if (typeNode.type === 'struct_type') {
183
- typeKind = 'struct';
184
- } else if (typeNode.type === 'interface_type') {
185
- typeKind = 'interface';
186
- }
187
-
188
- // Check if exported
189
- const isExported = /^[A-Z]/.test(name);
190
-
191
- const members = typeKind === 'struct' ? extractStructFields(typeNode, code)
192
- : typeKind === 'interface' ? extractInterfaceMembers(typeNode, code)
193
- : [];
194
-
195
- // Extract embedded field names as extends (Go composition)
196
- const embeddedBases = members
197
- .filter(m => m.embedded)
198
- .map(m => m.name);
199
-
200
- types.push({
201
- name,
202
- startLine,
203
- endLine,
204
- type: typeKind,
205
- members,
206
- modifiers: isExported ? ['export'] : [],
207
- ...(docstring && { docstring }),
208
- ...(typeParams && { generics: typeParams }),
209
- ...(embeddedBases.length > 0 && { extends: embeddedBases.join(', ') })
210
- });
211
- }
212
- }
213
- }
214
- return true;
215
- }
216
-
317
+ traverseTreeCached(tree.rootNode, (node) => {
318
+ _processClass(node, types, processedRanges, lines);
217
319
  return true;
218
320
  });
219
-
220
321
  types.sort((a, b) => a.startLine - b.startLine);
221
322
  return types;
222
323
  }
@@ -224,7 +325,7 @@ function findClasses(code, parser) {
224
325
  /**
225
326
  * Extract struct fields
226
327
  */
227
- function extractStructFields(structNode, code) {
328
+ function extractStructFields(structNode, codeOrLines) {
228
329
  const fields = [];
229
330
  // struct_type contains a field_declaration_list child (not a 'body' field)
230
331
  let fieldListNode = structNode.childForFieldName('body');
@@ -241,7 +342,7 @@ function extractStructFields(structNode, code) {
241
342
  for (let i = 0; i < fieldListNode.namedChildCount; i++) {
242
343
  const field = fieldListNode.namedChild(i);
243
344
  if (field.type === 'field_declaration') {
244
- const { startLine, endLine } = nodeToLocation(field, code);
345
+ const { startLine, endLine } = nodeToLocation(field, codeOrLines);
245
346
  const nameNode = field.childForFieldName('name');
246
347
  const typeNode = field.childForFieldName('type');
247
348
 
@@ -280,13 +381,13 @@ function extractStructFields(structNode, code) {
280
381
  /**
281
382
  * Extract interface method signatures
282
383
  */
283
- function extractInterfaceMembers(interfaceNode, code) {
384
+ function extractInterfaceMembers(interfaceNode, codeOrLines) {
284
385
  const members = [];
285
386
  for (let i = 0; i < interfaceNode.namedChildCount; i++) {
286
387
  const child = interfaceNode.namedChild(i);
287
388
  // tree-sitter Go uses method_elem (or method_spec in older versions)
288
389
  if (child.type === 'method_elem' || child.type === 'method_spec') {
289
- const { startLine, endLine } = nodeToLocation(child, code);
390
+ const { startLine, endLine } = nodeToLocation(child, codeOrLines);
290
391
  // Name is in a field_identifier child
291
392
  let nameText = null;
292
393
  let paramsText = null;
@@ -355,7 +456,7 @@ function extractInterfaceMembers(interfaceNode, code) {
355
456
  }
356
457
  } else if (child.type === 'type_identifier' || child.type === 'qualified_type') {
357
458
  // Standalone type identifier inside interface body — embedded interface
358
- const { startLine, endLine } = nodeToLocation(child, code);
459
+ const { startLine, endLine } = nodeToLocation(child, codeOrLines);
359
460
  let embName = child.text;
360
461
  const dotIdx = embName.indexOf('.');
361
462
  if (dotIdx >= 0) embName = embName.slice(dotIdx + 1);
@@ -373,7 +474,7 @@ function extractInterfaceMembers(interfaceNode, code) {
373
474
  for (let j = 0; j < child.namedChildCount; j++) {
374
475
  const sub = child.namedChild(j);
375
476
  if (sub.type === 'type_identifier' || sub.type === 'qualified_type') {
376
- const { startLine, endLine } = nodeToLocation(sub, code);
477
+ const { startLine, endLine } = nodeToLocation(sub, codeOrLines);
377
478
  let embName = sub.text;
378
479
  const dotIdx = embName.indexOf('.');
379
480
  if (dotIdx >= 0) embName = embName.slice(dotIdx + 1);
@@ -397,99 +498,12 @@ function extractInterfaceMembers(interfaceNode, code) {
397
498
  */
398
499
  function findStateObjects(code, parser) {
399
500
  const tree = parseTree(parser, code);
501
+ const lines = code.split('\n');
400
502
  const objects = [];
401
-
402
- const statePattern = /^(CONFIG|SETTINGS|[A-Z][A-Z0-9_]+|Default[A-Z][a-zA-Z]*|[A-Z][a-zA-Z]*(?:Config|Settings|Options))$/;
403
- // All exported (^[A-Z]) package-level const/var are indexed as state objects
404
- const isExportedName = (name) => /^[A-Z]/.test(name);
405
-
406
- // Check if a value node is a composite literal
407
- function isCompositeLiteral(valueNode) {
408
- if (!valueNode) return false;
409
- if (valueNode.type === 'composite_literal') return true;
410
- for (let i = 0; i < valueNode.namedChildCount; i++) {
411
- if (valueNode.namedChild(i).type === 'composite_literal') return true;
412
- }
413
- return false;
414
- }
415
-
416
- // Check if a const block uses iota (enum-like pattern)
417
- function blockHasIota(constDecl) {
418
- for (let i = 0; i < constDecl.namedChildCount; i++) {
419
- const spec = constDecl.namedChild(i);
420
- if (spec.type === 'const_spec') {
421
- const valueNode = spec.childForFieldName('value');
422
- if (valueNode) {
423
- // Check if any child is 'iota'
424
- const checkIota = (n) => {
425
- if (n.type === 'iota') return true;
426
- for (let j = 0; j < n.childCount; j++) {
427
- if (checkIota(n.child(j))) return true;
428
- }
429
- return false;
430
- };
431
- if (checkIota(valueNode)) return true;
432
- }
433
- }
434
- }
435
- return false;
436
- }
437
-
438
- traverseTree(tree.rootNode, (node) => {
439
- // Handle const declarations
440
- if (node.type === 'const_declaration') {
441
- const isIotaBlock = blockHasIota(node);
442
- for (let i = 0; i < node.namedChildCount; i++) {
443
- const spec = node.namedChild(i);
444
- if (spec.type === 'const_spec') {
445
- const nameNode = spec.childForFieldName('name');
446
- const valueNode = spec.childForFieldName('value');
447
- if (!nameNode) continue;
448
- const name = nameNode.text;
449
-
450
- // Include if: composite literal matching state pattern, OR exported const in iota block,
451
- // OR any exported (^[A-Z]) package-level const
452
- if (valueNode && isCompositeLiteral(valueNode) && statePattern.test(name)) {
453
- const { startLine, endLine } = nodeToLocation(spec, code);
454
- objects.push({ name, startLine, endLine });
455
- } else if (isIotaBlock && /^[A-Z]/.test(name)) {
456
- const { startLine, endLine } = nodeToLocation(spec, code);
457
- objects.push({ name, startLine, endLine, isConst: true });
458
- } else if (isExportedName(name)) {
459
- const { startLine, endLine } = nodeToLocation(spec, code);
460
- objects.push({ name, startLine, endLine, isConst: true });
461
- }
462
- }
463
- }
464
- return true;
465
- }
466
-
467
- // Handle var declarations
468
- if (node.type === 'var_declaration') {
469
- for (let i = 0; i < node.namedChildCount; i++) {
470
- const spec = node.namedChild(i);
471
- if (spec.type === 'var_spec') {
472
- const nameNode = spec.childForFieldName('name');
473
- const valueNode = spec.childForFieldName('value');
474
-
475
- if (nameNode) {
476
- const name = nameNode.text;
477
- if (valueNode && isCompositeLiteral(valueNode) && statePattern.test(name)) {
478
- const { startLine, endLine } = nodeToLocation(spec, code);
479
- objects.push({ name, startLine, endLine });
480
- } else if (isExportedName(name)) {
481
- const { startLine, endLine } = nodeToLocation(spec, code);
482
- objects.push({ name, startLine, endLine });
483
- }
484
- }
485
- }
486
- }
487
- return true;
488
- }
489
-
503
+ traverseTreeCached(tree.rootNode, (node) => {
504
+ _processState(node, objects, lines);
490
505
  return true;
491
506
  });
492
-
493
507
  objects.sort((a, b) => a.startLine - b.startLine);
494
508
  return objects;
495
509
  }
@@ -498,12 +512,31 @@ function findStateObjects(code, parser) {
498
512
  * Parse a Go file completely
499
513
  */
500
514
  function parse(code, parser) {
515
+ const tree = parseTree(parser, code);
516
+ const lines = code.split('\n');
517
+ const functions = [];
518
+ const classes = [];
519
+ const stateObjects = [];
520
+ const processedFn = new Set();
521
+ const processedCls = new Set();
522
+
523
+ traverseTreeCached(tree.rootNode, (node) => {
524
+ _processFunction(node, functions, processedFn, lines);
525
+ _processClass(node, classes, processedCls, lines);
526
+ _processState(node, stateObjects, lines);
527
+ return true;
528
+ });
529
+
530
+ functions.sort((a, b) => a.startLine - b.startLine);
531
+ classes.sort((a, b) => a.startLine - b.startLine);
532
+ stateObjects.sort((a, b) => a.startLine - b.startLine);
533
+
501
534
  return {
502
535
  language: 'go',
503
- totalLines: code.split('\n').length,
504
- functions: findFunctions(code, parser),
505
- classes: findClasses(code, parser),
506
- stateObjects: findStateObjects(code, parser),
536
+ totalLines: lines.length,
537
+ functions,
538
+ classes,
539
+ stateObjects,
507
540
  imports: [],
508
541
  exports: []
509
542
  };
@@ -1103,7 +1136,7 @@ function findImportsInCode(code, parser) {
1103
1136
  }
1104
1137
  }
1105
1138
 
1106
- traverseTree(tree.rootNode, (node) => {
1139
+ traverseTreeCached(tree.rootNode, (node) => {
1107
1140
  if (node.type === 'import_declaration') {
1108
1141
  for (let i = 0; i < node.namedChildCount; i++) {
1109
1142
  const child = node.namedChild(i);
@@ -1138,7 +1171,7 @@ function findExportsInCode(code, parser) {
1138
1171
  const tree = parseTree(parser, code);
1139
1172
  const exports = [];
1140
1173
 
1141
- traverseTree(tree.rootNode, (node) => {
1174
+ traverseTreeCached(tree.rootNode, (node) => {
1142
1175
  // Exported functions
1143
1176
  if (node.type === 'function_declaration') {
1144
1177
  const nameNode = node.childForFieldName('name');
@@ -1218,7 +1251,7 @@ function findUsagesInCode(code, name, parser) {
1218
1251
  const tree = parseTree(parser, code);
1219
1252
  const usages = [];
1220
1253
 
1221
- traverseTree(tree.rootNode, (node) => {
1254
+ traverseTreeCached(tree.rootNode, (node) => {
1222
1255
  // Look for identifier, field_identifier (method names in selector expressions),
1223
1256
  // and type_identifier (type references in params, return types, composite literals, etc.)
1224
1257
  const isIdentifier = node.type === 'identifier' || node.type === 'field_identifier' || node.type === 'type_identifier';