mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-11-11 08:37:35 +00:00
test(java): cover Record heritage review gaps
Document deferred enum and implicit-accessor behavior, make assertions order-independent, and add Record heritage to the capture benchmark.
This commit is contained in:
parent
9615f098c8
commit
8a7fc6d7c9
5 changed files with 67 additions and 44 deletions
|
|
@ -132,8 +132,9 @@
|
|||
"_rebaselined": "#1919 review CF3 fix: extended kotlin-local-property-owner (init/accessor destructuring) + new dart-accessor-owner fixture (getter/setter ownership). Fingerprint-only corpus drift; scaling ~1.0."
|
||||
},
|
||||
"java": {
|
||||
"fingerprint": "b29e263524f55151dcb7cfc4c929d3d1d7bb360355cee4e832158f927857f663",
|
||||
"fingerprint": "36d689c58526c4482fbd701d1d9ca156623a3970734ead145717858712271ab5",
|
||||
"scaling_budget": 1.5,
|
||||
"_rebaselined_2900_record_heritage": "#2900 review follow-up: the Java scale unit now includes a record implementing Marker, so the record-declaration @reference.inherits path is fingerprinted and exercised at scale. Prior b29e263524f55151dcb7cfc4c929d3d1d7bb360355cee4e832158f927857f663 -> 36d689c58526c4482fbd701d1d9ca156623a3970734ead145717858712271ab5; scaling 1.042 < 1.5.",
|
||||
"_rebaselined_callable_flow_2522_review": "PR #2522 review hardening: callable operands retain expression/qualified identity and formals retain signature metadata; same-name lexical regions use an O(ancestor-depth) ID-set lookup. Prior d5c59d7dc9e206637515d5aea1163f7c1cdd76410c38c5fe6143d13d19677d6a -> 004a3592998dca1193bd1429a8284513725de7764f2a3eceedaaa984cfd763b4; scaling 0.992 < 1.5.",
|
||||
"_rebaselined_callable_flow_2522_followup": "PR #2522 follow-up: Java method-reference/SAM callable flow facts with invocation-result suppression. Prior 062d754764aaa8a6772fb90875c710502a63e3e7a300e633942381ed914faada -> d5c59d7dc9e206637515d5aea1163f7c1cdd76410c38c5fe6143d13d19677d6a; scaling 1.074 < 1.5.",
|
||||
"_rebaselined": "#2357 (supersedes #2353): + java-cast-receiver, java-this-field-chain, java-this-dispatch fixtures (cast-wrapped receivers, this.field chains incl. initializer contexts, bare-this dispatch pinning). Drift is purely fixture-additive: with the three new dirs parked, the fingerprint reproduces the prior baseline byte-identically — no emit/capture change. #1956 synth-widening: + java-iface-extends fixture; synthesizeJavaInheritanceReferences now ALSO walks interface_declaration extends_interfaces (interface IA extends IB, IC<T>), matching the #1940 legacy leg. (Earlier U2+review: java-qualified-base fixture covers 2- AND 3-segment qualified bases guarding the legacy end-anchor; synth tail-resolves scoped bases.) Linear (~1.03). (Earliest: java added to bench, exposed+fixed the O(n^2) findNodeAtRange root-walk; 3.09 -> ~0.99.) | #942: scope-resolution-only cleanup reworded fixture comments; capture byte-positions shift, capture LOGIC unchanged.",
|
||||
|
|
@ -146,9 +147,9 @@
|
|||
"_rebaselined_2562_local_classes": "#2562: Java block-local classes, enums, records, and interfaces use source-type-relative JLS 13.1 Host$NLocal identities with javac-compatible per-(host, simple-name) numbering; anonymous numbering remains separate. Lexical aliases begin at each declaration and end with its immediate block. Expanded java-local-class-naming fixtures cover declaration order, disjoint blocks, initializers, lambdas, local type kinds, and recursive local/member/anonymous host chains. Prior d04298a91beec76d0fa7099b3d71265723be60c1df688969aa954f135dd49686 -> 6dd5913a58400a191ff54abf9b852b03d5add657d16c11e60a7c4608ba186197; scaling 1.204 < 1.5.",
|
||||
"_rebaselined_receiver_chain_2747": "#2747 receiver-chain rollout: call matches whose receiver is itself an expression now carry `@reference.receiver-chain`, a compact encoding of the receiver's structure, so resolution types it by folding instead of re-parsing receiver source text. Capture GROUP counts are unchanged — the tag is added to existing call matches, never a new match — so this is digest drift only. Prior 6dd5913a58400a191ff54abf9b852b03d5add657d16c11e60a7c4608ba186197 -> 310adbc2e0827b5ac749acaa981cd12d256fc5b7cbc5592c5bee219e92abf9ee.",
|
||||
"_rebaselined_2766_receiver_chain_wire_v2": "#2766: receiver-chain wire format v1 -> v2 (name-free `await` / `index` step kinds). The VERSION prefix is part of every emitted `@reference.receiver-chain` capture, so every chain-minting language's capture text changed. WIRE-FORMAT CHANGE, NOT A CAPTURE-SET CHANGE: the same chains are minted for the same sites, spelled `2|…` instead of `1|…`. Exactly the 12 chain-minting languages drifted; c, cobol and dart did not, which is the check that this is the prefix and not a capture regression. Accompanied by SCHEMA_BUMP 34 -> 37 and INCREMENTAL_SCHEMA_VERSION 28 -> 31 so a stale index is rejected rather than replaying chains a v2 decoder refuses. Prior 310adbc2e0827b5ac749acaa981cd12d256fc5b7cbc5592c5bee219e92abf9ee -> a9943355e945e03ddb87c800f4cc1f62b3d04feefb3ec64c258d8e0bb3b3fcd9.",
|
||||
"capture_groups_small": 5005,
|
||||
"capture_groups_large": 16005,
|
||||
"capture_groups_fp": 3452,
|
||||
"capture_groups_small": 5755,
|
||||
"capture_groups_large": 18405,
|
||||
"capture_groups_fp": 3512,
|
||||
"fixture_count": 206
|
||||
},
|
||||
"java-local-types": {
|
||||
|
|
|
|||
|
|
@ -267,14 +267,15 @@ const LANGS = [
|
|||
fixturePrefix: 'java',
|
||||
exts: ['.java'],
|
||||
file: 'bench.java',
|
||||
// Java was previously unbenched. Heritage-bearing: extends Base + implements
|
||||
// Marker (both forms) so the @reference.inherits synth (#1951) is driven at scale.
|
||||
// Java was previously unbenched. Class and record heritage both implement
|
||||
// Marker so the @reference.inherits synth (#1951, #2900) is driven at scale.
|
||||
header: 'package generated;\n\nclass Base {}\n\ninterface Marker {}\n\n',
|
||||
unit: (n) =>
|
||||
`class Entity${n} extends Base implements Marker {\n` +
|
||||
` long id = 0L;\n String name = "";\n` +
|
||||
` public long getId() { return this.id; }\n` +
|
||||
` public void setName(String v) { this.name = v; }\n}\n\n`,
|
||||
` public void setName(String v) { this.name = v; }\n}\n\n` +
|
||||
`record RecordEntity${n}(long id) implements Marker {}\n\n`,
|
||||
},
|
||||
{
|
||||
name: 'java-local-types',
|
||||
|
|
|
|||
|
|
@ -631,55 +631,42 @@ function findEnclosingTypeDeclaration(node: SyntaxNode): SyntaxNode | null {
|
|||
}
|
||||
|
||||
/**
|
||||
* Synthesize `@reference.inherits` captures from Java class heritage so the
|
||||
* registry-primary scope-resolution path emits EXTENDS / IMPLEMENTS edges
|
||||
* (mirrors C++ `emitCppInheritanceCaptures`). Without this, Java inheritance
|
||||
* edges came only from the legacy heritage-capture leg (removed in #942), which
|
||||
* is dropped for registry-primary languages in the worker pipeline (issue #1951).
|
||||
* Synthesize `@reference.inherits` captures from Java type heritage for the
|
||||
* authoritative registry-primary EXTENDS / IMPLEMENTS pre-pass (mirrors C++
|
||||
* `emitCppInheritanceCaptures`).
|
||||
*
|
||||
* Scope covers `class_declaration` (`superclass` extends + `interfaces`
|
||||
* implements clauses), `record_declaration` (`interfaces` implements clauses),
|
||||
* AND `interface_declaration` (`extends_interfaces` → interface-to-interface
|
||||
* EXTENDS), matching the legacy Java heritage query
|
||||
* (tree-sitter-queries.ts), which has a dedicated `interface_declaration
|
||||
* (extends_interfaces (type_list …))` arm. Without the interface arm the
|
||||
* registry-primary synth silently dropped every `interface IA extends IB`
|
||||
* edge while the legacy leg emitted it — the exact =0/=N parity break #1951
|
||||
* targets. Record heritage is emitted even though the legacy query had no
|
||||
* matching arm: Record graph nodes are canonical link targets since #2801, so
|
||||
* dropping their `implements` clause prevents interface dispatch (#2900). Enum
|
||||
* heritage remains unemitted. Generic
|
||||
* bases (`extends Box<T>`, `implements IFoo<T>`) ARE emitted here: the legacy
|
||||
* heritage query was widened to capture the inner `type_identifier` of a
|
||||
* `generic_type` (tree-sitter-queries.ts), so both paths now agree on SIMPLE
|
||||
* (unqualified) generic bases — the more-correct behavior, consistent with
|
||||
* C#/Rust (#1951). Qualified bases (`a.b.Base`, `a.b.Box<T>`, `a.b.IFoo<T>`) are
|
||||
* ALSO now at parity (#1956 tri-review U2): the synth resolves them by their
|
||||
* `scoped_type_identifier` tail, and the legacy heritage query was widened
|
||||
* with matching `scoped_type_identifier` arms (plain + generic-wrapped). The
|
||||
* and `interface_declaration` (`extends_interfaces` clauses). Interface
|
||||
* inheritance was restored for registry-primary resolution in #1951. Record
|
||||
* graph nodes became canonical link targets in #2801 / PR #2871, so their
|
||||
* `implements` clauses must participate for interface dispatch (#2900). Java
|
||||
* enum interface heritage remains a separately tracked gap (#2918).
|
||||
*
|
||||
* Generic bases (`extends Box<T>`, `implements IFoo<T>`) and qualified bases
|
||||
* (`a.b.Base`, `a.b.Box<T>`, `a.b.IFoo<T>`) are normalized to their simple
|
||||
* lookup-name tails, consistent with C#/Rust and the V1 binding contract. The
|
||||
* EXTENDS-vs-IMPLEMENTS split is decided downstream from the resolved target's
|
||||
* symbol kind (`preEmitInheritanceEdges`): a superclass resolves to a class
|
||||
* (EXTENDS), an implemented interface resolves to an interface (IMPLEMENTS).
|
||||
* An `interface IA extends IB` base resolves to an Interface too, so it is
|
||||
* emitted as IMPLEMENTS — matching the legacy `interface_declaration` arm,
|
||||
* which tagged the bases as implements (`kind: 'implements'`) and likewise
|
||||
* resolves them as interfaces. The synth therefore does not need to know the
|
||||
* declaration's own kind; it only emits inherits sites and lets the resolved
|
||||
* target decide the edge type.
|
||||
* Base names are normalized to their bare simple identifier (`Box<T>` → `Box`,
|
||||
* `java.io.Serializable` → `Serializable`) to match the V1 simple-name
|
||||
* `findClassBindingInScope` contract.
|
||||
* emitted as IMPLEMENTS. The synth only emits inheritance sites and lets the
|
||||
* resolved target decide the edge type.
|
||||
*/
|
||||
function synthesizeJavaInheritanceReferences(root: SyntaxNode): CaptureMatch[] {
|
||||
const out: CaptureMatch[] = [];
|
||||
const stack: SyntaxNode[] = [root];
|
||||
while (stack.length > 0) {
|
||||
const node = stack.pop()!;
|
||||
if (node.type === 'class_declaration' || node.type === 'record_declaration') {
|
||||
if (node.type === 'class_declaration') {
|
||||
const superclass = node.childForFieldName('superclass');
|
||||
if (superclass !== null) {
|
||||
for (const base of superclass.namedChildren) emitJavaInheritanceBase(out, base);
|
||||
}
|
||||
}
|
||||
if (node.type === 'class_declaration' || node.type === 'record_declaration') {
|
||||
// Records cannot declare a superclass; they share only the class
|
||||
// `interfaces` arm.
|
||||
const interfaces = node.childForFieldName('interfaces');
|
||||
if (interfaces !== null) {
|
||||
for (const typeList of interfaces.namedChildren) {
|
||||
|
|
|
|||
|
|
@ -1274,7 +1274,7 @@ describe('Java record method resolution (#2564)', () => {
|
|||
}
|
||||
}, 60000);
|
||||
|
||||
it('links a Record implementor to its canonical Interface node (#2900)', async () => {
|
||||
it('links and dispatches an explicit Record interface method (#2900)', async () => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'gitnexus-java-record-heritage-'));
|
||||
try {
|
||||
writeFixtureRepo(root, {
|
||||
|
|
@ -1300,13 +1300,45 @@ describe('Java record method resolution (#2564)', () => {
|
|||
|
||||
expect(implementsEdge?.sourceLabel).toBe('Record');
|
||||
expect(implementsEdge?.targetLabel).toBe('Interface');
|
||||
expect(fanout.map((edge) => `${edge.targetLabel}:${edge.targetFilePath}`)).toEqual([
|
||||
expect(fanout.map((edge) => `${edge.targetLabel}:${edge.targetFilePath}`).sort()).toEqual([
|
||||
'Method:RecordHeritage.java',
|
||||
]);
|
||||
} finally {
|
||||
fs.rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
}, 60000);
|
||||
|
||||
it('documents missing dispatch to an implicit Record component accessor (#2917)', async () => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'gitnexus-java-record-accessor-'));
|
||||
try {
|
||||
writeFixtureRepo(root, {
|
||||
'RecordAccessor.java': `interface Named { String name(); }
|
||||
record User(String name) implements Named {}
|
||||
class Reader {
|
||||
String read(Named value) { return value.name(); }
|
||||
}`,
|
||||
});
|
||||
|
||||
const linked = await runPipelineFromRepo(root, () => {});
|
||||
const implementsEdge = getRelationships(linked, 'IMPLEMENTS').find(
|
||||
(edge) => edge.source === 'User' && edge.target === 'Named',
|
||||
);
|
||||
const fanout = getRelationships(linked, 'CALLS').filter(
|
||||
(edge) =>
|
||||
edge.source === 'read' &&
|
||||
edge.target === 'name' &&
|
||||
edge.rel.reason === 'interface-dispatch',
|
||||
);
|
||||
|
||||
expect(implementsEdge?.sourceLabel).toBe('Record');
|
||||
expect(implementsEdge?.targetLabel).toBe('Interface');
|
||||
// TODO(#2917): implicit component accessors are not Method nodes yet.
|
||||
// Replace this characterization with the expected User.name target.
|
||||
expect(fanout).toEqual([]);
|
||||
} finally {
|
||||
fs.rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
}, 60000);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@ function ctorRefs(src: string) {
|
|||
function inheritanceRefs(src: string): string[] {
|
||||
return emitJavaScopeCaptures(src, 'C.java')
|
||||
.filter((m) => m['@reference.inherits'] !== undefined)
|
||||
.flatMap((m) => m['@reference.name']?.text ?? []);
|
||||
.flatMap((m) => m['@reference.name']?.text ?? [])
|
||||
.sort();
|
||||
}
|
||||
|
||||
describe('emitJavaScopeCaptures — constructor reference names (F35 #1928)', () => {
|
||||
|
|
@ -95,10 +96,11 @@ describe('emitJavaScopeCaptures — record interface heritage (#2900)', () => {
|
|||
'record User(int id) implements Named, Comparable<User>, audit.Auditable {}',
|
||||
);
|
||||
|
||||
expect(refs).toEqual(['Named', 'Comparable', 'Auditable']);
|
||||
expect(refs).toEqual(['Auditable', 'Comparable', 'Named']);
|
||||
});
|
||||
|
||||
it('does not widen enum heritage while adding record support', () => {
|
||||
it('does not yet emit enum heritage (#2918)', () => {
|
||||
// Delete this characterization when #2918 adds enum interface heritage.
|
||||
expect(inheritanceRefs('enum Status implements Named { ACTIVE }')).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue