From 5100460a243097b2a59df05789131828289e5957 Mon Sep 17 00:00:00 2001 From: Fabian Blechschmidt Date: Sun, 22 Dec 2019 13:02:49 +0100 Subject: [PATCH 1/3] Implement tests for current functionality --- .../RangeFunctionReturnTypeExtensionTest.php | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 tests/PHPStan/Type/Php/RangeFunctionReturnTypeExtensionTest.php diff --git a/tests/PHPStan/Type/Php/RangeFunctionReturnTypeExtensionTest.php b/tests/PHPStan/Type/Php/RangeFunctionReturnTypeExtensionTest.php new file mode 100644 index 0000000000..e261fc7380 --- /dev/null +++ b/tests/PHPStan/Type/Php/RangeFunctionReturnTypeExtensionTest.php @@ -0,0 +1,131 @@ +createMock(FunctionReflection::class); + $reflectionFunctionMock->expects($this->once())->method('getName')->willReturn('range'); + $this->extension->isFunctionSupported($reflectionFunctionMock); + } + + /** + * @phpstan-param class-string $startType + * @phpstan-param class-string $endType + * @phpstan-param class-string $stepType + * @phpstan-param class-string $returnType + * + * @dataProvider provideStartEndStopAndReturnType + */ + public function testArrayTypesDependingOnStartEndAndStepType( + string $startType, + string $endType, + string $stepType, + string $returnType + ): void { + $scope = $this->createMock(Scope::class); + $scope->method('getType') + ->willReturnOnConsecutiveCalls( + $this->createMock($startType), + $this->createMock($endType), + $this->createMock($stepType) + ); + + $type = $this->extension->getTypeFromFunctionCall($this->createMock(FunctionReflection::class), + $this->createFuncCallMock(), $scope); + + $this->assertInstanceOf(ArrayType::class, $type); + $this->assertInstanceOf(IntegerType::class, $type->getIterableKeyType()); + $this->assertInstanceOf($returnType, $type->getIterableValueType()); + } + + private function createFuncCallMock(): FuncCall + { + return new class('anything', [ + $this->createExprFromType(), + $this->createExprFromType(), + $this->createExprFromType() + ]) extends FuncCall { + }; + } + + /** + * @param string $argType + * + * @phpstan-param class-string $className + * @return Arg + * + */ + private function createExprFromType(): Arg + { + $expr = $this->createMock(Arg::class); + $expr->value = $this->createMock(Expr::class); + + return $expr; + } + + public function provideStartEndStopAndReturnType(): array + { + return [ + 'int,int,int' => [ + 'startType' => IntegerType::class, + 'endType' => IntegerType::class, + 'stepType' => IntegerType::class, + 'returnType' => IntegerType::class, + ], + 'float,int,int' => [ + 'startType' => FloatType::class, + 'endType' => IntegerType::class, + 'stepType' => IntegerType::class, + 'returnType' => FloatType::class, + ], + 'int,float,int' => [ + 'startType' => IntegerType::class, + 'endType' => FloatType::class, + 'stepType' => IntegerType::class, + 'returnType' => IntegerType::class, + ], + 'float,float,float' => [ + 'startType' => FloatType::class, + 'endType' => FloatType::class, + 'stepType' => FloatType::class, + 'returnType' => FloatType::class, + ], + 'float,int,float' => [ + 'startType' => FloatType::class, + 'endType' => IntegerType::class, + 'stepType' => FloatType::class, + 'returnType' => FloatType::class, + ], + 'int,float,float' => [ + 'startType' => IntegerType::class, + 'endType' => FloatType::class, + 'stepType' => FloatType::class, + 'returnType' => FloatType::class, + ], + ]; + } + + protected function setUp(): void + { + $this->extension = new RangeFunctionReturnTypeExtension(); + } +} From 911c948da0569c8be3547c1d9395e61ec4bdbe5d Mon Sep 17 00:00:00 2001 From: Fabian Blechschmidt Date: Sun, 22 Dec 2019 13:03:49 +0100 Subject: [PATCH 2/3] if end and step type is int, the result contains only ints as well --- src/Type/Php/RangeFunctionReturnTypeExtension.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Type/Php/RangeFunctionReturnTypeExtension.php b/src/Type/Php/RangeFunctionReturnTypeExtension.php index 65a7ab3236..26e13e6f2a 100644 --- a/src/Type/Php/RangeFunctionReturnTypeExtension.php +++ b/src/Type/Php/RangeFunctionReturnTypeExtension.php @@ -86,7 +86,6 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, if ( $startType instanceof IntegerType - && $endType instanceof IntegerType && $stepType instanceof IntegerType ) { return new ArrayType(new IntegerType(), new IntegerType()); From 940dd850b5bbfdfc1417128d0f5804a63a1ec246 Mon Sep 17 00:00:00 2001 From: Fabian Blechschmidt Date: Sun, 22 Dec 2019 13:10:11 +0100 Subject: [PATCH 3/3] FIXES https://github.com/phpstan/phpstan/issues/2378 --- src/Type/Php/RangeFunctionReturnTypeExtension.php | 7 +++++++ .../Type/Php/RangeFunctionReturnTypeExtensionTest.php | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/src/Type/Php/RangeFunctionReturnTypeExtension.php b/src/Type/Php/RangeFunctionReturnTypeExtension.php index 26e13e6f2a..50dd6579ec 100644 --- a/src/Type/Php/RangeFunctionReturnTypeExtension.php +++ b/src/Type/Php/RangeFunctionReturnTypeExtension.php @@ -12,6 +12,7 @@ use PHPStan\Type\Constant\ConstantIntegerType; use PHPStan\Type\FloatType; use PHPStan\Type\IntegerType; +use PHPStan\Type\StringType; use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; use PHPStan\Type\TypeUtils; @@ -99,6 +100,12 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, return new ArrayType(new IntegerType(), new FloatType()); } + if ($startType instanceof StringType + && $endType instanceof StringType + && $stepType instanceof IntegerType) { + return new ArrayType(new IntegerType(), new StringType()); + } + return new ArrayType(new IntegerType(), new UnionType([new IntegerType(), new FloatType()])); } diff --git a/tests/PHPStan/Type/Php/RangeFunctionReturnTypeExtensionTest.php b/tests/PHPStan/Type/Php/RangeFunctionReturnTypeExtensionTest.php index e261fc7380..2440353592 100644 --- a/tests/PHPStan/Type/Php/RangeFunctionReturnTypeExtensionTest.php +++ b/tests/PHPStan/Type/Php/RangeFunctionReturnTypeExtensionTest.php @@ -10,6 +10,7 @@ use PHPStan\Type\ArrayType; use PHPStan\Type\FloatType; use PHPStan\Type\IntegerType; +use PHPStan\Type\StringType; use PHPUnit\Framework\MockObject\MockObject; class RangeFunctionReturnTypeExtensionTest extends \PHPStan\Testing\TestCase @@ -121,6 +122,12 @@ public function provideStartEndStopAndReturnType(): array 'stepType' => FloatType::class, 'returnType' => FloatType::class, ], + 'string,string,int' => [ + 'startType' => StringType::class, + 'endType' => StringType::class, + 'stepType' => IntegerType::class, + 'returnType' => StringType::class, + ] ]; }