From 83e1163c3f9b447fce734fbd01b65e6fb33632c6 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Sat, 23 Mar 2019 18:39:00 +0100 Subject: [PATCH] Yield type check --- conf/config.level3.neon | 5 ++ src/Rules/Generators/YieldFromTypeRule.php | 2 - src/Rules/Generators/YieldTypeRule.php | 85 +++++++++++++++++++ .../Rules/Generators/YieldTypeRuleTest.php | 47 ++++++++++ tests/PHPStan/Rules/Generators/data/yield.php | 20 +++++ 5 files changed, 157 insertions(+), 2 deletions(-) create mode 100644 src/Rules/Generators/YieldTypeRule.php create mode 100644 tests/PHPStan/Rules/Generators/YieldTypeRuleTest.php create mode 100644 tests/PHPStan/Rules/Generators/data/yield.php diff --git a/conf/config.level3.neon b/conf/config.level3.neon index e95480beb3..0c84f17491 100644 --- a/conf/config.level3.neon +++ b/conf/config.level3.neon @@ -23,6 +23,8 @@ conditionalTags: phpstan.rules.rule: %featureToggles.yieldRules% PHPStan\Rules\Generators\YieldInGeneratorRule: phpstan.rules.rule: %featureToggles.yieldRules% + PHPStan\Rules\Generators\YieldTypeRule: + phpstan.rules.rule: %featureToggles.yieldRules% services: - @@ -63,6 +65,9 @@ services: arguments: reportMaybes: %reportMaybes% + - + class: PHPStan\Rules\Generators\YieldTypeRule + - class: PHPStan\Rules\Methods\MethodSignatureRule arguments: diff --git a/src/Rules/Generators/YieldFromTypeRule.php b/src/Rules/Generators/YieldFromTypeRule.php index 82663d423c..51c20dbc27 100644 --- a/src/Rules/Generators/YieldFromTypeRule.php +++ b/src/Rules/Generators/YieldFromTypeRule.php @@ -88,8 +88,6 @@ public function processNode(Node $node, Scope $scope): array ); } - // TODO test - return $messages; } diff --git a/src/Rules/Generators/YieldTypeRule.php b/src/Rules/Generators/YieldTypeRule.php new file mode 100644 index 0000000000..974fbb866d --- /dev/null +++ b/src/Rules/Generators/YieldTypeRule.php @@ -0,0 +1,85 @@ +ruleLevelHelper = $ruleLevelHelper; + } + + public function getNodeType(): string + { + return Node\Expr\Yield_::class; + } + + /** + * @param Node\Expr\Yield_ $node + * @param Scope $scope + * @return string[] + */ + public function processNode(Node $node, Scope $scope): array + { + $anonymousFunctionReturnType = $scope->getAnonymousFunctionReturnType(); + $scopeFunction = $scope->getFunction(); + if ($anonymousFunctionReturnType !== null) { + $returnType = $anonymousFunctionReturnType; + } elseif ($scopeFunction !== null) { + $returnType = ParametersAcceptorSelector::selectSingle($scopeFunction->getVariants())->getReturnType(); + } else { + return []; // already reported by YieldInGeneratorRule + } + + if ($returnType instanceof MixedType) { + return []; + } + + if ($node->key === null) { + $keyType = new IntegerType(); + } else { + $keyType = $scope->getType($node->key); + } + + if ($node->value === null) { + $valueType = new NullType(); + } else { + $valueType = $scope->getType($node->value); + } + + $messages = []; + if (!$this->ruleLevelHelper->accepts($returnType->getIterableKeyType(), $keyType, $scope->isDeclareStrictTypes())) { + $messages[] = sprintf( + 'Generator expects key type %s, %s given.', + $returnType->getIterableKeyType()->describe(VerbosityLevel::typeOnly()), + $keyType->describe(VerbosityLevel::typeOnly()) + ); + } + if (!$this->ruleLevelHelper->accepts($returnType->getIterableValueType(), $valueType, $scope->isDeclareStrictTypes())) { + $messages[] = sprintf( + 'Generator expects value type %s, %s given.', + $returnType->getIterableValueType()->describe(VerbosityLevel::typeOnly()), + $valueType->describe(VerbosityLevel::typeOnly()) + ); + } + + return $messages; + } + +} diff --git a/tests/PHPStan/Rules/Generators/YieldTypeRuleTest.php b/tests/PHPStan/Rules/Generators/YieldTypeRuleTest.php new file mode 100644 index 0000000000..1143832411 --- /dev/null +++ b/tests/PHPStan/Rules/Generators/YieldTypeRuleTest.php @@ -0,0 +1,47 @@ +createBroker(), true, false, true)); + } + + public function testRule(): void + { + $this->analyse([__DIR__ . '/data/yield.php'], [ + [ + 'Generator expects value type int, string given.', + 14, + ], + [ + 'Generator expects key type string, int given.', + 15, + ], + [ + 'Generator expects value type int, null given.', + 15, + ], + [ + 'Generator expects key type string, int given.', + 16, + ], + [ + 'Generator expects key type string, int given.', + 17, + ], + [ + 'Generator expects value type int, string given.', + 17, + ], + ]); + } + +} diff --git a/tests/PHPStan/Rules/Generators/data/yield.php b/tests/PHPStan/Rules/Generators/data/yield.php new file mode 100644 index 0000000000..88cd36450d --- /dev/null +++ b/tests/PHPStan/Rules/Generators/data/yield.php @@ -0,0 +1,20 @@ + + */ + public function doFoo(): \Generator + { + yield 'foo' => 1; + yield 'foo' => 'bar'; + yield; + yield 1; + yield 'foo'; + } + +}