diff --git a/config/code-complexity-rules.neon b/config/code-complexity-rules.neon index 6e758487..960c080a 100644 --- a/config/code-complexity-rules.neon +++ b/config/code-complexity-rules.neon @@ -4,3 +4,4 @@ rules: - Symplify\PHPStanRules\Rules\Complexity\NoArrayMapWithArrayCallableRule - Symplify\PHPStanRules\Rules\Complexity\NoConstructorOverrideRule - Symplify\PHPStanRules\Rules\Complexity\ForeachCeptionRule + - Symplify\PHPStanRules\Rules\Explicit\NoMissingVariableDimFetchRule diff --git a/src/Enum/RuleIdentifier.php b/src/Enum/RuleIdentifier.php index 9a749ea2..0cb7d91c 100644 --- a/src/Enum/RuleIdentifier.php +++ b/src/Enum/RuleIdentifier.php @@ -77,4 +77,6 @@ final class RuleIdentifier public const NO_ARRAY_MAP_WITH_ARRAY_CALLABLE = 'symplify.noArrayMapWithArrayCallable'; public const RULE_IDENTIFIER = 'symplify.foreachCeption'; + + public const NO_MISSING_VARIABLE_DIM_FETCH = 'symplify.noMissingVariableDimFetch'; } diff --git a/src/Rules/Explicit/NoMissingVariableDimFetchRule.php b/src/Rules/Explicit/NoMissingVariableDimFetchRule.php new file mode 100644 index 00000000..5275adf1 --- /dev/null +++ b/src/Rules/Explicit/NoMissingVariableDimFetchRule.php @@ -0,0 +1,63 @@ + + * @see \Symplify\PHPStanRules\Tests\Rules\Explicit\NoMissingVariableDimFetchRule\NoMissingVariableDimFetchRuleTest + */ +final class NoMissingVariableDimFetchRule implements Rule +{ + /** + * @api + * @var string + */ + public const ERROR_MESSAGE = 'Dim fetch assign variable is missing, create it first'; + + public function getNodeType(): string + { + return Assign::class; + } + + /** + * @param Assign $node + */ + public function processNode(Node $node, Scope $scope): array + { + if (! $node->var instanceof ArrayDimFetch) { + return []; + } + + $arrayDimFetch = $node->var; + if (! $arrayDimFetch->var instanceof Variable) { + return []; + } + + $dimFetchVariable = $arrayDimFetch->var; + + if (! is_string($dimFetchVariable->name)) { + return []; + } + + if (! $scope->hasVariableType($dimFetchVariable->name)->no()) { + return []; + } + + $identifierRuleError = RuleErrorBuilder::message(self::ERROR_MESSAGE) + ->identifier(RuleIdentifier::NO_MISSING_VARIABLE_DIM_FETCH) + ->build(); + + return [$identifierRuleError]; + } +} diff --git a/tests/Rules/Explicit/NoMissingVariableDimFetchRule/Fixture/MissingDimFetch.php b/tests/Rules/Explicit/NoMissingVariableDimFetchRule/Fixture/MissingDimFetch.php new file mode 100644 index 00000000..ddc2bae5 --- /dev/null +++ b/tests/Rules/Explicit/NoMissingVariableDimFetchRule/Fixture/MissingDimFetch.php @@ -0,0 +1,11 @@ +someProperty['key'] = 'value'; + } +} diff --git a/tests/Rules/Explicit/NoMissingVariableDimFetchRule/NoMissingVariableDimFetchRuleTest.php b/tests/Rules/Explicit/NoMissingVariableDimFetchRule/NoMissingVariableDimFetchRuleTest.php new file mode 100644 index 00000000..9bd4bcf8 --- /dev/null +++ b/tests/Rules/Explicit/NoMissingVariableDimFetchRule/NoMissingVariableDimFetchRuleTest.php @@ -0,0 +1,43 @@ +analyse([$filePath], $expectedErrorsWithLines); + } + + public static function provideData(): Iterator + { + yield [__DIR__ . '/Fixture/SkipDefinedVariable.php', []]; + yield [__DIR__ . '/Fixture/SkipProperty.php', []]; + + yield [__DIR__ . '/Fixture/MissingDimFetch.php', [ + [NoMissingVariableDimFetchRule::ERROR_MESSAGE, 9], + ]]; + } + + /** + * @return string[] + */ + public static function getAdditionalConfigFiles(): array + { + return [__DIR__ . '/config/configured_rule.neon']; + } + + protected function getRule(): Rule + { + return self::getContainer()->getByType(NoMissingVariableDimFetchRule::class); + } +} diff --git a/tests/Rules/Explicit/NoMissingVariableDimFetchRule/config/configured_rule.neon b/tests/Rules/Explicit/NoMissingVariableDimFetchRule/config/configured_rule.neon new file mode 100644 index 00000000..a3a861da --- /dev/null +++ b/tests/Rules/Explicit/NoMissingVariableDimFetchRule/config/configured_rule.neon @@ -0,0 +1,5 @@ +includes: + - ../../../../config/included_services.neon + +rules: + - Symplify\PHPStanRules\Rules\Explicit\NoMissingVariableDimFetchRule