diff --git a/CHANGELOG.md b/CHANGELOG.md index ac5aacf7..ebcf2d98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,9 @@ For a full diff see [`0.2.0...master`](https://github.com/localheinz/phpstan-rul error when a closure has a parameter with a nullable type declaration ([#33](https://github.com/localheinz/phpstan-rules/pull/33)), by [@localheinz](https://github.com/localheinz) * added `Functions\NoParameterWithNullableTypeDeclarationRule`, which reports an error when a function has a parameter with a nullable type declaration ([#34](https://github.com/localheinz/phpstan-rules/pull/34)), by [@localheinz](https://github.com/localheinz) +* added `Methods\NoParameterWithNullableTypeDeclarationRule`, which reports an + error when a method declared on an anonymous class, a class, or an interface + has a parameter with a nullable type declaration ([#35](https://github.com/localheinz/phpstan-rules/pull/35)), by [@localheinz](https://github.com/localheinz) ## [`0.2.0`](https://github.com/localheinz/phpstan-rules/releases/tag/0.2.0) diff --git a/README.md b/README.md index b6ca2b6a..23b17a79 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ This package provides the following rules for use with [`phpstan/phpstan`](https * [`Localheinz\PHPStan\Rules\Functions\NoParameterWithNullDefaultValueRule`](https://github.com/localheinz/phpstan-rules#functionsnoparameterwithnulldefaultvaluerule) * [`Localheinz\PHPStan\Rules\Functions\NoParameterWithNullableTypeDeclaration`](https://github.com/localheinz/phpstan-rules#functionsnoparameterwithnullabletypedeclarationrule) * [`Localheinz\PHPStan\Rules\Methods\NoNullableReturnTypeDeclarationRule`](https://github.com/localheinz/phpstan-rules#methodsnonullablereturntypedeclarationrule) +* [`Localheinz\PHPStan\Rules\Methods\NoParameterWithNullableTypeDeclarationRule`](https://github.com/localheinz/phpstan-rules#methodsnoparameterwithnullabletypedeclarationrule) * [`Localheinz\PHPStan\Rules\Methods\NoParameterWithNullDefaultValueRule`](https://github.com/localheinz/phpstan-rules#methodsnoparameterwithnulldefaultvaluerule) ### `Classes\AbstractOrFinalRule` @@ -157,6 +158,17 @@ rules: - Localheinz\PHPStan\Rules\Methods\NoNullableReturnTypeDeclarationRule ``` +### `Methods\NoParameterWithNullableTypeDeclarationRule` + +This rule reports an error when a method declared in an anonymous class, a class, or an interface has a parameter with a nullable type declaration. + +If you want to use this rule, add it to your `phpstan.neon`: + +```neon +rules: + - Localheinz\PHPStan\Rules\Methods\NoParameterWithNullableTypeDeclarationRule +``` + ### `Methods\NoParameterWithNullDefaultValueRule` This rule reports an error when a method declared in an anonymous class, a class, or an interface has a parameter with `null` as default value. diff --git a/phpstan.neon b/phpstan.neon index 6cfee2ca..1f57a20f 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -10,3 +10,4 @@ rules: - Localheinz\PHPStan\Rules\Functions\NoParameterWithNullableTypeDeclarationRule - Localheinz\PHPStan\Rules\Methods\NoNullableReturnTypeDeclarationRule - Localheinz\PHPStan\Rules\Methods\NoParameterWithNullDefaultValueRule + - Localheinz\PHPStan\Rules\Methods\NoParameterWithNullableTypeDeclarationRule diff --git a/src/Methods/NoParameterWithNullableTypeDeclarationRule.php b/src/Methods/NoParameterWithNullableTypeDeclarationRule.php new file mode 100644 index 00000000..2f5579de --- /dev/null +++ b/src/Methods/NoParameterWithNullableTypeDeclarationRule.php @@ -0,0 +1,86 @@ +params)) { + return []; + } + + $params = \array_filter($node->params, static function (Node\Param $node): bool { + return $node->type instanceof Node\NullableType; + }); + + if (0 === \count($params)) { + return []; + } + + $methodName = $node->name->toString(); + + /** @var Reflection\ClassReflection $classReflection */ + $classReflection = $scope->getClassReflection(); + + if ($classReflection->isAnonymous()) { + return \array_map(static function (Node\Param $node) use ($methodName): string { + /** @var Node\Expr\Variable $variable */ + $variable = $node->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + return \sprintf( + 'Parameter "$%s" of method "%s()" in anonymous class should not have a nullable type declaration.', + $parameterName, + $methodName + ); + }, $params); + } + + $className = $classReflection->getName(); + + return \array_map(static function (Node\Param $node) use ($className, $methodName): string { + /** @var Node\Expr\Variable $variable */ + $variable = $node->var; + + /** @var string $parameterName */ + $parameterName = $variable->name; + + return \sprintf( + 'Parameter "$%s" of method "%s::%s()" should not have a nullable type declaration.', + $parameterName, + $className, + $methodName + ); + }, $params); + } +} diff --git a/test/Fixture/Methods/NoParameterWithNullableTypeDeclarationRule/Failure/MethodInClassWithParameterWithNullableTypeDeclaration.php b/test/Fixture/Methods/NoParameterWithNullableTypeDeclarationRule/Failure/MethodInClassWithParameterWithNullableTypeDeclaration.php new file mode 100644 index 00000000..24ed637f --- /dev/null +++ b/test/Fixture/Methods/NoParameterWithNullableTypeDeclarationRule/Failure/MethodInClassWithParameterWithNullableTypeDeclaration.php @@ -0,0 +1,13 @@ + __DIR__ . '/../../Fixture/Methods/NoParameterWithNullableTypeDeclarationRule/Success/method-in-anonymous-class-with-parameter-with-type-declaration.php', + 'method-in-anonymous-class-with-parameter-without-type-declaration' => __DIR__ . '/../../Fixture/Methods/NoParameterWithNullableTypeDeclarationRule/Success/method-in-anonymous-class-with-parameter-without-type-declaration.php', + 'method-in-anonymous-class-without-parameters' => __DIR__ . '/../../Fixture/Methods/NoParameterWithNullableTypeDeclarationRule/Success/method-in-anonymous-class-without-parameters.php', + 'method-in-class-with-parameter-with-type-declaration' => __DIR__ . '/../../Fixture/Methods/NoParameterWithNullableTypeDeclarationRule/Success/MethodInClassWithParameterWithTypeDeclaration.php', + 'method-in-class-with-parameter-without-type-declaration' => __DIR__ . '/../../Fixture/Methods/NoParameterWithNullableTypeDeclarationRule/Success/MethodInClassWithParameterWithoutTypeDeclaration.php', + 'method-in-class-without-parameters' => __DIR__ . '/../../Fixture/Methods/NoParameterWithNullableTypeDeclarationRule/Success/MethodInClassWithoutParameters.php', + 'method-in-interface-with-parameter-with-type-declaration' => __DIR__ . '/../../Fixture/Methods/NoParameterWithNullableTypeDeclarationRule/Success/MethodInInterfaceWithParameterWithTypeDeclaration.php', + 'method-in-interface-with-parameter-without-type-declaration' => __DIR__ . '/../../Fixture/Methods/NoParameterWithNullableTypeDeclarationRule/Success/MethodInInterfaceWithParameterWithoutTypeDeclaration.php', + 'method-in-interface-without-parameters' => __DIR__ . '/../../Fixture/Methods/NoParameterWithNullableTypeDeclarationRule/Success/MethodInInterfaceWithoutParameters.php', + // traits are not supported + 'method-in-trait-with-parameter-with-type-declaration' => __DIR__ . '/../../Fixture/Methods/NoParameterWithNullableTypeDeclarationRule/Success/MethodInTraitWithParameterWithTypeDeclaration.php', + 'method-in-trait-with-parameter-without-type-declaration' => __DIR__ . '/../../Fixture/Methods/NoParameterWithNullableTypeDeclarationRule/Success/MethodInTraitWithParameterWithoutTypeDeclaration.php', + 'method-in-trait-without-parameters' => __DIR__ . '/../../Fixture/Methods/NoParameterWithNullableTypeDeclarationRule/Success/MethodInTraitWithoutParameters.php', + ]; + + foreach ($paths as $description => $path) { + yield $description => [ + $path, + ]; + } + } + + public function providerAnalysisFails(): \Generator + { + $paths = [ + 'method-in-anonymous-class-with-parameter-with-nullable-type-declaration' => [ + __DIR__ . '/../../Fixture/Methods/NoParameterWithNullableTypeDeclarationRule/Failure/method-in-anonymous-class-with-parameter-with-nullable-type-declaration.php', + [ + 'Parameter "$bar" of method "foo()" in anonymous class should not have a nullable type declaration.', + 8, + ], + ], + 'method-in-class-with-parameter-with-nullable-type-declaration' => [ + __DIR__ . '/../../Fixture/Methods/NoParameterWithNullableTypeDeclarationRule/Failure/MethodInClassWithParameterWithNullableTypeDeclaration.php', + [ + \sprintf( + 'Parameter "$bar" of method "%s::foo()" should not have a nullable type declaration.', + Fixture\Methods\NoParameterWithNullableTypeDeclarationRule\Failure\MethodInClassWithParameterWithNullableTypeDeclaration::class + ), + 9, + ], + ], + 'method-in-interface-with-parameter-with-nullable-type-declaration' => [ + __DIR__ . '/../../Fixture/Methods/NoParameterWithNullableTypeDeclarationRule/Failure/MethodInInterfaceWithParameterWithNullableTypeDeclaration.php', + [ + \sprintf( + 'Parameter "$bar" of method "%s::foo()" should not have a nullable type declaration.', + Fixture\Methods\NoParameterWithNullableTypeDeclarationRule\Failure\MethodInInterfaceWithParameterWithNullableTypeDeclaration::class + ), + 9, + ], + ], + ]; + + foreach ($paths as $description => [$path, $error]) { + yield $description => [ + $path, + $error, + ]; + } + } + + protected function getRule(): Rule + { + return new NoParameterWithNullableTypeDeclarationRule(); + } +}