Skip to content

Pascal/Delphi: parameterless method calls without () produce no calls edge (callers/impact miss most call sites) #2514

Description

@OleksiyPenkov

Summary

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;

interface

type
  TGetIntEvent = function: Integer of object;

  TProvider = class
  public
    function Value: Integer;
    function ValueParens(): Integer;
  end;

  TConsumer = class
  private
    FProvider: TProvider;
    FOnGet: TGetIntEvent;
  public
    function UseNoParens: Integer;
    function UseParens: Integer;
    function UseEvent: Integer;
    procedure Wire;
    property OnGet: TGetIntEvent read FOnGet write FOnGet;
  end;

implementation

function TProvider.Value: Integer;
begin
  Result := 1;
end;

function TProvider.ValueParens(): Integer;
begin
  Result := 2;
end;

function TConsumer.UseNoParens: Integer;
begin
  Result := FProvider.Value + 1;
end;

function TConsumer.UseParens: Integer;
begin
  Result := FProvider.ValueParens() + 1;
end;

procedure TConsumer.Wire;
begin
  OnGet := FProvider.Value;
end;

function TConsumer.UseEvent: Integer;
begin
  Result := FOnGet;
end;

end.
codegraph init
codegraph callers Value         -> 0 callers
codegraph callers ValueParens   -> 1 caller (UseParens)
codegraph callees UseNoParens   -> 0 callees
codegraph callees UseParens     -> 1 callee (ValueParens)

Expected

  • 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.

Activity

  1. colbymchenry commented on Oct 22, 2026

    @colbymchenry
    Owner

    #2797 is merged to main and 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 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.

  2. added a commit that references this issue on Oct 22, 2026
    407efb1
  3. colbymchenry commented on Oct 22, 2026

    @colbymchenry
    Owner

    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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions