Skip to content

Commit dbdc192

Browse files
github-actions[bot]phpstan-bot
authored andcommitted
Fix phpstan/phpstan#12686: Respect closure purity in type narrowing
- Added purity check for callable variable calls ($f()) in TypeSpecifier::createForExpr() - Previously, only named function calls (e.g. rand()) had purity checks that prevented type narrowing for impure functions; callable variable calls were missing this check - New regression test in tests/PHPStan/Analyser/nsrt/bug-12686.php
1 parent 4c6ef6e commit dbdc192

2 files changed

Lines changed: 47 additions & 0 deletions

File tree

src/Analyser/TypeSpecifier.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2231,6 +2231,30 @@ private function createForExpr(
22312231
}
22322232
}
22332233

2234+
if (
2235+
$expr instanceof FuncCall
2236+
&& !$expr->name instanceof Name
2237+
) {
2238+
$nameType = $scope->getType($expr->name);
2239+
if ($nameType->isCallable()->yes()) {
2240+
$isPure = null;
2241+
foreach ($nameType->getCallableParametersAcceptors($scope) as $variant) {
2242+
$variantIsPure = $variant->isPure();
2243+
$isPure = $isPure === null ? $variantIsPure : $isPure->and($variantIsPure);
2244+
}
2245+
2246+
if ($isPure !== null) {
2247+
if ($isPure->no()) {
2248+
return new SpecifiedTypes([], []);
2249+
}
2250+
2251+
if (!$this->rememberPossiblyImpureFunctionValues && !$isPure->yes()) {
2252+
return new SpecifiedTypes([], []);
2253+
}
2254+
}
2255+
}
2256+
}
2257+
22342258
if (
22352259
$expr instanceof MethodCall
22362260
&& $expr->name instanceof Node\Identifier
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug12686;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
/** @phpstan-impure */
8+
$f = function (): bool {
9+
return (bool) rand(0,1);
10+
};
11+
12+
if ($f()) {
13+
assertType('bool', $f());
14+
}
15+
16+
// Pure closure should still have narrowing
17+
$h = function (): bool {
18+
return true;
19+
};
20+
21+
if ($h()) {
22+
assertType('true', $h());
23+
}

0 commit comments

Comments
 (0)