fix(java): emit a graph node for record_declaration (#2564)

JAVA_QUERIES had no @definition.record capture, unlike its
class_declaration/interface_declaration/enum_declaration siblings and
unlike CSHARP_QUERIES' own record_declaration pattern. A Java record's
container node was never created, so its HAS_METHOD edges were dropped
at persistence even though ownership resolution computed a valid
ownerId for its methods.

Downstream label mapping, the class-extractor config, the dispatch
table, and ownership reconciliation already treated 'Record' correctly
- this was purely a missing structure-phase capture.
This commit is contained in:
Gergo Magyar 2026-07-21 06:16:32 +00:00
parent 16c758603b
commit 9766cb7abf
3 changed files with 47 additions and 1 deletions

View file

@ -743,10 +743,11 @@ export const PYTHON_QUERIES = `
// Java queries - works with tree-sitter-java
export const JAVA_QUERIES = `
; Classes, Interfaces, Enums, Annotations
; Classes, Interfaces, Enums, Records, Annotations
(class_declaration name: (identifier) @name) @definition.class
(interface_declaration name: (identifier) @name) @definition.interface
(enum_declaration name: (identifier) @name) @definition.enum
(record_declaration name: (identifier) @name) @definition.record
(annotation_type_declaration name: (identifier) @name) @definition.annotation
; Anonymous class bodies: new Runnable() { ... } — no @name capture; the

View file

@ -0,0 +1,11 @@
package probe;
public record Point(int x, int y) {
public int sum() {
return x + y;
}
public int scaled(int factor) {
return sum() * factor;
}
}

View file

@ -1174,6 +1174,40 @@ describe('Java chained method call resolution', () => {
});
});
// ---------------------------------------------------------------------------
// Java record: container node + HAS_METHOD edges
// Regression test for #2564: JAVA_QUERIES previously had no @definition.record
// capture, so a record never got a Class/Record graph node — its methods
// existed as ownerless orphans with no HAS_METHOD edge.
// ---------------------------------------------------------------------------
describe('Java record method resolution (#2564)', () => {
let result: PipelineResult;
beforeAll(async () => {
result = await runPipelineFromRepo(path.join(FIXTURES, 'java-record-methods'), () => {});
}, 60000);
it('detects a Record node for Point', () => {
const records = getNodesByLabel(result, 'Record');
expect(records).toContain('Point');
});
it('emits HAS_METHOD edges linking sum and scaled to Point', () => {
const hasMethod = getRelationships(result, 'HAS_METHOD');
const sumEdge = hasMethod.find((e) => e.source === 'Point' && e.target === 'sum');
const scaledEdge = hasMethod.find((e) => e.source === 'Point' && e.target === 'scaled');
expect(sumEdge).toBeDefined();
expect(scaledEdge).toBeDefined();
});
it('resolves scaled() calling sum() via a CALLS edge', () => {
const calls = getRelationships(result, 'CALLS');
const sumCall = calls.find((c) => c.target === 'sum' && c.source === 'scaled');
expect(sumCall).toBeDefined();
});
});
// ---------------------------------------------------------------------------
// Java 16+ instanceof pattern variable: `if (obj instanceof User user)`
// Phase 5.2: extractPatternBinding on instanceof_expression binds user → User.