From 180b2af22fbefe58e801f313a75acf4ede71b5f4 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Fri, 4 Feb 2022 10:03:49 +0100 Subject: [PATCH 1/3] refactor ImplodeFunctionReturnTypeExtension --- .../ImplodeFunctionReturnTypeExtension.php | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/Type/Php/ImplodeFunctionReturnTypeExtension.php b/src/Type/Php/ImplodeFunctionReturnTypeExtension.php index 1c59bbbf23..2affefa220 100644 --- a/src/Type/Php/ImplodeFunctionReturnTypeExtension.php +++ b/src/Type/Php/ImplodeFunctionReturnTypeExtension.php @@ -7,6 +7,7 @@ use PHPStan\Reflection\FunctionReflection; use PHPStan\Type\Accessory\AccessoryLiteralStringType; use PHPStan\Type\Accessory\AccessoryNonEmptyStringType; +use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\DynamicFunctionReturnTypeExtension; use PHPStan\Type\IntersectionType; use PHPStan\Type\StringType; @@ -35,20 +36,7 @@ public function getTypeFromFunctionCall( if (count($args) === 1) { $argType = $scope->getType($args[0]->value); if ($argType->isArray()->yes()) { - $accessoryTypes = []; - if ($argType->isIterableAtLeastOnce()->yes() && $argType->getIterableValueType()->isNonEmptyString()->yes()) { - $accessoryTypes[] = new AccessoryNonEmptyStringType(); - } - if ($argType->getIterableValueType()->isLiteralString()->yes()) { - $accessoryTypes[] = new AccessoryLiteralStringType(); - } - - if (count($accessoryTypes) > 0) { - $accessoryTypes[] = new StringType(); - return new IntersectionType($accessoryTypes); - } - - return new StringType(); + return $this->implode($argType, new ConstantStringType('')); } } @@ -58,6 +46,12 @@ public function getTypeFromFunctionCall( $separatorType = $scope->getType($args[0]->value); $arrayType = $scope->getType($args[1]->value); + + return $this->implode($arrayType, $separatorType); + } + + private function implode(Type $arrayType, Type $separatorType): Type + { $accessoryTypes = []; if ($arrayType->isIterableAtLeastOnce()->yes()) { if ($arrayType->getIterableValueType()->isNonEmptyString()->yes() || $separatorType->isNonEmptyString()->yes()) { From 2611a9375dcd6fd6d624a6284ae1f2dbe2215b01 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Fri, 4 Feb 2022 10:22:16 +0100 Subject: [PATCH 2/3] implemented constant-type inference in ImplodeFunctionReturnTypeExtension --- .../ImplodeFunctionReturnTypeExtension.php | 24 +++++++++++++++++++ .../Analyser/NodeScopeResolverTest.php | 3 +++ tests/PHPStan/Analyser/data/bug-5219.php | 4 ++-- tests/PHPStan/Analyser/data/implode.php | 24 +++++++++++++++++++ .../Analyser/data/non-empty-string.php | 8 +++---- 5 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 tests/PHPStan/Analyser/data/implode.php diff --git a/src/Type/Php/ImplodeFunctionReturnTypeExtension.php b/src/Type/Php/ImplodeFunctionReturnTypeExtension.php index 2affefa220..4d0fc70749 100644 --- a/src/Type/Php/ImplodeFunctionReturnTypeExtension.php +++ b/src/Type/Php/ImplodeFunctionReturnTypeExtension.php @@ -7,7 +7,9 @@ use PHPStan\Reflection\FunctionReflection; use PHPStan\Type\Accessory\AccessoryLiteralStringType; use PHPStan\Type\Accessory\AccessoryNonEmptyStringType; +use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\Constant\ConstantStringType; +use PHPStan\Type\ConstantScalarType; use PHPStan\Type\DynamicFunctionReturnTypeExtension; use PHPStan\Type\IntersectionType; use PHPStan\Type\StringType; @@ -52,6 +54,13 @@ public function getTypeFromFunctionCall( private function implode(Type $arrayType, Type $separatorType): Type { + if ($arrayType instanceof ConstantArrayType && $separatorType instanceof ConstantStringType) { + $constantType = $this->inferConstantType($arrayType, $separatorType); + if ($constantType !== null) { + return $constantType; + } + } + $accessoryTypes = []; if ($arrayType->isIterableAtLeastOnce()->yes()) { if ($arrayType->getIterableValueType()->isNonEmptyString()->yes() || $separatorType->isNonEmptyString()->yes()) { @@ -71,4 +80,19 @@ private function implode(Type $arrayType, Type $separatorType): Type return new StringType(); } + private function inferConstantType(ConstantArrayType $arrayType, ConstantStringType $separatorType):?Type + { + $valueTypes = $arrayType->getValueTypes(); + + $arrayValues = []; + foreach($valueTypes as $valueType) { + if (!$valueType instanceof ConstantScalarType) { + return null; + } + $arrayValues[] = $valueType->getValue(); + } + + return new ConstantStringType(implode($separatorType->getValue(), $arrayValues)); + } + } diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index aaf6bc04b3..9f1ed697ec 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -14,6 +14,9 @@ class NodeScopeResolverTest extends TypeInferenceTestCase public function dataFileAsserts(): iterable { + require_once __DIR__ . '/data/implode.php'; + yield from $this->gatherAssertTypes(__DIR__ . '/data/implode.php'); + require_once __DIR__ . '/data/bug2574.php'; yield from $this->gatherAssertTypes(__DIR__ . '/data/bug2574.php'); diff --git a/tests/PHPStan/Analyser/data/bug-5219.php b/tests/PHPStan/Analyser/data/bug-5219.php index cf7e4c0485..91ce62bb02 100644 --- a/tests/PHPStan/Analyser/data/bug-5219.php +++ b/tests/PHPStan/Analyser/data/bug-5219.php @@ -7,9 +7,9 @@ class HelloWorld { - protected function foo(string $message): void + protected function foo(string $message, string $x): void { - $header = sprintf('%s-%s', '', implode('-', ['x'])); + $header = sprintf('%s-%s', '', implode('-', [$x])); assertType('non-empty-string', $header); assertType('non-empty-array', [$header => $message]); diff --git a/tests/PHPStan/Analyser/data/implode.php b/tests/PHPStan/Analyser/data/implode.php new file mode 100644 index 0000000000..b2e2b9aa7c --- /dev/null +++ b/tests/PHPStan/Analyser/data/implode.php @@ -0,0 +1,24 @@ + Date: Fri, 4 Feb 2022 10:29:02 +0100 Subject: [PATCH 3/3] cs --- src/Type/Php/ImplodeFunctionReturnTypeExtension.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Type/Php/ImplodeFunctionReturnTypeExtension.php b/src/Type/Php/ImplodeFunctionReturnTypeExtension.php index 4d0fc70749..cd324b00b4 100644 --- a/src/Type/Php/ImplodeFunctionReturnTypeExtension.php +++ b/src/Type/Php/ImplodeFunctionReturnTypeExtension.php @@ -15,6 +15,7 @@ use PHPStan\Type\StringType; use PHPStan\Type\Type; use function count; +use function implode; use function in_array; class ImplodeFunctionReturnTypeExtension implements DynamicFunctionReturnTypeExtension @@ -80,12 +81,12 @@ private function implode(Type $arrayType, Type $separatorType): Type return new StringType(); } - private function inferConstantType(ConstantArrayType $arrayType, ConstantStringType $separatorType):?Type + private function inferConstantType(ConstantArrayType $arrayType, ConstantStringType $separatorType): ?Type { $valueTypes = $arrayType->getValueTypes(); $arrayValues = []; - foreach($valueTypes as $valueType) { + foreach ($valueTypes as $valueType) { if (!$valueType instanceof ConstantScalarType) { return null; }