gitnexus 1.6.10-rc.49 → 1.6.10-rc.50

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.
@@ -17,6 +17,9 @@ export const javaClassConfig = {
17
17
  // any other shape, so plain `new Foo()` constructor calls never
18
18
  // produce a Class node (#2550).
19
19
  'object_creation_expression',
20
+ // Enum constant bodies (`enum E { A { ... } }`) — javac's other
21
+ // anonymous shape, named E$N by the same authority (#2555).
22
+ 'enum_constant',
20
23
  ],
21
24
  fileScopeNodeTypes: ['package_declaration'],
22
25
  ancestorScopeNodeTypes: [
@@ -26,20 +29,22 @@ export const javaClassConfig = {
26
29
  'record_declaration',
27
30
  ],
28
31
  extractName(node) {
29
- if (node.type === 'object_creation_expression') {
32
+ if (node.type === 'object_creation_expression' || node.type === 'enum_constant') {
30
33
  return synthesizeJavaAnonymousClassName(node);
31
34
  }
32
35
  return undefined;
33
36
  },
34
- // An anonymous body whose name CANNOT be synthesized (no supported host
35
- // type declaration) must not become a Class node at all. Without this
36
- // skip, `extract()`'s `extractTypeNameFromNode` fallback names the node
37
- // after the CONSTRUCTED type — emitting a phantom `Class:...:Runnable`
38
- // for `new Runnable() { ... }` (empirically caught in review).
37
+ // An anonymous body whose name CANNOT be synthesized must not become a
38
+ // Class node at all. Without this skip, `extract()`'s
39
+ // `extractTypeNameFromNode` fallback fabricates a name — the CONSTRUCTED
40
+ // type for `new Runnable() { ... }` (phantom `Class:...:Runnable`,
41
+ // empirically caught in review) or the constant's own identifier for an
42
+ // `enum_constant` (`Class:...:A`).
39
43
  shouldSkipClassCapture({ definitionNode }) {
40
44
  return (definitionNode !== null &&
41
45
  definitionNode !== undefined &&
42
- definitionNode.type === 'object_creation_expression' &&
46
+ (definitionNode.type === 'object_creation_expression' ||
47
+ definitionNode.type === 'enum_constant') &&
43
48
  synthesizeJavaAnonymousClassName(definitionNode) === undefined);
44
49
  },
45
50
  };
@@ -255,8 +255,47 @@ function synthesizeJavaAnonymousClassDeclarations(rootNode) {
255
255
  }
256
256
  }
257
257
  }
258
+ // Enum constant bodies (`enum E { A { ... } }`) — javac's other
259
+ // anonymous shape (#2555). Same synthesis, same body anchor; the
260
+ // constant's class extends its HOST ENUM (javac semantics), so the
261
+ // inherits reference names the enum — giving `mroFor(E$N) ∋ E` and
262
+ // keeping bare calls from the body to the enum's own helpers alive
263
+ // through the ownership gate's MRO arm. No receiver typeBinding piece:
264
+ // constants are not variable initializers; `E.A.hook()` dispatch rides
265
+ // the existing enum receiver machinery.
266
+ for (const constant of rootNode.descendantsOfType('enum_constant')) {
267
+ const name = synthesizeJavaAnonymousClassName(constant);
268
+ if (name === undefined)
269
+ continue;
270
+ const body = constant.childForFieldName?.('body');
271
+ if (body === null || body === undefined || body.type !== 'class_body')
272
+ continue;
273
+ out.push({
274
+ '@declaration.class': nodeToCapture('@declaration.class', body),
275
+ '@declaration.name': syntheticCapture('@declaration.name', body, name),
276
+ });
277
+ const hostEnum = javaEnclosingEnumNameOf(constant);
278
+ if (hostEnum !== undefined) {
279
+ out.push({
280
+ '@reference.inherits': nodeToCapture('@reference.inherits', body),
281
+ '@reference.name': syntheticCapture('@reference.name', body, hostEnum),
282
+ });
283
+ }
284
+ }
258
285
  return out;
259
286
  }
