file_provider = new FakeFileProvider(); $this->project_analyzer = new ProjectAnalyzer( new TestConfig(), new Providers( $this->file_provider, new FakeParserCacheProvider() ) ); $this->project_analyzer->setPhpVersion('7.3', 'tests'); } public function providerValidCodeParse(): iterable { return [ 'whileCountUpdate' => [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' 3, "1" => 4, "2" => 5, ]; function takesInt(int $s) : void {} foreach ($foo as $i => $b) { takesInt($i); }', ], 'getValidIntStringOffset' => [ 'code' => ' 3, "1" => 4, "2" => 5, ]; $a = "2"; echo $foo["2"]; echo $foo[$a];', ], 'checkStringKeyValueAfterKnownIntStringOffset' => [ 'code' => ' 3, "1" => 4, "2" => 5, ]; $a = "2"; $foo[$a] = 6; function takesInt(int $s) : void {} foreach ($foo as $i => $b) { takesInt($i); }', ], 'regularComparison1' => [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [], 'ignored_issues' => ['MixedOperand', 'MixedAssignment'], ], 'incrementMixedCall' => [ 'code' => 'add(function() use (&$i) : void { if (rand(0, 1)) $i++; }); if ($i === 0) {} }', 'assertions' => [], 'ignored_issues' => ['MissingParamType', 'MixedMethodCall', 'MixedOperand', 'MixedAssignment'], ], 'regularValueReconciliation' => [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => 's = $s; } }', ], 'inArrayAssertionWithSwitch' => [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' 1, A::BAR => 1, A::BAM => 1, ]; if ($this->s !== null && isset($map[$this->s])) {} } }', ], 'noRedundantConditionWithMixed' => [ 'code' => ' [], 'ignored_issues' => ['MissingParamType', 'MixedAssignment'], ], 'numericToStringComparison' => [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' 1, 2 => 2, null => "hello", ]; $b = $a[""];', 'assertions' => [ '$b' => 'string', ], ], 'yodaConditionalsShouldHaveSameOutput1' => [ 'code' => ' false, "to" => false]; public function foo(string ...$things) : void { foreach ($things as $thing) { if ($thing !== "from" && $thing !== "to") { continue; } $this->things[$thing] = !$this->things[$thing]; } } } ', ], 'yodaConditionalsShouldHaveSameOutput2' => [ 'code' => ' false, "to" => false]; public function foo(string ...$things) : void { foreach ($things as $thing) { if ("from" !== $thing && "to" !== $thing) { continue; } $this->things[$thing] = !$this->things[$thing]; } } } ', ], 'supportSingleLiteralType' => [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' 0)) { echo $i; }', ], 'refinePositiveInt' => [ 'code' => ' 0) {}' ], 'assignOpThenCheck' => [ 'code' => ' 0]; if (rand(0, 1)) { $data["e"]++; } if ($data["e"] > 0) {}' ], 'compareToNullImplicitly' => [ 'code' => ' [ 'code' => '= 0; $i--) { $ret = ($ret === 10) ? 1 : $ret + 1; } return $ret; }' ], 'inArrayPreserveNull' => [ 'code' => ' [ 'code' => ' 1)){} }' ], 'returnFromUnionLiteral' => [ 'code' => ' [], 'ignored_issues' => [], 'php_version' => '8.0' ], 'returnFromUnionLiteralNegated' => [ 'code' => ' [], 'ignored_issues' => [], 'php_version' => '8.0' ], 'inArrayInsideLoop' => [ 'code' => ' */ $case_actions = []; if (!in_array(A::ACTION_ONE, $case_actions, true)) {} }' ], 'checkIdenticalArray' => [ 'code' => ' \is_string($value)); if ($array === $filtered) { foreach ($array as $obj) { echo strlen($obj); } } }', 'assertions' => [], 'ignored_issues' => [], 'php_version' => '7.4' ], 'zeroIsNonEmptyString' => [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [ 'code' => ' [ '$a===' => '5', ], ], ]; } public function providerInvalidCodeParse(): iterable { return [ 'neverEqualsType' => [ 'code' => ' 'TypeDoesNotContainType', ], 'alwaysIdenticalType' => [ 'code' => ' 'RedundantCondition', ], 'alwaysNotIdenticalType' => [ 'code' => ' 'RedundantCondition', ], 'neverNotIdenticalType' => [ 'code' => ' 'TypeDoesNotContainType', ], 'neverEqualsFloatType' => [ 'code' => ' 'TypeDoesNotContainType', ], 'alwaysIdenticalFloatType' => [ 'code' => ' 'RedundantCondition', ], 'alwaysNotIdenticalFloatType' => [ 'code' => ' 'RedundantCondition', ], 'inArrayRemoveNull' => [ 'code' => ' 'RedundantCondition', ], 'neverNotIdenticalFloatType' => [ 'code' => ' 'TypeDoesNotContainType', ], 'ifImpossibleString' => [ 'code' => ' 'TypeDoesNotContainType', ], 'arrayOffsetImpossibleValue' => [ 'code' => ' 1, "b" => 2, ]; if ($foo["a"] === 2) {}', 'error_message' => 'TypeDoesNotContainType', ], 'impossibleKeyInForeach' => [ 'code' => ' 3, "1" => 4, "2" => 5, ]; function takesInt(int $s) : void {} foreach ($foo as $i => $b) { if ($i === 3) {} }', 'error_message' => 'TypeDoesNotContainType', ], 'impossibleValueInForeach' => [ 'code' => ' 3, "1" => 4, "2" => 5, ]; function takesInt(int $s) : void {} foreach ($foo as $i => $b) { if ($b === $i) {} }', 'error_message' => 'TypeDoesNotContainType', ], 'invalidIntStringOffset' => [ 'code' => ' 3, "1" => 4, "2" => 5, ]; $a = "3"; echo $foo[$a];', 'error_message' => 'InvalidArrayOffset', ], 'noChangeToVariable' => [ 'code' => ' 'RedundantCondition', ], 'redefinedIntInIfAndImpossbleComparison' => [ 'code' => ' 'TypeDoesNotContainType', ], 'badIfOrAssertionWithSwitch' => [ 'code' => ' 'RedundantCondition', ], 'casedComparison' => [ 'code' => ' 'TypeDoesNotContainType', ], 'compareValueTwice' => [ 'code' => ' 0 && $i > 0) {}', 'error_message' => 'RedundantCondition', ], 'numericStringCoerceToLiteral' => [ 'code' => ' 'ArgumentTypeCoercion' ], 'stringCoercedToNonEmptyString' => [ 'code' => ' 'ArgumentTypeCoercion', ], ]; } }