mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-11-15 01:29:02 +08:00
063708ba535653659560e7e75bc511b9dc485068
4
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
063708ba53 |
fix(pascal): Delphi chained calls and method pointers follow declared types (#3065)
Four Delphi bugs that PR 2550 by @OleksiyPenkov found, reimplemented on
main's Pascal scope code rather than as a second resolver:
- A call through a chain of members and methods recorded only the outer
method's bare name, which name matching gave to any namesake: SynEdit's
Canvas.Font.Assign(Font) went to TProposalColumn::Assign, and
TLogger2.Create.Write('y') linked nothing, a bare Write being a System
name. The extractor now writes such a call as the typed chain
pascal-scope already resolves for paren-less names ({call} for a
statement, a new {args} mode for a last link written with its
arguments). Each link is typed by its declaration: field and property
types, function results, a local's declared type, through pointer and
class reference types (PVirtualNode = ^TVirtualNode, TSynLogClass =
class of TSynLog) and explicit dereferences (a^.Copy). A constructor
called on a class gives an instance of it, a unit name, dotted ones
too, qualifies its declarations, and a chain called with arguments may
end at data that holds a routine. There is no bare fallback: a chain
through a type from outside the project links nothing, and in a with
body only a chain from Self or a typed local is read.
- A method given to a target was a method pointer only when the target
was named On.../FOn...: Callback := FProvider.Value with Callback: TGet
recorded a call, and FOnGet := @FProvider.Value nothing. A value given
to an assignment now carries its target, whose declared type decides
(routine types written out, of object, reference to, project aliases,
the RTL's routine type names; a class, a record or a System value type
holds no routine); the name decides only where the declaration can't
be read. A function that returns a routine type is called for it, and
@Obj.Method is a reference anywhere.
- A nested routine's locals leaked into the routine around it: the name
matcher's Pascal receiver scan read every declaration above the call.
It now steps over routines nested elsewhere, reads the calling
routine's own name lists (Doc, Doc2: TDocVariantData), which main typed
only by luck through a nested routine's parameter, and follows pointer
and class reference types. Where the tree has errors, the source-text
scopes give the outer routine its own names back after a nested one.
- A nested routine declared forward is in scope from that declaration.
Validation:
- New __tests__/pascal-reference-boundaries.test.ts, 41 tests, 24 of
them ported from PR 2550: 22 fail on main's sources, the other 19 pin
what main already got right. pascal-chained-calls and pascal-for-in-var
pinned the bare outer-method names and now expect the typed chains
(4 tests, failing on main). Twenty mutations, one per rule, each fail
a test.
- A/B against main
|
||
|
|
f3fdba2955 |
fix(pascal): calls inside nested Delphi routines are indexed, each under its routine (#2810)
A Pascal routine may declare routines of its own between its header and
its begin. The extractor walked only a routine's body block, so a nested
routine's code was never read: what it called was missing, and a call to
it from the routine that declares it went by name to any routine
elsewhere with that name (in mORMot2's tests, Test, Check and One calls
landed on other classes' methods).
A nested routine is now a function node qualified under the routine it
is nested in (TFoo::Bar::Inner), contained by it and walked with itself
as the caller. It sees the parameters and locals of the routines around
it that are declared above it. Its name means it in the routine that
declares it, in the routines nested there after it and in its own body,
ahead of the class and the unit. A call resolved to a nested routine
from anywhere else is what the call's own scope declares, or nothing. A
nested routine's bare calls reach the class of the method it is nested
in first. Where a routine's tree has errors and its parse swallowed the
unit's routines after it (column 0, or after the body), those are walked
as the unit's own; of two bodies under {$IFDEF} the first is walked, as
before.
Validation against origin/main
|
||
|
|
7b50ca8321 |
fix(pascal): Delphi calls to compiler intrinsics and plain data no longer link namesakes (#2803)
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>
|
||
|
|
407efb1c65 |
fix(pascal): Delphi calls written without parentheses link to the routine (#2797)
Delphi calls a routine that takes no arguments without parentheses
(`Before;`, `X := FProvider.Value + 1`, `if Ready then`), and only a
statement-level `Obj.Method;` was recorded, so most call sites were
missing from callers, callees and impact. Bare names in arguments and
assignments were captured as function values and matched by name alone,
which linked locals and parameters to other classes' methods.
The extractor now records each name written without parentheses with
how it is used (a statement, a value, an event's method, a receiver),
the routine's class, and the declared type of a receiver the routine
declares. It leaves out what the routine declares, data the class and
the unit declare, names the class's code assigns or passes to Inc/Dec,
names Delphi's conventions give to data, and anything in a `with` body
but an event's method. A new resolver links such a name only to a method
or function Delphi's scope rules reach: the class and its ancestors, the
unit, then the units it uses, last first, or a method of a receiver's
declared type. Data of that name links nothing. A function runs (calls);
a procedure read as a value, an event's method, and a routine that needs
arguments written without them are method pointers (references). Where
the tree has errors, the class and declarations come from the source
text, and a routine's own header is no call. `class sealed (TBase)` and
`class abstract (TBase)` heads now name their ancestors. Pascal function
values capture only `@Handler`.
A/B against main
|