You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Pascal/Delphi: parameterless method calls without () produce no calls edge (callers/impact miss most call sites) #2514
Pascal/Delphi: method calls written without parentheses produce no calls edge. In Delphi, a parameterless method or function is normally called without (). Because of this, callers, callees, and impact miss most call sites in real Delphi code.
A related gap: when a method is assigned to an event (method-pointer) property, nothing links the handler to the event, so calls through the event are also invisible.
Environment
codegraph 1.6.2 (npm), Windows 11, Node 24
Language: pascal (.pas)
Minimal reproduction
UnitA.pas:
unit UnitA;
interfacetype
TGetIntEvent = function: Integer ofobject;
TProvider = classpublicfunctionValue: Integer;
functionValueParens(): Integer;
end;
TConsumer = classprivate
FProvider: TProvider;
FOnGet: TGetIntEvent;
publicfunctionUseNoParens: Integer;
functionUseParens: Integer;
functionUseEvent: Integer;
procedureWire;
property OnGet: TGetIntEvent read FOnGet write FOnGet;
end;
implementationfunctionTProvider.Value: Integer;
begin
Result := 1;
end;
functionTProvider.ValueParens(): Integer;
begin
Result := 2;
end;
functionTConsumer.UseNoParens: Integer;
begin
Result := FProvider.Value + 1;
end;
functionTConsumer.UseParens: Integer;
begin
Result := FProvider.ValueParens() + 1;
end;
procedureTConsumer.Wire;
begin
OnGet := FProvider.Value;
end;
functionTConsumer.UseEvent: Integer;
begin
Result := FOnGet;
end;
end.
callers Value includes UseNoParens. FProvider.Value resolves to the method TProvider.Value, not to a field, so it is a call.
Optionally: Wire has a references edge to TProvider.Value (method-pointer assignment), so that impact Value reaches the code that calls through FOnGet / OnGet.
Impact on a real project
On a ~260-unit VCL project, codegraph callers ActiveModelSeries returns 1 caller. A text search finds about 10 call sites in 3 units, all in the form FProjectPanel.ActiveModelSeries.Visible / .BeginUpdate. So impact can report a change as safe when it is not.
Possible approach
When a member-access or bare identifier in an expression resolves (through the receiver's declared type) to a method/function node with no required parameters, emit a calls edge, the same as for the () form. Properties with a getter method could get the same treatment.
Side note
XRayCalcVisualControls.dpk (a standard Delphi package file) reports "parse produced no symbols (tree has errors)". I can provide a sample if useful.
A routine called without parentheses now counts as a call. That covers forms like Before;, if Ready then, Result := FProvider.Value + 1, or a chain like FProjectPanel.ActiveModelSeries.Visible. A method assigned to an event, like OnGet := FProvider.Value, is a reference to it.
A name is linked only to a method or function its code can reach: the class and its ancestors, its unit, then the units it uses. It is never linked to a field, property or variable of that name.
On your UnitA.pas, callers Value now lists UseNoParens and Wire, and UseEvent has no callees since FOnGet is a field. The .dpk warning from your side note isn't covered here. Thanks for the report, and thanks @drakeo338 and you for the pull requests that guided this. It ships in the next release.
A follow-up, #2803, is also merged to main. It removes the other wrong Delphi call links we found while validating this: compiler intrinsics like TypeInfo, SizeOf and Length(S) in any case, and calls that landed on a field or property that doesn't hold a routine.
In the five Delphi projects we test with, we sampled the links in each category and checked them by hand:
calls that landed on data: correct in 20 of 50 sampled before, 50 of 50 after;
calls to System routines: correct in 2 of 50 sampled before, 47 of 47 after.
Activity
fix(pascal): calls inside a Delphi `for var x in` loop are indexed (#…
colbymchenry commentedon Oct 22, 2026
#2797 is merged to
mainand fixes this.A routine called without parentheses now counts as a call. That covers forms like
Before;,if Ready then,Result := FProvider.Value + 1, or a chain likeFProjectPanel.ActiveModelSeries.Visible. A method assigned to an event, likeOnGet := FProvider.Value, is a reference to it.A name is linked only to a method or function its code can reach: the class and its ancestors, its unit, then the units it uses. It is never linked to a field, property or variable of that name.
On your
UnitA.pas,callers Valuenow listsUseNoParensandWire, andUseEventhas no callees sinceFOnGetis a field. The.dpkwarning from your side note isn't covered here. Thanks for the report, and thanks @drakeo338 and you for the pull requests that guided this. It ships in the next release.fix(pascal): Delphi calls written without parentheses link to the rou…
fix(pascal): Delphi calls to compiler intrinsics and plain data no lo…
colbymchenry commentedon Oct 22, 2026
A follow-up, #2803, is also merged to
main. It removes the other wrong Delphi call links we found while validating this: compiler intrinsics likeTypeInfo,SizeOfandLength(S)in any case, and calls that landed on a field or property that doesn't hold a routine.In the five Delphi projects we test with, we sampled the links in each category and checked them by hand: