,ignored_issues?:list}> */ public function providerValidCodeParse(): iterable { return [ 'whileVar' => [ 'code' => ' [ '$worked' => 'bool', ], ], 'objectValueWithTwoTypes' => [ 'code' => 'parent = rand(0, 1) ? new A() : new B(); } } function makeA(): A { return new A(); } $a = makeA(); while ($a instanceof A) { $a = $a->parent; }', 'assertions' => [ '$a' => 'B', ], ], 'objectValueWithInstanceofProperty' => [ 'code' => 'parent = rand(0, 1) ? new A() : new B(); } } function makeA(): A { return new A(); } $a = makeA(); while ($a->parent instanceof A) { $a = $a->parent; } $b = $a->parent;', 'assertions' => [ '$a' => 'A', '$b' => 'A|B', ], ], 'objectValueNullable' => [ 'code' => 'parent = rand(0, 1) ? new A() : null; } } function makeA(): A { return new A(); } $a = makeA(); while ($a) { $a = $a->parent; }', 'assertions' => [ '$a' => 'null', ], ], 'objectValueWithAnd' => [ 'code' => 'parent = rand(0, 1) ? new A() : null; } } function makeA(): A { return new A(); } $a = makeA(); while ($a && rand(0, 10) > 5) { $a = $a->parent; }', 'assertions' => [ '$a' => 'A|null', ], ], 'loopWithNoParadox' => [ 'code' => ' [ 'code' => 'bar) {} }', ], 'whileTrueWithBreak' => [ 'code' => ' [ '$a' => 'string', '$b' => 'int', ], ], 'whileWithNotEmptyCheck' => [ 'code' => 'a = rand(0, 1) ? new A : null; } } function takesA(A $a): void {} $a = new A(); while ($a) { takesA($a); $a = $a->a; };', 'assertions' => [ '$a' => 'null', ], ], 'whileInstanceOf' => [ 'code' => 'parent instanceof B) { $a = $a->parent; }', ], 'whileInstanceOfAndNotEmptyCheck' => [ 'code' => 'parent; $foo = rand(0, 1) ? "hello" : null; if (!$foo) { while ($a instanceof B && !$foo) { $a = $a->parent; $foo = rand(0, 1) ? "hello" : null; } }', ], 'noRedundantConditionAfterArrayAssignment' => [ 'code' => ' false]; while (!$data["a"]) { if (rand() % 2 > 0) { $data = ["a" => true]; } }', ], 'additionSubtractionAssignment' => [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => 'foo; } } }', ], 'shouldBeFine' => [ 'code' => 'bar instanceof A) ) { if (!$node instanceof C) { $node = $node->foo; } else { $node = $node->bar; } } }', ], 'comparisonAfterContinue' => [ 'code' => ' [ 'code' => ' 0) {} echo $i === 0;', ], 'noRedundantConditionOnAddedSubtractedInLoop' => [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' $fields */ function changeVarAfterUse(array $values, array $fields): void { foreach ($fields as $field) { if (!isset($values[$field])) { continue; } /** @psalm-suppress MixedAssignment */ $value = $values[$field]; /** @psalm-suppress MixedArgument */ takesString($value); $values[$field] = null; } }', ], 'invalidateWhileAssertion' => [ 'code' => ' [ 'code' => ' [ 'code' => ']+>)|([^<]+)+/isU\'; // pour chaque element trouve : $str = ""; $offset = 0; while (preg_match($reg, $content, $parse, PREG_OFFSET_CAPTURE, $offset)) { $str .= "hello"; unset($parse); } }' ], 'assignToTKeyedArrayListPreserveListness' => [ 'code' => ' */ function foo(string $key): array { $elements = [$key]; while (rand(0, 1)) { $elements[] = $key; } return $elements; }', ], 'reconcilePositiveInt' => [ 'code' => ' 0) { $counter = $counter - 1; } else { $counter = $counter + 1; } }' ], 'nonEmptyListIterationChangeVarWithContinue' => [ 'code' => ' $arr */ function foo(array $arr) : void { while (array_shift($arr)) { if ($arr && $arr[0] === "a") {} if (rand(0, 1)) { $arr = array_merge($arr, ["a"]); continue; } echo "here"; } }' ], 'nonEmptyListIterationChangeVarWithoutContinue' => [ 'code' => ' $arr */ function foo(array $arr) : void { while (array_shift($arr)) { if ($arr && $arr[0] === "a") {} if (rand(0, 1)) { $arr = array_merge($arr, ["a"]); } echo "here"; } }' ], 'propertyAssertionInsideWhile' => [ 'code' => 'a) { $has_changes = true; $this->alter(); } return $has_changes; } public function two(): bool { $has_changes = false; while ($this->a || $this->b) { $has_changes = true; $this->alter(); } return $has_changes; } public function three(): bool { $has_changes = false; while ($this->a || $this->b || $this->c) { $has_changes = true; $this->alter(); } return $has_changes; } public function four(): bool { $has_changes = false; while (($this->a && $this->b) || $this->c) { $has_changes = true; $this->alter(); } return $has_changes; } public function alter() : void { if (rand(0, 1)) { array_pop($this->a); } elseif (rand(0, 1)) { array_pop($this->a); } else { array_pop($this->c); } } }' ], 'propertyAssertionInsideWhileNested' => [ 'code' => 'a || ($this->b && $this->c)) { $has_changes = true; $this->alter(); } return $has_changes; } public function alter() : void { if (rand(0, 1)) { array_pop($this->a); } elseif (rand(0, 1)) { array_pop($this->a); } else { array_pop($this->c); } } }' ], 'ifNestedInsideLoop' => [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => 'foo = 5; } } function bar(A $a): void { $a->foo = null; while (rand(0, 1)) { if (rand(0, 1)) { $a->setFoo(); } elseif ($a->foo !== null) {} } }' ], 'propertyTypeUpdatedInBranchWithBreak' => [ 'code' => 'foo = 5; } } function bar(A $a): void { $a->foo = null; while (rand(0, 1)) { if (rand(0, 1)) { $a->setFoo(); } elseif ($a->foo !== null) { break; } } if ($a->foo !== null) {} }' ], 'whileTrueDontHaveExitPathForReturn' => [ 'code' => ' [ 'code' => 'getResult(); } catch (Throwable $exception) { if ($attempt >= $this->retryAttempts) { throw $exception; } $attempt++; continue; } } } }' ], ]; } /** * @return iterable,php_version?:string}> */ public function providerInvalidCodeParse(): iterable { return [ 'whileTrueNoBreak' => [ 'code' => ' 'UndefinedGlobalVariable', ], 'invalidateByRefAssignmentWithRedundantCondition' => [ 'code' => ' 'RedundantCondition', ], ]; } }