Skip to content

fix(pascal): Delphi calls to compiler intrinsics and plain data no longer link namesakes - #2803

Merged
colbymchenry merged 1 commit into
mainfrom
claude/pascal-intrinsics-data-calls
Oct 22, 2026
Merged

colbymchenry merged 1 commit into
mainfrom
claude/pascal-intrinsics-data-calls

Conversation

@colbymchenry

Copy link
Copy Markdown
Owner

Summary

Follow-up to #2514, for the wrong Delphi call links found while validating it.

  • Compiler intrinsics and System routines. A bare call to a name the compiler or the System unit gives meaning to, like SizeOf, TypeInfo, High, Low, Length, Inc, Dec, Assigned, Ord, Exit, Include, Move, Assert, or a typecast like Pointer(P), went to name matching, which linked any project member of that name (mORMot's TRttiProp.TypeInfo, a record's length/high field, a property named Pointer). It now links only to a declaration the call's scope reaches: the class and its ancestors, the unit, then the units it uses, last first. A unit's stand-in declared under {$IFDEF}/{$IF} ({$IFNDEF UNICODE} UnicodeString = WideString;, SynEdit's Delphi 2 Assert stub) is skipped.
  • Case. The built-in name check ignores case (length(S), dec(N)). The RTL namespace prefixes keep their spelling, so a variable named data or rest (data.GetObjectFrom) still resolves; system. matches in any case.
  • Calls through data. A call that resolves to a field, property, variable or constant stands only when the data's declared type is a routine type: written out (procedure of object, reference to function), a project alias to one (generic, on two lines, or through another alias such as TFormatter = TFunc<Exception, string>), or an RTL procedural type by name (TNotifyEvent, TProc). A bare name is retargeted to what its scope declares (a gutter's own FOnChange, not another class's). A name the calling routine declares itself (a parameter, a local, a nested routine) links nothing. A name the scope doesn't declare at all (a with object's member, a declaration the parse misplaced) links to the one routine-holding field or variable of that name in the project.
  • Scope coverage. Scope lookups now see a unit whose unit header the parser lost, by its file name (Delphi requires them to match); members of a class such a unit's parse lost, which come out as indented routines of the unit, aren't taken for its routines by other units. An include file shares its unit's scope both ways, including a uses clause inside the .inc (mORMot's mormot.core.os.posix.inc).
  • with bodies. A bare call to a System name inside a with body is no longer extracted, since it may be the with object's member.

Validation

Index of each repo with origin/main at e8ddc4e2 and this branch, edges diffed by natural keys. The branch is now rebased on d82a4adc (#2801, #2802, both JS/TS-only); SynEdit, HeidiSQL and leveldb are byte-identical before and after the rebase.

Repo Files Edges removed Edges added
SynEdit 292 243 50
HeidiSQL 313 858 296
mORMot2 758 9039 987
DelphiMVCFramework 1527 2420 242
DWScript 2552 705 222
leveldb (C++, control) 134 0 0
SDWebImage (ObjC, control) 274 0 0

Nodes are unchanged everywhere; the controls are byte-identical.

Every changed-edge bucket was triaged. Removed: typecasts and intrinsics that had landed on fields and properties (length/high record fields, Pointer/Char properties, TypeInfo/Include/Default methods, dec/low variables, Int64 and PAnsiChar/NativeInt/RawByteString stand-ins), calls through parameters and locals that had landed on namesake fields, and chain-last data (.StartsWith, .Substring, Add.Log). Added: own-class Delete/Insert/Read/Write, mORMot's own FileSize/UpperCase/LowerCase, routine-holding variables now in scope (crc32c, DefaultHasher128, the Oracle OCI fields in with OCI do, DWScript's GMP bindings), and #2514's paren-less calls in include files and in units whose header the parser lost (GetTickSec, NowUtc, TAesPrng.Main). Retargeted: FOnChange-style events to the calling class's own field.

Precision, sampled (10 per category per repo, same seed, calls edges; data = target is a field/property/variable/constant, System = bare call to a System name):

Repo Data before Data after System before System after
SynEdit 5/10 10/10 0/10 7/7
HeidiSQL 4/10 10/10 0/10 10/10
mORMot2 0/10 10/10 0/10 10/10
DelphiMVCFramework 1/10 10/10 2/10 10/10
DWScript 10/10 10/10 0/10 10/10
Total 20/50 50/50 2/50 47/47

Category sizes before → after: data 11218 → 2761, System 3710 → 372. A sample of removed edges (40 across mORMot2, HeidiSQL, DelphiMVCFramework) was 40/40 wrong links.

Index time, median of 3 interleaved runs, origin/main vs this branch (wall, then CPU): SynEdit 4.96 s vs 4.90 s (16.9 s vs 17.8 s), HeidiSQL 8.41 s vs 8.59 s (28.5 s vs 28.6 s), mORMot2 17.79 s vs 18.23 s (67.1 s vs 68.6 s), DelphiMVCFramework 210.3 s vs 208.8 s (822 s vs 824 s, measured on the build before the last speed-up commit was folded in). The box is shared, so differences under about 5% are noise.

Known gaps

  • A System name a unit re-declares under a condition is never linked to that declaration, even when the project's compiler would compile it.
  • Code the parser mis-places still limits scope: SynEdit.pas from line 303 on (re-published properties, a separate fix), and a record whose last field is an anonymous record with a variant part, which swallows the declarations after it (DWScript's dwsMPIR.pas, recovered here only by the one-namesake rule).
  • A procedural variable the project declares once can still stand in for a Windows API function of the same name that the calling unit imports from the RTL (CreateToolhelp32Snapshot, SHGetFolderPath, GetProcessMemoryInfo, crc32: 5 edges across the five repos), as on main.

Test plan

  • __tests__/pascal-system-calls.test.ts (new, 13 cases): 10 fail on origin/main; the other 3 guard the scope rules (units whose header the parser lost, include files, lost class members), and each fails when its rule is taken out.
  • Pascal suites: pascal-parenless-calls, pascal-chained-calls, pascal-for-in-var, pascal-param-attributes, pascal-inc-detection.
  • Full suite on the pushed commit.

🤖 Generated with Claude Code

…nger link namesakes

A Pascal call to a name the compiler or the System unit gives meaning
to (SizeOf, TypeInfo, High, Low, Length, Inc, Dec, Assigned, Ord, Exit,
Include, Move, Assert, typecasts like Pointer(P)) went to name
matching, which linked any project member of that name: mORMot's
TRttiProp.TypeInfo, a record's length or high field, a property named
Pointer. The built-in name check compared case, so length(S) and dec(N)
got through too. And a call that resolved to a field, property or
variable linked whatever data had the name.

Now a bare call to a System name links only to a declaration its scope
reaches: the class and its ancestors, the unit, the units it uses last
first, skipping a unit's stand-in declared under {$IFDEF} or {$IF}. A
call resolved to data stands only when the data's declared type is a
routine type (written out, a project alias to one, generic or on two
lines, or an RTL procedural type by name), retargeted to what the scope
declares. A name the calling routine declares itself links nothing; a
name the scope doesn't declare at all (a with object's member, a
declaration the parse misplaced) links to the one routine-holding field
or variable of that name. Built-in names compare without case; the RTL
namespaces keep their spelling, except system. Scope lookups now see a
unit whose unit header the parser lost, by its file name (its indented,
lost class members aren't taken for its routines), and an include file
shares its unit's scope and uses clauses. A with body's bare call to a
System name is not extracted, since it may be the with object's.

Validation, edges removed/added against origin/main: SynEdit -243/+50,
HeidiSQL -858/+296, mORMot2 -9039/+987, DelphiMVCFramework -2420/+242,
DWScript -705/+222; leveldb and SDWebImage byte-identical. Sampled
precision of calls to data, before and after: 20/50 and 50/50; of calls
to System names: 2/50 and 47/47. Sampled removed edges: 40/40 wrong.

Follow-up to #2514.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant