Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 43 additions & 11 deletions src/Analyser/MutatingScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@
use function is_string;
use function ltrim;
use function md5;
use function min;
use function preg_match;
use function preg_match_all;
use function spl_object_id;
Expand Down Expand Up @@ -6020,25 +6021,56 @@ public function getPhpVersion(): PhpVersions
{
$constType = $this->getGlobalConstantType(new Name('PHP_VERSION_ID'));
if ($constType !== null && !$this->isOverallPhpVersionRange($constType)) {
if (
$constType instanceof IntegerRangeType
&& $constType->getMin() === ConstantResolver::PHP_MIN_ANALYZABLE_VERSION_ID
&& $constType->getMax() !== null
&& $this->getConfiguredPhpVersionRange() === null
) {
// A guard like `PHP_VERSION_ID < 80400` only gives an upper bound. Without
// a configured version range, the lower bound is merely the lowest version
// PHPStan knows, while outside the guard the single analysed PHP version
// applies. Keep the lower bound at that version, or as close to it as
// the guard allows.
$maxVersionId = $constType->getMax();
return new PhpVersions(IntegerRangeType::fromInterval(
min($this->phpVersion->getVersionId(), $maxVersionId),
$maxVersionId,
));
}

return new PhpVersions($constType);
}

// The analysed PHP version range comes either from the NEON phpVersion min/max
// config or from the composer.json "require.php" constraint - the very same
// source ConstantResolver narrows PHP_VERSION_ID with, so that
// Scope::getPhpVersion() never contradicts the PHP_VERSION_ID constant.
$configuredRange = $this->getConfiguredPhpVersionRange();
if ($configuredRange !== null) {
return new PhpVersions($configuredRange);
}

return new PhpVersions(new ConstantIntegerType($this->phpVersion->getVersionId()));
}

/**
* The analysed PHP version range comes either from the NEON phpVersion min/max
* config or from the composer.json "require.php" constraint - the very same
* source ConstantResolver narrows PHP_VERSION_ID with, so that
* Scope::getPhpVersion() never contradicts the PHP_VERSION_ID constant.
* Null when neither is set and a single PHP version is analysed.
*/
private function getConfiguredPhpVersionRange(): ?Type
{
[$minPhpVersion, $maxPhpVersion] = $this->configuredPhpVersionRangeHelper->getVersionRange();
if (
$minPhpVersion !== null
|| ($maxPhpVersion !== null && $maxPhpVersion->getVersionId() !== PhpVersionFactory::MAX_PHP_VERSION)
$minPhpVersion === null
&& ($maxPhpVersion === null || $maxPhpVersion->getVersionId() === PhpVersionFactory::MAX_PHP_VERSION)
) {
return new PhpVersions(IntegerRangeType::fromInterval(
$minPhpVersion !== null ? $minPhpVersion->getVersionId() : ConstantResolver::PHP_MIN_ANALYZABLE_VERSION_ID,
$maxPhpVersion !== null ? $maxPhpVersion->getVersionId() : null,
));
return null;
}

return new PhpVersions(new ConstantIntegerType($this->phpVersion->getVersionId()));
return IntegerRangeType::fromInterval(
$minPhpVersion !== null ? $minPhpVersion->getVersionId() : ConstantResolver::PHP_MIN_ANALYZABLE_VERSION_ID,
$maxPhpVersion !== null ? $maxPhpVersion->getVersionId() : null,
);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/Turbo/TurboExtensionEnabler.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
final class TurboExtensionEnabler
{

public const EXPECTED_EXTENSION_VERSION = 'a0f7086';
public const EXPECTED_EXTENSION_VERSION = 'b1d51b1';

private static bool $active = false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,10 @@ public function testConditionallyDeclaredClass(): void
$this->analyse([__DIR__ . '/data/promoted-properties-php-versions.php'], $errors);
}

#[RequiresPhp('>= 8.0.0')]
public function testBug15425(): void
{
$this->analyse([__DIR__ . '/data/bug-15425.php'], []);
}

}
16 changes: 16 additions & 0 deletions tests/PHPStan/Rules/Classes/data/bug-15425.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php // lint >= 8.0

declare(strict_types = 1);

namespace Bug15425;

if (PHP_VERSION_ID < 80400) {

@staabm staabm Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add another variant of this condition in the test in which the previously reported false-positive-error would be valid

\PHPStan\dumpType(PHP_VERSION_ID);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please drop the debug statement


final class Foo
{
public function __construct(public ?string $message = null)
{
}
}
}
106 changes: 71 additions & 35 deletions turbo-ext/src/MutatingScope.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3908,24 +3908,71 @@ class MutatingScope
if (UNEXPECTED(constType.isUndef())) return zv::Val();

bool isOverallPhpVersionRange = false;
bool hasMinAnalyzableVersion = false;
zv::Val max;
if (constType.ref().isObject() && constType.ref().instanceOf(pt_ce_integer_range_type)) {
zv::Val min = pt_type_call(constType.ref().asObject(), PT_LC("getmin"), 0, NULL);
if (UNEXPECTED(min.isUndef())) return zv::Val();
if (min.ref().isLong() && min.ref().asLong() == PT_MS_PHP_MIN_ANALYZABLE_VERSION_ID) {
zv::Val max = pt_type_call(constType.ref().asObject(), PT_LC("getmax"), 0, NULL);
hasMinAnalyzableVersion = true;
max = pt_type_call(constType.ref().asObject(), PT_LC("getmax"), 0, NULL);
if (UNEXPECTED(max.isUndef())) return zv::Val();
if (max.isNull() || (max.ref().isLong() && max.ref().asLong() == PT_MS_MAX_PHP_VERSION)) {
isOverallPhpVersionRange = true;
}
}
}

if (!constType.isNull() && !isOverallPhpVersionRange) return pt_type_new(PT_CLASS_PHP_VERSIONS, 1, constType.raw());
if (!constType.isNull() && !isOverallPhpVersionRange) {
if (hasMinAnalyzableVersion && max.ref().isLong()) {
zv::Val configuredRange = getConfiguredPhpVersionRange();
if (UNEXPECTED(configuredRange.isUndef())) return zv::Val();
if (configuredRange.isNull()) {
// A guard like `PHP_VERSION_ID < 80400` only gives an upper bound. Without
// a configured version range, the lower bound is merely the lowest version
// PHPStan knows, while outside the guard the single analysed PHP version
// applies. Keep the lower bound at that version, or as close to it as
// the guard allows.
zv::Ref phpVersion = slot(PT_MS_PROP_PHP_VERSION);
if (UNEXPECTED(!phpVersion.isObject())) return uninitializedProperty("phpVersion");
zv::Val versionId = pt_php_version_get_version_id(phpVersion.raw());
if (UNEXPECTED(versionId.isUndef())) return zv::Val();
zend_long maxVersionId = max.ref().asLong();
zend_long minVersionId = zval_get_long(versionId.raw());
zval interval[2];
ZVAL_LONG(&interval[0], minVersionId < maxVersionId ? minVersionId : maxVersionId);
ZVAL_LONG(&interval[1], maxVersionId);
zv::Val versionRange = pt_type_call_static_ce(pt_ce_integer_range_type, PT_LC("frominterval"), 2, interval);
if (UNEXPECTED(versionRange.isUndef())) return zv::Val();
return pt_type_new(PT_CLASS_PHP_VERSIONS, 1, versionRange.raw());
}
}

return pt_type_new(PT_CLASS_PHP_VERSIONS, 1, constType.raw());
}

zv::Val configuredRange = getConfiguredPhpVersionRange();
if (UNEXPECTED(configuredRange.isUndef())) return zv::Val();
if (!configuredRange.isNull()) return pt_type_new(PT_CLASS_PHP_VERSIONS, 1, configuredRange.raw());

zv::Ref phpVersion = slot(PT_MS_PROP_PHP_VERSION);
if (UNEXPECTED(!phpVersion.isObject())) return uninitializedProperty("phpVersion");
zv::Val versionId = pt_php_version_get_version_id(phpVersion.raw());
if (UNEXPECTED(versionId.isUndef())) return zv::Val();
zval constantInteger;
if (UNEXPECTED(!pt_constant_integer_type_new(&constantInteger, zval_get_long(versionId.raw())))) return zv::Val();
zv::Val constantIntegerType = zv::Val::adopt(constantInteger);
return pt_type_new(PT_CLASS_PHP_VERSIONS, 1, constantIntegerType.raw());
}

