diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index 900c3f3fff..4e510b52f4 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -4094,6 +4094,14 @@ private function createConditionalExpressions( continue; } + if ( + array_key_exists($exprString, $newVariableTypes) + && !$newVariableTypes[$exprString]->getCertainty()->equals($holder->getCertainty()) + && $newVariableTypes[$exprString]->equalTypes($holder) + ) { + continue; + } + unset($newVariableTypes[$exprString]); } diff --git a/src/Reflection/BetterReflection/SourceLocator/OptimizedDirectorySourceLocator.php b/src/Reflection/BetterReflection/SourceLocator/OptimizedDirectorySourceLocator.php index e66dfea5d6..f2b0f7e3a6 100644 --- a/src/Reflection/BetterReflection/SourceLocator/OptimizedDirectorySourceLocator.php +++ b/src/Reflection/BetterReflection/SourceLocator/OptimizedDirectorySourceLocator.php @@ -127,7 +127,7 @@ public function locateIdentifier(Reflector $reflector, Identifier $identifier): return null; } - [$reflectionCacheKey, $variableCacheKey] = $this->getCacheKeys($file, $identifier); // @phpstan-ignore variable.undefined + [$reflectionCacheKey, $variableCacheKey] = $this->getCacheKeys($file, $identifier); $classReflection = $this->nodeToReflection($reflector, $fetchedClassNode); $this->cache->save($reflectionCacheKey, $variableCacheKey, $classReflection->exportToCache()); @@ -171,7 +171,7 @@ public function locateIdentifier(Reflector $reflector, Identifier $identifier): return null; } - [$reflectionCacheKey, $variableCacheKey] = $this->getCacheKeys($file, $identifier); // @phpstan-ignore variable.undefined + [$reflectionCacheKey, $variableCacheKey] = $this->getCacheKeys($file, $identifier); $constantReflection = $this->nodeToReflection( $reflector, $fetchedConstantNode, diff --git a/tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php b/tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php index 1a9f84f7ab..d6a4ce9a4c 100644 --- a/tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php +++ b/tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php @@ -1323,6 +1323,26 @@ public function testBug5919(): void $this->analyse([__DIR__ . '/data/bug-5919.php'], []); } + public function testBug8430(): void + { + $this->cliArgumentsVariablesRegistered = true; + $this->polluteScopeWithLoopInitialAssignments = false; + $this->checkMaybeUndefinedVariables = true; + $this->polluteScopeWithAlwaysIterableForeach = true; + + $this->analyse([__DIR__ . '/data/bug-8430.php'], []); + } + + public function testBug8430b(): void + { + $this->cliArgumentsVariablesRegistered = true; + $this->polluteScopeWithLoopInitialAssignments = false; + $this->checkMaybeUndefinedVariables = true; + $this->polluteScopeWithAlwaysIterableForeach = true; + + $this->analyse([__DIR__ . '/data/bug-8430b.php'], []); + } + public function testBug5477(): void { $this->cliArgumentsVariablesRegistered = true; @@ -1333,4 +1353,51 @@ public function testBug5477(): void $this->analyse([__DIR__ . '/data/bug-5477.php'], []); } + public function testBug10657(): void + { + $this->cliArgumentsVariablesRegistered = true; + $this->polluteScopeWithLoopInitialAssignments = false; + $this->checkMaybeUndefinedVariables = true; + $this->polluteScopeWithAlwaysIterableForeach = true; + + $this->analyse([__DIR__ . '/data/bug-10657.php'], []); + } + + public function testBug6830(): void + { + $this->cliArgumentsVariablesRegistered = true; + $this->polluteScopeWithLoopInitialAssignments = false; + $this->checkMaybeUndefinedVariables = true; + $this->polluteScopeWithAlwaysIterableForeach = true; + + $this->analyse([__DIR__ . '/data/bug-6830.php'], []); + } + + public function testBug14117(): void + { + $this->cliArgumentsVariablesRegistered = true; + $this->polluteScopeWithLoopInitialAssignments = false; + $this->checkMaybeUndefinedVariables = true; + $this->polluteScopeWithAlwaysIterableForeach = true; + + $this->analyse([__DIR__ . '/data/bug-14117.php'], [ + [ + 'Variable $value might not be defined.', + 33, + ], + [ + 'Variable $value might not be defined.', + 49, + ], + [ + 'Undefined variable: $value', + 65, + ], + [ + 'Undefined variable: $value', + 81, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Variables/data/bug-10657.php b/tests/PHPStan/Rules/Variables/data/bug-10657.php new file mode 100644 index 0000000000..f9d6a508fb --- /dev/null +++ b/tests/PHPStan/Rules/Variables/data/bug-10657.php @@ -0,0 +1,18 @@ + $bools */ +function test(array $bools): void +{ + foreach ($bools as $bool) { + if ($bool) { + $foo = 'foo'; + } + if ($bool) { + echo $foo; + } + } +} diff --git a/tests/PHPStan/Rules/Variables/data/bug-8430.php b/tests/PHPStan/Rules/Variables/data/bug-8430.php new file mode 100644 index 0000000000..9ec7e1c436 --- /dev/null +++ b/tests/PHPStan/Rules/Variables/data/bug-8430.php @@ -0,0 +1,31 @@ + 1]; + } + if (!$a && !$b) { + echo $arr['a']; + } + } + } + + public function def(string $a, bool $b): void + { + if (!$b) { + $arr = ['a' => 1]; + } + if (!$a && !$b) { + echo $arr['a']; + } + } +} + diff --git a/tests/PHPStan/Rules/Variables/data/bug-8430b.php b/tests/PHPStan/Rules/Variables/data/bug-8430b.php new file mode 100644 index 0000000000..4622031dd7 --- /dev/null +++ b/tests/PHPStan/Rules/Variables/data/bug-8430b.php @@ -0,0 +1,111 @@ + */ + public array $c; + public string $d; +} + +class B +{ + private function abc(): void + { + } + + /** + * @param A[] $a + * @param array $b + */ + public function def(array $a, array $b, string $c, bool $d): void + { + $e = false; + $f = false; + switch ($b['repeat'] ?? null) { + case 'Y': + $e = true; + break; + case 'A': + $e = true; + $f = true; + break; + } + $g = 5; + $h = 0; + for ($i = 1; $i <= $g; $i++) { + if (!$d) { + $arr = ['a' => 1]; + } + $j = $a[$i] ?? null; + if ($j) { + /** @var array $k */ + $k = []; + if ($e) { + foreach ($j->a as $l) { + /** @var A[] $m */ + $m = $f || empty($k) ? $j->b : []; + array_push($m, ...$l); + array_push($k, $m); + } + if (empty($k)) { + array_push($k, $j->b); + } + } else { + array_push($k, $j->b); + foreach ($j->a as $l) { + array_push($k[0], ...$l); + break; + } + } + foreach ($k as $n) { + if (!$d) { + foreach ($n as $o) { + switch ($o->c['x'] ?? '') { + case 'y': + $p = $o->c[$o->d] ?? null; + if (is_array($p)) { + $this->abc(); + } + break; + case 'z': + $p = $o->c[$o->d] ?? null; + if (is_array($p)) { + $this->abc(); + } + break; + default: + $this->abc(); + break; + } + } + } + if (!empty($n)) { + $h++; + } + } + } + if (!$c && !$d) { + echo $arr['a']; + } + } + } + + public function ghi(string $a, bool $b): void + { + if (!$b) { + $arr = ['a' => 1]; + } + $this->abc(); + if (!$a && !$b) { + echo $arr['a']; + } + } +}