From 32b22943f5cafc2ebe09acbe7ece76c47ff0af65 Mon Sep 17 00:00:00 2001 From: Kevin Ullyott Date: Sat, 1 Nov 2025 15:10:01 -0400 Subject: [PATCH 1/9] Add BooleanArgumentFlag rule and related configurations - Implement BooleanArgumentFlag rule to detect boolean parameters indicating multiple responsibilities. - Introduce configuration options for ignored classes and ignore patterns. - Update README with new rule documentation. - Add tests for various scenarios including ignored classes and patterns. Signed-off-by: Kevin Ullyott --- README.md | 1 + config/extension.neon | 14 +- docs/BooleanArgumentFlag.md | 283 ++++++++++++++++++ .../BooleanArgumentFlagRule.php | 152 ++++++++++ src/Rules/BooleanArgumentFlag/Config.php | 28 ++ .../AllOptionsTrueTest.php | 34 +++ .../DefaultOptionsTest.php | 49 +++ .../Fixture/AllOptions.php | 41 +++ .../Fixture/DefaultExample.php | 78 +++++ .../Fixture/IgnorePattern.php | 46 +++ .../Fixture/IgnoredClasses.php | 35 +++ .../BooleanArgumentFlag/IgnorePatternTest.php | 36 +++ .../IgnoredInClassesTest.php | 34 +++ .../config/all-options.neon | 12 + .../BooleanArgumentFlag/config/default.neon | 11 + .../config/ignore-pattern.neon | 11 + .../config/ignored-classes.neon | 12 + 17 files changed, 876 insertions(+), 1 deletion(-) create mode 100644 docs/BooleanArgumentFlag.md create mode 100644 src/Rules/BooleanArgumentFlag/BooleanArgumentFlagRule.php create mode 100644 src/Rules/BooleanArgumentFlag/Config.php create mode 100644 tests/Rules/BooleanArgumentFlag/AllOptionsTrueTest.php create mode 100644 tests/Rules/BooleanArgumentFlag/DefaultOptionsTest.php create mode 100644 tests/Rules/BooleanArgumentFlag/Fixture/AllOptions.php create mode 100644 tests/Rules/BooleanArgumentFlag/Fixture/DefaultExample.php create mode 100644 tests/Rules/BooleanArgumentFlag/Fixture/IgnorePattern.php create mode 100644 tests/Rules/BooleanArgumentFlag/Fixture/IgnoredClasses.php create mode 100644 tests/Rules/BooleanArgumentFlag/IgnorePatternTest.php create mode 100644 tests/Rules/BooleanArgumentFlag/IgnoredInClassesTest.php create mode 100644 tests/Rules/BooleanArgumentFlag/config/all-options.neon create mode 100644 tests/Rules/BooleanArgumentFlag/config/default.neon create mode 100644 tests/Rules/BooleanArgumentFlag/config/ignore-pattern.neon create mode 100644 tests/Rules/BooleanArgumentFlag/config/ignored-classes.neon diff --git a/README.md b/README.md index c6bedbc..afde10f 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,7 @@ parameters: | Rule | Description | Target | |------|-------------|---------| +| **[BooleanArgumentFlag](docs/BooleanArgumentFlag.md)** | Detects boolean parameters in functions and methods that may indicate multiple responsibilities | Methods, Functions, Closures | | **[LongVariable](docs/LongVariable.md)** | Limits variable name length | Variables | | **[MissingClosureParameterTypehint](docs/MissingClosureParameterTypehint.md)** | Requires type hints on closure parameters | Closures | | **[ShortMethodName](docs/ShortMethodName.md)** | Enforces minimum method name length | Methods | diff --git a/config/extension.neon b/config/extension.neon index c5ade24..f47bcfe 100644 --- a/config/extension.neon +++ b/config/extension.neon @@ -60,6 +60,10 @@ parametersSchema: number_of_children: structure([ maximum: int(), ]), + boolean_argument_flag: structure([ + ignored_in_classes: listOf(string()), + ignore_pattern: string(), + ]), ]) # default parameters @@ -112,6 +116,9 @@ parameters: elseif_allowed: true number_of_children: maximum: 15 + boolean_argument_flag: + ignored_in_classes: [] + ignore_pattern: '' services: - @@ -190,4 +197,9 @@ services: - factory: Orrison\MeliorStan\Rules\NumberOfChildren\Config arguments: - - %meliorstan.number_of_children.maximum% \ No newline at end of file + - %meliorstan.number_of_children.maximum% + - + factory: Orrison\MeliorStan\Rules\BooleanArgumentFlag\Config + arguments: + - %meliorstan.boolean_argument_flag.ignored_in_classes% + - %meliorstan.boolean_argument_flag.ignore_pattern% \ No newline at end of file diff --git a/docs/BooleanArgumentFlag.md b/docs/BooleanArgumentFlag.md new file mode 100644 index 0000000..31edd5c --- /dev/null +++ b/docs/BooleanArgumentFlag.md @@ -0,0 +1,283 @@ +# BooleanArgumentFlag + +Boolean parameters in functions and methods are often indicators that a function is doing more than one thing, violating the Single Responsibility Principle. + +This rule detects boolean parameters in class methods, static methods, named functions, and closures. When a function or method accepts a boolean flag to control its behavior, it typically means the function has at least two different execution paths, suggesting it should be split into separate functions. + +## Configuration + +This rule supports the following configuration options: + +### `ignored_in_classes` +- **Type**: `array` +- **Default**: `[]` +- **Description**: A list of fully-qualified class names where boolean parameters in methods are allowed. Only class methods are affected by this configuration; functions and closures are not ignored even when defined within ignored classes. + +```php +// Example: 'App\\Service\\ConfigurationService' +``` + +### `ignore_pattern` +- **Type**: `string` +- **Default**: `''` +- **Description**: A regular expression pattern to match function or method names that should be ignored. This applies to class methods, functions, and closures with names. Common patterns include setter methods or boolean query methods. + +```php +// Example: '/^set/' to ignore all setter methods +// Example: '/^(is|has|should)/' to ignore boolean query methods +``` + +## Usage + +Add the rule to your PHPStan configuration: + +```neon +includes: + - vendor/orrison/meliorstan/config/extension.neon + +rules: + - Orrison\MeliorStan\Rules\BooleanArgumentFlag\BooleanArgumentFlagRule + +parameters: + meliorstan: + boolean_argument_flag: + ignored_in_classes: [] + ignore_pattern: '' +``` + +## Examples + +### Default Configuration + +```php +activateUser($user); + } else { + $this->deactivateUser($user); + } + } + + // ✗ Error: Method "UserService::save()" has boolean parameter "$validate" which may indicate the method has multiple responsibilities. + public function save(User $user, bool $validate) + { + } + + // ✗ Error: Method "UserService::handleNullable()" has boolean parameter "$option" which may indicate the method has multiple responsibilities. + public function handleNullable(?bool $option) + { + } + + // ✗ Error: Method "UserService::unionType()" has boolean parameter "$flag" which may indicate the method has multiple responsibilities. + public function unionType(bool|null $flag) + { + } + + // ✓ Valid: No boolean parameters + public function getUser(int $id): User + { + } +} + +// ✗ Error: Function "processData()" has boolean parameter "$flag" which may indicate the function has multiple responsibilities. +function processData(array $data, bool $flag) +{ +} + +// ✗ Error: Closure has boolean parameter "$enabled" which may indicate the closure has multiple responsibilities. +$handler = function (string $name, bool $enabled) { +}; +``` + +### Configuration Examples + +#### Ignored Classes + +Useful for specific classes where boolean flags are acceptable (e.g., configuration builders, option classes). + +```neon +parameters: + meliorstan: + boolean_argument_flag: + ignored_in_classes: + - 'App\Service\ConfigurationBuilder' + - 'App\Options\UserOptions' + ignore_pattern: '' +``` + +```php + + */ +class BooleanArgumentFlagRule implements Rule +{ + public const ERROR_MESSAGE_TEMPLATE_METHOD = 'Method "%s::%s()" has boolean parameter "$%s" which may indicate the method has multiple responsibilities.'; + + public const ERROR_MESSAGE_TEMPLATE_FUNCTION = 'Function "%s()" has boolean parameter "$%s" which may indicate the function has multiple responsibilities.'; + + public const ERROR_MESSAGE_TEMPLATE_CLOSURE = 'Closure has boolean parameter "$%s" which may indicate the closure has multiple responsibilities.'; + + public function __construct( + protected Config $config, + ) { + } + + public function getNodeType(): string + { + return FunctionLike::class; + } + + public function processNode(Node $node, Scope $scope): array + { + // Check if this is a class method and if the class should be ignored + if ($node instanceof ClassMethod && $this->shouldIgnoreByClass($scope)) { + return []; + } + + // Get the function/method name for pattern matching + $functionName = $this->getFunctionName($node); + + if ($functionName !== null && $this->shouldIgnoreByPattern($functionName)) { + return []; + } + + $errors = []; + + // Check each parameter for boolean types + foreach ($node->getParams() as $param) { + if ($this->isBooleanType($param->type)) { + $paramName = $param->var instanceof Node\Expr\Variable && is_string($param->var->name) + ? $param->var->name + : 'unknown'; + + $errors[] = RuleErrorBuilder::message($this->buildErrorMessage($node, $scope, $paramName)) + ->identifier('MeliorStan.booleanArgumentFlag') + ->build(); + } + } + + return $errors; + } + + protected function shouldIgnoreByClass(Scope $scope): bool + { + $classReflection = $scope->getClassReflection(); + + if ($classReflection === null) { + return false; + } + + $className = $classReflection->getName(); + $ignoredClasses = $this->config->getIgnoredInClasses(); + + return in_array($className, $ignoredClasses, true); + } + + protected function shouldIgnoreByPattern(string $functionName): bool + { + $pattern = $this->config->getIgnorePattern(); + + if ($pattern === '') { + return false; + } + + return @preg_match($pattern, $functionName) === 1; + } + + protected function getFunctionName(FunctionLike $node): ?string + { + if ($node instanceof ClassMethod || $node instanceof Function_) { + return $node->name->toString(); + } + + // Closures don't have names + return null; + } + + protected function isBooleanType(?Node $type): bool + { + if ($type === null) { + return false; + } + + // Direct bool identifier + if ($type instanceof Identifier) { + return $type->toString() === 'bool'; + } + + // Nullable type: ?bool + if ($type instanceof NullableType) { + return $this->isBooleanType($type->type); + } + + // Union types: bool|null, int|bool, etc. + if ($type instanceof UnionType) { + foreach ($type->types as $unionType) { + if ($this->isBooleanType($unionType)) { + return true; + } + } + } + + return false; + } + + protected function buildErrorMessage(FunctionLike $node, Scope $scope, string $paramName): string + { + if ($node instanceof ClassMethod) { + $classReflection = $scope->getClassReflection(); + $className = $classReflection !== null ? $classReflection->getName() : 'Unknown'; + $methodName = $node->name->toString(); + + return sprintf(self::ERROR_MESSAGE_TEMPLATE_METHOD, $className, $methodName, $paramName); + } + + if ($node instanceof Function_) { + $functionName = $node->name->toString(); + + return sprintf(self::ERROR_MESSAGE_TEMPLATE_FUNCTION, $functionName, $paramName); + } + + // Closure + return sprintf(self::ERROR_MESSAGE_TEMPLATE_CLOSURE, $paramName); + } +} diff --git a/src/Rules/BooleanArgumentFlag/Config.php b/src/Rules/BooleanArgumentFlag/Config.php new file mode 100644 index 0000000..8bff7a5 --- /dev/null +++ b/src/Rules/BooleanArgumentFlag/Config.php @@ -0,0 +1,28 @@ + $ignoredInClasses + * @param string $ignorePattern + */ + public function __construct( + protected array $ignoredInClasses, + protected string $ignorePattern, + ) {} + + /** + * @return array + */ + public function getIgnoredInClasses(): array + { + return $this->ignoredInClasses; + } + + public function getIgnorePattern(): string + { + return $this->ignorePattern; + } +} diff --git a/tests/Rules/BooleanArgumentFlag/AllOptionsTrueTest.php b/tests/Rules/BooleanArgumentFlag/AllOptionsTrueTest.php new file mode 100644 index 0000000..17eb49b --- /dev/null +++ b/tests/Rules/BooleanArgumentFlag/AllOptionsTrueTest.php @@ -0,0 +1,34 @@ + + */ +class AllOptionsTrueTest extends RuleTestCase +{ + public function testAllOptions(): void + { + $this->analyse([ + __DIR__ . '/Fixture/AllOptions.php', + ], [ + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\AllOptionsNotIgnored', 'handle', 'flag'), 18], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\AllOptionsNotIgnored', 'processData', 'flag'), 20], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_FUNCTION, 'processConfig', 'value'), 25], + ]); + } + + public static function getAdditionalConfigFiles(): array + { + return [__DIR__ . '/config/all-options.neon']; + } + + protected function getRule(): Rule + { + return self::getContainer()->getByType(BooleanArgumentFlagRule::class); + } +} diff --git a/tests/Rules/BooleanArgumentFlag/DefaultOptionsTest.php b/tests/Rules/BooleanArgumentFlag/DefaultOptionsTest.php new file mode 100644 index 0000000..f90f7bb --- /dev/null +++ b/tests/Rules/BooleanArgumentFlag/DefaultOptionsTest.php @@ -0,0 +1,49 @@ + + */ +class DefaultOptionsTest extends RuleTestCase +{ + public function testDefaultExample(): void + { + $this->analyse([ + __DIR__ . '/Fixture/DefaultExample.php', + ], [ + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', '__construct', 'debug'), 7], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'processWithFlag', 'flag'), 9], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'handleNullable', 'option'), 11], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'unionType', 'value'), 13], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'multiUnion', 'mixed'), 15], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'staticMethod', 'enabled'), 19], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', '__set', 'value'), 21], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'multipleParams', 'enabled'), 23], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'multipleBools', 'first'), 25], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'multipleBools', 'second'), 25], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'protectedMethod', 'flag'), 27], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'privateMethod', 'flag'), 29], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_FUNCTION, 'namedFunction', 'flag'), 32], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'flag'), 36], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'flag'), 40], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'enabled'), 44], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'outer'), 46], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'inner'), 47], + ]); + } + + public static function getAdditionalConfigFiles(): array + { + return [__DIR__ . '/config/default.neon']; + } + + protected function getRule(): Rule + { + return self::getContainer()->getByType(BooleanArgumentFlagRule::class); + } +} diff --git a/tests/Rules/BooleanArgumentFlag/Fixture/AllOptions.php b/tests/Rules/BooleanArgumentFlag/Fixture/AllOptions.php new file mode 100644 index 0000000..96034a5 --- /dev/null +++ b/tests/Rules/BooleanArgumentFlag/Fixture/AllOptions.php @@ -0,0 +1,41 @@ + $flag; + +$validArrow = fn (string $name) => $name; + +$closureMultipleParams = function (string $name, bool $enabled) {}; + +$nestedClosure = function (bool $outer) { + return function (bool $inner) { + return $inner; + }; +}; diff --git a/tests/Rules/BooleanArgumentFlag/Fixture/IgnorePattern.php b/tests/Rules/BooleanArgumentFlag/Fixture/IgnorePattern.php new file mode 100644 index 0000000..010ace2 --- /dev/null +++ b/tests/Rules/BooleanArgumentFlag/Fixture/IgnorePattern.php @@ -0,0 +1,46 @@ + $value; + +$processorClosure = fn (bool $value) => $value; diff --git a/tests/Rules/BooleanArgumentFlag/Fixture/IgnoredClasses.php b/tests/Rules/BooleanArgumentFlag/Fixture/IgnoredClasses.php new file mode 100644 index 0000000..4cfba0d --- /dev/null +++ b/tests/Rules/BooleanArgumentFlag/Fixture/IgnoredClasses.php @@ -0,0 +1,35 @@ + + */ +class IgnorePatternTest extends RuleTestCase +{ + public function testIgnorePattern(): void + { + $this->analyse([ + __DIR__ . '/Fixture/IgnorePattern.php', + ], [ + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\IgnorePatternExample', 'processWithFlag', 'flag'), 13], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\IgnorePatternExample', 'handleBool', 'value'), 15], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_FUNCTION, 'processGlobal', 'value'), 24], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'value'), 26], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'value'), 28], + ]); + } + + public static function getAdditionalConfigFiles(): array + { + return [__DIR__ . '/config/ignore-pattern.neon']; + } + + protected function getRule(): Rule + { + return self::getContainer()->getByType(BooleanArgumentFlagRule::class); + } +} diff --git a/tests/Rules/BooleanArgumentFlag/IgnoredInClassesTest.php b/tests/Rules/BooleanArgumentFlag/IgnoredInClassesTest.php new file mode 100644 index 0000000..704d28d --- /dev/null +++ b/tests/Rules/BooleanArgumentFlag/IgnoredInClassesTest.php @@ -0,0 +1,34 @@ + + */ +class IgnoredInClassesTest extends RuleTestCase +{ + public function testIgnoredClasses(): void + { + $this->analyse([ + __DIR__ . '/Fixture/IgnoredClasses.php', + ], [ + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\NotIgnoredClass', 'methodWithBool', 'flag'), 16], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_FUNCTION, 'functionWithBool', 'flag'), 21], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'flag'), 23], + ]); + } + + public static function getAdditionalConfigFiles(): array + { + return [__DIR__ . '/config/ignored-classes.neon']; + } + + protected function getRule(): Rule + { + return self::getContainer()->getByType(BooleanArgumentFlagRule::class); + } +} diff --git a/tests/Rules/BooleanArgumentFlag/config/all-options.neon b/tests/Rules/BooleanArgumentFlag/config/all-options.neon new file mode 100644 index 0000000..697ea56 --- /dev/null +++ b/tests/Rules/BooleanArgumentFlag/config/all-options.neon @@ -0,0 +1,12 @@ +includes: + - ../../../../config/extension.neon + +rules: + - Orrison\MeliorStan\Rules\BooleanArgumentFlag\BooleanArgumentFlagRule + +parameters: + meliorstan: + boolean_argument_flag: + ignored_in_classes: + - 'Fixtures\BooleanArgumentFlag\AllOptionsIgnoredClass' + ignore_pattern: '/^set/' diff --git a/tests/Rules/BooleanArgumentFlag/config/default.neon b/tests/Rules/BooleanArgumentFlag/config/default.neon new file mode 100644 index 0000000..8c9ee36 --- /dev/null +++ b/tests/Rules/BooleanArgumentFlag/config/default.neon @@ -0,0 +1,11 @@ +includes: + - ../../../../config/extension.neon + +rules: + - Orrison\MeliorStan\Rules\BooleanArgumentFlag\BooleanArgumentFlagRule + +parameters: + meliorstan: + boolean_argument_flag: + ignored_in_classes: [] + ignore_pattern: '' diff --git a/tests/Rules/BooleanArgumentFlag/config/ignore-pattern.neon b/tests/Rules/BooleanArgumentFlag/config/ignore-pattern.neon new file mode 100644 index 0000000..81d4016 --- /dev/null +++ b/tests/Rules/BooleanArgumentFlag/config/ignore-pattern.neon @@ -0,0 +1,11 @@ +includes: + - ../../../../config/extension.neon + +rules: + - Orrison\MeliorStan\Rules\BooleanArgumentFlag\BooleanArgumentFlagRule + +parameters: + meliorstan: + boolean_argument_flag: + ignored_in_classes: [] + ignore_pattern: '/^set/' diff --git a/tests/Rules/BooleanArgumentFlag/config/ignored-classes.neon b/tests/Rules/BooleanArgumentFlag/config/ignored-classes.neon new file mode 100644 index 0000000..c465573 --- /dev/null +++ b/tests/Rules/BooleanArgumentFlag/config/ignored-classes.neon @@ -0,0 +1,12 @@ +includes: + - ../../../../config/extension.neon + +rules: + - Orrison\MeliorStan\Rules\BooleanArgumentFlag\BooleanArgumentFlagRule + +parameters: + meliorstan: + boolean_argument_flag: + ignored_in_classes: + - 'Fixtures\BooleanArgumentFlag\IgnoredClass' + ignore_pattern: '' From b9ac65240fdb69b711d34b48f46a6805dab4781f Mon Sep 17 00:00:00 2001 From: Kevin Ullyott Date: Sat, 1 Nov 2025 15:14:24 -0400 Subject: [PATCH 2/9] Update error message line numbers in BooleanArgumentFlag tests for accuracy Signed-off-by: Kevin Ullyott --- .../AllOptionsTrueTest.php | 6 ++-- .../DefaultOptionsTest.php | 34 +++++++++---------- .../BooleanArgumentFlag/IgnorePatternTest.php | 10 +++--- .../IgnoredInClassesTest.php | 6 ++-- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/tests/Rules/BooleanArgumentFlag/AllOptionsTrueTest.php b/tests/Rules/BooleanArgumentFlag/AllOptionsTrueTest.php index 17eb49b..1a77b29 100644 --- a/tests/Rules/BooleanArgumentFlag/AllOptionsTrueTest.php +++ b/tests/Rules/BooleanArgumentFlag/AllOptionsTrueTest.php @@ -16,9 +16,9 @@ public function testAllOptions(): void $this->analyse([ __DIR__ . '/Fixture/AllOptions.php', ], [ - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\AllOptionsNotIgnored', 'handle', 'flag'), 18], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\AllOptionsNotIgnored', 'processData', 'flag'), 20], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_FUNCTION, 'processConfig', 'value'), 25], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\AllOptionsNotIgnored', 'handle', 'flag'), 26], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\AllOptionsNotIgnored', 'processData', 'flag'), 30], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_FUNCTION, 'processConfig', 'value'), 39], ]); } diff --git a/tests/Rules/BooleanArgumentFlag/DefaultOptionsTest.php b/tests/Rules/BooleanArgumentFlag/DefaultOptionsTest.php index f90f7bb..d017adf 100644 --- a/tests/Rules/BooleanArgumentFlag/DefaultOptionsTest.php +++ b/tests/Rules/BooleanArgumentFlag/DefaultOptionsTest.php @@ -17,23 +17,23 @@ public function testDefaultExample(): void __DIR__ . '/Fixture/DefaultExample.php', ], [ [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', '__construct', 'debug'), 7], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'processWithFlag', 'flag'), 9], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'handleNullable', 'option'), 11], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'unionType', 'value'), 13], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'multiUnion', 'mixed'), 15], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'staticMethod', 'enabled'), 19], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', '__set', 'value'), 21], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'multipleParams', 'enabled'), 23], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'multipleBools', 'first'), 25], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'multipleBools', 'second'), 25], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'protectedMethod', 'flag'), 27], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'privateMethod', 'flag'), 29], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_FUNCTION, 'namedFunction', 'flag'), 32], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'flag'), 36], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'flag'), 40], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'enabled'), 44], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'outer'), 46], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'inner'), 47], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'processWithFlag', 'flag'), 11], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'handleNullable', 'option'), 15], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'unionType', 'value'), 19], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'multiUnion', 'mixed'), 23], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'staticMethod', 'enabled'), 31], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', '__set', 'value'), 35], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'multipleParams', 'enabled'), 39], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'multipleBools', 'first'), 43], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'multipleBools', 'second'), 43], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'protectedMethod', 'flag'), 47], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'privateMethod', 'flag'), 51], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_FUNCTION, 'namedFunction', 'flag'), 56], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'flag'), 64], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'flag'), 68], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'enabled'), 72], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'outer'), 74], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'inner'), 75], ]); } diff --git a/tests/Rules/BooleanArgumentFlag/IgnorePatternTest.php b/tests/Rules/BooleanArgumentFlag/IgnorePatternTest.php index 73622aa..99e4a54 100644 --- a/tests/Rules/BooleanArgumentFlag/IgnorePatternTest.php +++ b/tests/Rules/BooleanArgumentFlag/IgnorePatternTest.php @@ -16,11 +16,11 @@ public function testIgnorePattern(): void $this->analyse([ __DIR__ . '/Fixture/IgnorePattern.php', ], [ - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\IgnorePatternExample', 'processWithFlag', 'flag'), 13], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\IgnorePatternExample', 'handleBool', 'value'), 15], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_FUNCTION, 'processGlobal', 'value'), 24], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'value'), 26], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'value'), 28], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\IgnorePatternExample', 'processWithFlag', 'flag'), 19], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\IgnorePatternExample', 'handleBool', 'value'), 23], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_FUNCTION, 'processGlobal', 'value'), 40], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'value'), 44], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'value'), 46], ]); } diff --git a/tests/Rules/BooleanArgumentFlag/IgnoredInClassesTest.php b/tests/Rules/BooleanArgumentFlag/IgnoredInClassesTest.php index 704d28d..8fde598 100644 --- a/tests/Rules/BooleanArgumentFlag/IgnoredInClassesTest.php +++ b/tests/Rules/BooleanArgumentFlag/IgnoredInClassesTest.php @@ -16,9 +16,9 @@ public function testIgnoredClasses(): void $this->analyse([ __DIR__ . '/Fixture/IgnoredClasses.php', ], [ - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\NotIgnoredClass', 'methodWithBool', 'flag'), 16], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_FUNCTION, 'functionWithBool', 'flag'), 21], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'flag'), 23], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\NotIgnoredClass', 'methodWithBool', 'flag'), 22], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_FUNCTION, 'functionWithBool', 'flag'), 31], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'flag'), 35], ]); } From 1a115ff12727872a5d73f8296e90fd59264c52ef Mon Sep 17 00:00:00 2001 From: Kevin Ullyott Date: Sat, 1 Nov 2025 15:20:19 -0400 Subject: [PATCH 3/9] Refactor BooleanArgumentFlag rule tests and fixtures for consistency and accuracy in error message line numbers Signed-off-by: Kevin Ullyott --- .../BooleanArgumentFlagRule.php | 3 +- .../AllOptionsTrueTest.php | 6 +- .../DefaultOptionsTest.php | 34 +++++------ .../Fixture/AllOptions.php | 32 +++-------- .../Fixture/DefaultExample.php | 56 +++++-------------- .../Fixture/IgnorePattern.php | 36 +++--------- .../Fixture/IgnoredClasses.php | 24 ++------ .../BooleanArgumentFlag/IgnorePatternTest.php | 10 ++-- .../IgnoredInClassesTest.php | 6 +- 9 files changed, 66 insertions(+), 141 deletions(-) diff --git a/src/Rules/BooleanArgumentFlag/BooleanArgumentFlagRule.php b/src/Rules/BooleanArgumentFlag/BooleanArgumentFlagRule.php index 2dee308..d60d9c8 100644 --- a/src/Rules/BooleanArgumentFlag/BooleanArgumentFlagRule.php +++ b/src/Rules/BooleanArgumentFlag/BooleanArgumentFlagRule.php @@ -27,8 +27,7 @@ class BooleanArgumentFlagRule implements Rule public function __construct( protected Config $config, - ) { - } + ) {} public function getNodeType(): string { diff --git a/tests/Rules/BooleanArgumentFlag/AllOptionsTrueTest.php b/tests/Rules/BooleanArgumentFlag/AllOptionsTrueTest.php index 1a77b29..17eb49b 100644 --- a/tests/Rules/BooleanArgumentFlag/AllOptionsTrueTest.php +++ b/tests/Rules/BooleanArgumentFlag/AllOptionsTrueTest.php @@ -16,9 +16,9 @@ public function testAllOptions(): void $this->analyse([ __DIR__ . '/Fixture/AllOptions.php', ], [ - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\AllOptionsNotIgnored', 'handle', 'flag'), 26], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\AllOptionsNotIgnored', 'processData', 'flag'), 30], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_FUNCTION, 'processConfig', 'value'), 39], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\AllOptionsNotIgnored', 'handle', 'flag'), 18], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\AllOptionsNotIgnored', 'processData', 'flag'), 20], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_FUNCTION, 'processConfig', 'value'), 25], ]); } diff --git a/tests/Rules/BooleanArgumentFlag/DefaultOptionsTest.php b/tests/Rules/BooleanArgumentFlag/DefaultOptionsTest.php index d017adf..f90f7bb 100644 --- a/tests/Rules/BooleanArgumentFlag/DefaultOptionsTest.php +++ b/tests/Rules/BooleanArgumentFlag/DefaultOptionsTest.php @@ -17,23 +17,23 @@ public function testDefaultExample(): void __DIR__ . '/Fixture/DefaultExample.php', ], [ [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', '__construct', 'debug'), 7], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'processWithFlag', 'flag'), 11], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'handleNullable', 'option'), 15], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'unionType', 'value'), 19], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'multiUnion', 'mixed'), 23], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'staticMethod', 'enabled'), 31], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', '__set', 'value'), 35], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'multipleParams', 'enabled'), 39], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'multipleBools', 'first'), 43], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'multipleBools', 'second'), 43], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'protectedMethod', 'flag'), 47], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'privateMethod', 'flag'), 51], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_FUNCTION, 'namedFunction', 'flag'), 56], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'flag'), 64], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'flag'), 68], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'enabled'), 72], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'outer'), 74], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'inner'), 75], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'processWithFlag', 'flag'), 9], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'handleNullable', 'option'), 11], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'unionType', 'value'), 13], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'multiUnion', 'mixed'), 15], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'staticMethod', 'enabled'), 19], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', '__set', 'value'), 21], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'multipleParams', 'enabled'), 23], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'multipleBools', 'first'), 25], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'multipleBools', 'second'), 25], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'protectedMethod', 'flag'), 27], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'privateMethod', 'flag'), 29], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_FUNCTION, 'namedFunction', 'flag'), 32], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'flag'), 36], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'flag'), 40], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'enabled'), 44], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'outer'), 46], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'inner'), 47], ]); } diff --git a/tests/Rules/BooleanArgumentFlag/Fixture/AllOptions.php b/tests/Rules/BooleanArgumentFlag/Fixture/AllOptions.php index 96034a5..5bf68e9 100644 --- a/tests/Rules/BooleanArgumentFlag/Fixture/AllOptions.php +++ b/tests/Rules/BooleanArgumentFlag/Fixture/AllOptions.php @@ -4,38 +4,22 @@ class AllOptionsIgnoredClass { - public function setFlag(bool $flag) - { - } + public function setFlag(bool $flag) {} - public function process(bool $flag) - { - } + public function process(bool $flag) {} - public function handle(bool $value) - { - } + public function handle(bool $value) {} } class AllOptionsNotIgnored { - public function setOption(bool $value) - { - } + public function setOption(bool $value) {} - public function handle(bool $flag) - { - } + public function handle(bool $flag) {} - public function processData(bool $flag) - { - } + public function processData(bool $flag) {} } -function setConfig(bool $value) -{ -} +function setConfig(bool $value) {} -function processConfig(bool $value) -{ -} +function processConfig(bool $value) {} diff --git a/tests/Rules/BooleanArgumentFlag/Fixture/DefaultExample.php b/tests/Rules/BooleanArgumentFlag/Fixture/DefaultExample.php index 1b5ae8c..528e667 100644 --- a/tests/Rules/BooleanArgumentFlag/Fixture/DefaultExample.php +++ b/tests/Rules/BooleanArgumentFlag/Fixture/DefaultExample.php @@ -4,62 +4,34 @@ class DefaultExample { - public function __construct(bool $debug) - { - } + public function __construct(bool $debug) {} - public function processWithFlag(bool $flag) - { - } + public function processWithFlag(bool $flag) {} - public function handleNullable(?bool $option) - { - } + public function handleNullable(?bool $option) {} - public function unionType(bool|null $value) - { - } + public function unionType(bool|null $value) {} - public function multiUnion(int|bool $mixed) - { - } + public function multiUnion(int|bool $mixed) {} - public function validMethod(string $name, int $count) - { - } + public function validMethod(string $name, int $count) {} - public static function staticMethod(bool $enabled) - { - } + public static function staticMethod(bool $enabled) {} - public function __set(string $name, bool $value) - { - } + public function __set(string $name, bool $value) {} - public function multipleParams(string $name, bool $enabled, int $count) - { - } + public function multipleParams(string $name, bool $enabled, int $count) {} - public function multipleBools(bool $first, bool $second) - { - } + public function multipleBools(bool $first, bool $second) {} - protected function protectedMethod(bool $flag) - { - } + protected function protectedMethod(bool $flag) {} - private function privateMethod(bool $flag) - { - } + private function privateMethod(bool $flag) {} } -function namedFunction(bool $flag) -{ -} +function namedFunction(bool $flag) {} -function validNamedFunction(string $name) -{ -} +function validNamedFunction(string $name) {} $closure = function (bool $flag) {}; diff --git a/tests/Rules/BooleanArgumentFlag/Fixture/IgnorePattern.php b/tests/Rules/BooleanArgumentFlag/Fixture/IgnorePattern.php index 010ace2..57e6571 100644 --- a/tests/Rules/BooleanArgumentFlag/Fixture/IgnorePattern.php +++ b/tests/Rules/BooleanArgumentFlag/Fixture/IgnorePattern.php @@ -4,42 +4,24 @@ class IgnorePatternExample { - public function setEnabled(bool $enabled) - { - } + public function setEnabled(bool $enabled) {} - public function setFlag(bool $flag) - { - } + public function setFlag(bool $flag) {} - public function setDebugMode(bool $debug) - { - } + public function setDebugMode(bool $debug) {} - public function processWithFlag(bool $flag) - { - } + public function processWithFlag(bool $flag) {} - public function handleBool(bool $value) - { - } + public function handleBool(bool $value) {} - public function isValid(string $name) - { - } + public function isValid(string $name) {} - public function setName(string $name) - { - } + public function setName(string $name) {} } -function setGlobal(bool $value) -{ -} +function setGlobal(bool $value) {} -function processGlobal(bool $value) -{ -} +function processGlobal(bool $value) {} $setterClosure = fn (bool $value) => $value; diff --git a/tests/Rules/BooleanArgumentFlag/Fixture/IgnoredClasses.php b/tests/Rules/BooleanArgumentFlag/Fixture/IgnoredClasses.php index 4cfba0d..fdb7dfc 100644 --- a/tests/Rules/BooleanArgumentFlag/Fixture/IgnoredClasses.php +++ b/tests/Rules/BooleanArgumentFlag/Fixture/IgnoredClasses.php @@ -4,32 +4,20 @@ class IgnoredClass { - public function methodWithBool(bool $flag) - { - } + public function methodWithBool(bool $flag) {} - public function anotherMethodWithBool(bool $enabled, bool $debug) - { - } + public function anotherMethodWithBool(bool $enabled, bool $debug) {} - public static function staticWithBool(bool $flag) - { - } + public static function staticWithBool(bool $flag) {} } class NotIgnoredClass { - public function methodWithBool(bool $flag) - { - } + public function methodWithBool(bool $flag) {} - public function validMethod(string $name) - { - } + public function validMethod(string $name) {} } -function functionWithBool(bool $flag) -{ -} +function functionWithBool(bool $flag) {} $closureWithBool = function (bool $flag) {}; diff --git a/tests/Rules/BooleanArgumentFlag/IgnorePatternTest.php b/tests/Rules/BooleanArgumentFlag/IgnorePatternTest.php index 99e4a54..73622aa 100644 --- a/tests/Rules/BooleanArgumentFlag/IgnorePatternTest.php +++ b/tests/Rules/BooleanArgumentFlag/IgnorePatternTest.php @@ -16,11 +16,11 @@ public function testIgnorePattern(): void $this->analyse([ __DIR__ . '/Fixture/IgnorePattern.php', ], [ - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\IgnorePatternExample', 'processWithFlag', 'flag'), 19], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\IgnorePatternExample', 'handleBool', 'value'), 23], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_FUNCTION, 'processGlobal', 'value'), 40], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'value'), 44], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'value'), 46], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\IgnorePatternExample', 'processWithFlag', 'flag'), 13], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\IgnorePatternExample', 'handleBool', 'value'), 15], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_FUNCTION, 'processGlobal', 'value'), 24], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'value'), 26], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'value'), 28], ]); } diff --git a/tests/Rules/BooleanArgumentFlag/IgnoredInClassesTest.php b/tests/Rules/BooleanArgumentFlag/IgnoredInClassesTest.php index 8fde598..704d28d 100644 --- a/tests/Rules/BooleanArgumentFlag/IgnoredInClassesTest.php +++ b/tests/Rules/BooleanArgumentFlag/IgnoredInClassesTest.php @@ -16,9 +16,9 @@ public function testIgnoredClasses(): void $this->analyse([ __DIR__ . '/Fixture/IgnoredClasses.php', ], [ - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\NotIgnoredClass', 'methodWithBool', 'flag'), 22], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_FUNCTION, 'functionWithBool', 'flag'), 31], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'flag'), 35], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\NotIgnoredClass', 'methodWithBool', 'flag'), 16], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_FUNCTION, 'functionWithBool', 'flag'), 21], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'flag'), 23], ]); } From b0b411bf377a8d5d310b6c25fafad076684ef299 Mon Sep 17 00:00:00 2001 From: Kevin Ullyott Date: Sat, 1 Nov 2025 15:35:49 -0400 Subject: [PATCH 4/9] Add tests for InvalidArgumentException in BooleanArgumentFlagRule and include invalid regex configuration Signed-off-by: Kevin Ullyott --- .../BooleanArgumentFlagRule.php | 13 ++++++- .../InvalidPatternTest.php | 34 +++++++++++++++++++ .../config/invalid-pattern.neon | 11 ++++++ 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 tests/Rules/BooleanArgumentFlag/InvalidPatternTest.php create mode 100644 tests/Rules/BooleanArgumentFlag/config/invalid-pattern.neon diff --git a/src/Rules/BooleanArgumentFlag/BooleanArgumentFlagRule.php b/src/Rules/BooleanArgumentFlag/BooleanArgumentFlagRule.php index d60d9c8..0746586 100644 --- a/src/Rules/BooleanArgumentFlag/BooleanArgumentFlagRule.php +++ b/src/Rules/BooleanArgumentFlag/BooleanArgumentFlagRule.php @@ -2,6 +2,7 @@ namespace Orrison\MeliorStan\Rules\BooleanArgumentFlag; +use InvalidArgumentException; use PhpParser\Node; use PhpParser\Node\Expr\Closure; use PhpParser\Node\FunctionLike; @@ -88,7 +89,17 @@ protected function shouldIgnoreByPattern(string $functionName): bool return false; } - return @preg_match($pattern, $functionName) === 1; + $result = preg_match($pattern, $functionName); + + if ($result === false) { + $error = preg_last_error_msg(); + + throw new InvalidArgumentException( + sprintf('Invalid regex pattern in ignore_pattern configuration: "%s". Error: %s', $pattern, $error) + ); + } + + return $result === 1; } protected function getFunctionName(FunctionLike $node): ?string diff --git a/tests/Rules/BooleanArgumentFlag/InvalidPatternTest.php b/tests/Rules/BooleanArgumentFlag/InvalidPatternTest.php new file mode 100644 index 0000000..8d873c9 --- /dev/null +++ b/tests/Rules/BooleanArgumentFlag/InvalidPatternTest.php @@ -0,0 +1,34 @@ + + */ +class InvalidPatternTest extends RuleTestCase +{ + public function testInvalidPattern(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid regex pattern in ignore_pattern configuration'); + + $this->analyse([ + __DIR__ . '/Fixture/IgnorePattern.php', + ], []); + } + + public static function getAdditionalConfigFiles(): array + { + return [__DIR__ . '/config/invalid-pattern.neon']; + } + + protected function getRule(): Rule + { + return self::getContainer()->getByType(BooleanArgumentFlagRule::class); + } +} diff --git a/tests/Rules/BooleanArgumentFlag/config/invalid-pattern.neon b/tests/Rules/BooleanArgumentFlag/config/invalid-pattern.neon new file mode 100644 index 0000000..04d9a48 --- /dev/null +++ b/tests/Rules/BooleanArgumentFlag/config/invalid-pattern.neon @@ -0,0 +1,11 @@ +includes: + - ../../../../config/extension.neon + +rules: + - Orrison\MeliorStan\Rules\BooleanArgumentFlag\BooleanArgumentFlagRule + +parameters: + meliorstan: + boolean_argument_flag: + ignored_in_classes: [] + ignore_pattern: '/invalid[regex' # Invalid regex - missing closing bracket From 519b534a03dc10a437567c5fd778573c293e4dcb Mon Sep 17 00:00:00 2001 From: Kevin Ullyott Date: Sat, 1 Nov 2025 15:37:46 -0400 Subject: [PATCH 5/9] Clarify description of `ignore_pattern` configuration in BooleanArgumentFlag documentation Signed-off-by: Kevin Ullyott --- docs/BooleanArgumentFlag.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/BooleanArgumentFlag.md b/docs/BooleanArgumentFlag.md index 31edd5c..9533dcf 100644 --- a/docs/BooleanArgumentFlag.md +++ b/docs/BooleanArgumentFlag.md @@ -20,7 +20,7 @@ This rule supports the following configuration options: ### `ignore_pattern` - **Type**: `string` - **Default**: `''` -- **Description**: A regular expression pattern to match function or method names that should be ignored. This applies to class methods, functions, and closures with names. Common patterns include setter methods or boolean query methods. +- **Description**: A regular expression pattern to match function or method names that should be ignored. This applies to class methods and named functions. Closures are not affected by this pattern. Common patterns include setter methods or boolean query methods. ```php // Example: '/^set/' to ignore all setter methods From e5520fa59c366732dc207596ae58dacca0ea780e Mon Sep 17 00:00:00 2001 From: Kevin Ullyott Date: Sat, 1 Nov 2025 15:42:42 -0400 Subject: [PATCH 6/9] Remove unused Closure import from BooleanArgumentFlagRule Signed-off-by: Kevin Ullyott --- src/Rules/BooleanArgumentFlag/BooleanArgumentFlagRule.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Rules/BooleanArgumentFlag/BooleanArgumentFlagRule.php b/src/Rules/BooleanArgumentFlag/BooleanArgumentFlagRule.php index 0746586..e519a82 100644 --- a/src/Rules/BooleanArgumentFlag/BooleanArgumentFlagRule.php +++ b/src/Rules/BooleanArgumentFlag/BooleanArgumentFlagRule.php @@ -4,7 +4,6 @@ use InvalidArgumentException; use PhpParser\Node; -use PhpParser\Node\Expr\Closure; use PhpParser\Node\FunctionLike; use PhpParser\Node\Identifier; use PhpParser\Node\NullableType; From 62e5cc1f04a83175d55eb72030fe5d43cdb79825 Mon Sep 17 00:00:00 2001 From: Kevin Ullyott Date: Sat, 1 Nov 2025 15:47:58 -0400 Subject: [PATCH 7/9] Update src/Rules/BooleanArgumentFlag/Config.php Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/Rules/BooleanArgumentFlag/Config.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Rules/BooleanArgumentFlag/Config.php b/src/Rules/BooleanArgumentFlag/Config.php index 8bff7a5..fbb35e9 100644 --- a/src/Rules/BooleanArgumentFlag/Config.php +++ b/src/Rules/BooleanArgumentFlag/Config.php @@ -5,8 +5,8 @@ class Config { /** - * @param array $ignoredInClasses - * @param string $ignorePattern + * @param array $ignoredInClasses List of fully-qualified class names in which boolean argument flag checks should be ignored. + * @param string $ignorePattern Regular expression pattern for method names to ignore. */ public function __construct( protected array $ignoredInClasses, From e73cc3f1e9a56c7e543ff5eaa6b5a5cef733f498 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 1 Nov 2025 19:51:13 +0000 Subject: [PATCH 8/9] Initial plan From 6c1884677226ac3974e229d424b292373ca0107f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 1 Nov 2025 20:06:26 +0000 Subject: [PATCH 9/9] Fix inconsistent schema definition: change listOf to arrayOf for ignored_in_classes Co-authored-by: Orrison <6799341+Orrison@users.noreply.github.com> --- config/extension.neon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/extension.neon b/config/extension.neon index f47bcfe..9033734 100644 --- a/config/extension.neon +++ b/config/extension.neon @@ -61,7 +61,7 @@ parametersSchema: maximum: int(), ]), boolean_argument_flag: structure([ - ignored_in_classes: listOf(string()), + ignored_in_classes: arrayOf(string()), ignore_pattern: string(), ]), ])