// The analysed PHP version range comes either from the NEON phpVersion min/max
// config or from the composer.json "require.php" constraint - the very same
// source ConstantResolver narrows PHP_VERSION_ID with, so that
// Scope::getPhpVersion() never contradicts the PHP_VERSION_ID constant.
/* The analysed PHP version range comes either from the NEON phpVersion min/max
* config or from the composer.json "require.php" constraint - the very same
* source ConstantResolver narrows PHP_VERSION_ID with, so that
* Scope::getPhpVersion() never contradicts the PHP_VERSION_ID constant.
* NULL when neither is set and a single PHP version is analysed; UNDEF =
* pending exception */
zv::Val getConfiguredPhpVersionRange()
{
if (UNEXPECTED(!requireSlot(PT_MS_PROP_CONFIGURED_PHP_VERSION_RANGE_HELPER, "configuredPhpVersionRangeHelper"))) return zv::Val();
zv::Ref rangeHelper = slot(PT_MS_PROP_CONFIGURED_PHP_VERSION_RANGE_HELPER);
zv::Val range = pt_type_call(rangeHelper.asObject(), PT_LC("getversionrange"), 0, NULL);
Expand All @@ -3952,36 +3999,25 @@ class MutatingScope
if (UNEXPECTED(maxVersionId.isUndef())) return zv::Val();
narrowed = !(maxVersionId.ref().isLong() && maxVersionId.ref().asLong() == PT_MS_MAX_PHP_VERSION);
}
if (narrowed) {
zval interval[2];
zv::Val minVersionId, maxVersionId;
if (minPhpVersion != NULL) {
minVersionId = pt_php_version_get_version_id(minPhpVersion);
if (UNEXPECTED(minVersionId.isUndef())) return zv::Val();
ZVAL_COPY_VALUE(&interval[0], minVersionId.raw());
} else {
ZVAL_LONG(&interval[0], PT_MS_PHP_MIN_ANALYZABLE_VERSION_ID);
}
if (maxPhpVersion != NULL) {
maxVersionId = pt_php_version_get_version_id(maxPhpVersion);
if (UNEXPECTED(maxVersionId.isUndef())) return zv::Val();
ZVAL_COPY_VALUE(&interval[1], maxVersionId.raw());
} else {
ZVAL_NULL(&interval[1]);
}
zv::Val versionRange = pt_type_call_static_ce(pt_ce_integer_range_type, PT_LC("frominterval"), 2, interval);
if (UNEXPECTED(versionRange.isUndef())) return zv::Val();
return pt_type_new(PT_CLASS_PHP_VERSIONS, 1, versionRange.raw());
}
if (!narrowed) return zv::Val::null();

zv::Ref phpVersion = slot(PT_MS_PROP_PHP_VERSION);
if (UNEXPECTED(!phpVersion.isObject())) return uninitializedProperty("phpVersion");
zv::Val versionId = pt_php_version_get_version_id(phpVersion.raw());
if (UNEXPECTED(versionId.isUndef())) return zv::Val();
zval constantInteger;
if (UNEXPECTED(!pt_constant_integer_type_new(&constantInteger, zval_get_long(versionId.raw())))) return zv::Val();
zv::Val constantIntegerType = zv::Val::adopt(constantInteger);
return pt_type_new(PT_CLASS_PHP_VERSIONS, 1, constantIntegerType.raw());
zval interval[2];
zv::Val minVersionId, maxVersionId;
if (minPhpVersion != NULL) {
minVersionId = pt_php_version_get_version_id(minPhpVersion);
if (UNEXPECTED(minVersionId.isUndef())) return zv::Val();
ZVAL_COPY_VALUE(&interval[0], minVersionId.raw());
} else {
ZVAL_LONG(&interval[0], PT_MS_PHP_MIN_ANALYZABLE_VERSION_ID);
}
if (maxPhpVersion != NULL) {
maxVersionId = pt_php_version_get_version_id(maxPhpVersion);
if (UNEXPECTED(maxVersionId.isUndef())) return zv::Val();
ZVAL_COPY_VALUE(&interval[1], maxVersionId.raw());
} else {
ZVAL_NULL(&interval[1]);
}
return pt_type_call_static_ce(pt_ce_integer_range_type, PT_LC("frominterval"), 2, interval);
}

