Skip to content

Commit 61b98ea

Browse files
Respect original variable type when using extract on optional keys
1 parent 1eead47 commit 61b98ea

File tree

4 files changed

+40
-1
lines changed

4 files changed

+40
-1
lines changed

src/Analyser/NodeScopeResolver.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2957,7 +2957,17 @@ static function (): void {
29572957
}
29582958
foreach ($properties as $name => $type) {
29592959
$optional = in_array($name, $optionalProperties, true) || $refCount[$name] < count($constantArrays);
2960-
$scope = $scope->assignVariable($name, $type, $type, $optional ? TrinaryLogic::createMaybe() : TrinaryLogic::createYes());
2960+
2961+
if (!$optional) {
2962+
$scope = $scope->assignVariable($name, $type, $type, TrinaryLogic::createYes());
2963+
} else {
2964+
$hasVariable = $scope->hasVariableType($name);
2965+
if (!$hasVariable->no()) {
2966+
$type = TypeCombinator::union($scope->getVariableType($name), $type);
2967+
}
2968+
2969+
$scope = $scope->assignVariable($name, $type, $type, $scope->hasVariableType($name)->or(TrinaryLogic::createMaybe()));
2970+
}
29612971
}
29622972
} else {
29632973
$scope = $scope->afterExtractCall();

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ private static function findTestFiles(): iterable
226226
yield __DIR__ . '/../Rules/Comparison/data/bug-5365.php';
227227
yield __DIR__ . '/../Rules/Comparison/data/bug-6551.php';
228228
yield __DIR__ . '/../Rules/Variables/data/bug-9403.php';
229+
yield __DIR__ . '/../Rules/Variables/data/bug-12364.php';
229230
yield __DIR__ . '/../Rules/Methods/data/bug-9542.php';
230231
yield __DIR__ . '/../Rules/Functions/data/bug-9803.php';
231232
yield __DIR__ . '/../Rules/PhpDoc/data/bug-10594.php';

tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,6 +1011,15 @@ public function testIsStringNarrowsCertainty(): void
10111011
]);
10121012
}
10131013

1014+
public function testBug12364(): void
1015+
{
1016+
$this->cliArgumentsVariablesRegistered = true;
1017+
$this->polluteScopeWithLoopInitialAssignments = true;
1018+
$this->checkMaybeUndefinedVariables = true;
1019+
$this->polluteScopeWithAlwaysIterableForeach = true;
1020+
$this->analyse([__DIR__ . '/data/bug-12364.php'], []);
1021+
}
1022+
10141023
public function testDiscussion10252(): void
10151024
{
10161025
$this->cliArgumentsVariablesRegistered = true;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace Bug12364;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
/** @return array{x: string, y?: string} */
8+
function foo(): array {
9+
return [ 'x' => 'foo' ];
10+
}
11+
12+
$x = $y = null;
13+
assertType('null', $x);
14+
assertType('null', $y);
15+
extract(foo());
16+
assertType('string', $x);
17+
assertType('string|null', $y); // <-- should be: null|string
18+
var_dump($x);
19+
var_dump($y); // <-- does exist

0 commit comments

Comments
 (0)