diff --git a/conf/config.level0.neon b/conf/config.level0.neon index f1b32b1ee0..825b0586c6 100644 --- a/conf/config.level0.neon +++ b/conf/config.level0.neon @@ -17,6 +17,7 @@ parametersSchema: rules: - PHPStan\Rules\Arrays\DuplicateKeysInLiteralArraysRule + - PHPStan\Rules\Arrays\EmptyArrayItemRule - PHPStan\Rules\Arrays\OffsetAccessWithoutDimForReadingRule - PHPStan\Rules\Classes\ClassConstantRule - PHPStan\Rules\Classes\DuplicateDeclarationRule diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index e05c123bf7..1ef47b1371 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -1755,10 +1755,10 @@ function (MutatingScope $scope) use ($expr, $nodeCallback, $context): Expression $itemNodes = []; $hasYield = false; foreach ($expr->items as $arrayItem) { + $itemNodes[] = new LiteralArrayItem($scope, $arrayItem); if ($arrayItem === null) { - throw new \PHPStan\ShouldNotHappenException(); + continue; } - $itemNodes[] = new LiteralArrayItem($scope, $arrayItem); $result = $this->processExprNode($arrayItem, $scope, $nodeCallback, $context->enterDeep()); $hasYield = $hasYield || $result->hasYield(); $scope = $result->getScope(); diff --git a/src/Node/LiteralArrayItem.php b/src/Node/LiteralArrayItem.php index 9c06351f09..014f711946 100644 --- a/src/Node/LiteralArrayItem.php +++ b/src/Node/LiteralArrayItem.php @@ -10,9 +10,9 @@ class LiteralArrayItem private Scope $scope; - private ArrayItem $arrayItem; + private ?ArrayItem $arrayItem; - public function __construct(Scope $scope, ArrayItem $arrayItem) + public function __construct(Scope $scope, ?ArrayItem $arrayItem) { $this->scope = $scope; $this->arrayItem = $arrayItem; @@ -23,7 +23,7 @@ public function getScope(): Scope return $this->scope; } - public function getArrayItem(): ArrayItem + public function getArrayItem(): ?ArrayItem { return $this->arrayItem; } diff --git a/src/Rules/Arrays/DuplicateKeysInLiteralArraysRule.php b/src/Rules/Arrays/DuplicateKeysInLiteralArraysRule.php index e972c5fbde..72e17c0525 100644 --- a/src/Rules/Arrays/DuplicateKeysInLiteralArraysRule.php +++ b/src/Rules/Arrays/DuplicateKeysInLiteralArraysRule.php @@ -35,6 +35,9 @@ public function processNode(\PhpParser\Node $node, Scope $scope): array $valueLines = []; foreach ($node->getItemNodes() as $itemNode) { $item = $itemNode->getArrayItem(); + if ($item === null) { + continue; + } if ($item->key === null) { continue; } diff --git a/src/Rules/Arrays/EmptyArrayItemRule.php b/src/Rules/Arrays/EmptyArrayItemRule.php new file mode 100644 index 0000000000..fa55e6a1f9 --- /dev/null +++ b/src/Rules/Arrays/EmptyArrayItemRule.php @@ -0,0 +1,40 @@ + + */ +class EmptyArrayItemRule implements Rule +{ + + public function getNodeType(): string + { + return LiteralArrayNode::class; + } + + public function processNode(Node $node, Scope $scope): array + { + foreach ($node->getItemNodes() as $itemNode) { + $item = $itemNode->getArrayItem(); + if ($item !== null) { + continue; + } + + return [ + RuleErrorBuilder::message('Literal array contains empty item.') + ->nonIgnorable() + ->build(), + ]; + } + + return []; + } + +} diff --git a/src/Rules/Arrays/UnpackIterableInArrayRule.php b/src/Rules/Arrays/UnpackIterableInArrayRule.php index 9362c85cbb..a061c670a2 100644 --- a/src/Rules/Arrays/UnpackIterableInArrayRule.php +++ b/src/Rules/Arrays/UnpackIterableInArrayRule.php @@ -37,6 +37,9 @@ public function processNode(Node $node, Scope $scope): array $errors = []; foreach ($node->getItemNodes() as $itemNode) { $item = $itemNode->getArrayItem(); + if ($item === null) { + continue; + } if (!$item->unpack) { continue; } diff --git a/tests/PHPStan/Rules/Arrays/EmptyArrayItemRuleTest.php b/tests/PHPStan/Rules/Arrays/EmptyArrayItemRuleTest.php new file mode 100644 index 0000000000..51ba629e16 --- /dev/null +++ b/tests/PHPStan/Rules/Arrays/EmptyArrayItemRuleTest.php @@ -0,0 +1,29 @@ + + */ +class EmptyArrayItemRuleTest extends RuleTestCase +{ + + protected function getRule(): Rule + { + return new EmptyArrayItemRule(); + } + + public function testRule(): void + { + $this->analyse([__DIR__ . '/data/empty-array-item.php'], [ + [ + 'Literal array contains empty item.', + 5, + ], + ]); + } + +} diff --git a/tests/PHPStan/Rules/Arrays/data/empty-array-item.php b/tests/PHPStan/Rules/Arrays/data/empty-array-item.php new file mode 100644 index 0000000000..4a08a799a8 --- /dev/null +++ b/tests/PHPStan/Rules/Arrays/data/empty-array-item.php @@ -0,0 +1,7 @@ +