/* $this->getPhpVersion()->supportsNamedArguments()->no() negated — the
Expand Down
61 changes: 32 additions & 29 deletions turbo-ext/src/generated/MutatingScope.h
Original file line number Diff line number Diff line change
Expand Up @@ -528,21 +528,22 @@ inline constexpr char strings[] =
"getIterableValueType\0" /* 9182 */
"PHPStan\\Php\\PhpVersions\0" /* 9203 */
"getPhpVersion\0" /* 9227 */
"isOverallPhpVersionRange\0" /* 9241 */
"PhpParser\\Node\0" /* 9266 */
"invokeNodeCallback\0" /* 9281 */
"collectorType\0" /* 9300 */
"data\0" /* 9314 */
"emitCollectedData\0" /* 9319 */
"extensionClass\0" /* 9337 */
"trackValueDependency\0" /* 9352 */
"file\0" /* 9373 */
"trackFileDependency\0" /* 9378 */
"directory\0" /* 9398 */
"pattern\0" /* 9408 */
"'*'\0" /* 9416 */
"trackDirectoryDependency\0" /* 9420 */
"trackClassDependency"; /* 9445 */
"getConfiguredPhpVersionRange\0" /* 9241 */
"isOverallPhpVersionRange\0" /* 9270 */
"PhpParser\\Node\0" /* 9295 */
"invokeNodeCallback\0" /* 9310 */
"collectorType\0" /* 9329 */
"data\0" /* 9343 */
"emitCollectedData\0" /* 9348 */
"extensionClass\0" /* 9366 */
"trackValueDependency\0" /* 9381 */
"file\0" /* 9402 */
"trackFileDependency\0" /* 9407 */
"directory\0" /* 9427 */
"pattern\0" /* 9437 */
"'*'\0" /* 9445 */
"trackDirectoryDependency\0" /* 9449 */
"trackClassDependency"; /* 9474 */
inline constexpr reg::PackedArg args[] = {
reg::packed(0, 0, 10), /* __construct $container */
reg::packed(48, 0, 61), /* __construct $scopeFactory */
Expand Down Expand Up @@ -1070,20 +1071,21 @@ inline constexpr reg::PackedArg args[] = {
reg::packed(5878, 0, 2162), /* getIterableValueType $iteratee */
reg::packed(1476, 0, 2162), /* getIterableValueType return */
reg::packed(1476, 0, 9203), /* getPhpVersion return */
reg::packed(1476, MAY_BE_NULL, 2162), /* getConfiguredPhpVersionRange return */
reg::packed(4614, 0, 2162), /* isOverallPhpVersionRange $type */
reg::packed(1476, MAY_BE_BOOL), /* isOverallPhpVersionRange return */
reg::packed(2500, 0, 9266), /* invokeNodeCallback $node */
reg::packed(2500, 0, 9295), /* invokeNodeCallback $node */
reg::packed(1476, MAY_BE_VOID), /* invokeNodeCallback return */
reg::packed(9300, MAY_BE_STRING), /* emitCollectedData $collectorType */
reg::packed(9314, MAY_BE_ANY), /* emitCollectedData $data */
reg::packed(9329, MAY_BE_STRING), /* emitCollectedData $collectorType */
reg::packed(9343, MAY_BE_ANY), /* emitCollectedData $data */
reg::packed(1476, MAY_BE_VOID), /* emitCollectedData return */
reg::packed(9337, MAY_BE_STRING), /* trackValueDependency $extensionClass */
reg::packed(9366, MAY_BE_STRING), /* trackValueDependency $extensionClass */
reg::packed(3280, MAY_BE_STRING), /* trackValueDependency $key */
reg::packed(1476, MAY_BE_VOID), /* trackValueDependency return */
reg::packed(9373, MAY_BE_STRING), /* trackFileDependency $file */
reg::packed(9402, MAY_BE_STRING), /* trackFileDependency $file */
reg::packed(1476, MAY_BE_VOID), /* trackFileDependency return */
reg::packed(9398, MAY_BE_STRING), /* trackDirectoryDependency $directory */
reg::packed(9408, MAY_BE_STRING, reg::NoString, false, false, 9416), /* trackDirectoryDependency $pattern */
reg::packed(9427, MAY_BE_STRING), /* trackDirectoryDependency $directory */
reg::packed(9437, MAY_BE_STRING, reg::NoString, false, false, 9445), /* trackDirectoryDependency $pattern */
reg::packed(1476, MAY_BE_VOID), /* trackDirectoryDependency return */
reg::packed(3717, MAY_BE_STRING), /* trackClassDependency $className */
reg::packed(1476, MAY_BE_VOID), /* trackClassDependency return */
Expand Down Expand Up @@ -1285,13 +1287,14 @@ inline constexpr sigtab::Sig getNativeConstantTypes = { { 9140 /* getNativeConst
inline constexpr sigtab::Sig getIterableKeyType = { { 9163 /* getIterableKeyType */, 1, 521, 1, 522, ZEND_ACC_PUBLIC } };
inline constexpr sigtab::Sig getIterableValueType = { { 9182 /* getIterableValueType */, 1, 523, 1, 524, ZEND_ACC_PUBLIC } };
inline constexpr sigtab::Sig getPhpVersion = { { 9227 /* getPhpVersion */, 0, 525, 0, 525, ZEND_ACC_PUBLIC } };
inline constexpr sigtab::Sig isOverallPhpVersionRange = { { 9241 /* isOverallPhpVersionRange */, 1, 526, 1, 527, ZEND_ACC_PRIVATE } };
inline constexpr sigtab::Sig invokeNodeCallback = { { 9281 /* invokeNodeCallback */, 1, 528, 1, 529, ZEND_ACC_PUBLIC } };
inline constexpr sigtab::Sig emitCollectedData = { { 9319 /* emitCollectedData */, 2, 530, 2, 532, ZEND_ACC_PUBLIC } };
inline constexpr sigtab::Sig trackValueDependency = { { 9352 /* trackValueDependency */, 2, 533, 2, 535, ZEND_ACC_PUBLIC } };
inline constexpr sigtab::Sig trackFileDependency = { { 9378 /* trackFileDependency */, 1, 536, 1, 537, ZEND_ACC_PUBLIC } };
inline constexpr sigtab::Sig trackDirectoryDependency = { { 9420 /* trackDirectoryDependency */, 1, 538, 2, 540, ZEND_ACC_PUBLIC } };
inline constexpr sigtab::Sig trackClassDependency = { { 9445 /* trackClassDependency */, 1, 541, 1, 542, ZEND_ACC_PUBLIC } };
inline constexpr sigtab::Sig getConfiguredPhpVersionRange = { { 9241 /* getConfiguredPhpVersionRange */, 0, 526, 0, 526, ZEND_ACC_PRIVATE } };
inline constexpr sigtab::Sig isOverallPhpVersionRange = { { 9270 /* isOverallPhpVersionRange */, 1, 527, 1, 528, ZEND_ACC_PRIVATE } };
inline constexpr sigtab::Sig invokeNodeCallback = { { 9310 /* invokeNodeCallback */, 1, 529, 1, 530, ZEND_ACC_PUBLIC } };
inline constexpr sigtab::Sig emitCollectedData = { { 9348 /* emitCollectedData */, 2, 531, 2, 533, ZEND_ACC_PUBLIC } };
inline constexpr sigtab::Sig trackValueDependency = { { 9381 /* trackValueDependency */, 2, 534, 2, 536, ZEND_ACC_PUBLIC } };
inline constexpr sigtab::Sig trackFileDependency = { { 9407 /* trackFileDependency */, 1, 537, 1, 538, ZEND_ACC_PUBLIC } };
inline constexpr sigtab::Sig trackDirectoryDependency = { { 9449 /* trackDirectoryDependency */, 1, 539, 2, 541, ZEND_ACC_PUBLIC } };
inline constexpr sigtab::Sig trackClassDependency = { { 9474 /* trackClassDependency */, 1, 542, 1, 543, ZEND_ACC_PUBLIC } };
} // namespace sig

} // namespace ptdecl::MutatingScope
Expand Down
32 changes: 32 additions & 0 deletions turbo-ext/tests/scope-family.php
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,35 @@ public function exprRef(Node\Expr $expr): mixed

