From 3e5cd7fba7655307cbc758142e265b475a106a57 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Sat, 17 Aug 2019 13:57:35 +0200 Subject: [PATCH] InstantiationRule - test new static() in cases where it's possible --- src/Rules/Classes/InstantiationRule.php | 17 ++++++- .../Rules/Classes/InstantiationRuleTest.php | 12 +++++ .../Rules/Classes/data/instantiation.php | 50 +++++++++++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) diff --git a/src/Rules/Classes/InstantiationRule.php b/src/Rules/Classes/InstantiationRule.php index 6ae280b9d8..cf8afad37b 100644 --- a/src/Rules/Classes/InstantiationRule.php +++ b/src/Rules/Classes/InstantiationRule.php @@ -7,6 +7,7 @@ use PHPStan\Analyser\Scope; use PHPStan\Broker\Broker; use PHPStan\Reflection\ParametersAcceptorSelector; +use PHPStan\Reflection\Php\PhpMethodReflection; use PHPStan\Rules\ClassCaseSensitivityCheck; use PHPStan\Rules\ClassNameNodePair; use PHPStan\Rules\FunctionCallParametersCheck; @@ -72,7 +73,21 @@ private function checkClassName(string $class, Node $node, Scope $scope): array sprintf('Using %s outside of class scope.', $class), ]; } - return []; + + $classReflection = $scope->getClassReflection(); + if (!$classReflection->isFinal()) { + if (!$classReflection->hasConstructor()) { + return []; + } + + $constructor = $classReflection->getConstructor(); + if (!$constructor->getPrototype()->getDeclaringClass()->isInterface()) { + $isFinal = $constructor instanceof PhpMethodReflection && $constructor->isFinal(); + if (!$isFinal) { + return []; + } + } + } } elseif ($lowercasedClass === 'self') { if (!$scope->isInClass()) { return [ diff --git a/tests/PHPStan/Rules/Classes/InstantiationRuleTest.php b/tests/PHPStan/Rules/Classes/InstantiationRuleTest.php index 70d40a1a86..a164665829 100644 --- a/tests/PHPStan/Rules/Classes/InstantiationRuleTest.php +++ b/tests/PHPStan/Rules/Classes/InstantiationRuleTest.php @@ -155,6 +155,18 @@ public function testInstantiation(): void 'Instantiated class UndefinedClass3 not found.', 176, ], + [ + 'Class TestInstantiation\FinalClass does not have a constructor and must be instantiated without any parameters.', + 187, + ], + [ + 'Class TestInstantiation\ClassWithFinalConstructor constructor invoked with 0 parameters, 1 required.', + 203, + ], + [ + 'Class TestInstantiation\ConstructorComingFromAnInterface constructor invoked with 0 parameters, 1 required.', + 226, + ], ] ); } diff --git a/tests/PHPStan/Rules/Classes/data/instantiation.php b/tests/PHPStan/Rules/Classes/data/instantiation.php index 143fc2a2be..a9bf4be074 100644 --- a/tests/PHPStan/Rules/Classes/data/instantiation.php +++ b/tests/PHPStan/Rules/Classes/data/instantiation.php @@ -177,3 +177,53 @@ public static function doFoo(string $key): void } } + +final class FinalClass +{ + + public function doFoo() + { + new static(); + new static(1); + } + +} + +class ClassWithFinalConstructor +{ + + final public function __construct(int $i) + { + + } + + public function doFoo() + { + new static(1); + new static(); + } + +} + +interface InterfaceWithConstructor +{ + + public function __construct(int $i); + +} + +class ConstructorComingFromAnInterface implements InterfaceWithConstructor +{ + + public function __construct(int $i) + { + + } + + public function doFoo() + { + new static(1); + new static(); + } + +}