gitnexus 1.6.6-rc.144 → 1.6.6-rc.145

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.
@@ -161,7 +161,101 @@ export function emitJavaScopeCaptures(sourceText, _filePath, cachedTree) {
161
161
  }
162
162
  out.push(grouped);
163
163
  }
164
- return [...resolveVarTypeBindings(out), ...synthesizeJavaInheritanceReferences(tree.rootNode)];
164
+ return [
165
+ ...resolveVarTypeBindings(out),
166
+ ...synthesizeJavaInheritanceReferences(tree.rootNode),
167
+ ...synthesizeJavaExplicitConstructorReferences(tree.rootNode),
168
+ ];
169
+ }
170
+ /**
171
+ * Synthesize `@reference.call.constructor` captures for explicit constructor
172
+ * invocations — `super(...)` and `this(...)` (F38 #1928). tree-sitter-java
173
+ * models these as `explicit_constructor_invocation` nodes, which the scope
174
+ * query does not match, so the chained-constructor CALLS edges (subclass ctor →
175
+ * superclass ctor; ctor → sibling overload) were silently dropped.
176
+ *
177
+ * The grammar gives no constructor *name* at the call site (the child is a bare
178
+ * `(super)` / `(this)` token), so the target name is resolved structurally:
179
+ * - `this(...)` → the enclosing type's own simple name (constructor symbols
180
+ * are keyed by the declaring class name).
181
+ * - `super(...)` → the enclosing class's superclass simple-name tail (reusing
182
+ * `javaBaseLookupNameNode` so qualified/generic supers reduce
183
+ * to the bare class name, matching the EXTENDS synth). An
184
+ * implicit `Object` super (no `superclass` field) has no
185
+ * in-graph symbol, so it is skipped rather than emitting a
186
+ * dangling reference.
187
+ * Arity is attached so overloaded constructors disambiguate downstream, mirroring
188
+ * the call-site arity synthesized for `new X(...)`.
189
+ */
190
+ function synthesizeJavaExplicitConstructorReferences(root) {
191
+ const out = [];
192
+ const stack = [root];
193
+ while (stack.length > 0) {
194
+ const node = stack.pop();
195
+ if (node.type === 'explicit_constructor_invocation') {
196
+ emitJavaExplicitConstructorRef(out, node);
197
+ }
198
+ for (let i = 0; i < node.namedChildCount; i++) {
199
+ const child = node.namedChild(i);
200
+ if (child !== null)
201
+ stack.push(child);
202
+ }
203
+ }
204
+ return out;
205
+ }
206
+ const TYPE_DECL_NODE_TYPES = new Set([
207
+ 'class_declaration',
208
+ 'enum_declaration',
209
+ 'record_declaration',
210
+ ]);
211
+ function emitJavaExplicitConstructorRef(out, node) {
212
+ const ctor = node.childForFieldName('constructor');
213
+ if (ctor === null)
214
+ return;
215
+ const enclosingType = findEnclosingTypeDeclaration(node);
216
+ if (enclosingType === null)
217
+ return;
218
+ let targetNameNode = null;
219
+ if (ctor.type === 'this') {
220
+ targetNameNode = enclosingType.childForFieldName('name');
221
+ }
222
+ else if (ctor.type === 'super') {
223
+ // Only class_declaration carries a `superclass` field; enum/record cannot
224
+ // declare an explicit superclass, so `super(...)` there has no resolvable
225
+ // target symbol.
226
+ const superclass = enclosingType.childForFieldName('superclass');
227
+ if (superclass === null)
228
+ return;
229
+ for (const base of superclass.namedChildren) {
230
+ if (base === null)
231
+ continue;
232
+ const nameNode = javaBaseLookupNameNode(base);
233
+ if (nameNode !== null) {
234
+ targetNameNode = nameNode;
235
+ break;
236
+ }
237
+ }
238
+ }
239
+ if (targetNameNode === null)
240
+ return;
241
+ const argList = node.childForFieldName('arguments');
242
+ const args = argList === null
243
+ ? []
244
+ : argList.namedChildren.filter((c) => c !== null && c.type !== 'block_comment' && c.type !== 'line_comment');
245
+ out.push({
246
+ '@reference.call.constructor': nodeToCapture('@reference.call.constructor', node),
247
+ '@reference.name': nodeToCapture('@reference.name', targetNameNode),
248
+ '@reference.arity': syntheticCapture('@reference.arity', node, String(args.length)),
249
+ });
250
+ }
251
+ function findEnclosingTypeDeclaration(node) {
252
+ let cur = node.parent;
253
+ while (cur !== null) {
254
+ if (TYPE_DECL_NODE_TYPES.has(cur.type))
255
+ return cur;
256
+ cur = cur.parent;
257
+ }
258
+ return null;
165
259
  }