return [$table, $table, []];
};
// PHP_VERSION_ID under a guard that only gives an upper bound (`PHP_VERSION_ID < X`),
// with a single analysed version pinned to phpVersion: 80500 whatever PHP runs
// this: getPhpVersion() raises the lower bound to the analysed version, or as
// close to it as the guard allows - once with the analysed version above the
// guard's bound and once below it
$sfGuardedVersionScopes = [];
foreach ([80399, 80599] as $sfGuardedMax) {
$sfGuardedVersionFetch = new \PhpParser\Node\Expr\ConstFetch(new \PhpParser\Node\Name('PHP_VERSION_ID'));
$sfGuardedVersionArgs = $sfHarness->constructorArgs(
$sfScopeFactory->create(\PHPStan\Analyser\ScopeContext::create($sfFile))
->assignExpression($sfGuardedVersionFetch, \PHPStan\Type\IntegerRangeType::fromInterval(\PHPStan\Analyser\ConstantResolver::PHP_MIN_ANALYZABLE_VERSION_ID, $sfGuardedMax), new \PHPStan\Type\IntegerType()),
);
$sfGuardedVersionArgs['phpVersion'] = new \PHPStan\Php\PhpVersion(80500);
$sfGuardedVersionArgs['configuredPhpVersionRangeHelper'] = new \PHPStan\Php\ConfiguredPhpVersionRangeHelper(80500, $scContainer->getByType(\PHPStan\Php\ComposerPhpVersionFactory::class));
$sfGuardedVersionScope = new \PHPStan\Analyser\MutatingScope(...array_values($sfGuardedVersionArgs));
$sfScopes[spl_object_id($sfGuardedVersionScope)] = [$sfGuardedVersionScope, [$sfGuardedVersionFetch], null];
$sfGuardedVersionScopes[] = [$sfGuardedVersionScope, static function (string $side) use ($sfHarness, $sfGuardedVersionFetch, $sfGuardedMax): array {
$native = $side === 'native';
$range = $native
? \PHPStanTurbo\IntegerRangeType::fromInterval(\PHPStan\Analyser\ConstantResolver::PHP_MIN_ANALYZABLE_VERSION_ID, $sfGuardedMax)
: \PHPStan\Type\IntegerRangeType::fromInterval(\PHPStan\Analyser\ConstantResolver::PHP_MIN_ANALYZABLE_VERSION_ID, $sfGuardedMax);
$holder = $native
? new \PHPStanTurbo\ExpressionTypeHolder($sfGuardedVersionFetch, $range, \PHPStanTurbo\TrinaryLogic::createYes())
: new \PHPStan\Analyser\ExpressionTypeHolder($sfGuardedVersionFetch, $range, \PHPStan\TrinaryLogic::createYes());
$table = [$sfHarness->key($sfGuardedVersionFetch) => $holder];

return [$table, $table, []];
}];
}
foreach ([80500, 70400] as $sfVersionId) {
$sfVersionScope = $sfScopeFactory->create(\PHPStan\Analyser\ScopeContext::create($sfFile)->enterClass($sfReflectionProvider->getClass(\ScopeFamilyFixture\Holder::class)))
->assignExpression(new \PhpParser\Node\Expr\ConstFetch(new \PhpParser\Node\Name('PHP_VERSION_ID')), new \PHPStan\Type\Constant\ConstantIntegerType($sfVersionId), new \PHPStan\Type\IntegerType());
Expand Down Expand Up @@ -759,6 +788,9 @@ public function exprRef(Node\Expr $expr): mixed
// the scopes whose tables are built per side (each side's own Type classes)
$sfScopes[spl_object_id($sfSyntheticScope)][3] = $sfSyntheticTables;
$sfScopes[spl_object_id($sfOverallVersionScope)][3] = $sfOverallVersionTables;
foreach ($sfGuardedVersionScopes as [$sfGuardedVersionScope, $sfGuardedVersionTables]) {
$sfScopes[spl_object_id($sfGuardedVersionScope)][3] = $sfGuardedVersionTables;
}
$sfPromotedArgs = $sfHarness->constructorArgs($sfSyntheticScope);
$sfPromotedArgs['nativeTypesPromoted'] = true;
$sfPromotedScope = new \PHPStan\Analyser\MutatingScope(...array_values($sfPromotedArgs));
Expand Down
Loading