Repository navigation
fix(pascal): the receiver calls of a chained Delphi call are indexed - #2787
Merged
Merged
Conversation
A Pascal call whose receiver is itself a call recorded only its outer method: in `MD5FileSized(TMeta.ExtendedPath(APath)).IsSame(AKey)` the receiver call `MD5FileSized` and the `TMeta.ExtendedPath` in its arguments were dropped, as were paren'd calls deeper in a chain (`A(1).B.C`) or in an index (`List[GetIndex(x)].Free`). The extractor read a receiver call only for a `TFoo.GetInstance()` factory chain. The extractor now walks a call's receiver chain and records each paren'd call in it, with the calls in its arguments and indexes. A paren-less link stays a read. A link inside a chain is recorded only when it can be named precisely (`Obj.Method`, a bare routine, a `TFoo.Factory().X` chain), never as the bare method name a chain like `Client.New.BaseURL(…)` leaves, which matched namesakes. The calls main already read keep their names. Receivers are very often typecasts (`TButton(Sender).Caption`), which read like calls. Delphi builds objects only through a constructor (`TFoo.Create`), so a bare-name Pascal call that resolves to a type is a cast: resolution now gives it a `references` edge to the type instead of `instantiates` or `calls`. That also corrects the edges main already made for value casts (`F := TFoo(Obj)`). Validation: - New __tests__/pascal-chained-calls.test.ts (7 tests, the issue's unit included): all 7 fail with main's sources. Without only the resolver rule the typecast test fails; without only the chain-link gate the fluent-chain test fails. - A/B against main 88ebefa, both arms built from their own sources, diffed by natural keys: - Every removed edge is a cast changing kind at the same site, with the same source and target (SynEdit 491, HeidiSQL 1,262, mORMot2 4,832, DWScript 3,115, DelphiMVCFramework 2,761), apart from 10 function references that moved to an earlier use in the same routine. - New edges: SynEdit 55, HeidiSQL 249, mORMot2 1,196, DWScript 690, DelphiMVCFramework 2,727. Mostly casts read as references to their type (14 of 14 sampled right) and receiver calls. - 178 new call edges sampled: 140 right. instance-method 30/30, qualified-name 10/10, bare functions 26/30, bare methods 70/92, calls landing on a property, field or variable 0/12. The wrong ones follow rules main applies to every call: the TypeInfo intrinsic (main already links 1,709 such calls to a project method), calls through local procedural variables, and calls now read inside a receiver's arguments, named as main names them. - Controls leveldb (C++, kernel) and SDWebImage (Objective-C, wasm) are byte-identical in dump-graph. - Cost within noise: index time medians of 3 interleaved rounds, HeidiSQL 7.10 to 7.30 s, mORMot2 18.80 to 18.90 s, DWScript 20.00 to 19.40 s wall. Fixes #2547 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
In Delphi, a call made on the result of another call now has its inner calls indexed. For
Result := MD5FileSized(TMeta.ExtendedPath(APath)).IsSame(AKey)andMD5FileSized(TMeta.ExtendedPath(APath)).Reset;, the issue's unit now givesTargetIsTheSamethe calleesMD5FileSized,ExtendedPathandIsSame, andStmtthe calleesMD5FileSized,ExtendedPathandReset. BothMD5FileSizedandExtendedPathlist both routines as callers.Along the way, a typecast like
TButton(Sender).CaptionorF := TFoo(Obj)now counts as a use of the type, not as creating one.Root cause
extractPascalCallread anexprDotcallee's receiver only to build theTFoo.GetInstance().DoItname of aT/I-prefixed factory chain (#645/#608). It recorded neither the receiver call nor the calls in its arguments.extractPascalParenlessCallrecorded the receiver only behind the sameT/Igate. In expression position,visitPascalBlocklooked only one level down, so it missedA(1)inX := A(1).B.CandGetIndex(x)inList[GetIndex(x)].Free.Fix
Extraction (
src/extraction/tree-sitter.ts):.B) is still a read, not a call. Pascal/Delphi: parameterless method calls without () produce no calls edge (callers/impact miss most call sites) #2514 is about those.Obj.Method, a bare routine (MD5FileSized(…), an own-classGet(i)), or aTFoo.Factory().Xchain. It is never recorded as the bare method name that a chain likeClient.New.BaseURL(…)orRESTClient.HandleRedirects(False).Accept(…)would leave. In the first A/B those bare middle links were half wrong (13 of 25 sampled right), matching namesakes like an unrelatedTAppConfig.BaseURL.Resolution (
src/resolution/index.ts):TButton(Sender).Caption,PInt64Rec(P)^.Lo), and syntax can't tellNullableUInt64(x)from the issue'sMD5FileSized(x).TFoo.Create). So a bare-name Pascal call that resolves to a type (class, record, interface, enum, type alias) is a cast, and it now gets areferencesedge to the type instead ofinstantiatesorcalls.instantiates → classedge in the five Pascal repos is a typecast, so this corrects them too.Validation
Tests: new
__tests__/pascal-chained-calls.test.ts, 7 tests:GetInstanceits caller and still resolvesDoIton what it returns;Tprefix, a pointer type alias), each areferencesedge with no construction;All 7 fail with main's sources. Removing only the resolver rule fails the typecast test, and removing only the chain-link gate fails the fluent-chain test.
A/B against main 88ebefa, both arms built from their own sources, kernel included, diffed by natural keys. The branch was then rebased onto c465230; the three commits in between (#2782 and #2784 for Go, #2785 for context ranking) leave Pascal untouched, and the SynEdit, mORMot2 and DelphiMVCFramework graphs on the rebased build are byte-identical to the ones triaged here:
Removed. Each of the 12,461 cast edges reappears as
referenceswith the same source, target and position. They wereinstantiates → class, orcalls → type alias / enum / interface. The "moved" ones are 10 function references whose first use in the same routine is now an earlier line.Added.
XxxVTable(vTable)casts andNullableUInt64(…)records, which have noTprefix.Precision of the new calls. I sampled 178 of the new call edges and 140 are right:
The misses follow rules main applies to every call:
TypeInfo(…), a compiler intrinsic. Main already links 1,709 such calls to a project method namedTypeInfoin mORMot2 and DelphiMVCFramework, and this adds 76.high(V), which the built-ins list misses because the check is case-sensitive.F(ACtx, NextRef),Next()) matched to unrelated methods.Memo1.Lines.Addinside an anonymous method.Cost: within noise. Index time medians of 3 interleaved rounds: HeidiSQL 7.10 → 7.30 s wall (22.8 → 23.1 s CPU), mORMot2 18.80 → 18.90 s (60.2 → 61.4 s), DWScript 20.00 → 19.40 s (48.0 → 47.5 s).
Suite: the full suite ran on this commit (
CODEGRAPH_KERNEL_EXPECT=1, kernel built): 714 files (708 passed, 6 skipped) and 8,558 tests (8,476 passed, 0 failed, 82 skipped).Fixes #2547
Notes
uses): calls through local procedural variables, and a bare call landing on another class's method or property.TypeInfo(…)and the case-sensitive built-ins check (high/High). Main already shows both at much larger volume, and they are not about receivers.🤖 Generated with Claude Code