MCPcopy Create free account
hub / github.com/colbymchenry/codegraph / inferLocalReceiverType

Function inferLocalReceiverType

src/resolution/name-matcher.ts:1372–1522  ·  view source on GitHub ↗

* Infer a receiver's type from its local declaration/initializer in the * enclosing function body. Language-dispatched; returns null for languages * without patterns or when no declaration is found. Bounded to the enclosing * scope so a same-named variable in another function can't leak in.

(
  receiverName: string,
  ref: UnresolvedRef,
  context: ResolutionContext,
)

Source from the content-addressed store, hash-verified

1370 * scope so a same-named variable in another function can't leak in.
1371 */
1372function inferLocalReceiverType(
1373 receiverName: string,
1374 ref: UnresolvedRef,
1375 context: ResolutionContext,
1376): string | null {
1377 // CFML scope prefixes: `variables.svc` / `this.svc` name a COMPONENT-scoped
1378 // field whose assignment or `property` declaration usually lives outside the
1379 // calling function (the init-pseudoconstructor / WireBox-injection pattern),
1380 // and `local.svc` is an explicit function-local. Strip the prefix so the
1381 // declaration patterns match (`variables.svc = new X()`, `property
1382 // name="svc" …`, `var svc = …` all bind the bare name), and widen the scan
1383 // to the whole file for the component-scoped forms — nearest-declaration-
1384 // backward still wins, so a function-local shadowing the field is preferred.
1385 let scanReceiver = receiverName;
1386 let componentScoped = false;
1387 if (ref.language === 'cfml' || ref.language === 'cfscript') {
1388 const scoped = receiverName.match(/^(variables|this|local|arguments)\.(.+)$/i);
1389 if (scoped) {
1390 scanReceiver = scoped[2]!;
1391 const scope = scoped[1]!.toLowerCase();
1392 componentScoped = scope === 'variables' || scope === 'this';
1393 }
1394 }
1395 // PHP `$this->prop` receiver — the property's declaration lives outside the
1396 // calling method (a promoted constructor parameter `private readonly Foo $prop`,
1397 // a typed property `private Foo $prop;`, or a classic constructor parameter
1398 // `Foo $prop` assigned in __construct). Strip the prefix and widen the scan to
1399 // the whole file (the constructor may sit below the calling method), but —
1400 // unlike CFML's scopes above — switch to PROPERTY-shaped patterns: a plain
1401 // `$prop` local or parameter lives in a different namespace than `$this->prop`
1402 // and can never shadow it, so the generic local patterns would type the
1403 // property from unrelated same-named variables in other methods (a wrong
1404 // 0.9-confidence edge, not a missing one).
1405 let phpProperty = false;
1406 if (ref.language === 'php') {
1407 const scoped = receiverName.match(/^this->(.+)$/);
1408 if (scoped) {
1409 scanReceiver = scoped[1]!;
1410 componentScoped = true;
1411 phpProperty = true;
1412 }
1413 }
1414
1415 const escapedReceiver = scanReceiver.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
1416 const patterns = phpProperty
1417 ? phpPropertyTypePatterns(escapedReceiver)
1418 : localReceiverTypePatterns(ref.language, escapedReceiver);
1419 if (patterns.length === 0) return null;
1420
1421 // Split through the context's per-file lines cache when available: this runs
1422 // for EVERY `receiver.method()` ref, and re-splitting the whole file per ref
1423 // was ~20% of total index CPU on Java-heavy repos (#1122).
1424 const lines = context.getFileLines
1425 ? context.getFileLines(ref.filePath)
1426 : (context.readFile(ref.filePath)?.split(/\r?\n/) ?? null);
1427 if (!lines || lines.length === 0) return null;
1428
1429 const callIdx = Math.max(0, Math.min(lines.length - 1, ref.line - 1));

Callers 2

matchMethodCallFunction · 0.85
matchGoFieldChainCallFunction · 0.85

Calls 10

phpPropertyTypePatternsFunction · 0.85
enclosingScopeStartLineFunction · 0.85
getInferScanStatesFunction · 0.85
matchLineFunction · 0.85
getFileLinesMethod · 0.80
readFileMethod · 0.80
getMethod · 0.65
setMethod · 0.45

Tested by

no test coverage detected