feat(pascal): extract paren-less method calls (Obj.Free; / TFoo.GetInstance.DoIt;) (#793)
Pascal/Delphi lets a no-arg method or procedure drop its parens, so the call parses as a bare `exprDot` (not an `exprCall`) and was never recorded as a call — callers/impact/trace missed all of them (e.g. `Obj.Free`, `List.Clear`, the paren-less factory chain `TFoo.GetInstance.DoIt`). extractPascalParenlessCall handles these, wired into visitPascalBlock scoped to STATEMENT position only: a bare `Obj.Field;` statement is a no-op, so a statement-level dot expression is a call — but a dot in assignment LHS/RHS or a condition is left alone, since there it's genuinely ambiguous with a field/property access. The chained paren-less form reuses the #750 chain encoding (gated on the Delphi `TFoo`/`IFoo` type convention) and resolves the same way. PascalCoin A/B: +1131 / -1 — purely additive, and all 1131 new edges resolve to METHOD nodes (zero field/property false positives, confirming the statement-level gate). 3 new synthetic tests (paren-less call, paren-less chained factory, and the property-write/read non-extraction guard). EXTRACTION_VERSION 16->17. Full suite green. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
4c35b72136
commit
35dce04e1f
@@ -3265,5 +3265,92 @@ end.
|
||||
// TBar has no OnlyOther — must not mis-attach to the same-named TOther::OnlyOther.
|
||||
expect(isCalled('TOther::OnlyOther')).toBe(false);
|
||||
});
|
||||
|
||||
it('extracts paren-less method calls (Pascal lets a no-arg method drop its parens)', async () => {
|
||||
fs.writeFileSync(
|
||||
path.join(tempDir, 'main.pas'),
|
||||
`unit Main;
|
||||
interface
|
||||
type
|
||||
TFoo = class
|
||||
procedure DoThing;
|
||||
procedure Reset;
|
||||
end;
|
||||
implementation
|
||||
procedure TFoo.DoThing; begin end;
|
||||
procedure TFoo.Reset; begin end;
|
||||
procedure Run(f: TFoo);
|
||||
begin
|
||||
f.DoThing;
|
||||
f.Reset;
|
||||
end;
|
||||
end.
|
||||
`
|
||||
);
|
||||
cg = await CodeGraph.init(tempDir, { index: true });
|
||||
expect(isCalled('TFoo::DoThing')).toBe(true);
|
||||
expect(isCalled('TFoo::Reset')).toBe(true);
|
||||
});
|
||||
|
||||
it('resolves a PAREN-LESS chained factory call TFoo.GetInstance.DoIt via the return type', async () => {
|
||||
fs.writeFileSync(
|
||||
path.join(tempDir, 'main.pas'),
|
||||
`unit Main;
|
||||
interface
|
||||
type
|
||||
TBar = class
|
||||
procedure DoIt;
|
||||
end;
|
||||
TDecoy = class
|
||||
procedure DoIt;
|
||||
end;
|
||||
TFoo = class
|
||||
class function GetInstance: TBar;
|
||||
end;
|
||||
implementation
|
||||
procedure TBar.DoIt; begin end;
|
||||
procedure TDecoy.DoIt; begin end;
|
||||
class function TFoo.GetInstance: TBar; begin Result := nil; end;
|
||||
procedure Run;
|
||||
begin
|
||||
TFoo.GetInstance.DoIt;
|
||||
end;
|
||||
end.
|
||||
`
|
||||
);
|
||||
cg = await CodeGraph.init(tempDir, { index: true });
|
||||
expect(isCalled('TBar::DoIt')).toBe(true);
|
||||
expect(isCalled('TDecoy::DoIt')).toBe(false);
|
||||
});
|
||||
|
||||
it('does NOT turn a property write/read into a call edge (only statement-level dots are calls)', async () => {
|
||||
fs.writeFileSync(
|
||||
path.join(tempDir, 'main.pas'),
|
||||
`unit Main;
|
||||
interface
|
||||
type
|
||||
TFoo = class
|
||||
function GetValue: Integer;
|
||||
procedure SetValue(v: Integer);
|
||||
property Value: Integer read GetValue write SetValue;
|
||||
end;
|
||||
implementation
|
||||
function TFoo.GetValue: Integer; begin Result := 0; end;
|
||||
procedure TFoo.SetValue(v: Integer); begin end;
|
||||
procedure Run(f: TFoo);
|
||||
var x: Integer;
|
||||
begin
|
||||
f.Value := 5;
|
||||
x := f.Value;
|
||||
end;
|
||||
end.
|
||||
`
|
||||
);
|
||||
cg = await CodeGraph.init(tempDir, { index: true });
|
||||
// A property read/write is a bare dot in assignment position, not a statement,
|
||||
// so it must not be mis-extracted as a call to the property's getter/setter.
|
||||
expect(isCalled('TFoo::GetValue')).toBe(false);
|
||||
expect(isCalled('TFoo::SetValue')).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user