Files
codegraph/src/extraction/languages/pascal.ts
T
af56f3539d fix(pascal): resolve chained factory calls TFoo.GetInstance().DoIt() (#750) (#791)
Ports the #645/#608 chained-receiver mechanism to Pascal/Delphi — which I'd
previously mis-scoped as blocked. The paren'd chained form extracts fine; it just
hit the chained-call gap like the others (with a decoy, `TFoo.GetInstance().DoIt()`
mis-resolved to a same-named method on an unrelated class).

- pascal.ts: getReturnType reads the method's `typeref` (a `function GetInstance:
  TBar` returns TBar; an interface return `IFoo` is captured too).
- tree-sitter.ts: extractPascalCall now re-encodes a chained call `TFoo.GetInstance().DoIt`
  (the exprDot's receiver is an exprCall) instead of collapsing it to bare `DoIt`.
  Gated on the Delphi type-naming convention (`TFoo`/`IFoo`) so a capitalized
  VARIABLE chain (Pascal capitalizes locals too — `Curve.X().Y()`, `Self.X().Y()`)
  stays bare and keeps its existing bare-name resolution.
- name-matcher.ts: `pascal` joins the dotted-chain gate + CHAIN_LANGUAGES +
  CONSTRUCTS_VIA_BARE_CALL (a `TFoo(x)` typecast yields a TFoo). When the factory's
  return type wasn't captured (a `constructor Create` has no `: TBar` but returns
  its class), resolve the method on the factory class itself. resolveMethodOnType
  validates, so a wrong inference yields no edge.

Validation: 4 synthetic tests (factory+decoy, constructor chain, typecast chain,
absent-method safety). Real-repo A/B on PascalCoin (772 files): +19 / -18 — 15 of
the -18 are correct class→interface retargets (`GetInstance(): IAsn1OctetString`
resolves `.GetOctets` on the declared interface, not baseline's concrete-class
guess); 3 are negligible drops (0.02%). EXTRACTION_VERSION 15->16. Full suite green.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 08:37:04 -04:00

73 lines
2.5 KiB
TypeScript

import type { Node as SyntaxNode } from 'web-tree-sitter';
import { getNodeText, getChildByField } from '../tree-sitter-helpers';
import type { LanguageExtractor } from '../tree-sitter-types';
export const pascalExtractor: LanguageExtractor = {
functionTypes: ['declProc'],
classTypes: ['declClass'],
methodTypes: ['declProc'],
interfaceTypes: ['declIntf'],
structTypes: [],
enumTypes: ['declEnum'],
typeAliasTypes: ['declType'],
importTypes: ['declUses'],
callTypes: ['exprCall'],
variableTypes: ['declField', 'declConst'],
nameField: 'name',
bodyField: 'body',
paramsField: 'args',
returnField: 'type',
// Pascal/Delphi `function GetInstance: TBar` — the return type is a `typeref`
// child. Capture its bare class name for the chained static-factory call
// mechanism (#750). A procedure (no return) has no typeref → undefined.
getReturnType: (node, source) => {
const typeref = node.namedChildren.find((c: SyntaxNode) => c.type === 'typeref');
if (!typeref) return undefined;
const id = typeref.namedChildren.find((c: SyntaxNode) => c.type === 'identifier') ?? typeref;
const name = getNodeText(id, source).trim();
return /^[A-Za-z_]\w*$/.test(name) ? name : undefined;
},
getSignature: (node, source) => {
const args = getChildByField(node, 'args');
const returnType = node.namedChildren.find(
(c: SyntaxNode) => c.type === 'typeref'
);
if (!args && !returnType) return undefined;
let sig = '';
if (args) sig = getNodeText(args, source);
if (returnType) {
sig += ': ' + getNodeText(returnType, source);
}
return sig || undefined;
},
getVisibility: (node) => {
let current = node.parent;
while (current) {
if (current.type === 'declSection') {
for (let i = 0; i < current.childCount; i++) {
const child = current.child(i);
if (child?.type === 'kPublic' || child?.type === 'kPublished')
return 'public';
if (child?.type === 'kPrivate') return 'private';
if (child?.type === 'kProtected') return 'protected';
}
}
current = current.parent;
}
return undefined;
},
isExported: (_node, _source) => {
// In Pascal, symbols declared in the interface section are exported
return false;
},
isStatic: (node) => {
for (let i = 0; i < node.childCount; i++) {
if (node.child(i)?.type === 'kClass') return true;
}
return false;
},
isConst: (node) => {
return node.type === 'declConst';
},
};