diff --git a/src/Type/Php/ArrayMergeFunctionDynamicReturnTypeExtension.php b/src/Type/Php/ArrayMergeFunctionDynamicReturnTypeExtension.php index c7ed216b7d..4fcc3338bf 100644 --- a/src/Type/Php/ArrayMergeFunctionDynamicReturnTypeExtension.php +++ b/src/Type/Php/ArrayMergeFunctionDynamicReturnTypeExtension.php @@ -8,8 +8,10 @@ use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Type\Accessory\NonEmptyArrayType; use PHPStan\Type\ArrayType; +use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\DynamicFunctionReturnTypeExtension; use PHPStan\Type\GeneralizePrecision; +use PHPStan\Type\NeverType; use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; use PHPStan\Type\UnionType; @@ -52,8 +54,13 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, $nonEmpty = true; } + $keyType = TypeCombinator::union(...$keyTypes); + if ($keyType instanceof NeverType && !$keyType->isExplicit()) { + return new ConstantArrayType([], []); + } + $arrayType = new ArrayType( - TypeCombinator::union(...$keyTypes), + $keyType, TypeCombinator::union(...$valueTypes), ); diff --git a/tests/PHPStan/Analyser/AnalyserIntegrationTest.php b/tests/PHPStan/Analyser/AnalyserIntegrationTest.php index 5358539191..e70ee51877 100644 --- a/tests/PHPStan/Analyser/AnalyserIntegrationTest.php +++ b/tests/PHPStan/Analyser/AnalyserIntegrationTest.php @@ -557,6 +557,12 @@ public function testBug6681(): void $this->assertNoErrors($errors); } + public function testBug6212(): void + { + $errors = $this->runAnalyse(__DIR__ . '/data/bug-6212.php'); + $this->assertNoErrors($errors); + } + /** * @param string[]|null $allAnalysedFiles * @return Error[] diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index 8ee492f3f8..7e6d2df634 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -737,6 +737,18 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6698.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/date-format.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6070.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6108.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-1516.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6174.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Methods/data/bug-5749.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-5675.php'); + + if (PHP_VERSION_ID >= 80000) { + yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6505.php'); + } + + yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6699.php'); } /** diff --git a/tests/PHPStan/Analyser/data/bug-1516.php b/tests/PHPStan/Analyser/data/bug-1516.php new file mode 100644 index 0000000000..d62795a24b --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-1516.php @@ -0,0 +1,37 @@ + 'barr', + 'ftt' => [] + ]; + + foreach ($a as $k => $b) { + $str = 'toto'; + assertType('\'toto\'|array{}', $out[$k]); + + if (is_array($b)) { + // $out[$k] is redefined there before the array_merge + assertType('\'toto\'|array{}', $out[$k]); + $out[$k] = []; + assertType('array{}', $out[$k]); + $out[$k] = array_merge($out[$k], []); + assertType('array{}', $out[$k]); + + } else { + // I think phpstan takes this definition as a string and takes no account of the foreach + $out[$k] = $str; + assertType('\'toto\'', $out[$k]); + } + } + } +} diff --git a/tests/PHPStan/Analyser/data/bug-5675.php b/tests/PHPStan/Analyser/data/bug-5675.php new file mode 100644 index 0000000000..bde301d4ec --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-5675.php @@ -0,0 +1,55 @@ +): void)|array|Bar $column + */ + public function foo($column): void + { + // ... + } + + public function bar() + { + /** @var Hello */ + $a = new Hello; + + $a->foo(function (Hello $h) : void { + assertType('Bug5675\Hello', $h); + }); + } +} + +/** + * @template T + */ +class Hello2 +{ + /** + * @param (\Closure(static): void)|array|Bar $column + */ + public function foo($column): void + { + // ... + } + + public function bar() + { + /** @var Hello2 */ + $a = new Hello2; + + $a->foo(function (Hello2 $h) : void { + \PHPStan\Testing\assertType('Bug5675\Hello2', $h); + }); + } +} diff --git a/tests/PHPStan/Analyser/data/bug-6070.php b/tests/PHPStan/Analyser/data/bug-6070.php new file mode 100644 index 0000000000..ac1355eee0 --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-6070.php @@ -0,0 +1,23 @@ +>', $nonEmptyArray); + + return $nonEmptyArray; + } +} diff --git a/tests/PHPStan/Analyser/data/bug-6108.php b/tests/PHPStan/Analyser/data/bug-6108.php new file mode 100644 index 0000000000..e19760df07 --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-6108.php @@ -0,0 +1,38 @@ + [1, 2], + 'b' => [3, 4, 5], + 'c' => true, + ]; + } + + function doBar() + { + $x = $this->doFoo(); + $test = ['a' => true, 'b' => false]; + foreach ($test as $key => $value) { + if ($value) { + assertType('\'a\'|\'b\'', $key); // could be just 'a' + assertType('array', $x[$key]); + } + } + } + +} diff --git a/tests/PHPStan/Analyser/data/bug-6174.php b/tests/PHPStan/Analyser/data/bug-6174.php new file mode 100644 index 0000000000..08dcc48747 --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-6174.php @@ -0,0 +1,21 @@ +returnValue() ?? self::DEFAULT_VALUE); + assertType('-1|int<1, max>', $tempValue === -1 || $tempValue > 0 ? $tempValue : self::DEFAULT_VALUE); + } + + public function returnValue(): ?string + { + return null; + } +} diff --git a/tests/PHPStan/Analyser/data/bug-6212.php b/tests/PHPStan/Analyser/data/bug-6212.php new file mode 100644 index 0000000000..a9aa125bde --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-6212.php @@ -0,0 +1,13 @@ += 8.0 + +namespace Bug6505; + +use function PHPStan\Testing\assertType; + +/** @template T */ +interface Type +{ + /** + * @param T $val + * @return T + */ + public function validate($val); +} + +/** + * @template T + * @implements Type> + */ +final class ClassStringType implements Type +{ + /** @param class-string $classString */ + public function __construct(public string $classString) + { + } + + public function validate($val) { + return $val; + } +} + +/** + * @implements Type> + */ +final class StdClassType implements Type +{ + public function validate($val) { + return $val; + } +} + + +/** + * @template T + * @implements Type + */ +final class TypeCollection implements Type +{ + /** @param Type $type */ + public function __construct(public Type $type) + { + } + public function validate($val) { + return $val; + } +} + +class Foo +{ + + public function doFoo() + { + $c = new TypeCollection(new ClassStringType(\stdClass::class)); + assertType('c', $c->validate([\stdClass::class])); + $c2 = new TypeCollection(new StdClassType()); + assertType('c', $c2->validate([\stdClass::class])); + } + + /** + * @template T + * @param T $t + * @return T + */ + function unbounded($t) { + return $t; + } + + /** + * @template T of string + * @param T $t + * @return T + */ + function bounded1($t) { + return $t; + } + + /** + * @template T of object|class-string + * @param T $t + * @return T + */ + function bounded2($t) { + return $t; + } + + /** @param class-string<\stdClass> $p */ + function test($p): void { + assertType('c', $this->unbounded($p)); + assertType('c', $this->bounded1($p)); + assertType('c', $this->bounded2($p)); + } + +} + +/** + * @template TKey of array-key + * @template TValue + */ +class Collection +{ + /** + * @var array + */ + protected array $items; + + /** + * Create a new collection. + * + * @param array|null $items + * @return void + */ + public function __construct(?array $items = []) + { + $this->items = $items ?? []; + } +} + +class Example +{ + /** @var array> */ + private array $factories = []; + + public function getFactories(): void + { + assertType('c', new Collection($this->factories)); + } +} + diff --git a/tests/PHPStan/Analyser/data/bug-6699.php b/tests/PHPStan/Analyser/data/bug-6699.php new file mode 100644 index 0000000000..6e5bad05c4 --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-6699.php @@ -0,0 +1,30 @@ +value = $value; + } + + /** + * @param class-string<\Exception> $exceptionClass + * @return void + */ + public function doFoo(string $exceptionClass) + { + assertType('class-string', (new Foo($exceptionClass))->value); + } +} diff --git a/tests/PHPStan/Rules/Comparison/BooleanAndConstantConditionRuleTest.php b/tests/PHPStan/Rules/Comparison/BooleanAndConstantConditionRuleTest.php index e53e3a8630..e16bdb0b06 100644 --- a/tests/PHPStan/Rules/Comparison/BooleanAndConstantConditionRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/BooleanAndConstantConditionRuleTest.php @@ -201,4 +201,15 @@ public function testBug2231(): void ]); } + public function testBug1746(): void + { + $this->treatPhpDocTypesAsCertain = true; + $this->analyse([__DIR__ . '/data/bug-1746.php'], [ + [ + 'Left side of && is always true.', + 20, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php index 5587459ac4..c01487f343 100644 --- a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php @@ -455,4 +455,11 @@ public function testBug6698(): void $this->analyse([__DIR__ . '/data/bug-6698.php'], []); } + public function testBug5369(): void + { + $this->checkAlwaysTrueCheckTypeFunctionCall = true; + $this->treatPhpDocTypesAsCertain = true; + $this->analyse([__DIR__ . '/data/bug-5369.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Comparison/NumberComparisonOperatorsConstantConditionRuleTest.php b/tests/PHPStan/Rules/Comparison/NumberComparisonOperatorsConstantConditionRuleTest.php index 7b68ee5a1d..68fa36e038 100644 --- a/tests/PHPStan/Rules/Comparison/NumberComparisonOperatorsConstantConditionRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/NumberComparisonOperatorsConstantConditionRuleTest.php @@ -88,4 +88,14 @@ public function testBug5707(): void $this->analyse([__DIR__ . '/data/bug-5707.php'], []); } + public function testBug5969(): void + { + $this->analyse([__DIR__ . '/data/bug-5969.php'], []); + } + + public function testBug5295(): void + { + $this->analyse([__DIR__ . '/data/bug-5295.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Comparison/data/bug-1746.php b/tests/PHPStan/Rules/Comparison/data/bug-1746.php new file mode 100644 index 0000000000..c6b36ee3a7 --- /dev/null +++ b/tests/PHPStan/Rules/Comparison/data/bug-1746.php @@ -0,0 +1,30 @@ + ['blah' => 'boohoo']]; + $assocModel = 'foo'; + $parents = ['Class' => ['foo' => 'bar', 'bar' => 'baz', 'foreignKey' => 'blah']]; + + // initial value + $isMatch = true; + foreach ($parents as $parentModel) { + $fk = $parentModel['foreignKey']; + if (isset($data[$fk])) { + // redetermine whether $isMatch is still true + $isMatch = $isMatch && ($data[$fk] == $existing[$assocModel][$fk]); + + // bail + if (!$isMatch) { + break; + } + } + } + } + +} diff --git a/tests/PHPStan/Rules/Comparison/data/bug-5295.php b/tests/PHPStan/Rules/Comparison/data/bug-5295.php new file mode 100644 index 0000000000..4818153a87 --- /dev/null +++ b/tests/PHPStan/Rules/Comparison/data/bug-5295.php @@ -0,0 +1,23 @@ +getValue() as $key => $val) { + if ($key >= 5 && $key <= 10) { + } elseif ($key > 10 && $key <= 15) { + } else { + } + } + } + + /** + * @return array + */ + public function getValue(): array { + return []; + } +} diff --git a/tests/PHPStan/Rules/Comparison/data/bug-5369.php b/tests/PHPStan/Rules/Comparison/data/bug-5369.php new file mode 100644 index 0000000000..84707aa8ad --- /dev/null +++ b/tests/PHPStan/Rules/Comparison/data/bug-5369.php @@ -0,0 +1,23 @@ +date)) return; + + if (strtotime($o->date) < time()) echo "a"; + + // surprisingly this is not an issue + if (strtotime($this->date) < time()) echo "b"; + + if (is_string($o->date) && strtotime($o->date) < time()) echo "c"; + } +} diff --git a/tests/PHPStan/Rules/Exceptions/CatchWithUnthrownExceptionRuleTest.php b/tests/PHPStan/Rules/Exceptions/CatchWithUnthrownExceptionRuleTest.php index 2b399d7b3c..f337acac0d 100644 --- a/tests/PHPStan/Rules/Exceptions/CatchWithUnthrownExceptionRuleTest.php +++ b/tests/PHPStan/Rules/Exceptions/CatchWithUnthrownExceptionRuleTest.php @@ -196,4 +196,9 @@ public function testFirstClassCallables(): void ]); } + public function testBug6262(): void + { + $this->analyse([__DIR__ . '/data/bug-6262.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Exceptions/data/bug-6262.php b/tests/PHPStan/Rules/Exceptions/data/bug-6262.php new file mode 100644 index 0000000000..d11b718d17 --- /dev/null +++ b/tests/PHPStan/Rules/Exceptions/data/bug-6262.php @@ -0,0 +1,20 @@ +analyse([__DIR__ . '/data/bug-6423.php'], []); } + public function testBug5869(): void + { + $this->checkThisOnly = false; + $this->checkNullables = true; + $this->checkUnionTypes = true; + $this->checkExplicitMixed = true; + $this->analyse([__DIR__ . '/data/bug-5869.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php b/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php index f3ae9b61db..52b4d91fad 100644 --- a/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php +++ b/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php @@ -21,10 +21,12 @@ class CallStaticMethodsRuleTest extends RuleTestCase private bool $checkThisOnly; + private bool $checkExplicitMixed = false; + protected function getRule(): Rule { $reflectionProvider = $this->createReflectionProvider(); - $ruleLevelHelper = new RuleLevelHelper($reflectionProvider, true, $this->checkThisOnly, true, false); + $ruleLevelHelper = new RuleLevelHelper($reflectionProvider, true, $this->checkThisOnly, true, $this->checkExplicitMixed); return new CallStaticMethodsRule( new StaticMethodCallCheck($reflectionProvider, $ruleLevelHelper, new ClassCaseSensitivityCheck($reflectionProvider, true), true, true), new FunctionCallParametersCheck($ruleLevelHelper, new NullsafeCheck(), new PhpVersion(80000), new UnresolvableTypeHelper(), new PropertyReflectionFinder(), true, true, true, true), @@ -468,4 +470,26 @@ public function testFirstClassCallables(): void $this->analyse([__DIR__ . '/data/first-class-static-method-callable.php'], []); } + public function testBug5893(): void + { + $this->checkThisOnly = false; + $this->checkExplicitMixed = true; + $this->analyse([__DIR__ . '/data/bug-5893.php'], []); + } + + public function testBug6249(): void + { + // discussion https://github.com/phpstan/phpstan/discussions/6249 + $this->checkThisOnly = false; + $this->checkExplicitMixed = true; + $this->analyse([__DIR__ . '/data/bug-6249.php'], []); + } + + public function testBug5749(): void + { + $this->checkThisOnly = false; + $this->checkExplicitMixed = true; + $this->analyse([__DIR__ . '/data/bug-5749.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php b/tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php index dd17e454e9..ff1d352d58 100644 --- a/tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php +++ b/tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php @@ -621,4 +621,16 @@ public function testBug5860(): void $this->analyse([__DIR__ . '/data/bug-5860.php'], []); } + public function testBug6266(): void + { + $this->checkExplicitMixed = true; + $this->analyse([__DIR__ . '/data/bug-6266.php'], []); + } + + public function testBug6023(): void + { + $this->checkExplicitMixed = true; + $this->analyse([__DIR__ . '/data/bug-6023.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Methods/data/bug-5749.php b/tests/PHPStan/Rules/Methods/data/bug-5749.php new file mode 100644 index 0000000000..4ca314c953 --- /dev/null +++ b/tests/PHPStan/Rules/Methods/data/bug-5749.php @@ -0,0 +1,50 @@ +|null', $type); + + if ($type) { + assertType('non-empty-array', $type); + $typeSql = ' AND type IN ' . self::dbarray_int($type) . ' '; + } else { + assertType('0|array{}|null', $type); + $typeSql = ''; + } + + // ... + + return $typeSql; + } +} diff --git a/tests/PHPStan/Rules/Methods/data/bug-5869.php b/tests/PHPStan/Rules/Methods/data/bug-5869.php new file mode 100644 index 0000000000..43fd5607c2 --- /dev/null +++ b/tests/PHPStan/Rules/Methods/data/bug-5869.php @@ -0,0 +1,32 @@ +sayHello($class); + } + + /** + * @param T $class + */ + public function sayHello(BaseInterface $class): void + { + echo 'Hello', PHP_EOL; + } +} diff --git a/tests/PHPStan/Rules/Methods/data/bug-5893.php b/tests/PHPStan/Rules/Methods/data/bug-5893.php new file mode 100644 index 0000000000..f1666d75fe --- /dev/null +++ b/tests/PHPStan/Rules/Methods/data/bug-5893.php @@ -0,0 +1,31 @@ + + */ + public static function getClass($object) + { + return get_class($object); + } +} + +/** + * @phpstan-template T of object + */ +class Foo { + /** @phpstan-param T $object */ + public function foo(object $object): string { + if (method_exists($object, '__toString') && null !== $object->__toString()) { + return $object->__toString(); + } + + return Test::getClass($object); + } +} diff --git a/tests/PHPStan/Rules/Methods/data/bug-6023.php b/tests/PHPStan/Rules/Methods/data/bug-6023.php new file mode 100644 index 0000000000..59628e4bac --- /dev/null +++ b/tests/PHPStan/Rules/Methods/data/bug-6023.php @@ -0,0 +1,22 @@ +, leftovers: array} $groups + * @return array{commissions: array, leftovers: array} + */ + public function groupByType(array $groups, Clearable $clearable): array + { + $group = $clearable->type === 'foo' ? 'commissions' : 'leftovers'; + $groups[$group][] = $clearable; + return $groups; + } +} + +class Clearable +{ + public string $type = 'foo'; +} diff --git a/tests/PHPStan/Rules/Methods/data/bug-6249.php b/tests/PHPStan/Rules/Methods/data/bug-6249.php new file mode 100644 index 0000000000..7d2db1ed3e --- /dev/null +++ b/tests/PHPStan/Rules/Methods/data/bug-6249.php @@ -0,0 +1,136 @@ + + */ +interface CollectionInterface extends Countable, IteratorAggregate +{ +} + +namespace Bug6249N2; + +use ArrayIterator; +use InvalidArgumentException; +use IteratorIterator; +use Bug6249N1\EntityInterface; +use Traversable; +/** + * @extends \IteratorIterator> + */ +final class Eii extends IteratorIterator +{ + /** + * @param iterable $iterable + */ + public function __construct(iterable $iterable) + { + parent::__construct($iterable instanceof Traversable ? $iterable : new ArrayIterator($iterable)); + } + + /** + * @return EntityInterface + */ + public function current() + { + $current = parent::current(); + + if (!$current instanceof EntityInterface) { + throw new InvalidArgumentException(sprintf('Item "%s" must be an instance of "%s".', gettype($current), EntityInterface::class)); + } + + return $current; + } + + /** + * return ?string + */ + public function key() + { + if ($this->valid()) { + /** @var EntityInterface $current */ + $current = $this->current(); + + return $current->getId(); + } + + return null; + } +} + +namespace Bug6249N3; + +use ArrayIterator; +use Countable; +use Bug6249N1\CollectionInterface; +use Traversable; + +/** + * @template TKey of array-key + * @template T + * @implements CollectionInterface + */ +final class Cw implements CollectionInterface +{ + /** + * @var iterable + */ + private iterable $iterable; + + /** + * @param iterable $iterable + */ + private function __construct(iterable $iterable) + { + $this->iterable = $iterable; + } + + /** + * @param iterable $iterable + * + * @return self + */ + public static function fromIterable(iterable $iterable): self + { + return new self($iterable); + } + + public function count(): int + { + if (is_array($this->iterable) || $this->iterable instanceof Countable) { + return count($this->iterable); + } + + return count(iterator_to_array($this->iterable, false)); + } + + public function getIterator(): Traversable + { + if (is_array($this->iterable)) { + return new ArrayIterator($this->iterable); + } + + return $this->iterable; + } +} + +class Foo +{ + + public function doFoo() + { + \Bug6249N3\Cw::fromIterable(new \Bug6249N2\Eii([])); + } + +} diff --git a/tests/PHPStan/Rules/Methods/data/bug-6266.php b/tests/PHPStan/Rules/Methods/data/bug-6266.php new file mode 100644 index 0000000000..058e18eec3 --- /dev/null +++ b/tests/PHPStan/Rules/Methods/data/bug-6266.php @@ -0,0 +1,27 @@ +analyse([__DIR__ . '/data/bug-6020.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Properties/data/bug-6020.php b/tests/PHPStan/Rules/Properties/data/bug-6020.php new file mode 100644 index 0000000000..3ac3d4d3fd --- /dev/null +++ b/tests/PHPStan/Rules/Properties/data/bug-6020.php @@ -0,0 +1,9 @@ +Whatever'); + + $xml->foo?->bar?->baz; +}; diff --git a/tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php b/tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php index 1b10f5ad36..6349d31808 100644 --- a/tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php +++ b/tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php @@ -853,4 +853,13 @@ public function testFirstClassCallables(): void ]); } + public function testBug6112(): void + { + $this->cliArgumentsVariablesRegistered = true; + $this->polluteScopeWithLoopInitialAssignments = false; + $this->checkMaybeUndefinedVariables = true; + $this->polluteScopeWithAlwaysIterableForeach = true; + $this->analyse([__DIR__ . '/data/bug-6112.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Variables/data/bug-6112.php b/tests/PHPStan/Rules/Variables/data/bug-6112.php new file mode 100644 index 0000000000..2d0d580e58 --- /dev/null +++ b/tests/PHPStan/Rules/Variables/data/bug-6112.php @@ -0,0 +1,20 @@ +