287
+ /** Simple name of the enum_declaration enclosing an enum_constant, or
288
+ * undefined (grammar guarantees one exists in well-formed source). */
289
+ function javaEnclosingEnumNameOf(constant) {
290
+ let cursor = constant.parent;
291
+ while (cursor !== null) {
292
+ if (cursor.type === 'enum_declaration') {
293
+ return cursor.childForFieldName?.('name')?.text ?? undefined;
294
+ }
295
+ cursor = cursor.parent;
296
+ }
297
+ return undefined;
298
+ }
260
299
  /**
261
300
  * Synthesize `@reference.call.constructor` captures for explicit constructor
262
301
  * invocations — `super(...)` and `this(...)` (F38 #1928). tree-sitter-java
@@ -45,6 +45,12 @@ const JAVA_SCOPE_QUERY = `
45
45
  (object_creation_expression
46
46
  (class_body) @scope.class)
47
47
 
48
+ ;; Enum constant body: \`enum E { A { public void hook() {} } }\` --
49
+ ;; javac's other anonymous-class shape (E$N), same scope-boundary need
50
+ ;; and same class_body anchor (#2555).
51
+ (enum_constant
52
+ body: (class_body) @scope.class)
53
+
48
54
  (method_declaration) @scope.function
49
55
  (constructor_declaration) @scope.function
50
56
 
@@ -763,6 +763,16 @@ export function populateClassOwnedMembers(parsed) {
763
763
  return;
764
764
  if (q.includes('.'))
765
765
  return; // already qualified (dotted)
766
+ // A synthesized anonymous-class def (Java `$`-chain binary name,
767
+ // #2550/#2555 — `M3$2`, `EnumWrap$Mode$1`) already carries its
768
+ // COMPLETE name. Prefixing it (`M3.M3$2`) desyncs from the
769
+ // structure-phase node id (`M3$2.hook`), so same-named methods
770
+ // across sibling enum-constant bodies collapse onto the first
771
+ // body's node via the simple-name fallback (empirically caught in
772
+ // review). Class-like only: `$`-named MEMBERS (legal in JS/TS)
773
+ // still qualify normally against their class.
774
+ if (isClassLike(def.type) && q.includes('$'))
775
+ return;
766
776
  const classQ = classDef.qualifiedName;
767
777
  if (classQ === undefined || classQ.length === 0)
768
778
  return;
@@ -1,7 +1,7 @@
1
1
  export declare const TYPESCRIPT_QUERIES: string;
2
2
  export declare const JAVASCRIPT_QUERIES: string;
3
3
  export declare const PYTHON_QUERIES = "\n(class_definition\n name: (identifier) @name) @definition.class\n\n(function_definition\n name: (identifier) @name) @definition.function\n\n(import_statement\n name: (dotted_name) @import.source) @import\n\n; import numpy as np \u2192 aliased_import captures the module name so the\n; import path is resolved and named-binding extraction stores \"np\" \u2192 \"numpy\".\n(import_statement\n name: (aliased_import\n name: (dotted_name) @import.source)) @import\n\n(import_from_statement\n module_name: (dotted_name) @import.source) @import\n\n(import_from_statement\n module_name: (relative_import) @import.source) @import\n\n(call\n function: (identifier) @call.name) @call\n\n(call\n function: (attribute\n attribute: (identifier) @call.name)) @call\n\n; Class attribute type annotations \u2014 PEP 526: address: Address or address: Address = Address()\n; Both bare annotations (address: Address) and annotated assignments (name: str = \"test\")\n; are parsed as (assignment left: ... type: ...) in tree-sitter-python.\n(expression_statement\n (assignment\n left: (identifier) @name\n type: (type)) @definition.property)\n\n; Plain variable assignments without type annotation: x = 5, MAX_SIZE = 100\n; Overlap with @definition.property (typed) is handled by parse-worker dedup.\n(expression_statement\n (assignment\n left: (identifier) @name)) @definition.variable\n\n; Write access: obj.field = value\n(assignment\n left: (attribute\n object: (_) @assignment.receiver\n attribute: (identifier) @assignment.property)\n right: (_)) @assignment\n\n; Write access: obj.field += value (compound assignment)\n(augmented_assignment\n left: (attribute\n object: (_) @assignment.receiver\n attribute: (identifier) @assignment.property)\n right: (_)) @assignment\n\n; Python HTTP clients: requests.get('/path'), httpx.post('/path'), session.get('/path')\n(call\n function: (attribute\n attribute: (identifier) @http_client.method)\n arguments: (argument_list\n (string (string_content) @http_client.url))) @http_client\n\n; Python decorators: @app.route, @router.get, etc.\n; The first positional argument is captured three ways (#2391): a string literal\n; path via @decorator.arg (quote-free, the fast path); a bare constant name or a\n; plus-concatenation via @decorator.arg_expr (resolved cross-file by the constant\n; resolver). The anchored optional alternation pins to the FIRST arg and stays\n; optional, so no-arg decorators (@app.tool(), etc.) and non-path first args still\n; match.\n(decorator\n (call\n function: (attribute\n object: (identifier) @decorator.receiver\n attribute: (identifier) @decorator.name)\n arguments: (argument_list\n .\n [\n (string (string_content)? @decorator.arg) @decorator.arg_str\n (identifier) @decorator.arg_expr\n (binary_operator) @decorator.arg_expr\n ]?))) @decorator\n";
4
- export declare const JAVA_QUERIES = "\n; Classes, Interfaces, Enums, Annotations\n(class_declaration name: (identifier) @name) @definition.class\n(interface_declaration name: (identifier) @name) @definition.interface\n(enum_declaration name: (identifier) @name) @definition.enum\n(annotation_type_declaration name: (identifier) @name) @definition.annotation\n\n; Anonymous class bodies: new Runnable() { ... } \u2014 no @name capture; the\n; class extractor synthesizes the javac-style Worker$N name (#2550)\n(object_creation_expression (class_body)) @definition.class\n\n; Methods & Constructors\n(method_declaration name: (identifier) @name) @definition.method\n(constructor_declaration name: (identifier) @name) @definition.constructor\n\n; Fields \u2014 typed field declarations inside class bodies\n(field_declaration\n declarator: (variable_declarator\n name: (identifier) @name)) @definition.property\n\n; Imports - capture any import declaration child as source\n(import_declaration (_) @import.source) @import\n\n; Calls\n(method_invocation name: (identifier) @call.name) @call\n(method_invocation object: (_) name: (identifier) @call.name) @call\n(method_reference) @call\n\n; Constructor calls: new Foo()\n(object_creation_expression type: (type_identifier) @call.name) @call\n\n; Local variable declarations inside method bodies\n(local_variable_declaration\n declarator: (variable_declarator\n name: (identifier) @name)) @definition.variable\n\n; Write access: obj.field = value\n(assignment_expression\n left: (field_access\n object: (_) @assignment.receiver\n field: (identifier) @assignment.property)\n right: (_)) @assignment\n";
4
+ export declare const JAVA_QUERIES = "\n; Classes, Interfaces, Enums, Annotations\n(class_declaration name: (identifier) @name) @definition.class\n(interface_declaration name: (identifier) @name) @definition.interface\n(enum_declaration name: (identifier) @name) @definition.enum\n(annotation_type_declaration name: (identifier) @name) @definition.annotation\n\n; Anonymous class bodies: new Runnable() { ... } \u2014 no @name capture; the\n; class extractor synthesizes the javac-style Worker$N name (#2550)\n(object_creation_expression (class_body)) @definition.class\n\n; Enum constant bodies: enum E { A { ... } } \u2014 javac's other anonymous\n; shape, synthesized as E$N by the same naming authority (#2555)\n(enum_constant body: (class_body)) @definition.class\n\n; Methods & Constructors\n(method_declaration name: (identifier) @name) @definition.method\n(constructor_declaration name: (identifier) @name) @definition.constructor\n\n; Fields \u2014 typed field declarations inside class bodies\n(field_declaration\n declarator: (variable_declarator\n name: (identifier) @name)) @definition.property\n\n; Imports - capture any import declaration child as source\n(import_declaration (_) @import.source) @import\n\n; Calls\n(method_invocation name: (identifier) @call.name) @call\n(method_invocation object: (_) name: (identifier) @call.name) @call\n(method_reference) @call\n\n; Constructor calls: new Foo()\n(object_creation_expression type: (type_identifier) @call.name) @call\n\n; Local variable declarations inside method bodies\n(local_variable_declaration\n declarator: (variable_declarator\n name: (identifier) @name)) @definition.variable\n\n; Write access: obj.field = value\n(assignment_expression\n left: (field_access\n object: (_) @assignment.receiver\n field: (identifier) @assignment.property)\n right: (_)) @assignment\n";
5
5
  export declare const C_QUERIES = "\n; Functions (direct declarator)\n(function_definition declarator: (function_declarator declarator: (identifier) @name)) @definition.function\n(declaration declarator: (function_declarator declarator: (identifier) @name)) @definition.function\n\n; Functions returning pointers (pointer_declarator wraps function_declarator)\n(function_definition declarator: (pointer_declarator declarator: (function_declarator declarator: (identifier) @name))) @definition.function\n(declaration declarator: (pointer_declarator declarator: (function_declarator declarator: (identifier) @name))) @definition.function\n\n; Functions returning double pointers (nested pointer_declarator)\n(function_definition declarator: (pointer_declarator declarator: (pointer_declarator declarator: (function_declarator declarator: (identifier) @name)))) @definition.function\n\n; Structs, Unions, Enums, Typedefs\n(struct_specifier name: (type_identifier) @name) @definition.struct\n(type_definition\n type: (struct_specifier\n body: (field_declaration_list))\n declarator: (type_identifier) @name) @definition.struct\n(union_specifier name: (type_identifier) @name) @definition.union\n(enum_specifier name: (type_identifier) @name) @definition.enum\n(type_definition\n type: (enum_specifier\n body: (enumerator_list))\n declarator: (type_identifier) @name) @definition.enum\n(enumerator name: (identifier) @name) @definition.const\n(type_definition declarator: (type_identifier) @name) @definition.typedef\n\n; Macros\n(preproc_function_def name: (identifier) @name) @definition.macro\n(preproc_def name: (identifier) @name) @definition.macro\n\n; Includes\n(preproc_include path: (_) @import.source) @import\n\n; Calls\n(call_expression function: (identifier) @call.name) @call\n(call_expression function: (field_expression field: (field_identifier) @call.name)) @call\n\n; Variable declarations: int x = 5; or int x;\n(declaration\n declarator: (init_declarator\n declarator: (identifier) @name)) @definition.variable\n";
6
6
  export declare const GO_QUERIES = "\n; Functions & Methods\n(function_declaration name: (identifier) @name) @definition.function\n(method_declaration name: (field_identifier) @name) @definition.method\n(method_elem name: (field_identifier) @name) @definition.method\n\n; Types\n(type_declaration (type_spec name: (type_identifier) @name type: (struct_type))) @definition.struct\n(type_declaration (type_spec name: (type_identifier) @name type: (interface_type))) @definition.interface\n\n; Imports\n(import_declaration (import_spec path: (interpreted_string_literal) @import.source)) @import\n(import_declaration (import_spec_list (import_spec path: (interpreted_string_literal) @import.source))) @import\n\n; Struct fields \u2014 named field declarations inside struct types\n(field_declaration_list\n (field_declaration\n name: (field_identifier) @name) @definition.property)\n\n; Calls\n(call_expression function: (identifier) @call.name) @call\n(call_expression function: (selector_expression field: (field_identifier) @call.name)) @call\n\n; Const/var declarations\n(const_declaration (const_spec (identifier) @name)) @definition.const\n(var_declaration (var_spec (identifier) @name)) @definition.variable\n(var_declaration (var_spec_list (var_spec (identifier) @name))) @definition.variable\n\n; Short variable declaration: x := 5\n(short_var_declaration left: (expression_list (identifier) @name)) @definition.variable\n\n; Struct literal construction: User{Name: \"Alice\"}\n(composite_literal type: (type_identifier) @call.name) @call\n\n; Write access: obj.field = value\n(assignment_statement\n left: (expression_list\n (selector_expression\n operand: (_) @assignment.receiver\n field: (field_identifier) @assignment.property))\n right: (_)) @assignment\n\n; Write access: obj.field++ / obj.field--\n(inc_statement\n (selector_expression\n operand: (_) @assignment.receiver\n field: (field_identifier) @assignment.property)) @assignment\n(dec_statement\n (selector_expression\n operand: (_) @assignment.receiver\n field: (field_identifier) @assignment.property)) @assignment\n";
7
7
  export declare const CPP_QUERIES = "\n; Classes, Structs, Namespaces\n(class_specifier name: (type_identifier) @name) @definition.class\n(class_specifier\n name: (template_type\n (type_identifier) @name\n (template_argument_list) @template-arguments)) @definition.class\n; Out-of-line nested definition: class Outer::Inner { ... } / struct Outer::Inner { ... }.\n; Key the node by the full qualified_identifier text so the def materializes a\n; node that matches the HAS_METHOD owner id (also the full qualified text) and\n; stays distinct from a same-tail type in another scope (#1975, #1978).\n(class_specifier name: (qualified_identifier) @name) @definition.class\n(struct_specifier name: (qualified_identifier) @name) @definition.struct\n(struct_specifier name: (type_identifier) @name) @definition.struct\n(struct_specifier\n name: (template_type\n (type_identifier) @name\n (template_argument_list) @template-arguments)) @definition.struct\n(namespace_definition name: (namespace_identifier) @name) @definition.namespace\n(enum_specifier name: (type_identifier) @name) @definition.enum\n\n; Typedefs and unions (common in C-style headers and mixed C/C++ code)\n(type_definition\n type: (struct_specifier\n body: (field_declaration_list))\n declarator: (type_identifier) @name) @definition.struct\n(type_definition\n type: (enum_specifier\n body: (enumerator_list))\n declarator: (type_identifier) @name) @definition.enum\n(enumerator name: (identifier) @name) @definition.const\n(type_definition declarator: (type_identifier) @name) @definition.typedef\n(union_specifier name: (type_identifier) @name) @definition.union\n\n; Macros\n(preproc_function_def name: (identifier) @name) @definition.macro\n(preproc_def name: (identifier) @name) @definition.macro\n\n; Functions & Methods (direct declarator)\n(function_definition declarator: (function_declarator declarator: (identifier) @name)) @definition.function\n(function_definition declarator: (function_declarator declarator: (operator_name) @name)) @definition.function\n(function_definition declarator: (function_declarator declarator: (qualified_identifier name: (identifier) @name))) @definition.method\n(function_definition declarator: (function_declarator declarator: (qualified_identifier name: (operator_name) @name))) @definition.method\n\n; Functions/methods returning pointers (pointer_declarator wraps function_declarator)\n(function_definition declarator: (pointer_declarator declarator: (function_declarator declarator: (identifier) @name))) @definition.function\n(function_definition declarator: (pointer_declarator declarator: (function_declarator declarator: (qualified_identifier name: (identifier) @name)))) @definition.method\n\n; Functions/methods returning double pointers (nested pointer_declarator)\n(function_definition declarator: (pointer_declarator declarator: (pointer_declarator declarator: (function_declarator declarator: (identifier) @name)))) @definition.function\n(function_definition declarator: (pointer_declarator declarator: (pointer_declarator declarator: (function_declarator declarator: (qualified_identifier name: (identifier) @name))))) @definition.method\n\n; Functions/methods returning references (reference_declarator wraps function_declarator)\n(function_definition declarator: (reference_declarator (function_declarator declarator: (identifier) @name))) @definition.function\n(function_definition declarator: (reference_declarator (function_declarator declarator: (operator_name) @name))) @definition.function\n(function_definition declarator: (reference_declarator (function_declarator declarator: (qualified_identifier name: (identifier) @name)))) @definition.method\n(function_definition declarator: (reference_declarator (function_declarator declarator: (qualified_identifier name: (operator_name) @name)))) @definition.method\n\n; Destructors (destructor_name is distinct from identifier in tree-sitter-cpp)\n(function_definition declarator: (function_declarator declarator: (qualified_identifier name: (destructor_name) @name))) @definition.method\n\n; Function declarations / prototypes (common in headers)\n(declaration declarator: (function_declarator declarator: (identifier) @name)) @definition.function\n(declaration declarator: (function_declarator declarator: (operator_name) @name)) @definition.function\n(declaration declarator: (pointer_declarator declarator: (function_declarator declarator: (identifier) @name))) @definition.function\n(declaration declarator: (reference_declarator (function_declarator declarator: (operator_name) @name))) @definition.function\n\n; Class/struct data member fields (Address address; int count;)\n; Uses field_identifier to exclude method declarations (which use function_declarator)\n(field_declaration\n declarator: (field_identifier) @name) @definition.property\n\n; Pointer member fields (Address* address;)\n(field_declaration\n declarator: (pointer_declarator\n declarator: (field_identifier) @name)) @definition.property\n\n; Reference member fields (Address& address;)\n(field_declaration\n declarator: (reference_declarator\n (field_identifier) @name)) @definition.property\n\n; Inline class method declarations (inside class body, no body: void save();)\n; tree-sitter-cpp uses field_identifier (not identifier) for names inside class bodies\n(field_declaration declarator: (function_declarator declarator: [(field_identifier) (identifier) (operator_name)] @name)) @definition.method\n\n; Inline class method declarations returning a pointer (User* lookup();)\n(field_declaration declarator: (pointer_declarator declarator: (function_declarator declarator: [(field_identifier) (identifier)] @name))) @definition.method\n\n; Inline class method declarations returning a reference (User& lookup();)\n(field_declaration declarator: (reference_declarator (function_declarator declarator: [(field_identifier) (identifier) (operator_name)] @name))) @definition.method\n\n; Inline class method definitions (inside class body, with body: void Foo() { ... })\n(field_declaration_list\n (function_definition\n declarator: (function_declarator\n declarator: [(field_identifier) (identifier) (operator_name) (destructor_name)] @name)) @definition.method)\n\n; Inline class methods returning a pointer type (User* lookup(int id) { ... })\n(field_declaration_list\n (function_definition\n declarator: (pointer_declarator\n declarator: (function_declarator\n declarator: [(field_identifier) (identifier) (operator_name)] @name))) @definition.method)\n\n; Inline class methods returning a reference type (User& lookup(int id) { ... })\n(field_declaration_list\n (function_definition\n declarator: (reference_declarator\n (function_declarator\n declarator: [(field_identifier) (identifier) (operator_name)] @name))) @definition.method)\n\n; Templates\n(template_declaration (class_specifier name: (type_identifier) @name)) @definition.template\n(template_declaration\n (class_specifier\n name: (template_type\n (type_identifier) @name\n (template_argument_list) @template-arguments))) @definition.template\n(template_declaration (function_definition declarator: (function_declarator declarator: (identifier) @name))) @definition.template\n\n; Includes\n(preproc_include path: (_) @import.source) @import\n\n; Calls\n(call_expression function: (identifier) @call.name) @call\n(call_expression function: (field_expression field: (field_identifier) @call.name)) @call\n(call_expression function: (qualified_identifier name: (identifier) @call.name)) @call\n(call_expression function: (template_function name: (identifier) @call.name)) @call\n(binary_expression operator: \"+\" @call.name) @call\n(binary_expression operator: \"<<\" @call.name) @call\n\n; Constructor calls: new User()\n(new_expression type: (type_identifier) @call.name) @call\n\n; Variable declarations: int x = 5; or auto x = 5;\n(declaration\n declarator: (init_declarator\n declarator: (identifier) @name)) @definition.variable\n\n; Structured bindings: auto [a, b] = makePair(); (one @name per bound identifier)\n(declaration\n declarator: (init_declarator\n declarator: (structured_binding_declarator\n (identifier) @name))) @definition.variable\n\n; Structured bindings, reference form: auto& [x, y] = tup;\n(declaration\n declarator: (init_declarator\n declarator: (reference_declarator\n (structured_binding_declarator\n (identifier) @name)))) @definition.variable\n\n; Write access: obj.field = value\n(assignment_expression\n left: (field_expression\n argument: (_) @assignment.receiver\n field: (field_identifier) @assignment.property)\n right: (_)) @assignment\n\n";
@@ -748,6 +748,10 @@ export const JAVA_QUERIES = `
748
748
  ; class extractor synthesizes the javac-style Worker$N name (#2550)
749
749
  (object_creation_expression (class_body)) @definition.class
750
750
 
751
+ ; Enum constant bodies: enum E { A { ... } } — javac's other anonymous
752
+ ; shape, synthesized as E$N by the same naming authority (#2555)
753
+ (enum_constant body: (class_body)) @definition.class
754
+
751
755
  ; Methods & Constructors
752
756
  (method_declaration name: (identifier) @name) @definition.method
753
757
  (constructor_declaration name: (identifier) @name) @definition.constructor
@@ -375,15 +375,39 @@ const MAX_ENCLOSING_WALK_ITERATIONS = 4096;
375
375
  * this one helper.
376
376
  */
