diff --git a/README.md b/README.md index d22abcf8..151899f3 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ * Always true `instanceof`, type-checking `is_*` functions and strict comparisons `===`/`!==`. These checks can be turned off by setting `checkAlwaysTrueInstanceof`/`checkAlwaysTrueCheckTypeFunctionCall`/`checkAlwaysTrueStrictComparison` to false. * Correct case for referenced and called function names. * Correct case for inherited and implemented method names. +* Disallows the usage of deprecated code. Additional rules are coming in subsequent releases! diff --git a/rules.neon b/rules.neon index a5bc9d92..dd4dd5b0 100644 --- a/rules.neon +++ b/rules.neon @@ -12,6 +12,17 @@ rules: - PHPStan\Rules\BooleansInConditions\BooleanInElseIfConditionRule - PHPStan\Rules\BooleansInConditions\BooleanInIfConditionRule - PHPStan\Rules\BooleansInConditions\BooleanInTernaryOperatorRule + - PHPStan\Rules\Deprecations\AccessDeprecatedPropertyRule + - PHPStan\Rules\Deprecations\AccessDeprecatedStaticPropertyRule + - PHPStan\Rules\Deprecations\CallToDeprecatedFunctionRule + - PHPStan\Rules\Deprecations\CallToDeprecatedMethodRule + - PHPStan\Rules\Deprecations\CallToDeprecatedStaticMethodRule + - PHPStan\Rules\Deprecations\FetchingClassConstOfDeprecatedClassRule + - PHPStan\Rules\Deprecations\ImplementationOfDeprecatedInterfaceRule + - PHPStan\Rules\Deprecations\InheritanceOfDeprecatedClassRule + - PHPStan\Rules\Deprecations\InheritanceOfDeprecatedInterfaceRule + - PHPStan\Rules\Deprecations\InstantiationOfDeprecatedClassRule + - PHPStan\Rules\Deprecations\UsageOfDeprecatedTraitRule - PHPStan\Rules\DisallowedConstructs\DisallowedEmptyRule - PHPStan\Rules\DisallowedConstructs\DisallowedImplicitArrayCreationRule - PHPStan\Rules\Functions\MissingFunctionParameterTypehintRule diff --git a/src/Rules/Deprecations/AccessDeprecatedPropertyRule.php b/src/Rules/Deprecations/AccessDeprecatedPropertyRule.php new file mode 100644 index 00000000..c0362024 --- /dev/null +++ b/src/Rules/Deprecations/AccessDeprecatedPropertyRule.php @@ -0,0 +1,74 @@ +broker = $broker; + } + + public function getNodeType(): string + { + return PropertyFetch::class; + } + + /** + * @param PropertyFetch $node + * @param \PHPStan\Analyser\Scope $scope + * @return string[] errors + */ + public function processNode(Node $node, Scope $scope): array + { + if (DeprecatedScopeHelper::isScopeDeprecated($scope)) { + return []; + } + + if (!$node->name instanceof Identifier) { + return []; + } + + $propertyName = $node->name->name; + $propertyAccessedOnType = $scope->getType($node->var); + $referencedClasses = TypeUtils::getDirectClassNames($propertyAccessedOnType); + + foreach ($referencedClasses as $referencedClass) { + try { + $classReflection = $this->broker->getClass($referencedClass); + $propertyReflection = $classReflection->getProperty($propertyName, $scope); + + if (!$propertyReflection instanceof DeprecatableReflection) { + continue; + } + + if ($propertyReflection->isDeprecated()) { + return [sprintf( + 'Access to deprecated property $%s of class %s.', + $propertyName, + $propertyReflection->getDeclaringClass()->getName() + )]; + } + } catch (\PHPStan\Broker\ClassNotFoundException $e) { + // Other rules will notify if the class is not found + } catch (\PHPStan\Reflection\MissingPropertyFromReflectionException $e) { + // Other rules will notify if the property is not found + } + } + + return []; + } + +} diff --git a/src/Rules/Deprecations/AccessDeprecatedStaticPropertyRule.php b/src/Rules/Deprecations/AccessDeprecatedStaticPropertyRule.php new file mode 100644 index 00000000..c4768b26 --- /dev/null +++ b/src/Rules/Deprecations/AccessDeprecatedStaticPropertyRule.php @@ -0,0 +1,95 @@ +broker = $broker; + $this->ruleLevelHelper = $ruleLevelHelper; + } + + public function getNodeType(): string + { + return StaticPropertyFetch::class; + } + + /** + * @param StaticPropertyFetch $node + * @param \PHPStan\Analyser\Scope $scope + * @return string[] errors + */ + public function processNode(Node $node, Scope $scope): array + { + if (DeprecatedScopeHelper::isScopeDeprecated($scope)) { + return []; + } + + if (!$node->name instanceof Identifier) { + return []; + } + + $propertyName = $node->name->name; + $referencedClasses = []; + + if ($node->class instanceof Name) { + $referencedClasses[] = (string) $node->class; + } else { + $classTypeResult = $this->ruleLevelHelper->findTypeToCheck( + $scope, + $node->class, + '', // We don't care about the error message + function (Type $type) use ($propertyName) { + return $type->canAccessProperties()->yes() && $type->hasProperty($propertyName); + } + ); + + if ($classTypeResult->getType() instanceof ErrorType) { + return []; + } + + $referencedClasses = $classTypeResult->getReferencedClasses(); + } + + foreach ($referencedClasses as $referencedClass) { + try { + $class = $this->broker->getClass($referencedClass); + $property = $class->getProperty($propertyName, $scope); + } catch (\PHPStan\Broker\ClassNotFoundException $e) { + continue; + } catch (\PHPStan\Reflection\MissingPropertyFromReflectionException $e) { + continue; + } + + if ($property instanceof DeprecatableReflection && $property->isDeprecated()) { + return [sprintf( + 'Access to deprecated static property $%s of class %s.', + $propertyName, + $referencedClass + )]; + } + } + + return []; + } + +} diff --git a/src/Rules/Deprecations/CallToDeprecatedFunctionRule.php b/src/Rules/Deprecations/CallToDeprecatedFunctionRule.php new file mode 100644 index 00000000..fd4fbad0 --- /dev/null +++ b/src/Rules/Deprecations/CallToDeprecatedFunctionRule.php @@ -0,0 +1,58 @@ +broker = $broker; + } + + public function getNodeType(): string + { + return FuncCall::class; + } + + /** + * @param FuncCall $node + * @param \PHPStan\Analyser\Scope $scope + * @return string[] errors + */ + public function processNode(Node $node, Scope $scope): array + { + if (DeprecatedScopeHelper::isScopeDeprecated($scope)) { + return []; + } + + if (!($node->name instanceof \PhpParser\Node\Name)) { + return []; + } + + try { + $function = $this->broker->getFunction($node->name, $scope); + } catch (\PHPStan\Broker\FunctionNotFoundException $e) { + // Other rules will notify if the function is not found + return []; + } + + if ($function->isDeprecated()) { + return [sprintf( + 'Call to deprecated function %s().', + $function->getName() + )]; + } + + return []; + } + +} diff --git a/src/Rules/Deprecations/CallToDeprecatedMethodRule.php b/src/Rules/Deprecations/CallToDeprecatedMethodRule.php new file mode 100644 index 00000000..5870176c --- /dev/null +++ b/src/Rules/Deprecations/CallToDeprecatedMethodRule.php @@ -0,0 +1,74 @@ +broker = $broker; + } + + public function getNodeType(): string + { + return MethodCall::class; + } + + /** + * @param MethodCall $node + * @param \PHPStan\Analyser\Scope $scope + * @return string[] errors + */ + public function processNode(Node $node, Scope $scope): array + { + if (DeprecatedScopeHelper::isScopeDeprecated($scope)) { + return []; + } + + if (!$node->name instanceof Identifier) { + return []; + } + + $methodName = $node->name->name; + $methodCalledOnType = $scope->getType($node->var); + $referencedClasses = TypeUtils::getDirectClassNames($methodCalledOnType); + + foreach ($referencedClasses as $referencedClass) { + try { + $classReflection = $this->broker->getClass($referencedClass); + $methodReflection = $classReflection->getMethod($methodName, $scope); + + if (!$methodReflection instanceof DeprecatableReflection) { + continue; + } + + if ($methodReflection->isDeprecated()) { + return [sprintf( + 'Call to deprecated method %s() of class %s.', + $methodReflection->getName(), + $methodReflection->getDeclaringClass()->getName() + )]; + } + } catch (\PHPStan\Broker\ClassNotFoundException $e) { + // Other rules will notify if the class is not found + } catch (\PHPStan\Reflection\MissingMethodFromReflectionException $e) { + // Other rules will notify if the the method is not found + } + } + + return []; + } + +} diff --git a/src/Rules/Deprecations/CallToDeprecatedStaticMethodRule.php b/src/Rules/Deprecations/CallToDeprecatedStaticMethodRule.php new file mode 100644 index 00000000..47184892 --- /dev/null +++ b/src/Rules/Deprecations/CallToDeprecatedStaticMethodRule.php @@ -0,0 +1,107 @@ +broker = $broker; + $this->ruleLevelHelper = $ruleLevelHelper; + } + + public function getNodeType(): string + { + return StaticCall::class; + } + + /** + * @param StaticCall $node + * @param \PHPStan\Analyser\Scope $scope + * @return string[] errors + */ + public function processNode(Node $node, Scope $scope): array + { + if (DeprecatedScopeHelper::isScopeDeprecated($scope)) { + return []; + } + + if (!$node->name instanceof Identifier) { + return []; + } + + $methodName = $node->name->name; + $referencedClasses = []; + + if ($node->class instanceof Name) { + $referencedClasses[] = $scope->resolveName($node->class); + } else { + $classTypeResult = $this->ruleLevelHelper->findTypeToCheck( + $scope, + $node->class, + '', // We don't care about the error message + function (Type $type) use ($methodName) { + return $type->canCallMethods()->yes() && $type->hasMethod($methodName); + } + ); + + if ($classTypeResult->getType() instanceof ErrorType) { + return []; + } + + $referencedClasses = $classTypeResult->getReferencedClasses(); + } + + $errors = []; + + foreach ($referencedClasses as $referencedClass) { + try { + $class = $this->broker->getClass($referencedClass); + $methodReflection = $class->getMethod($methodName, $scope); + } catch (\PHPStan\Broker\ClassNotFoundException $e) { + continue; + } catch (\PHPStan\Reflection\MissingMethodFromReflectionException $e) { + continue; + } + + if ($class->isDeprecated()) { + $errors[] = sprintf( + 'Call to method %s() of deprecated class %s.', + $methodReflection->getName(), + $methodReflection->getDeclaringClass()->getName() + ); + } + + if (!$methodReflection instanceof DeprecatableReflection || !$methodReflection->isDeprecated()) { + continue; + } + + $errors[] = sprintf( + 'Call to deprecated method %s() of class %s.', + $methodReflection->getName(), + $methodReflection->getDeclaringClass()->getName() + ); + } + + return $errors; + } + +} diff --git a/src/Rules/Deprecations/DeprecatedScopeHelper.php b/src/Rules/Deprecations/DeprecatedScopeHelper.php new file mode 100644 index 00000000..bd99a418 --- /dev/null +++ b/src/Rules/Deprecations/DeprecatedScopeHelper.php @@ -0,0 +1,31 @@ +getClassReflection(); + if ($class !== null && $class->isDeprecated()) { + return true; + } + + $trait = $scope->getTraitReflection(); + if ($trait !== null && $trait->isDeprecated()) { + return true; + } + + $function = $scope->getFunction(); + if ($function instanceof DeprecatableReflection && $function->isDeprecated()) { + return true; + } + + return false; + } + +} diff --git a/src/Rules/Deprecations/FetchingClassConstOfDeprecatedClassRule.php b/src/Rules/Deprecations/FetchingClassConstOfDeprecatedClassRule.php new file mode 100644 index 00000000..7bac6b1e --- /dev/null +++ b/src/Rules/Deprecations/FetchingClassConstOfDeprecatedClassRule.php @@ -0,0 +1,110 @@ +broker = $broker; + $this->ruleLevelHelper = $ruleLevelHelper; + } + + public function getNodeType(): string + { + return ClassConstFetch::class; + } + + /** + * @param ClassConstFetch $node + * @param \PHPStan\Analyser\Scope $scope + * @return string[] errors + */ + public function processNode(Node $node, Scope $scope): array + { + if (DeprecatedScopeHelper::isScopeDeprecated($scope)) { + return []; + } + + if (!$node->name instanceof Identifier) { + return []; + } + + $constantName = $node->name->name; + $referencedClasses = []; + + if ($node->class instanceof Name) { + $referencedClasses[] = $scope->resolveName($node->class); + } else { + $classTypeResult = $this->ruleLevelHelper->findTypeToCheck( + $scope, + $node->class, + '', // We don't care about the error message + function (Type $type) use ($constantName) { + return $type->canAccessConstants()->yes() && $type->hasConstant($constantName); + } + ); + + if ($classTypeResult->getType() instanceof ErrorType) { + return []; + } + + $referencedClasses = $classTypeResult->getReferencedClasses(); + } + + $errors = []; + + foreach ($referencedClasses as $referencedClass) { + try { + $class = $this->broker->getClass($referencedClass); + } catch (\PHPStan\Broker\ClassNotFoundException $e) { + continue; + } + + if ($class->isDeprecated()) { + $errors[] = sprintf( + 'Fetching class constant %s of deprecated class %s.', + $constantName, + $referencedClass + ); + } + + if (!$class->hasConstant($constantName)) { + continue; + } + + $constantReflection = $class->getConstant($constantName); + + if (!$constantReflection instanceof DeprecatableReflection || !$constantReflection->isDeprecated()) { + continue; + } + + $errors[] = sprintf( + 'Fetching deprecated class constant %s of class %s.', + $constantName, + $referencedClass + ); + } + + return $errors; + } + +} diff --git a/src/Rules/Deprecations/ImplementationOfDeprecatedInterfaceRule.php b/src/Rules/Deprecations/ImplementationOfDeprecatedInterfaceRule.php new file mode 100644 index 00000000..183b38ee --- /dev/null +++ b/src/Rules/Deprecations/ImplementationOfDeprecatedInterfaceRule.php @@ -0,0 +1,77 @@ +broker = $broker; + } + + public function getNodeType(): string + { + return Class_::class; + } + + /** + * @param Class_ $node + * @param \PHPStan\Analyser\Scope $scope + * @return string[] errors + */ + public function processNode(Node $node, Scope $scope): array + { + $errors = []; + + $className = isset($node->namespacedName) + ? (string) $node->namespacedName + : (string) $node->name; + + try { + $class = $this->broker->getClass($className); + } catch (\PHPStan\Broker\ClassNotFoundException $e) { + return []; + } + + if ($class->isDeprecated()) { + return []; + } + + foreach ($node->implements as $implement) { + $interfaceName = (string) $implement; + + try { + $interface = $this->broker->getClass($interfaceName); + + if ($interface->isDeprecated()) { + if (!$class->getNativeReflection()->isAnonymous()) { + $errors[] = sprintf( + 'Class %s implements deprecated interface %s.', + $className, + $interfaceName + ); + } else { + $errors[] = sprintf( + 'Anonymous class implements deprecated interface %s.', + $interfaceName + ); + } + } + } catch (\PHPStan\Broker\ClassNotFoundException $e) { + // Other rules will notify if the interface is not found + } + } + + return $errors; + } + +} diff --git a/src/Rules/Deprecations/InheritanceOfDeprecatedClassRule.php b/src/Rules/Deprecations/InheritanceOfDeprecatedClassRule.php new file mode 100644 index 00000000..161ae423 --- /dev/null +++ b/src/Rules/Deprecations/InheritanceOfDeprecatedClassRule.php @@ -0,0 +1,79 @@ +broker = $broker; + } + + public function getNodeType(): string + { + return Class_::class; + } + + /** + * @param Class_ $node + * @param \PHPStan\Analyser\Scope $scope + * @return string[] errors + */ + public function processNode(Node $node, Scope $scope): array + { + if (DeprecatedScopeHelper::isScopeDeprecated($scope)) { + return []; + } + + if ($node->extends === null) { + return []; + } + + $errors = []; + + $className = isset($node->namespacedName) + ? (string) $node->namespacedName + : (string) $node->name; + + try { + $class = $this->broker->getClass($className); + } catch (\PHPStan\Broker\ClassNotFoundException $e) { + return []; + } + + $parentClassName = (string) $node->extends; + + try { + $parentClass = $this->broker->getClass($parentClassName); + + if ($parentClass->isDeprecated()) { + if (!$class->getNativeReflection()->isAnonymous()) { + $errors[] = sprintf( + 'Class %s extends deprecated class %s.', + $className, + $parentClassName + ); + } else { + $errors[] = sprintf( + 'Anonymous class extends deprecated class %s.', + $parentClassName + ); + } + } + } catch (\PHPStan\Broker\ClassNotFoundException $e) { + // Other rules will notify if the interface is not found + } + + return $errors; + } + +} diff --git a/src/Rules/Deprecations/InheritanceOfDeprecatedInterfaceRule.php b/src/Rules/Deprecations/InheritanceOfDeprecatedInterfaceRule.php new file mode 100644 index 00000000..9cc2b8b1 --- /dev/null +++ b/src/Rules/Deprecations/InheritanceOfDeprecatedInterfaceRule.php @@ -0,0 +1,74 @@ +broker = $broker; + } + + public function getNodeType(): string + { + return Interface_::class; + } + + /** + * @param Interface_ $node + * @param \PHPStan\Analyser\Scope $scope + * @return string[] errors + */ + public function processNode(Node $node, Scope $scope): array + { + if ($node->extends === null) { + return []; + } + + $interfaceName = isset($node->namespacedName) + ? (string) $node->namespacedName + : (string) $node->name; + + try { + $interface = $this->broker->getClass($interfaceName); + } catch (\PHPStan\Broker\ClassNotFoundException $e) { + return []; + } + + if ($interface->isDeprecated()) { + return []; + } + + $errors = []; + + foreach ($node->extends as $parentInterfaceName) { + $parentInterfaceName = (string) $parentInterfaceName; + + try { + $parentInterface = $this->broker->getClass($parentInterfaceName); + + if ($parentInterface->isDeprecated()) { + $errors[] = sprintf( + 'Interface %s extends deprecated interface %s.', + $interfaceName, + $parentInterfaceName + ); + } + } catch (\PHPStan\Broker\ClassNotFoundException $e) { + // Other rules will notify if the interface is not found + } + } + + return $errors; + } + +} diff --git a/src/Rules/Deprecations/InstantiationOfDeprecatedClassRule.php b/src/Rules/Deprecations/InstantiationOfDeprecatedClassRule.php new file mode 100644 index 00000000..363b03ca --- /dev/null +++ b/src/Rules/Deprecations/InstantiationOfDeprecatedClassRule.php @@ -0,0 +1,90 @@ +broker = $broker; + $this->ruleLevelHelper = $ruleLevelHelper; + } + + public function getNodeType(): string + { + return New_::class; + } + + /** + * @param New_ $node + * @param \PHPStan\Analyser\Scope $scope + * @return string[] errors + */ + public function processNode(Node $node, Scope $scope): array + { + if (DeprecatedScopeHelper::isScopeDeprecated($scope)) { + return []; + } + + $referencedClasses = []; + + if ($node->class instanceof Name) { + $referencedClasses[] = $scope->resolveName($node->class); + } elseif ($node->class instanceof Class_) { + $referencedClasses[] = $scope->resolveName($node->class->namespacedName); + } else { + $classTypeResult = $this->ruleLevelHelper->findTypeToCheck( + $scope, + $node->class, + '', // We don't care about the error message + function () { + return true; + } + ); + + if ($classTypeResult->getType() instanceof ErrorType) { + return []; + } + + $referencedClasses = $classTypeResult->getReferencedClasses(); + } + + $errors = []; + + foreach ($referencedClasses as $referencedClass) { + try { + $class = $this->broker->getClass($referencedClass); + } catch (\PHPStan\Broker\ClassNotFoundException $e) { + continue; + } + + if (!$class->isDeprecated()) { + continue; + } + + $errors[] = sprintf( + 'Instantiation of deprecated class %s.', + $referencedClass + ); + } + + return $errors; + } + +} diff --git a/src/Rules/Deprecations/UsageOfDeprecatedTraitRule.php b/src/Rules/Deprecations/UsageOfDeprecatedTraitRule.php new file mode 100644 index 00000000..19018ce3 --- /dev/null +++ b/src/Rules/Deprecations/UsageOfDeprecatedTraitRule.php @@ -0,0 +1,66 @@ +broker = $broker; + } + + public function getNodeType(): string + { + return TraitUse::class; + } + + /** + * @param TraitUse $node + * @param \PHPStan\Analyser\Scope $scope + * @return string[] errors + */ + public function processNode(Node $node, Scope $scope): array + { + if (DeprecatedScopeHelper::isScopeDeprecated($scope)) { + return []; + } + + $classReflection = $scope->getClassReflection(); + if ($classReflection === null) { + throw new \PHPStan\ShouldNotHappenException(); + } + + $errors = []; + $className = $classReflection->getName(); + + foreach ($node->traits as $traitNameNode) { + $traitName = (string) $traitNameNode; + + try { + $trait = $this->broker->getClass($traitName); + + if ($trait->isDeprecated()) { + $errors[] = sprintf( + 'Usage of deprecated trait %s in class %s.', + $traitName, + $className + ); + } + } catch (\PHPStan\Broker\ClassNotFoundException $e) { + continue; + } + } + + return $errors; + } + +} diff --git a/tests/Rules/Deprecations/AccessDeprecatedPropertyRuleTest.php b/tests/Rules/Deprecations/AccessDeprecatedPropertyRuleTest.php new file mode 100644 index 00000000..74a43c5a --- /dev/null +++ b/tests/Rules/Deprecations/AccessDeprecatedPropertyRuleTest.php @@ -0,0 +1,40 @@ +createBroker(); + return new AccessDeprecatedPropertyRule($broker); + } + + public function testAccessDeprecatedProperty(): void + { + require_once __DIR__ . '/data/access-deprecated-property-definition.php'; + $this->analyse( + [__DIR__ . '/data/access-deprecated-property.php'], + [ + [ + 'Access to deprecated property $deprecatedFoo of class AccessDeprecatedProperty\Foo.', + 10, + ], + [ + 'Access to deprecated property $deprecatedFoo of class AccessDeprecatedProperty\Foo.', + 11, + ], + [ + 'Access to deprecated property $deprecatedFooFromTrait of class AccessDeprecatedProperty\Foo.', + 16, + ], + [ + 'Access to deprecated property $deprecatedFooFromTrait of class AccessDeprecatedProperty\Foo.', + 17, + ], + ] + ); + } + +} diff --git a/tests/Rules/Deprecations/AccessDeprecatedStaticPropertyRuleTest.php b/tests/Rules/Deprecations/AccessDeprecatedStaticPropertyRuleTest.php new file mode 100644 index 00000000..75bc1bd6 --- /dev/null +++ b/tests/Rules/Deprecations/AccessDeprecatedStaticPropertyRuleTest.php @@ -0,0 +1,60 @@ +createBroker(); + $ruleLevelHelper = new RuleLevelHelper($this->createBroker(), true, false, true); + + return new AccessDeprecatedStaticPropertyRule($broker, $ruleLevelHelper); + } + + public function testAccessDeprecatedStaticProperty(): void + { + require_once __DIR__ . '/data/access-deprecated-static-property-definition.php'; + $this->analyse( + [__DIR__ . '/data/access-deprecated-static-property.php'], + [ + [ + 'Access to deprecated static property $deprecatedFoo of class AccessDeprecatedStaticProperty\Foo.', + 8, + ], + [ + 'Access to deprecated static property $deprecatedFoo of class AccessDeprecatedStaticProperty\Foo.', + 9, + ], + [ + 'Access to deprecated static property $deprecatedFoo of class AccessDeprecatedStaticProperty\Foo.', + 16, + ], + [ + 'Access to deprecated static property $deprecatedFoo of class AccessDeprecatedStaticProperty\Foo.', + 17, + ], + [ + 'Access to deprecated static property $deprecatedFooFromTrait of class AccessDeprecatedStaticProperty\FooTrait.', + 22, + ], + [ + 'Access to deprecated static property $deprecatedFooFromTrait of class AccessDeprecatedStaticProperty\FooTrait.', + 23, + ], + [ + 'Access to deprecated static property $deprecatedFooFromTrait of class AccessDeprecatedStaticProperty\Foo.', + 30, + ], + [ + 'Access to deprecated static property $deprecatedFooFromTrait of class AccessDeprecatedStaticProperty\Foo.', + 31, + ], + ] + ); + } + +} diff --git a/tests/Rules/Deprecations/CallToDeprecatedFunctionRuleTest.php b/tests/Rules/Deprecations/CallToDeprecatedFunctionRuleTest.php new file mode 100644 index 00000000..291e76a6 --- /dev/null +++ b/tests/Rules/Deprecations/CallToDeprecatedFunctionRuleTest.php @@ -0,0 +1,32 @@ +createBroker(); + return new CallToDeprecatedFunctionRule($broker); + } + + public function testDeprecatedFunctionCall(): void + { + require_once __DIR__ . '/data/call-to-deprecated-function-definition.php'; + $this->analyse( + [__DIR__ . '/data/call-to-deprecated-function.php'], + [ + [ + 'Call to deprecated function CheckDeprecatedFunctionCall\deprecated_foo().', + 8, + ], + [ + 'Call to deprecated function CheckDeprecatedFunctionCall\deprecated_foo().', + 9, + ], + ] + ); + } + +} diff --git a/tests/Rules/Deprecations/CallToDeprecatedMethodRuleTest.php b/tests/Rules/Deprecations/CallToDeprecatedMethodRuleTest.php new file mode 100644 index 00000000..38e2e13d --- /dev/null +++ b/tests/Rules/Deprecations/CallToDeprecatedMethodRuleTest.php @@ -0,0 +1,36 @@ +createBroker(); + return new CallToDeprecatedMethodRule($broker); + } + + public function testDeprecatedMethodCall(): void + { + require_once __DIR__ . '/data/call-to-deprecated-method-definition.php'; + $this->analyse( + [__DIR__ . '/data/call-to-deprecated-method.php'], + [ + [ + 'Call to deprecated method deprecatedFoo() of class CheckDeprecatedMethodCall\Foo.', + 7, + ], + [ + 'Call to deprecated method deprecatedFoo2() of class CheckDeprecatedMethodCall\Foo.', + 11, + ], + [ + 'Call to deprecated method deprecatedFooFromTrait() of class CheckDeprecatedMethodCall\Foo.', + 14, + ], + ] + ); + } + +} diff --git a/tests/Rules/Deprecations/CallToDeprecatedStaticMethodRuleTest.php b/tests/Rules/Deprecations/CallToDeprecatedStaticMethodRuleTest.php new file mode 100644 index 00000000..e5b586db --- /dev/null +++ b/tests/Rules/Deprecations/CallToDeprecatedStaticMethodRuleTest.php @@ -0,0 +1,60 @@ +createBroker(); + $ruleLevelHelper = new RuleLevelHelper($this->createBroker(), true, false, true); + + return new CallToDeprecatedStaticMethodRule($broker, $ruleLevelHelper); + } + + public function testDeprecatedStaticMethodCall(): void + { + require_once __DIR__ . '/data/call-to-deprecated-static-method-definition.php'; + $this->analyse( + [__DIR__ . '/data/call-to-deprecated-static-method.php'], + [ + [ + 'Call to deprecated method deprecatedFoo() of class CheckDeprecatedStaticMethodCall\Foo.', + 6, + ], + [ + 'Call to deprecated method deprecatedFoo2() of class CheckDeprecatedStaticMethodCall\Foo.', + 9, + ], + [ + 'Call to method foo() of deprecated class CheckDeprecatedStaticMethodCall\Foo.', + 11, + ], + [ + 'Call to method deprecatedFoo() of deprecated class CheckDeprecatedStaticMethodCall\Foo.', + 12, + ], + [ + 'Call to deprecated method deprecatedFoo() of class CheckDeprecatedStaticMethodCall\Foo.', + 12, + ], + [ + 'Call to method deprecatedFoo2() of deprecated class CheckDeprecatedStaticMethodCall\Foo.', + 13, + ], + [ + 'Call to deprecated method deprecatedFoo2() of class CheckDeprecatedStaticMethodCall\Foo.', + 13, + ], + [ + 'Call to deprecated method deprecatedFoo() of class CheckDeprecatedStaticMethodCall\Foo.', + 21, + ], + ] + ); + } + +} diff --git a/tests/Rules/Deprecations/FetchingClassConstOfDeprecatedClassRuleTest.php b/tests/Rules/Deprecations/FetchingClassConstOfDeprecatedClassRuleTest.php new file mode 100644 index 00000000..d64551c3 --- /dev/null +++ b/tests/Rules/Deprecations/FetchingClassConstOfDeprecatedClassRuleTest.php @@ -0,0 +1,44 @@ +createBroker(); + $ruleLevelHelper = new RuleLevelHelper($this->createBroker(), true, false, true); + + return new FetchingClassConstOfDeprecatedClassRule($broker, $ruleLevelHelper); + } + + public function testFetchingClassConstOfDeprecatedClass(): void + { + require_once __DIR__ . '/data/fetching-class-const-of-deprecated-class-definition.php'; + $this->analyse( + [__DIR__ . '/data/fetching-class-const-of-deprecated-class.php'], + [ + [ + 'Fetching class constant class of deprecated class FetchingClassConstOfDeprecatedClass\DeprecatedFoo.', + 6, + ], + [ + 'Fetching deprecated class constant DEPRECATED_FOO of class FetchingClassConstOfDeprecatedClass\Foo.', + 9, + ], + [ + 'Fetching class constant class of deprecated class FetchingClassConstOfDeprecatedClass\DeprecatedFoo.', + 11, + ], + [ + 'Fetching class constant class of deprecated class FetchingClassConstOfDeprecatedClass\DeprecatedFoo.', + 12, + ], + ] + ); + } + +} diff --git a/tests/Rules/Deprecations/ImplementationOfDeprecatedInterfaceRuleTest.php b/tests/Rules/Deprecations/ImplementationOfDeprecatedInterfaceRuleTest.php new file mode 100644 index 00000000..3a5c3673 --- /dev/null +++ b/tests/Rules/Deprecations/ImplementationOfDeprecatedInterfaceRuleTest.php @@ -0,0 +1,60 @@ +createBroker(); + return new ImplementationOfDeprecatedInterfaceRule($broker); + } + + public function testImplementationOfDeprecatedInterfacesInClasses(): void + { + require_once __DIR__ . '/data/implementation-of-deprecated-interface-definition.php'; + $this->analyse( + [__DIR__ . '/data/implementation-of-deprecated-interface-in-classes.php'], + [ + [ + 'Class ImplementationOfDeprecatedInterface\Foo2 implements deprecated interface ImplementationOfDeprecatedInterface\DeprecatedFooable.', + 10, + ], + [ + 'Class ImplementationOfDeprecatedInterface\Foo3 implements deprecated interface ImplementationOfDeprecatedInterface\DeprecatedFooable.', + 15, + ], + [ + 'Class ImplementationOfDeprecatedInterface\Foo3 implements deprecated interface ImplementationOfDeprecatedInterface\DeprecatedFooable2.', + 15, + ], + ] + ); + } + + public function testImplementationOfDeprecatedInterfacesInAnonymousClasses(): void + { + static::markTestSkipped('The `isAnonymous` method in the ReflectionClass doesn\'t work for some reason.'); + + require_once __DIR__ . '/data/implementation-of-deprecated-interface-definition.php'; + $this->analyse( + [__DIR__ . '/data/implementation-of-deprecated-interface-in-anonymous-classes.php'], + [ + [ + 'Anonymous class implements deprecated interface ImplementationOfDeprecatedInterface\DeprecatedFooable.', + 9, + ], + [ + 'Anonymous class implements deprecated interface ImplementationOfDeprecatedInterface\DeprecatedFooable.', + 13, + ], + [ + 'Anonymous class implements deprecated interface ImplementationOfDeprecatedInterface\DeprecatedFooable2.', + 13, + ], + ] + ); + } + +} diff --git a/tests/Rules/Deprecations/InheritanceOfDeprecatedClassRuleTest.php b/tests/Rules/Deprecations/InheritanceOfDeprecatedClassRuleTest.php new file mode 100644 index 00000000..9b6f41d3 --- /dev/null +++ b/tests/Rules/Deprecations/InheritanceOfDeprecatedClassRuleTest.php @@ -0,0 +1,44 @@ +createBroker(); + return new InheritanceOfDeprecatedClassRule($broker); + } + + public function testInheritanceOfDeprecatedClassInClasses(): void + { + require_once __DIR__ . '/data/inheritance-of-deprecated-class-definition.php'; + $this->analyse( + [__DIR__ . '/data/inheritance-of-deprecated-class-in-classes.php'], + [ + [ + 'Class InheritanceOfDeprecatedClass\Bar2 extends deprecated class InheritanceOfDeprecatedClass\DeprecatedFoo.', + 10, + ], + ] + ); + } + + public function testInheritanceOfDeprecatedClassInAnonymousClasses(): void + { + static::markTestSkipped('The `isAnonymous` method in the ReflectionClass doesn\'t work for some reason.'); + + require_once __DIR__ . '/data/inheritance-of-deprecated-class-definition.php'; + $this->analyse( + [__DIR__ . '/data/inheritance-of-deprecated-class-in-anonymous-classes.php'], + [ + [ + 'Anonymous class extends deprecated class InheritanceOfDeprecatedClass\DeprecatedFoo.', + 9, + ], + ] + ); + } + +} diff --git a/tests/Rules/Deprecations/InheritanceOfDeprecatedInterfaceRuleTest.php b/tests/Rules/Deprecations/InheritanceOfDeprecatedInterfaceRuleTest.php new file mode 100644 index 00000000..769892b6 --- /dev/null +++ b/tests/Rules/Deprecations/InheritanceOfDeprecatedInterfaceRuleTest.php @@ -0,0 +1,36 @@ +createBroker(); + return new InheritanceOfDeprecatedInterfaceRule($broker); + } + + public function testInheritanceOfDeprecatedInterfaces(): void + { + require_once __DIR__ . '/data/inheritance-of-deprecated-interface-definition.php'; + $this->analyse( + [__DIR__ . '/data/inheritance-of-deprecated-interface.php'], + [ + [ + 'Interface InheritanceOfDeprecatedInterface\Foo2 extends deprecated interface InheritanceOfDeprecatedInterface\DeprecatedFooable.', + 10, + ], + [ + 'Interface InheritanceOfDeprecatedInterface\Foo3 extends deprecated interface InheritanceOfDeprecatedInterface\DeprecatedFooable.', + 15, + ], + [ + 'Interface InheritanceOfDeprecatedInterface\Foo3 extends deprecated interface InheritanceOfDeprecatedInterface\DeprecatedFooable2.', + 15, + ], + ] + ); + } + +} diff --git a/tests/Rules/Deprecations/InstantiationOfDeprecatedClassRuleTest.php b/tests/Rules/Deprecations/InstantiationOfDeprecatedClassRuleTest.php new file mode 100644 index 00000000..9b3a8fd4 --- /dev/null +++ b/tests/Rules/Deprecations/InstantiationOfDeprecatedClassRuleTest.php @@ -0,0 +1,32 @@ +createBroker(); + $ruleLevelHelper = new RuleLevelHelper($this->createBroker(), true, false, true); + + return new InstantiationOfDeprecatedClassRule($broker, $ruleLevelHelper); + } + + public function testInstantiationOfDeprecatedClass(): void + { + require_once __DIR__ . '/data/instantiation-of-deprecated-class-definition.php'; + $this->analyse( + [__DIR__ . '/data/instantiation-of-deprecated-class.php'], + [ + [ + 'Instantiation of deprecated class InstantiationOfDeprecatedClass\DeprecatedFoo.', + 6, + ], + ] + ); + } + +} diff --git a/tests/Rules/Deprecations/UsageOfDeprecatedTraitRuleTest.php b/tests/Rules/Deprecations/UsageOfDeprecatedTraitRuleTest.php new file mode 100644 index 00000000..7bceaddd --- /dev/null +++ b/tests/Rules/Deprecations/UsageOfDeprecatedTraitRuleTest.php @@ -0,0 +1,32 @@ +createBroker(); + return new UsageOfDeprecatedTraitRule($broker); + } + + public function testUsageOfDeprecatedTrait(): void + { + require_once __DIR__ . '/data/usage-of-deprecated-trait-definition.php'; + $this->analyse( + [__DIR__ . '/data/usage-of-deprecated-trait.php'], + [ + [ + 'Usage of deprecated trait UsageOfDeprecatedTrait\DeprecatedFooTrait in class UsageOfDeprecatedTrait\Foo.', + 9, + ], + [ + 'Usage of deprecated trait UsageOfDeprecatedTrait\DeprecatedFooTrait in class UsageOfDeprecatedTrait\Foo2.', + 16, + ], + ] + ); + } + +} diff --git a/tests/Rules/Deprecations/data/access-deprecated-property-definition.php b/tests/Rules/Deprecations/data/access-deprecated-property-definition.php new file mode 100644 index 00000000..8049813b --- /dev/null +++ b/tests/Rules/Deprecations/data/access-deprecated-property-definition.php @@ -0,0 +1,28 @@ +foo = 'foo'; +$foo->foo; + +$foo->deprecatedFoo = 'deprecatedFoo'; +$foo->deprecatedFoo; + +$foo->fooFromTrait = 'fooFromTrait'; +$foo->fooFromTrait; + +$foo->deprecatedFooFromTrait = 'deprecatedFooFromTrait'; +$foo->deprecatedFooFromTrait; + +/** + * @deprecated + */ +function deprecated_scope() +{ + $foo = new Foo(); + + $foo->foo = 'foo'; + $foo->foo; + + $foo->deprecatedFoo = 'deprecatedFoo'; + $foo->deprecatedFoo; + + $foo->fooFromTrait = 'fooFromTrait'; + $foo->fooFromTrait; + + $foo->deprecatedFooFromTrait = 'deprecatedFooFromTrait'; + $foo->deprecatedFooFromTrait; +} + +/** + * @deprecated + */ +class DeprecatedScope +{ + + public function foo() + { + $foo = new Foo(); + + $foo->foo = 'foo'; + $foo->foo; + + $foo->deprecatedFoo = 'deprecatedFoo'; + $foo->deprecatedFoo; + + $foo->fooFromTrait = 'fooFromTrait'; + $foo->fooFromTrait; + + $foo->deprecatedFooFromTrait = 'deprecatedFooFromTrait'; + $foo->deprecatedFooFromTrait; + } + +} diff --git a/tests/Rules/Deprecations/data/access-deprecated-static-property-definition.php b/tests/Rules/Deprecations/data/access-deprecated-static-property-definition.php new file mode 100644 index 00000000..36be08c3 --- /dev/null +++ b/tests/Rules/Deprecations/data/access-deprecated-static-property-definition.php @@ -0,0 +1,27 @@ +foo(); +$foo->deprecatedFoo(); + +$bar = new Bar(); +$bar->deprecatedFoo(); +$bar->deprecatedFoo2(); + +$foo->fooFromTrait(); +$foo->deprecatedFooFromTrait(); + +/** + * @deprecated + */ +function deprecated_scope() +{ + $foo = new Foo(); + $foo->foo(); + $foo->deprecatedFoo(); + + $bar = new Bar(); + $bar->deprecatedFoo(); + $bar->deprecatedFoo2(); + + $foo->fooFromTrait(); + $foo->deprecatedFooFromTrait(); + +} + +/** + * @deprecated + */ +class DeprecatedScope +{ + + public function foo() + { + $foo = new Foo(); + $foo->foo(); + $foo->deprecatedFoo(); + + $bar = new Bar(); + $bar->deprecatedFoo(); + $bar->deprecatedFoo2(); + + $foo->fooFromTrait(); + $foo->deprecatedFooFromTrait(); + } + +} diff --git a/tests/Rules/Deprecations/data/call-to-deprecated-static-method-definition.php b/tests/Rules/Deprecations/data/call-to-deprecated-static-method-definition.php new file mode 100644 index 00000000..0ec9d461 --- /dev/null +++ b/tests/Rules/Deprecations/data/call-to-deprecated-static-method-definition.php @@ -0,0 +1,47 @@ +