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>
73 lines
2.5 KiB
TypeScript
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';
|
|
},
|
|
};
|