377
377
  /** Type-declaration node types that can host (and name) a Java anonymous
378
- * class body — javac numbers `$N` per top-level type of any of these
379
- * kinds. Enum constant bodies (`A { ... }`) are a different node shape
380
- * and remain unmodeled. */
378
+ * class body. Naming follows JLS 13.1: the binary name is the
379
+ * IMMEDIATELY enclosing type's binary name + `$N`, so the synthesized
380
+ * name is the `$`-joined chain of enclosing host names
381
+ * (`EnumWrap$Mode$1`), numbered per immediate host in source order. */
381
382
  const JAVA_ANON_HOST_TYPES = new Set([
382
383
  'class_declaration',
383
384
  'enum_declaration',
384
385
  'interface_declaration',
385
386
  'record_declaration',
386
387
  ]);
388
+ /** The two Java anonymous-class-body shapes (#2550/#2555): an
389
+ * `object_creation_expression` with a `class_body` child
390
+ * (`new Runnable() { ... }`), and an `enum_constant` with a `body:`
391
+ * field (`enum E { A { ... } }` — javac's other `E$N` shape). */
392
+ const isJavaAnonymousBodyNode = (node) => (node.type === 'object_creation_expression' &&
393
+ node.namedChildren?.some((c) => c.type === 'class_body') === true) ||
394
+ (node.type === 'enum_constant' && node.childForFieldName?.('body')?.type === 'class_body');
395
+ /** Nearest ancestor of `node` that is an enclosing TYPE per JLS 13.1 —
396
+ * a named host declaration OR another anonymous body (both shapes).
397
+ * Anonymous ancestors chain through: an anon inside an anon is
398
+ * `Host$1$1`, and an anon inside an enum constant body is `E$1$1`. */
399
+ const nearestJavaEnclosingType = (node) => {
400
+ let cursor = node.parent;
401
+ let iterations = 0;
402
+ while (cursor) {
403
+ if (++iterations > MAX_ENCLOSING_WALK_ITERATIONS)
404
+ return null;
405
+ if (JAVA_ANON_HOST_TYPES.has(cursor.type) || isJavaAnonymousBodyNode(cursor))
406
+ return cursor;
407
+ cursor = cursor.parent;
408
+ }
409
+ return null;
410
+ };
387
411
  /** Per-parse-tree memo of anonymous-body numbering: tree → (startIndex →
388
412
  * synthesized name). Keyed by the tree OBJECT via WeakMap so entries die
389
413
  * with the parse; without it every call re-scans the host subtree
@@ -392,10 +416,7 @@ const JAVA_ANON_HOST_TYPES = new Set([
392
416
  * listener-per-widget Java). */
