From 49e9f5741aad6faadd72af61cdec078210404b28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Wed, 4 Dec 2019 11:36:38 +0100 Subject: [PATCH] Upgrading to PHPStan 0.12 This PR upgrades the code to PHPStan 0.12. Because the new level 6 of PHPStan is about checking type hints, all the type-hint related rules of this package are now useless. This PR therefore removes all those rules. --- composer.json | 9 +- phpstan-strict-rules.neon | 8 - phpstan.neon | 3 - .../SwitchMustContainDefaultRule.php | 2 + .../DoNotThrowExceptionBaseClassRule.php | 2 + src/Rules/Exceptions/EmptyExceptionRule.php | 3 + src/Rules/Exceptions/MustRethrowRule.php | 6 + .../ThrowMustBundlePreviousExceptionRule.php | 10 + src/Rules/Superglobals/NoSuperglobalsRule.php | 2 + .../TypeHints/AbstractMissingTypeHintRule.php | 371 ------------------ src/Rules/TypeHints/DebugContextInterface.php | 10 - src/Rules/TypeHints/FunctionDebugContext.php | 46 --- .../MissingTypeHintInFunctionRule.php | 50 --- .../TypeHints/MissingTypeHintInMethodRule.php | 101 ----- src/Rules/TypeHints/ParameterDebugContext.php | 56 --- .../MissingTypeHintRuleInFunctionTest.php | 100 ----- .../MissingTypeHintRuleInMethodTest.php | 32 -- tests/Rules/TypeHints/data/StubClass.php | 19 - tests/Rules/TypeHints/data/StubInterface.php | 9 - tests/Rules/TypeHints/data/StubIterator.php | 14 - tests/Rules/TypeHints/data/typehints.php | 141 ------- .../TypeHints/data/typehints_in_methods.php | 40 -- 22 files changed, 29 insertions(+), 1005 deletions(-) delete mode 100644 src/Rules/TypeHints/AbstractMissingTypeHintRule.php delete mode 100644 src/Rules/TypeHints/DebugContextInterface.php delete mode 100644 src/Rules/TypeHints/FunctionDebugContext.php delete mode 100644 src/Rules/TypeHints/MissingTypeHintInFunctionRule.php delete mode 100644 src/Rules/TypeHints/MissingTypeHintInMethodRule.php delete mode 100644 src/Rules/TypeHints/ParameterDebugContext.php delete mode 100644 tests/Rules/TypeHints/MissingTypeHintRuleInFunctionTest.php delete mode 100644 tests/Rules/TypeHints/MissingTypeHintRuleInMethodTest.php delete mode 100644 tests/Rules/TypeHints/data/StubClass.php delete mode 100644 tests/Rules/TypeHints/data/StubInterface.php delete mode 100644 tests/Rules/TypeHints/data/StubIterator.php delete mode 100644 tests/Rules/TypeHints/data/typehints.php delete mode 100644 tests/Rules/TypeHints/data/typehints_in_methods.php diff --git a/composer.json b/composer.json index 943e232..cddf05f 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ ], "require": { "php": "^7.1", - "phpstan/phpstan": "^0.11.7" + "phpstan/phpstan": "^0.12" }, "require-dev": { "phpunit/phpunit": "^7.1", @@ -24,19 +24,18 @@ }, "autoload-dev": { "classmap": [ - "tests/Rules/Exceptions/data/", - "tests/Rules/TypeHints/data/" + "tests/Rules/Exceptions/data/" ], "psr-4": { "TheCodingMachine\\PHPStan\\": "tests/" } }, "scripts": { - "phpstan": "phpstan analyse src -c phpstan.neon --level=5 --no-progress -vvv" + "phpstan": "phpstan analyse src -c phpstan.neon --level=6 --no-progress -vvv" }, "extra": { "branch-alias": { - "dev-master": "0.10-dev" + "dev-master": "0.12-dev" }, "phpstan": { "includes": [ diff --git a/phpstan-strict-rules.neon b/phpstan-strict-rules.neon index b607878..211335d 100644 --- a/phpstan-strict-rules.neon +++ b/phpstan-strict-rules.neon @@ -15,14 +15,6 @@ services: class: TheCodingMachine\PHPStan\Rules\Exceptions\MustRethrowRule tags: - phpstan.rules.rule - - - class: TheCodingMachine\PHPStan\Rules\TypeHints\MissingTypeHintInFunctionRule - tags: - - phpstan.rules.rule - - - class: TheCodingMachine\PHPStan\Rules\TypeHints\MissingTypeHintInMethodRule - tags: - - phpstan.rules.rule - class: TheCodingMachine\PHPStan\Rules\Superglobals\NoSuperglobalsRule tags: diff --git a/phpstan.neon b/phpstan.neon index b09fefe..a4ece3f 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,5 +1,2 @@ -parameters: - ignoreErrors: - - '#Access to an undefined property PhpParser\\Node\\FunctionLike::\$name.#' includes: - phpstan-strict-rules.neon diff --git a/src/Rules/Conditionals/SwitchMustContainDefaultRule.php b/src/Rules/Conditionals/SwitchMustContainDefaultRule.php index e869212..a0c5278 100644 --- a/src/Rules/Conditionals/SwitchMustContainDefaultRule.php +++ b/src/Rules/Conditionals/SwitchMustContainDefaultRule.php @@ -13,6 +13,8 @@ /** * A switch statement must always contain a "default" statement. + * + * @implements Rule */ class SwitchMustContainDefaultRule implements Rule { diff --git a/src/Rules/Exceptions/DoNotThrowExceptionBaseClassRule.php b/src/Rules/Exceptions/DoNotThrowExceptionBaseClassRule.php index f0c3228..578d48c 100644 --- a/src/Rules/Exceptions/DoNotThrowExceptionBaseClassRule.php +++ b/src/Rules/Exceptions/DoNotThrowExceptionBaseClassRule.php @@ -11,6 +11,8 @@ /** * This rule checks that the base \Exception class is never thrown. Instead, developers should subclass the \Exception * base class and throw the sub-type. + * + * @implements Rule */ class DoNotThrowExceptionBaseClassRule implements Rule { diff --git a/src/Rules/Exceptions/EmptyExceptionRule.php b/src/Rules/Exceptions/EmptyExceptionRule.php index b1a9cf9..fdef067 100644 --- a/src/Rules/Exceptions/EmptyExceptionRule.php +++ b/src/Rules/Exceptions/EmptyExceptionRule.php @@ -10,6 +10,9 @@ use PHPStan\Rules\Rule; use function strpos; +/** + * @implements Rule + */ class EmptyExceptionRule implements Rule { public function getNodeType(): string diff --git a/src/Rules/Exceptions/MustRethrowRule.php b/src/Rules/Exceptions/MustRethrowRule.php index 5d3e842..11393de 100644 --- a/src/Rules/Exceptions/MustRethrowRule.php +++ b/src/Rules/Exceptions/MustRethrowRule.php @@ -19,6 +19,8 @@ /** * When catching \Exception, \RuntimeException or \Throwable, the exception MUST be thrown again * (unless you are developing an exception handler...) + * + * @implements Rule */ class MustRethrowRule implements Rule { @@ -49,6 +51,9 @@ public function processNode(Node $node, Scope $scope): array // Let's visit and find a throw. $visitor = new class() extends NodeVisitorAbstract { + /** + * @var bool + */ private $throwFound = false; public function leaveNode(Node $node) @@ -56,6 +61,7 @@ public function leaveNode(Node $node) if ($node instanceof Node\Stmt\Throw_) { $this->throwFound = true; } + return null; } /** diff --git a/src/Rules/Exceptions/ThrowMustBundlePreviousExceptionRule.php b/src/Rules/Exceptions/ThrowMustBundlePreviousExceptionRule.php index 3793569..b725f9c 100644 --- a/src/Rules/Exceptions/ThrowMustBundlePreviousExceptionRule.php +++ b/src/Rules/Exceptions/ThrowMustBundlePreviousExceptionRule.php @@ -14,6 +14,8 @@ /** * When throwing into a catch block, checks that the previous exception is passed to the new "throw" clause * (the initial stack trace must not be lost). + * + * @implements Rule */ class ThrowMustBundlePreviousExceptionRule implements Rule { @@ -34,7 +36,13 @@ public function processNode(Node $node, Scope $scope): array * @var string */ private $catchedVariableName; + /** + * @var int + */ private $exceptionUsedCount = 0; + /** + * @var Node\Stmt\Throw_[] + */ private $unusedThrows = []; public function __construct(string $catchedVariableName) @@ -48,6 +56,7 @@ public function leaveNode(Node $node) if ($node->name === $this->catchedVariableName) { $this->exceptionUsedCount++; } + return null; } // If the variable is used in the context of a method call (like $e->getMessage()), the exception is not passed as a "previous exception". @@ -60,6 +69,7 @@ public function leaveNode(Node $node) if ($node instanceof Node\Stmt\Throw_ && $this->exceptionUsedCount === 0) { $this->unusedThrows[] = $node; } + return null; } /** diff --git a/src/Rules/Superglobals/NoSuperglobalsRule.php b/src/Rules/Superglobals/NoSuperglobalsRule.php index 6856663..2bd72fa 100644 --- a/src/Rules/Superglobals/NoSuperglobalsRule.php +++ b/src/Rules/Superglobals/NoSuperglobalsRule.php @@ -13,6 +13,8 @@ /** * This rule checks that no superglobals are used in code. + * + * @implements Rule */ class NoSuperglobalsRule implements Rule { diff --git a/src/Rules/TypeHints/AbstractMissingTypeHintRule.php b/src/Rules/TypeHints/AbstractMissingTypeHintRule.php deleted file mode 100644 index 1abf228..0000000 --- a/src/Rules/TypeHints/AbstractMissingTypeHintRule.php +++ /dev/null @@ -1,371 +0,0 @@ -broker = $broker; - } - - abstract public function getNodeType(): string; - - abstract public function isReturnIgnored(Node $node): bool; - - abstract protected function getReflection(Node\FunctionLike $function, Scope $scope, Broker $broker) : ParametersAcceptorWithPhpDocs; - - abstract protected function shouldSkip(Node\FunctionLike $function, Scope $scope): bool; - - /** - * @param \PhpParser\Node\Stmt\Function_|\PhpParser\Node\Stmt\ClassMethod $node - * @param \PHPStan\Analyser\Scope $scope - * @return string[] - */ - public function processNode(Node $node, Scope $scope): array - { - /*if ($node->getLine() < 0) { - // Fixes some problems with methods in anonymous class (the line number is poorly reported). - return []; - }*/ - - if ($this->shouldSkip($node, $scope)) { - return []; - } - - $parametersAcceptor = $this->getReflection($node, $scope, $this->broker); - - $errors = []; - - foreach ($parametersAcceptor->getParameters() as $parameter) { - if ($parameter instanceof PhpParameterReflection) { - $debugContext = new ParameterDebugContext($scope, $node, $parameter); - $result = $this->analyzeParameter($debugContext, $parameter); - - if ($result !== null) { - $errors[] = $result; - } - } - } - - if (!$this->isReturnIgnored($node)) { - $debugContext = new FunctionDebugContext($scope, $node); - $returnTypeError = $this->analyzeReturnType($debugContext, $parametersAcceptor); - if ($returnTypeError !== null) { - $errors[] = $returnTypeError; - } - } - - return $errors; - } - - /** - * Analyzes a parameter and returns the error string if something goes wrong or null if everything is ok. - * - * @param PhpParameterReflection $parameter - * @return null|string - */ - private function analyzeParameter(DebugContextInterface $context, PhpParameterReflection $parameter): ?string - { - //$typeResolver = new \phpDocumentor\Reflection\TypeResolver(); - - $phpTypeHint = $parameter->getNativeType(); - //try { - $docBlockTypeHints = $parameter->getPhpDocType(); - /*} catch (\InvalidArgumentException $e) { - return sprintf('%s, for parameter $%s, invalid docblock @param encountered. %s', - $this->getContext($parameter), - $parameter->getName(), - $e->getMessage() - ); - }*/ - - if ($phpTypeHint instanceof MixedType && $phpTypeHint->isExplicitMixed() === false) { - return $this->analyzeWithoutTypehint($context, $docBlockTypeHints); - } else { - // If there is a type-hint, we have nothing to say unless it is an array. - if ($parameter->isVariadic()) { - // Hack: wrap the native type in an array is variadic - $phpTypeHint = new ArrayType(new IntegerType(), $phpTypeHint); - } - - return $this->analyzeWithTypehint($context, $phpTypeHint, $docBlockTypeHints); - } - } - - /** - * @return null|string - */ - private function analyzeReturnType(DebugContextInterface $debugContext, ParametersAcceptorWithPhpDocs $function): ?string - { - $phpTypeHint = $function->getNativeReturnType(); - $docBlockTypeHints = $function->getPhpDocReturnType(); - - // If there is a type-hint, we have nothing to say unless it is an array. - if ($phpTypeHint instanceof MixedType && $phpTypeHint->isExplicitMixed() === false) { - return $this->analyzeWithoutTypehint($debugContext, $docBlockTypeHints); - } else { - return $this->analyzeWithTypehint($debugContext, $phpTypeHint, $docBlockTypeHints); - } - } - - /** - * @param DebugContextInterface $debugContext - * @param Type $phpTypeHint - * @param Type $docBlockTypeHints - * @return null|string - */ - private function analyzeWithTypehint(DebugContextInterface $debugContext, Type $phpTypeHint, Type $docBlockTypeHints): ?string - { - $docblockWithoutNullable = $this->typesWithoutNullable($docBlockTypeHints); - - if (!$this->isTypeIterable($phpTypeHint)) { - // FIXME: this should be handled with the "accepts" method of types (and actually, this is already triggered by PHPStan 0.10) - - if ($docBlockTypeHints instanceof MixedType && $docBlockTypeHints->isExplicitMixed() === false) { - // No docblock. - return null; - } - - // Let's detect mismatches between docblock and PHP typehint - if ($docblockWithoutNullable instanceof UnionType) { - $docblocks = $docblockWithoutNullable->getTypes(); - } else { - $docblocks = [$docblockWithoutNullable]; - } - $phpTypeHintWithoutNullable = $this->typesWithoutNullable($phpTypeHint); - foreach ($docblocks as $docblockTypehint) { - if (get_class($docblockTypehint) !== get_class($phpTypeHintWithoutNullable)) { - if ($debugContext instanceof ParameterDebugContext) { - return sprintf('%s type is type-hinted to "%s" but the @param annotation says it is a "%s". Please fix the @param annotation.', (string) $debugContext, $phpTypeHint->describe(VerbosityLevel::typeOnly()), $docblockTypehint->describe(VerbosityLevel::typeOnly())); - } elseif (!$docblockTypehint instanceof MixedType || $docblockTypehint->isExplicitMixed()) { - return sprintf('%s return type is type-hinted to "%s" but the @return annotation says it is a "%s". Please fix the @return annotation.', (string) $debugContext, $phpTypeHint->describe(VerbosityLevel::typeOnly()), $docblockTypehint->describe(VerbosityLevel::typeOnly())); - } - } - } - - return null; - } - - if ($phpTypeHint instanceof ArrayType) { - if ($docblockWithoutNullable instanceof MixedType && !$docblockWithoutNullable->isExplicitMixed()) { - if ($debugContext instanceof ParameterDebugContext) { - return sprintf('%s type is "array". Please provide a @param annotation to further specify the type of the array. For instance: @param int[] $%s. More info: http://bit.ly/typehintarray', (string) $debugContext, $debugContext->getName()); - } else { - return sprintf('%s return type is "array". Please provide a @return annotation to further specify the type of the array. For instance: @return int[]. More info: http://bit.ly/typehintarray', (string) $debugContext); - } - } else { - if ($docblockWithoutNullable instanceof UnionType) { - $docblocks = $docblockWithoutNullable->getTypes(); - } else { - $docblocks = [$docblockWithoutNullable]; - } - foreach ($docblocks as $docblockTypehint) { - if (!$this->isTypeIterable($docblockTypehint)) { - if ($debugContext instanceof ParameterDebugContext) { - return sprintf('%s mismatching type-hints for parameter %s. PHP type hint is "array" and docblock type hint is %s.', (string) $debugContext, $debugContext->getName(), $docblockTypehint->describe(VerbosityLevel::typeOnly())); - } else { - return sprintf('%s mismatching type-hints for return type. PHP type hint is "array" and docblock declared return type is %s.', (string) $debugContext, $docblockTypehint->describe(VerbosityLevel::typeOnly())); - } - } - - if ($docblockTypehint instanceof ArrayType && $docblockTypehint->getKeyType() instanceof MixedType && $docblockTypehint->getItemType() instanceof MixedType && $docblockTypehint->getKeyType()->isExplicitMixed() && $docblockTypehint->getItemType()->isExplicitMixed()) { - if ($debugContext instanceof ParameterDebugContext) { - return sprintf('%s type is "array". Please provide a more specific @param annotation in the docblock. For instance: @param int[] $%s. Use @param mixed[] $%s if this is really an array of mixed values. More info: http://bit.ly/typehintarray', (string) $debugContext, $debugContext->getName(), $debugContext->getName()); - } else { - return sprintf('%s return type is "array". Please provide a more specific @return annotation. For instance: @return int[]. Use @return mixed[] if this is really an array of mixed values. More info: http://bit.ly/typehintarray', (string) $debugContext); - } - } - } - } - } - - return null; - } - - private function isTypeIterable(Type $phpTypeHint) : bool - { - return /*$phpTypeHint->isIterable()->maybe() ||*/ $phpTypeHint->isIterable()->yes(); - /*if ($phpTypeHint instanceof Array_ || $phpTypeHint instanceof Iterable_) { - return true; - } - if ($phpTypeHint instanceof Object_) { - // TODO: cache BetterReflection for better performance! - try { - $class = (new BetterReflection())->classReflector()->reflect((string) $phpTypeHint); - } catch (IdentifierNotFound $e) { - // Class not found? Let's not throw an error. It will be caught by other rules anyway. - return false; - } - if ($class->implementsInterface('Traversable')) { - return true; - } - } - - return false;*/ - } - - /** - * @param DebugContextInterface $debugContext - * @param Type $docBlockTypeHints - * @return null|string - */ - private function analyzeWithoutTypehint(DebugContextInterface $debugContext, Type $docBlockTypeHints): ?string - { - if ($docBlockTypeHints instanceof MixedType && $docBlockTypeHints->isExplicitMixed() === false) { - if ($debugContext instanceof ParameterDebugContext) { - return sprintf('%s has no type-hint and no @param annotation. More info: http://bit.ly/usetypehint', (string) $debugContext); - } else { - return sprintf('%s there is no return type and no @return annotation. More info: http://bit.ly/usetypehint', (string) $debugContext); - } - } - - $nativeTypehint = $this->isNativelyTypehintable($docBlockTypeHints); - - if ($nativeTypehint !== null) { - if ($debugContext instanceof ParameterDebugContext) { - return sprintf('%s can be type-hinted to "%s". More info: http://bit.ly/usetypehint', (string) $debugContext, $nativeTypehint); - } else { - return sprintf('%s a "%s" return type can be added. More info: http://bit.ly/usetypehint', (string) $debugContext, $nativeTypehint); - } - } - - return null; - } - - /** - * @param Type $docBlockTypeHints - * @return string|null - */ - private function isNativelyTypehintable(Type $docBlockTypeHints): ?string - { - if ($docBlockTypeHints instanceof UnionType) { - $count = count($docBlockTypeHints->getTypes()); - } else { - $count = 1; - } - - if ($count > 2) { - return null; - } - $isNullable = $this->isNullable($docBlockTypeHints); - if ($count === 2 && !$isNullable) { - return null; - } - - $type = $this->typesWithoutNullable($docBlockTypeHints); - // At this point, there is at most one element here - /*if (empty($type)) { - return null; - }*/ - - //$type = $types[0]; - - // "object" type-hint is not available in PHP 7.1 - if ($type instanceof ObjectWithoutClassType) { - // In PHP 7.2, this is true but not in PHP 7.1 - return null; - } - - if ($type instanceof ObjectType) { - return ($isNullable?'?':'').'\\'.$type->describe(VerbosityLevel::typeOnly()); - } - - if ($type instanceof ArrayType) { - return ($isNullable?'?':'').'array'; - } - - if ($this->isNativeType($type)) { - return ($isNullable?'?':'').$type->describe(VerbosityLevel::typeOnly()); - } - - // TODO: more definitions to add here - // Manage interface/classes - // Manage array of things => (cast to array) - - - return null; - } - - private function isNativeType(Type $type): bool - { - if ($type instanceof StringType - || $type instanceof IntegerType - || $type instanceof BooleanType - || $type instanceof FloatType - || $type instanceof CallableType - || $type instanceof IterableType - || $type instanceof VoidType - ) { - return true; - } - return false; - } - - /** - * @param Type $docBlockTypeHints - * @return bool - */ - private function isNullable(Type $docBlockTypeHints): bool - { - if ($docBlockTypeHints instanceof UnionType) { - foreach ($docBlockTypeHints->getTypes() as $docBlockTypeHint) { - if ($docBlockTypeHint instanceof NullType) { - return true; - } - } - } - return false; - } - - /** - * Removes "null" from the list of types. - * - * @param Type $docBlockTypeHints - * @return Type - */ - private function typesWithoutNullable(Type $docBlockTypeHints): Type - { - if ($docBlockTypeHints instanceof UnionType) { - $filteredTypes = array_values(array_filter($docBlockTypeHints->getTypes(), function (Type $item) { - return !$item instanceof NullType; - })); - if (\count($filteredTypes) === 1) { - return $filteredTypes[0]; - } - return new UnionType($filteredTypes); - } - return $docBlockTypeHints; - } -} diff --git a/src/Rules/TypeHints/DebugContextInterface.php b/src/Rules/TypeHints/DebugContextInterface.php deleted file mode 100644 index 759208f..0000000 --- a/src/Rules/TypeHints/DebugContextInterface.php +++ /dev/null @@ -1,10 +0,0 @@ -function = $function; - $this->scope = $scope; - } - - public function __toString() - { - if ($this->function instanceof ClassMethod) { - if (!$this->scope->isInClass()) { - return 'Should not happen'; - } - - return sprintf('In method "%s::%s",', $this->scope->getClassReflection()->getDisplayName(), $this->function->name->name); - } - elseif ($this->function instanceof Function_) { - return sprintf('In function "%s",', $this->function->name->name); - } - return 'Should not happen'; - } -} \ No newline at end of file diff --git a/src/Rules/TypeHints/MissingTypeHintInFunctionRule.php b/src/Rules/TypeHints/MissingTypeHintInFunctionRule.php deleted file mode 100644 index 1b0a86a..0000000 --- a/src/Rules/TypeHints/MissingTypeHintInFunctionRule.php +++ /dev/null @@ -1,50 +0,0 @@ -name->name; - if (isset($function->namespacedName)) { - $functionName = (string) $function->namespacedName; - } - $functionNameName = new Node\Name($functionName); - if (!$broker->hasCustomFunction($functionNameName, null)) { - throw new \RuntimeException("Cannot find function '$functionName'"); - } - $functionReflection = $broker->getCustomFunction($functionNameName, null); - /** @var \PHPStan\Reflection\ParametersAcceptorWithPhpDocs $parametersAcceptor */ - $parametersAcceptor = ParametersAcceptorSelector::selectSingle($functionReflection->getVariants()); - return $parametersAcceptor; - } - - protected function shouldSkip(Node\FunctionLike $function, Scope $scope): bool - { - return false; - } -} diff --git a/src/Rules/TypeHints/MissingTypeHintInMethodRule.php b/src/Rules/TypeHints/MissingTypeHintInMethodRule.php deleted file mode 100644 index 8c59690..0000000 --- a/src/Rules/TypeHints/MissingTypeHintInMethodRule.php +++ /dev/null @@ -1,101 +0,0 @@ - true, - '__destruct' => true, - '__call' => true, - '__callStatic' => true, - '__get' => true, - '__set' => true, - '__isset' => true, - '__unset' => true, - '__sleep' => true, - '__wakeup' => true, - '__toString' => true, - '__invoke' => true, - '__set_state' => true, - '__clone' => true, - '__debugInfo' => true - ]; - - public function getNodeType(): string - { - return Node\Stmt\ClassMethod::class; - } - - /** - * @param Node\Stmt\ClassMethod $node - * @return bool - */ - public function isReturnIgnored(Node $node): bool - { - return isset(self::RETURN_BLACKLIST[$node->name->name]); - } - - protected function getReflection(Node\FunctionLike $function, Scope $scope, Broker $broker) : ParametersAcceptorWithPhpDocs - { - if (!$scope->isInClass()) { - throw new \PHPStan\ShouldNotHappenException(); - } - $nativeMethod = $scope->getClassReflection()->getNativeMethod($function->name->name); - if (!$nativeMethod instanceof PhpMethodReflection) { - throw new \PHPStan\ShouldNotHappenException(); - } - /** @var \PHPStan\Reflection\ParametersAcceptorWithPhpDocs $parametersAcceptor */ - $parametersAcceptor = ParametersAcceptorSelector::selectSingle($nativeMethod->getVariants()); - return $parametersAcceptor; - } - - protected function shouldSkip(Node\FunctionLike $function, Scope $scope): bool - { - // We should skip if the method is inherited! - if (!$scope->isInClass()) { - throw new \PHPStan\ShouldNotHappenException(); - } - $nativeMethod = $scope->getClassReflection()->getNativeMethod($function->name->name); - - return $this->isInherited2($nativeMethod, $scope->getClassReflection()); - - } - - private function isInherited2(MethodReflection $method, ClassReflection $class = null): bool - { - if ($class === null) { - $class = $method->getDeclaringClass(); - } - $interfaces = $class->getInterfaces(); - foreach ($interfaces as $interface) { - if ($interface->hasMethod($method->getName())) { - return true; - } - } - - $parentClass = $class->getParentClass(); - if ($parentClass !== false) { - if ($parentClass->hasMethod($method->getName())) { - return true; - } - return $this->isInherited2($method, $parentClass); - } - - return false; - } -} diff --git a/src/Rules/TypeHints/ParameterDebugContext.php b/src/Rules/TypeHints/ParameterDebugContext.php deleted file mode 100644 index b184e31..0000000 --- a/src/Rules/TypeHints/ParameterDebugContext.php +++ /dev/null @@ -1,56 +0,0 @@ -function = $function; - $this->parameter = $parameter; - $this->scope = $scope; - } - - public function __toString() - { - if ($this->function instanceof ClassMethod) { - if (!$this->scope->isInClass()) { - return 'Should not happen'; - } - - return sprintf('In method "%s::%s", parameter $%s', $this->scope->getClassReflection()->getDisplayName(), $this->function->name->name, $this->parameter->getName()); - } - elseif ($this->function instanceof Function_) { - return sprintf('In function "%s", parameter $%s', $this->function->name->name, $this->parameter->getName()); - } - return 'Should not happen'; - } - - public function getName(): string - { - return $this->parameter->getName(); - } -} \ No newline at end of file diff --git a/tests/Rules/TypeHints/MissingTypeHintRuleInFunctionTest.php b/tests/Rules/TypeHints/MissingTypeHintRuleInFunctionTest.php deleted file mode 100644 index c04d0ec..0000000 --- a/tests/Rules/TypeHints/MissingTypeHintRuleInFunctionTest.php +++ /dev/null @@ -1,100 +0,0 @@ -createBroker() - ); - } - - public function testCheckCatchedException() - { - require_once __DIR__.'/data/typehints.php'; - - $this->analyse([__DIR__ . '/data/typehints.php'], [ - [ - 'In function "test", parameter $no_type_hint has no type-hint and no @param annotation. More info: http://bit.ly/usetypehint', - 3, - ], - [ - 'In function "test", there is no return type and no @return annotation. More info: http://bit.ly/usetypehint', - 3, - ], - [ - 'In function "test2", parameter $type_hintable can be type-hinted to "?string". More info: http://bit.ly/usetypehint', - 11, - ], - [ - 'In function "test2", a "string" return type can be added. More info: http://bit.ly/usetypehint', - 11, - ], - [ - 'In function "test3", parameter $type_hintable can be type-hinted to "\DateTimeInterface". More info: http://bit.ly/usetypehint', - 19, - ], - [ - 'In function "test3", a "\DateTimeInterface" return type can be added. More info: http://bit.ly/usetypehint', - 19, - ], - [ - 'In function "test4", parameter $type_hintable can be type-hinted to "array". More info: http://bit.ly/usetypehint', - 27, - ], - [ - 'In function "test4", a "array" return type can be added. More info: http://bit.ly/usetypehint', - 27, - ], - [ - 'In function "test6", parameter $better_type_hint type is "array". Please provide a @param annotation to further specify the type of the array. For instance: @param int[] $better_type_hint. More info: http://bit.ly/typehintarray', - 38, - ], - [ - 'In function "test6", return type is "array". Please provide a @return annotation to further specify the type of the array. For instance: @return int[]. More info: http://bit.ly/typehintarray', - 38, - ], - [ - 'In function "mismatch", parameter $param type is type-hinted to "string|null" but the @param annotation says it is a "int". Please fix the @param annotation.', - 46, - ], - [ - 'In function "mismatch", return type is type-hinted to "string" but the @return annotation says it is a "int". Please fix the @return annotation.', - 46, - ], - [ - 'In function "test8", parameter $any_array type is "array". Please provide a more specific @param annotation in the docblock. For instance: @param int[] $any_array. Use @param mixed[] $any_array if this is really an array of mixed values. More info: http://bit.ly/typehintarray', - 62, - ], - [ - 'In function "test8", return type is "array". Please provide a more specific @return annotation. For instance: @return int[]. Use @return mixed[] if this is really an array of mixed values. More info: http://bit.ly/typehintarray', - 62, - ], - [ - 'In function "test10", parameter $id has no type-hint and no @param annotation. More info: http://bit.ly/usetypehint', - 76, - ], - [ - 'In function "test13", parameter $type_hintable type is type-hinted to "ClassDoesNotExist" but the @param annotation says it is a "array". Please fix the @param annotation.', - 97, - ], - [ - 'In function "test15", parameter $foo type is "array". Please provide a @param annotation to further specify the type of the array. For instance: @param int[] $foo. More info: http://bit.ly/typehintarray', - 110, - ], - [ - 'In function "test15", mismatching type-hints for return type. PHP type hint is "array" and docblock declared return type is a.', - 110, - ], - [ - 'In function "test19", a "void" return type can be added. More info: http://bit.ly/usetypehint', - 139, - ] - - ]); - } -} diff --git a/tests/Rules/TypeHints/MissingTypeHintRuleInMethodTest.php b/tests/Rules/TypeHints/MissingTypeHintRuleInMethodTest.php deleted file mode 100644 index 6e2db8c..0000000 --- a/tests/Rules/TypeHints/MissingTypeHintRuleInMethodTest.php +++ /dev/null @@ -1,32 +0,0 @@ -createBroker() - ); - } - - public function testCheckCatchedException() - { - require_once __DIR__.'/data/typehints_in_methods.php'; - - $this->analyse([__DIR__ . '/data/typehints_in_methods.php'], [ - [ - 'In method "TheCodingMachine\PHPStan\Rules\TypeHints\data\Foo::test", parameter $no_type_hint has no type-hint and no @param annotation. More info: http://bit.ly/usetypehint', - 9, - ], - [ - 'In method "TheCodingMachine\PHPStan\Rules\TypeHints\data\BazClass::notInherited", parameter $no_type_hint has no type-hint and no @param annotation. More info: http://bit.ly/usetypehint', - 37, - ], - - ]); - } -} diff --git a/tests/Rules/TypeHints/data/StubClass.php b/tests/Rules/TypeHints/data/StubClass.php deleted file mode 100644 index 6e04dd1..0000000 --- a/tests/Rules/TypeHints/data/StubClass.php +++ /dev/null @@ -1,19 +0,0 @@ -