166
260
  /**
167
261
  * Synthesize `@reference.inherits` captures from Java class heritage so the
@@ -67,10 +67,14 @@ export function interpretJavaTypeBinding(captures) {
67
67
  const typeCap = captures['@type-binding.type'];
68
68
  if (nameCap === undefined || typeCap === undefined)
69
69
  return null;
70
- // Strip qualifier first so that `com.example.BaseModel<T>` becomes
71
- // `BaseModel<T>` before stripGeneric — the JVM-erasure fallback pattern
72
- // requires an unqualified identifier at the start of the string.
73
- const rawType = stripGeneric(stripQualifier(typeCap.text.trim()));
70
+ // Strip generics BEFORE the qualifier (F41 #1928). Stripping the qualifier
71
+ // first uses `lastIndexOf('.')`, which for a qualified *type argument*
72
+ // (`Map<String, com.example.User>`) cuts inside the generic and yields a
73
+ // corrupted `User>`. Unwrapping generics first reduces the string to a single
74
+ // (possibly qualified) class name, then the qualifier strip leaves the bare
75
+ // simple name. `stripGeneric`'s erasure fallback is qualifier-tolerant so a
76
+ // qualified generic base (`com.example.BaseModel<T>`) still reduces correctly.
77
+ const rawType = stripQualifier(stripGeneric(typeCap.text.trim()));
74
78
  // Skip `var` — tree-sitter-java parses `var` as type_identifier with
75
79
  // text "var". When used without a constructor initializer, there's no
76
80
  // concrete type to bind.
@@ -120,8 +124,10 @@ function stripGeneric(text) {
120
124
  // `BaseModel<T>` → `BaseModel`, `Builder<Self>` → `Builder`.
121
125
  // This mirrors JVM type erasure — the raw class name is the resolvable symbol.
122
126
  // The pattern matches up to the first `<` to handle nested generics safely
123
- // (e.g. `BaseModel<List<String>>` → `BaseModel`).
124
- const fallback = text.match(/^([A-Za-z_$][A-Za-z0-9_$]*)<.+>$/s);
127
+ // (e.g. `BaseModel<List<String>>` → `BaseModel`). The base is allowed to be
128
+ // qualified (`com.example.BaseModel<T>` → `com.example.BaseModel`) since the
129
+ // caller strips the qualifier afterwards (F41 #1928).
130
+ const fallback = text.match(/^((?:[A-Za-z_$][A-Za-z0-9_$]*\.)*[A-Za-z_$][A-Za-z0-9_$]*)<.+>$/s);
125
131
  if (fallback !== null)
126
132
  return fallback[1].trim();
127
133
  return text;
@@ -212,8 +212,23 @@ const JAVA_SCOPE_QUERY = `
212
212
  type: (generic_type
213
213
  (type_identifier) @reference.name)) @reference.call.constructor
214
214
 
215
+ ;; References — qualified constructor calls: new pkg.Foo(), new a.b.Foo() (F35 #1928)
216
+ ;; tree-sitter-java parses \`pkg.Foo\` as a scoped_type_identifier whose final
217
+ ;; child is the simple type. Bind that tail as @reference.name (trailing \`.\`
218
+ ;; anchor = last child) so resolution targets \`Foo\`, not the raw \`pkg.Foo\` text.
219
+ ;; Mirrors the TS/JS new-expression qualified-constructor capture.
215
220
  (object_creation_expression
216
- type: (scoped_type_identifier) @reference.call.constructor.qualified) @reference.call.constructor
221
+ type: (scoped_type_identifier
222
+ (type_identifier) @reference.name .) @reference.call.constructor.qualified) @reference.call.constructor
223
+
224
+ ;; References — qualified + generic constructor calls: new pkg.Box<T>() (F35 #1928)
225
+ ;; The base is a generic_type whose first child is a scoped_type_identifier, so
226
+ ;; neither the simple-generic nor the plain-scoped arm above matches it. Bind the
227
+ ;; scoped tail as @reference.name.
228
+ (object_creation_expression
229
+ type: (generic_type
230
+ (scoped_type_identifier
231
+ (type_identifier) @reference.name .) @reference.call.constructor.qualified)) @reference.call.constructor
217
232
 
218
233
  ;; References — method references: User::getName, obj::method
219
234
  (method_reference
@@ -48,6 +48,15 @@ function isCallerAnchorLabel(label) {
48
48
  label === 'Struct' ||
49
49
  label === 'Enum');
50
50
  }
51
+ /**
52
+ * Callables whose same-name overloads occupy distinct graph nodes keyed by
53
+ * parameter types / shape. Must mirror `isOverloadableCallable` in
54
+ * `node-lookup.ts` so registration and lookup agree (Constructor included —
55
+ * #1928 F38).
56
+ */
57
+ function isOverloadableCallable(label) {
58
+ return label === 'Function' || label === 'Method' || label === 'Constructor';
59
+ }
51
60
  /**
52
61
  * Look up a `SymbolDefinition` in the graph node lookup.
53
62
  *
@@ -83,7 +92,7 @@ export function resolveDefGraphId(filePath, def, nodeLookup) {
83
92
  if (cHit !== undefined)
84
93
  return cHit;
85
94
  }
86
- if ((def.type === 'Function' || def.type === 'Method') &&
95
+ if (isOverloadableCallable(def.type) &&
87
96
  def.parameterTypes !== undefined &&
88
97
  def.parameterTypeClasses !== undefined) {
89
98
  const shapeTag = parameterShapeIdTag(def.parameterTypes, def.parameterTypeClasses);
@@ -96,8 +105,11 @@ export function resolveDefGraphId(filePath, def, nodeLookup) {
96
105
  }
97
106
  // Overload disambiguation: when the def carries parameter types,
98
107
  // try the parameter-typed key first so same-name same-arity
99
- // overloads route to their distinct graph nodes.
100
- if ((def.type === 'Function' || def.type === 'Method') &&
108
+ // overloads route to their distinct graph nodes. Constructors are
109
+ // included so a `this(int)`/`super(int)` chain or `new Foo(int)`
110
+ // resolves to the matching ctor overload instead of first-wins
111
+ // collapsing onto another `Foo` ctor (a self-loop) — #1928 F38.
112
+ if (isOverloadableCallable(def.type) &&
101
113
  def.parameterTypes !== undefined &&
102
114
  def.parameterTypes.length > 0) {
103
115
  const pKey = qualifiedKey(filePath, def.type, `${qn}~${def.parameterTypes.join(',')}`);
@@ -87,9 +87,7 @@ export function buildGraphNodeLookup(graph) {
87
87
  // a parameter-types-suffixed key so resolveDefGraphId can find
88
88
  // the right overload by matching its def's parameterTypes.
89
89
  const pTypes = props.parameterTypes;
90
- if (pTypes !== undefined &&
91
- pTypes.length > 0 &&
92
- (node.label === 'Function' || node.label === 'Method')) {
90
+ if (pTypes !== undefined && pTypes.length > 0 && isOverloadableCallable(node.label)) {
93
91
  const pKey = qualifiedKey(props.filePath, node.label, `${keyQualified}~${pTypes.join(',')}`);
94
92
  // Each overload is unique — set unconditionally.
95
93
  if (!lookup.has(pKey))
@@ -98,7 +96,7 @@ export function buildGraphNodeLookup(graph) {
98
96
  const pClasses = props
99
97
  .parameterTypeClasses;
100
98
  const shapeTag = parameterShapeIdTag(pTypes, pClasses);
101
- if (shapeTag !== '' && (node.label === 'Function' || node.label === 'Method')) {
99
+ if (shapeTag !== '' && isOverloadableCallable(node.label)) {
102
100
  const shapeKey = qualifiedKey(props.filePath, node.label, `${keyQualified}${shapeTag}`);
103
101
  if (!lookup.has(shapeKey))
104
102
  lookup.set(shapeKey, node.id);
@@ -136,6 +134,17 @@ export function buildGraphNodeLookup(graph) {
136
134
  }
137
135
  return lookup;
138
136
  }
137
+ /**
138
+ * Callables whose same-name overloads must route to distinct graph nodes via
139
+ * the parameter-types / shape key. Constructors belong here too: a class with
140
+ * `Foo()` and `Foo(int)` mints distinct `#0`/`#1` Constructor nodes, and a
141
+ * `this(...)`/`super(...)` edge (or any `new Foo(args)`) must reach the right
142
+ * one. Without the overload key both ctor nodes collapse onto the first-wins
143
+ * qualified/simple key, turning a `this()` chain into a self-loop (#1928 F38).
144
+ */
145
+ function isOverloadableCallable(label) {
146
+ return label === 'Function' || label === 'Method' || label === 'Constructor';
147
+ }
139
148
  export function isLinkableLabel(label) {
140
149
  return (label === 'Function' ||
141
150
  label === 'Method' ||
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.6.6-rc.144",
3
+ "version": "1.6.6-rc.145",
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",