393
417
  const javaAnonNameMemo = new WeakMap();
394
418
  export const synthesizeJavaAnonymousClassName = (node) => {
395
- if (node.type !== 'object_creation_expression')
396
- return undefined;
397
- const hasClassBody = node.namedChildren?.some((c) => c.type === 'class_body');
398
- if (hasClassBody !== true)
419
+ if (!isJavaAnonymousBodyNode(node))
399
420
  return undefined;
400
421
  const tree = node.tree;
401
422
  if (tree !== undefined) {
@@ -403,38 +424,68 @@ export const synthesizeJavaAnonymousClassName = (node) => {
403
424
  if (cached !== undefined)
404
425
  return cached;
405
426
  }
406
- // Topmost enclosing host type declaration — javac numbers per top-level type.
407
- let topHost = null;
408
- let cursor = node.parent;
409
- let iterations = 0;
410
- while (cursor) {
411
- if (++iterations > MAX_ENCLOSING_WALK_ITERATIONS)
427
+ // JLS 13.1: the binary name is the IMMEDIATELY ENCLOSING TYPE's binary
428
+ // name + `$N`. The enclosing type may itself be anonymous — then its
429
+ // own synthesized name is the prefix (recursion, memo-bounded):
430
+ // `NestHost$1$1` for an anon inside an anon, `E$1$1` for an anon
431
+ // inside an enum constant body. For a named enclosing type the prefix
432
+ // is the `$`-joined chain of named hosts (`EnumWrap$Mode`).
433
+ const enclosing = nearestJavaEnclosingType(node);
434
+ if (enclosing === null)
435
+ return undefined;
436
+ let prefix;
437
+ if (isJavaAnonymousBodyNode(enclosing)) {
438
+ const enclosingName = synthesizeJavaAnonymousClassName(enclosing);
439
+ if (enclosingName === undefined)
412
440
  return undefined;
413
- if (JAVA_ANON_HOST_TYPES.has(cursor.type))
414
- topHost = cursor;
415
- cursor = cursor.parent;
441
+ prefix = enclosingName;
416
442
  }
417
- if (topHost === null)
418
- return undefined;
419
- const topName = topHost.childForFieldName?.('name')?.text;
420
- if (topName === undefined || topName.length === 0)
421
- return undefined;
422
- const anonBodies = (topHost.descendantsOfType?.('object_creation_expression') ?? []).filter((c) => c.namedChildren?.some((n) => n.type === 'class_body'));
443
+ else {
444
+ const hostNames = [];
445
+ let cursor = enclosing;
446
+ let iterations = 0;
447
+ while (cursor) {
448
+ if (++iterations > MAX_ENCLOSING_WALK_ITERATIONS)
449
+ return undefined;
450
+ if (JAVA_ANON_HOST_TYPES.has(cursor.type)) {
451
+ const hostName = cursor.childForFieldName?.('name')?.text;
452
+ if (hostName === undefined || hostName.length === 0)
453
+ return undefined;
454
+ hostNames.unshift(hostName);
455
+ }
456
+ cursor = cursor.parent;
457
+ }
458
+ prefix = hostNames.join('$');
459
+ }
460
+ // All anonymous bodies (both shapes) whose immediately enclosing TYPE
461
+ // is THIS one, in source order. `descendantsOfType` over the subtree
462
+ // also finds bodies belonging to nested enclosing types — filter them
463
+ // out by re-deriving each candidate's own enclosing type.
464
+ const candidates = [
465
+ ...(enclosing.descendantsOfType?.('object_creation_expression') ?? []),
466
+ ...(enclosing.descendantsOfType?.('enum_constant') ?? []),
467
+ ]
468
+ .filter(isJavaAnonymousBodyNode)
469
+ .filter((c) => {
470
+ const host = nearestJavaEnclosingType(c);
471
+ return (host !== null && host.startIndex === enclosing.startIndex && host.type === enclosing.type);
472
+ })
473
+ .sort((a, b) => a.startIndex - b.startIndex);
423
474
  if (tree !== undefined) {
424
475
  let byStart = javaAnonNameMemo.get(tree);
425
476
  if (byStart === undefined) {
426
477
  byStart = new Map();
427
478
  javaAnonNameMemo.set(tree, byStart);
428
479
  }
429
- for (let i = 0; i < anonBodies.length; i++) {
430
- byStart.set(anonBodies[i].startIndex, `${topName}$${i + 1}`);
480
+ for (let i = 0; i < candidates.length; i++) {
481
+ byStart.set(candidates[i].startIndex, `${prefix}$${i + 1}`);
431
482
  }
432
483
  return byStart.get(node.startIndex);
433
484
  }
434
- const index = anonBodies.findIndex((c) => c.startIndex === node.startIndex);
485
+ const index = candidates.findIndex((c) => c.startIndex === node.startIndex);
435
486
  if (index === -1)
436
487
  return undefined;
437
- return `${topName}$${index + 1}`;
488
+ return `${prefix}$${index + 1}`;
438
489
  };
439
490
  export const findEnclosingClassInfo = (node, filePath, resolveEnclosingOwner,
440
491
  /**
@@ -494,13 +545,15 @@ getQualifiedOwnerName) => {
494
545
  }
495
546
  }
496
547
  }
497
- // Java: an anonymous class body (`new Runnable() { ... }`) owns its
498
- // members — attribute to the synthesized `Worker$N` class, not the
499
- // lexically enclosing named class (#2550). `synthesizeJavaAnonymousClassName`
500
- // returns undefined for `object_creation_expression` without a
501
- // `class_body` (plain `new Foo()`, and every C# shape), so the walk
502
- // continues unchanged for those.
503
- if (current.type === 'object_creation_expression') {
548
+ // Java: an anonymous class body owns its members — attribute to the
549
+ // synthesized `Worker$N`/`E$N` class, not the lexically enclosing
550
+ // named type (#2550/#2555). Covers both shapes: `new Runnable() { ... }`
551
+ // and enum constant bodies (`enum E { A { ... } }`). The synthesis
552
+ // returns undefined for shape-less nodes (plain `new Foo()`, a body-less
553
+ // enum constant, and every C# `object_creation_expression`), so the
554
+ // walk continues unchanged for those — including on to
555
+ // `enum_declaration`, which sits in CLASS_CONTAINER_TYPES below.
556
+ if (current.type === 'object_creation_expression' || current.type === 'enum_constant') {
504
557
  const anonName = synthesizeJavaAnonymousClassName(current);
505
558
  if (anonName !== undefined) {
506
559
  return {
@@ -52,7 +52,7 @@ import { fileURLToPath } from 'url';
52
52
  // the main thread (the #1983 OOM). Because the two stores share this version,
53
53
  // any future change to the `ParsedFile` serialization shape MUST bump
54
54
  // SCHEMA_BUMP so both invalidate in lockstep.
55
- const SCHEMA_BUMP = 18; // Java anonymous class bodies emit synthesized Worker$N Class nodes and re-keyed methods (#2550). (17 = callable-value-flow operand identity; 16 = direct callee identity; 15 = always-on callableFlowSites.)
55
+ const SCHEMA_BUMP = 19; // Java enum constant bodies emit E$N Class nodes; anonymous naming switched to JLS 13.1 immediate-host chains (#2555). (18 = Worker$N anonymous bodies; 17 = callable-value-flow operand identity; 16 = direct callee identity.)
56
56
  const GITNEXUS_PKG_VERSION = (() => {
57
57
  try {
58
58
  // package.json sits at gitnexus/package.json — two levels up from
@@ -341,8 +341,14 @@ export interface RepoMeta {
341
341
  * unchanged files — a top-up against a pre-v8 index would strand the old
342
342
  * `Worker.run`-keyed Method nodes alongside the new ones (the v5 Route
343
343
  * precedent); force a full re-analyze instead.
344
- */
345
- export declare const INCREMENTAL_SCHEMA_VERSION = 8;
344
+ * v9: Java enum constant bodies joined the instance model and anonymous
345
+ * naming switched to JLS 13.1 immediately-enclosing-type chains (#2555): `enum E { A {
346
+ * hook(){} } }` now emits `Class:...:E$1` with methods re-keyed from
347
+ * `E.hook` to `E$1.hook`, and nested-host anonymous names re-key
348
+ * (`EnumWrap$1` → `EnumWrap$Mode$1`). Same contract as v8: identities move
349
+ * on unchanged files; force a full re-analyze.
350
+ */
351
+ export declare const INCREMENTAL_SCHEMA_VERSION = 9;
346
352
  export interface IndexedRepo {
347
353
  repoPath: string;
348
354
  storagePath: string;
@@ -118,8 +118,14 @@ export const cloneDirBelongsToEntry = (cloneDir, entryPath) => registryPathEqual
118
118
  * unchanged files — a top-up against a pre-v8 index would strand the old
119
119
  * `Worker.run`-keyed Method nodes alongside the new ones (the v5 Route
120
120
  * precedent); force a full re-analyze instead.
121
+ * v9: Java enum constant bodies joined the instance model and anonymous
122
+ * naming switched to JLS 13.1 immediately-enclosing-type chains (#2555): `enum E { A {
123
+ * hook(){} } }` now emits `Class:...:E$1` with methods re-keyed from
124
+ * `E.hook` to `E$1.hook`, and nested-host anonymous names re-key
125
+ * (`EnumWrap$1` → `EnumWrap$Mode$1`). Same contract as v8: identities move
126
+ * on unchanged files; force a full re-analyze.
121
127
  */
122
- export const INCREMENTAL_SCHEMA_VERSION = 8;
128
+ export const INCREMENTAL_SCHEMA_VERSION = 9;
123
129
  const GITNEXUS_DIR = '.gitnexus';
124
130
  const GITNEXUS_EXCLUDE_ENTRY = `${GITNEXUS_DIR}/`;
125
131
  export const INDEX_METADATA_FILE = 'gitnexus.json';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.6.10-rc.49",
3
+ "version": "1.6.10-rc.50",
4
4
  "description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
5
5
  "author": "Abhigyan Patwari",
6
6
  "license": "PolyForm-Noncommercial-1.0.0",