From a696de6fc186ef6a73c1b3407afdf8de8ad7c595 Mon Sep 17 00:00:00 2001 From: Martin Herndl Date: Wed, 16 Feb 2022 21:33:28 +0100 Subject: [PATCH 1/3] Add missing SpecifiedTypes for count and strlen identical expressions --- src/Analyser/TypeSpecifier.php | 8 ++- tests/PHPStan/Analyser/TypeSpecifierTest.php | 53 ++++++++++++++++++++ tests/PHPStan/Analyser/data/bug-2648.php | 2 +- 3 files changed, 60 insertions(+), 3 deletions(-) diff --git a/src/Analyser/TypeSpecifier.php b/src/Analyser/TypeSpecifier.php index 44bc014119..2d15a06835 100644 --- a/src/Analyser/TypeSpecifier.php +++ b/src/Analyser/TypeSpecifier.php @@ -210,7 +210,9 @@ public function specifyTypesInCondition( } $argType = $scope->getType($exprNode->getArgs()[0]->value); if ($argType->isArray()->yes()) { - return $this->create($exprNode->getArgs()[0]->value, new NonEmptyArrayType(), $newContext, false, $scope); + $leftTypes = $this->create($exprNode, $constantType, $context, false, $scope); + $rightTypes = $this->create($exprNode->getArgs()[0]->value, new NonEmptyArrayType(), $newContext, false, $scope); + return $leftTypes->unionWith($rightTypes); } } } @@ -230,7 +232,9 @@ public function specifyTypesInCondition( } $argType = $scope->getType($exprNode->getArgs()[0]->value); if ($argType instanceof StringType) { - return $this->create($exprNode->getArgs()[0]->value, new AccessoryNonEmptyStringType(), $newContext, false, $scope); + $leftTypes = $this->create($exprNode, $constantType, $context, false, $scope); + $rightTypes = $this->create($exprNode->getArgs()[0]->value, new AccessoryNonEmptyStringType(), $newContext, false, $scope); + return $leftTypes->unionWith($rightTypes); } } } diff --git a/tests/PHPStan/Analyser/TypeSpecifierTest.php b/tests/PHPStan/Analyser/TypeSpecifierTest.php index d86f095c54..e0240946e5 100644 --- a/tests/PHPStan/Analyser/TypeSpecifierTest.php +++ b/tests/PHPStan/Analyser/TypeSpecifierTest.php @@ -1022,6 +1022,59 @@ public function dataCondition(): array ], [], ], + [ + new Expr\BinaryOp\BooleanAnd( + $this->createFunctionCall('is_array', 'foo'), + new Expr\BinaryOp\GreaterOrEqual( + new FuncCall( + new Name('count'), + [new Arg(new Variable('foo'))], + ), + new LNumber(2), + ), + ), + [ + '$foo' => 'non-empty-array', + 'count($foo)' => 'mixed~int|false|null', + ], + [], + ], + [ + new Expr\BinaryOp\BooleanAnd( + $this->createFunctionCall('is_array', 'foo'), + new Identical( + new FuncCall( + new Name('count'), + [new Arg(new Variable('foo'))], + ), + new LNumber(2), + ), + ), + [ + '$foo' => 'non-empty-array', + 'count($foo)' => '2', + ], + [], + ], + [ + new Expr\BinaryOp\BooleanAnd( + $this->createFunctionCall('is_string', 'foo'), + new NotIdentical( + new FuncCall( + new Name('strlen'), + [new Arg(new Variable('foo'))], + ), + new LNumber(0), + ), + ), + [ + '$foo' => 'non-empty-string', + 'strlen($foo)' => '~0', + ], + [ + '$foo' => '~non-empty-string', + ], + ], ]; } diff --git a/tests/PHPStan/Analyser/data/bug-2648.php b/tests/PHPStan/Analyser/data/bug-2648.php index 23797170a1..7e51f4909e 100644 --- a/tests/PHPStan/Analyser/data/bug-2648.php +++ b/tests/PHPStan/Analyser/data/bug-2648.php @@ -37,7 +37,7 @@ public function doBar(array $list): void assertType('int<0, max>', count($list)); if (count($list) === 1) { - assertType('int<1, max>', count($list)); + assertType('1', count($list)); break; } } From 2530153b125c1f284100df36910099382e11f4cb Mon Sep 17 00:00:00 2001 From: Martin Herndl Date: Wed, 16 Feb 2022 21:46:07 +0100 Subject: [PATCH 2/3] Rename type variables to make more sense --- src/Analyser/TypeSpecifier.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Analyser/TypeSpecifier.php b/src/Analyser/TypeSpecifier.php index 2d15a06835..5c9dad5762 100644 --- a/src/Analyser/TypeSpecifier.php +++ b/src/Analyser/TypeSpecifier.php @@ -210,9 +210,9 @@ public function specifyTypesInCondition( } $argType = $scope->getType($exprNode->getArgs()[0]->value); if ($argType->isArray()->yes()) { - $leftTypes = $this->create($exprNode, $constantType, $context, false, $scope); - $rightTypes = $this->create($exprNode->getArgs()[0]->value, new NonEmptyArrayType(), $newContext, false, $scope); - return $leftTypes->unionWith($rightTypes); + $funcTypes = $this->create($exprNode, $constantType, $context, false, $scope); + $valueTypes = $this->create($exprNode->getArgs()[0]->value, new NonEmptyArrayType(), $newContext, false, $scope); + return $funcTypes->unionWith($valueTypes); } } } @@ -232,9 +232,9 @@ public function specifyTypesInCondition( } $argType = $scope->getType($exprNode->getArgs()[0]->value); if ($argType instanceof StringType) { - $leftTypes = $this->create($exprNode, $constantType, $context, false, $scope); - $rightTypes = $this->create($exprNode->getArgs()[0]->value, new AccessoryNonEmptyStringType(), $newContext, false, $scope); - return $leftTypes->unionWith($rightTypes); + $funcTypes = $this->create($exprNode, $constantType, $context, false, $scope); + $valueTypes = $this->create($exprNode->getArgs()[0]->value, new AccessoryNonEmptyStringType(), $newContext, false, $scope); + return $funcTypes->unionWith($valueTypes); } } } From 823f72fe8dc5d3f922434081e42573ba94b904c4 Mon Sep 17 00:00:00 2001 From: Martin Herndl Date: Thu, 17 Feb 2022 20:56:14 +0100 Subject: [PATCH 3/3] Add another testcase for staabm :) --- tests/PHPStan/Analyser/data/bug-2648.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/PHPStan/Analyser/data/bug-2648.php b/tests/PHPStan/Analyser/data/bug-2648.php index 7e51f4909e..11087dfbaa 100644 --- a/tests/PHPStan/Analyser/data/bug-2648.php +++ b/tests/PHPStan/Analyser/data/bug-2648.php @@ -38,6 +38,8 @@ public function doBar(array $list): void if (count($list) === 1) { assertType('1', count($list)); + $list[] = false; + assertType('int<1, max>', count($list)); break; } }