fix(pascal): attribute a free routine's calls to it, not the file (#795)

A Pascal/Delphi procedure or function defined ONLY in the implementation section
(no interface declaration, not a class method) had no node of its own, so
extractPascalDefProc's caller lookup fell through to the nodeStack top — the file
node. Every call in such a routine's body was lumped under the unit: callers
returned the file, and impact couldn't attribute the call to the routine. (Methods
were fine — they get a node from their class declaration.)

Fix: when extractPascalDefProc finds no existing node for a FREE routine (a name
with no `.`), create a function node for it and attribute the body's calls to it.
Interface-declared free routines already have a node (found via the methodIndex),
so there's no duplicate; methods keep their existing class-declaration node.

PascalCoin A/B: +511 / -145 — the +511 are calls now correctly attributed to their
actual routine (`allocate_new_datablock -> TDisposables::GetMem`), replacing -145
file-level aggregates; +248 new function nodes for the implementation-only
routines. New synthetic test asserts a free routine's call attributes to it
alongside a method caller. EXTRACTION_VERSION 17->18. Full suite green.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Colby Mchenry
2026-06-11 09:05:08 -04:00
committed by GitHub
co-authored by Claude Opus 4.8
parent 5342f7a93e
commit dac00e7d44
4 changed files with 50 additions and 4 deletions
+25
View File
@@ -3352,5 +3352,30 @@ end.
expect(isCalled('TFoo::GetValue')).toBe(false);
expect(isCalled('TFoo::SetValue')).toBe(false);
});
it('attributes an implementation-only free procedure\'s calls to the procedure, not the file', async () => {
fs.writeFileSync(
path.join(tempDir, 'main.pas'),
`unit Main;
interface
type
TTgt = class
procedure Hit;
end;
TFoo = class
procedure DoStuff;
end;
implementation
procedure TTgt.Hit; begin end;
procedure TFoo.DoStuff; var t: TTgt; begin t.Hit; end;
procedure Helper; var t: TTgt; begin t.Hit; end;
`
);
cg = await CodeGraph.init(tempDir, { index: true });
// `Helper` is implementation-only (no interface decl, not a method), but its
// body's call must attribute to `Helper`, not the file/module — alongside the
// method `DoStuff`.
expect(callerNamesOf('TTgt::Hit')).toEqual(['DoStuff', 'Helper']);
});
});
});