Carries #1221 by @w0lan plus a hardening pass: property-receiver typing consults property-shaped declarations only (typed property / promoted ctor param / pseudoconstructor assignment / assignment-followed classic ctor and typed setter), so same-named locals and parameters can never mistype a property. Co-authored-by: Roman Wolan <roman.wolan@morizon-gratka.pl> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Roman Wolan
Claude Fable 5
parent
9d0cd3a7d1
commit
70b1be6a21
+19
-3
@@ -16,7 +16,7 @@ import {
|
||||
FrameworkResolver,
|
||||
ImportMapping,
|
||||
} from './types';
|
||||
import { matchReference, matchFunctionRef, matchDottedCallChain, matchScopedCallChain, sameLanguageFamily, crossesKnownFamily } from './name-matcher';
|
||||
import { matchReference, matchFunctionRef, matchDottedCallChain, matchScopedCallChain, matchMethodCall, sameLanguageFamily, crossesKnownFamily } from './name-matcher';
|
||||
import { resolveViaImport, resolveJvmImport, extractImportMappings, extractReExports, loadCppIncludeDirs, isPhpIncludePathRef, isCobolCopybookRef, isNixPathImportRef } from './import-resolver';
|
||||
import { detectFrameworks } from './frameworks';
|
||||
import { synthesizeCallbackEdges } from './callback-synthesizer';
|
||||
@@ -44,6 +44,9 @@ const SCOPED_CHAIN_LANGUAGES = new Set(['rust']);
|
||||
/** The extractor's chained-receiver encoding: `<inner>().<method>`. */
|
||||
const CHAIN_SHAPE = /^(.+)\(\)\.(\w+)$/;
|
||||
|
||||
/** PHP `$this->prop->method()` encoded as `this->prop.method` — no `()`, so CHAIN_SHAPE misses it. */
|
||||
const PHP_PROP_SHAPE = /^this->\w+\.\w+$/;
|
||||
|
||||
/**
|
||||
* Cache size limits. Each per-resolver cache is bounded so memory
|
||||
* stays flat on large codebases (20k+ files). Sizes were chosen to
|
||||
@@ -919,6 +922,15 @@ export class ReferenceResolver {
|
||||
CHAIN_SHAPE.test(ref.referenceName)
|
||||
) {
|
||||
this.deferredChainRefs.push(ref);
|
||||
} else if (
|
||||
// PHP `$this->prop->method()` (encoded `this->prop.method`): its method
|
||||
// may live on the property's declared supertype, resolvable only once
|
||||
// implements/extends edges exist — defer to the same conformance pass.
|
||||
ref.referenceKind === 'calls' &&
|
||||
ref.language === 'php' &&
|
||||
PHP_PROP_SHAPE.test(ref.referenceName)
|
||||
) {
|
||||
this.deferredChainRefs.push(ref);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -1117,9 +1129,13 @@ export class ReferenceResolver {
|
||||
const maybeYield = createYielder();
|
||||
const resolved: ResolvedRef[] = [];
|
||||
for (const ref of deferred) {
|
||||
// `::`-receiver languages (Rust) split on `::` (matchScopedCallChain);
|
||||
// PHP `this->prop.method` resolves via matchMethodCall (declared-type
|
||||
// inference + resolveMethodOnType conformance walk); `::`-receiver
|
||||
// languages (Rust) split on `::` (matchScopedCallChain); other
|
||||
// dotted-receiver languages on `.` (matchDottedCallChain).
|
||||
const chainMatch = SCOPED_CHAIN_LANGUAGES.has(ref.language)
|
||||
const chainMatch = (ref.language === 'php' && PHP_PROP_SHAPE.test(ref.referenceName))
|
||||
? matchMethodCall(ref, this.context)
|
||||
: SCOPED_CHAIN_LANGUAGES.has(ref.language)
|
||||
? matchScopedCallChain(ref, this.context)
|
||||
: matchDottedCallChain(ref, this.context);
|
||||
const match = this.gateLanguage(chainMatch, ref);
|
||||
|
||||
Reference in New Issue
Block a user