diff --git a/src/Rules/RestrictedUsage/RestrictedClassConstantUsageRule.php b/src/Rules/RestrictedUsage/RestrictedClassConstantUsageRule.php index 43505b34532..7aa7257df68 100644 --- a/src/Rules/RestrictedUsage/RestrictedClassConstantUsageRule.php +++ b/src/Rules/RestrictedUsage/RestrictedClassConstantUsageRule.php @@ -15,6 +15,7 @@ use PHPStan\Rules\RuleLevelHelper; use PHPStan\Type\ErrorType; use PHPStan\Type\Type; +use PHPStan\Type\TypeCombinator; /** * @implements Rule @@ -71,7 +72,7 @@ public function processNode(Node $node, Scope $scope): array return []; } - $referencedClasses = $classTypeResult->getReferencedClasses(); + $referencedClasses = TypeCombinator::removeNull($classTypeResult->getType())->getObjectTypeOrClassStringObjectType()->getObjectClassNames(); } $errors = []; diff --git a/src/Rules/RestrictedUsage/RestrictedMethodCallableUsageRule.php b/src/Rules/RestrictedUsage/RestrictedMethodCallableUsageRule.php index f983f281f4c..2cfbf92c4d7 100644 --- a/src/Rules/RestrictedUsage/RestrictedMethodCallableUsageRule.php +++ b/src/Rules/RestrictedUsage/RestrictedMethodCallableUsageRule.php @@ -12,6 +12,8 @@ use PHPStan\Reflection\ReflectionProvider; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; +use PHPStan\Type\ObjectWithoutClassType; +use PHPStan\Type\TypeCombinator; /** * @implements Rule @@ -52,7 +54,7 @@ public function processNode(Node $node, Scope $scope): array $methodName = $node->getName()->name; $methodCalledOnType = $scope->getType($node->getVar()); - $referencedClasses = $methodCalledOnType->getObjectClassNames(); + $referencedClasses = TypeCombinator::intersect($methodCalledOnType, new ObjectWithoutClassType())->getObjectClassNames(); $errors = []; diff --git a/src/Rules/RestrictedUsage/RestrictedMethodUsageRule.php b/src/Rules/RestrictedUsage/RestrictedMethodUsageRule.php index 7a869fddef4..1f04eb0bbeb 100644 --- a/src/Rules/RestrictedUsage/RestrictedMethodUsageRule.php +++ b/src/Rules/RestrictedUsage/RestrictedMethodUsageRule.php @@ -12,6 +12,8 @@ use PHPStan\Reflection\ReflectionProvider; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; +use PHPStan\Type\ObjectWithoutClassType; +use PHPStan\Type\TypeCombinator; /** * @implements Rule @@ -52,7 +54,7 @@ public function processNode(Node $node, Scope $scope): array $methodName = $node->name->name; $methodCalledOnType = $scope->getType($node->var); - $referencedClasses = $methodCalledOnType->getObjectClassNames(); + $referencedClasses = TypeCombinator::intersect($methodCalledOnType, new ObjectWithoutClassType())->getObjectClassNames(); $errors = []; diff --git a/src/Rules/RestrictedUsage/RestrictedPropertyUsageRule.php b/src/Rules/RestrictedUsage/RestrictedPropertyUsageRule.php index 1d3dc8a126a..b86084ed174 100644 --- a/src/Rules/RestrictedUsage/RestrictedPropertyUsageRule.php +++ b/src/Rules/RestrictedUsage/RestrictedPropertyUsageRule.php @@ -11,6 +11,8 @@ use PHPStan\Reflection\ReflectionProvider; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; +use PHPStan\Type\ObjectWithoutClassType; +use PHPStan\Type\TypeCombinator; /** * @implements Rule @@ -51,7 +53,7 @@ public function processNode(Node $node, Scope $scope): array $propertyName = $node->name->name; $propertyCalledOnType = $scope->getType($node->var); - $referencedClasses = $propertyCalledOnType->getObjectClassNames(); + $referencedClasses = TypeCombinator::intersect($propertyCalledOnType, new ObjectWithoutClassType())->getObjectClassNames(); $errors = []; diff --git a/src/Rules/RestrictedUsage/RestrictedStaticMethodCallableUsageRule.php b/src/Rules/RestrictedUsage/RestrictedStaticMethodCallableUsageRule.php index cc5b2b46f28..9ed23eed04b 100644 --- a/src/Rules/RestrictedUsage/RestrictedStaticMethodCallableUsageRule.php +++ b/src/Rules/RestrictedUsage/RestrictedStaticMethodCallableUsageRule.php @@ -16,6 +16,7 @@ use PHPStan\Rules\RuleLevelHelper; use PHPStan\Type\ErrorType; use PHPStan\Type\Type; +use PHPStan\Type\TypeCombinator; /** * @implements Rule @@ -72,7 +73,7 @@ public function processNode(Node $node, Scope $scope): array return []; } - $referencedClasses = $classTypeResult->getReferencedClasses(); + $referencedClasses = TypeCombinator::removeNull($classTypeResult->getType())->getObjectTypeOrClassStringObjectType()->getObjectClassNames(); } $errors = []; diff --git a/src/Rules/RestrictedUsage/RestrictedStaticMethodUsageRule.php b/src/Rules/RestrictedUsage/RestrictedStaticMethodUsageRule.php index 7028d8e68c5..711fc8dcaac 100644 --- a/src/Rules/RestrictedUsage/RestrictedStaticMethodUsageRule.php +++ b/src/Rules/RestrictedUsage/RestrictedStaticMethodUsageRule.php @@ -15,6 +15,7 @@ use PHPStan\Rules\RuleLevelHelper; use PHPStan\Type\ErrorType; use PHPStan\Type\Type; +use PHPStan\Type\TypeCombinator; /** * @implements Rule @@ -71,7 +72,7 @@ public function processNode(Node $node, Scope $scope): array return []; } - $referencedClasses = $classTypeResult->getReferencedClasses(); + $referencedClasses = TypeCombinator::removeNull($classTypeResult->getType())->getObjectTypeOrClassStringObjectType()->getObjectClassNames(); } $errors = []; diff --git a/src/Rules/RestrictedUsage/RestrictedStaticPropertyUsageRule.php b/src/Rules/RestrictedUsage/RestrictedStaticPropertyUsageRule.php index da220231c7c..6046e368fac 100644 --- a/src/Rules/RestrictedUsage/RestrictedStaticPropertyUsageRule.php +++ b/src/Rules/RestrictedUsage/RestrictedStaticPropertyUsageRule.php @@ -15,6 +15,7 @@ use PHPStan\Rules\RuleLevelHelper; use PHPStan\Type\ErrorType; use PHPStan\Type\Type; +use PHPStan\Type\TypeCombinator; /** * @implements Rule @@ -71,7 +72,7 @@ public function processNode(Node $node, Scope $scope): array return []; } - $referencedClasses = $classTypeResult->getReferencedClasses(); + $referencedClasses = TypeCombinator::removeNull($classTypeResult->getType())->getObjectTypeOrClassStringObjectType()->getObjectClassNames(); } $errors = []; diff --git a/src/Rules/RestrictedUsage/RestrictedUsageOfDeprecatedStringCastRule.php b/src/Rules/RestrictedUsage/RestrictedUsageOfDeprecatedStringCastRule.php index 4641a2f19d6..1cb985bd801 100644 --- a/src/Rules/RestrictedUsage/RestrictedUsageOfDeprecatedStringCastRule.php +++ b/src/Rules/RestrictedUsage/RestrictedUsageOfDeprecatedStringCastRule.php @@ -11,6 +11,8 @@ use PHPStan\Reflection\ReflectionProvider; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; +use PHPStan\Type\ObjectWithoutClassType; +use PHPStan\Type\TypeCombinator; /** * @implements Rule @@ -43,7 +45,7 @@ public function processNode(Node $node, Scope $scope): array } $exprType = $scope->getType($node->expr); - $referencedClasses = $exprType->getObjectClassNames(); + $referencedClasses = TypeCombinator::intersect($exprType, new ObjectWithoutClassType())->getObjectClassNames(); $errors = []; diff --git a/tests/PHPStan/Rules/RestrictedUsage/RestrictedClassConstantUsageRuleTest.php b/tests/PHPStan/Rules/RestrictedUsage/RestrictedClassConstantUsageRuleTest.php index 460af0a1fd8..9eaa348f0b7 100644 --- a/tests/PHPStan/Rules/RestrictedUsage/RestrictedClassConstantUsageRuleTest.php +++ b/tests/PHPStan/Rules/RestrictedUsage/RestrictedClassConstantUsageRuleTest.php @@ -5,6 +5,7 @@ use PHPStan\Rules\Rule; use PHPStan\Rules\RuleLevelHelper; use PHPStan\Testing\RuleTestCase; +use PHPUnit\Framework\Attributes\RequiresPhp; /** * @extends RuleTestCase @@ -41,6 +42,33 @@ public function testRule(): void ]); } + #[RequiresPhp('>= 8.1.0')] + public function testBug15681(): void + { + $this->analyse([__DIR__ . '/data/bug-15681.php'], [ + [ + 'Cannot access FOO', + 73, + ], + [ + 'Cannot access FOO', + 85, + ], + [ + 'Cannot access FOO', + 90, + ], + [ + 'Cannot access FOO', + 118, + ], + [ + 'Cannot access FOO', + 123, + ], + ]); + } + public static function getAdditionalConfigFiles(): array { return [ diff --git a/tests/PHPStan/Rules/RestrictedUsage/RestrictedMethodCallableUsageRuleTest.php b/tests/PHPStan/Rules/RestrictedUsage/RestrictedMethodCallableUsageRuleTest.php index 331116731e8..0540915c05d 100644 --- a/tests/PHPStan/Rules/RestrictedUsage/RestrictedMethodCallableUsageRuleTest.php +++ b/tests/PHPStan/Rules/RestrictedUsage/RestrictedMethodCallableUsageRuleTest.php @@ -31,6 +31,21 @@ public function testRule(): void ]); } + #[RequiresPhp('>= 8.1.0')] + public function testBug15681(): void + { + $this->analyse([__DIR__ . '/data/bug-15681.php'], [ + [ + 'Cannot call doFoo', + 64, + ], + [ + 'Cannot call doFoo', + 65, + ], + ]); + } + public static function getAdditionalConfigFiles(): array { return [ diff --git a/tests/PHPStan/Rules/RestrictedUsage/RestrictedMethodUsageRuleTest.php b/tests/PHPStan/Rules/RestrictedUsage/RestrictedMethodUsageRuleTest.php index d3c6fa093cd..baa5b7226ef 100644 --- a/tests/PHPStan/Rules/RestrictedUsage/RestrictedMethodUsageRuleTest.php +++ b/tests/PHPStan/Rules/RestrictedUsage/RestrictedMethodUsageRuleTest.php @@ -4,6 +4,7 @@ use PHPStan\Rules\Rule as TRule; use PHPStan\Testing\RuleTestCase; +use PHPUnit\Framework\Attributes\RequiresPhp; /** * @extends RuleTestCase @@ -29,6 +30,41 @@ public function testRule(): void ]); } + #[RequiresPhp('>= 8.1.0')] + public function testBug15681(): void + { + $this->analyse([__DIR__ . '/data/bug-15681.php'], [ + [ + 'Cannot call doFoo', + 51, + ], + [ + 'Cannot call doFoo', + 52, + ], + [ + 'Cannot call doFoo', + 53, + ], + [ + 'Cannot call doFoo', + 54, + ], + [ + 'Cannot call doFoo', + 55, + ], + [ + 'Cannot call doFoo', + 56, + ], + [ + 'Cannot call doFoo', + 57, + ], + ]); + } + public static function getAdditionalConfigFiles(): array { return [ diff --git a/tests/PHPStan/Rules/RestrictedUsage/RestrictedPropertyUsageRuleTest.php b/tests/PHPStan/Rules/RestrictedUsage/RestrictedPropertyUsageRuleTest.php index f011b645f9c..4bf3c0ad779 100644 --- a/tests/PHPStan/Rules/RestrictedUsage/RestrictedPropertyUsageRuleTest.php +++ b/tests/PHPStan/Rules/RestrictedUsage/RestrictedPropertyUsageRuleTest.php @@ -4,6 +4,7 @@ use PHPStan\Rules\Rule as TRule; use PHPStan\Testing\RuleTestCase; +use PHPUnit\Framework\Attributes\RequiresPhp; /** * @extends RuleTestCase @@ -29,6 +30,29 @@ public function testRule(): void ]); } + #[RequiresPhp('>= 8.1.0')] + public function testBug15681(): void + { + $this->analyse([__DIR__ . '/data/bug-15681.php'], [ + [ + 'Cannot access $foo', + 59, + ], + [ + 'Cannot access $foo', + 60, + ], + [ + 'Cannot access $foo', + 61, + ], + [ + 'Cannot access $foo', + 62, + ], + ]); + } + public static function getAdditionalConfigFiles(): array { return [ diff --git a/tests/PHPStan/Rules/RestrictedUsage/RestrictedStaticMethodCallableUsageRuleTest.php b/tests/PHPStan/Rules/RestrictedUsage/RestrictedStaticMethodCallableUsageRuleTest.php index a643faf3034..6f34206703d 100644 --- a/tests/PHPStan/Rules/RestrictedUsage/RestrictedStaticMethodCallableUsageRuleTest.php +++ b/tests/PHPStan/Rules/RestrictedUsage/RestrictedStaticMethodCallableUsageRuleTest.php @@ -55,6 +55,33 @@ public function testBug12951(): void ]); } + #[RequiresPhp('>= 8.1.0')] + public function testBug15681(): void + { + $this->analyse([__DIR__ . '/data/bug-15681.php'], [ + [ + 'Cannot call doFoo', + 71, + ], + [ + 'Cannot call doFoo', + 83, + ], + [ + 'Cannot call doFoo', + 88, + ], + [ + 'Cannot call doFoo', + 116, + ], + [ + 'Cannot call doFoo', + 121, + ], + ]); + } + public static function getAdditionalConfigFiles(): array { return [ diff --git a/tests/PHPStan/Rules/RestrictedUsage/RestrictedStaticMethodUsageRuleTest.php b/tests/PHPStan/Rules/RestrictedUsage/RestrictedStaticMethodUsageRuleTest.php index 11c4d674504..a63a545ae81 100644 --- a/tests/PHPStan/Rules/RestrictedUsage/RestrictedStaticMethodUsageRuleTest.php +++ b/tests/PHPStan/Rules/RestrictedUsage/RestrictedStaticMethodUsageRuleTest.php @@ -54,6 +54,33 @@ public function testBug12951(): void ]); } + #[RequiresPhp('>= 8.1.0')] + public function testBug15681(): void + { + $this->analyse([__DIR__ . '/data/bug-15681.php'], [ + [ + 'Cannot call doFoo', + 70, + ], + [ + 'Cannot call doFoo', + 82, + ], + [ + 'Cannot call doFoo', + 87, + ], + [ + 'Cannot call doFoo', + 115, + ], + [ + 'Cannot call doFoo', + 120, + ], + ]); + } + public static function getAdditionalConfigFiles(): array { return [ diff --git a/tests/PHPStan/Rules/RestrictedUsage/RestrictedStaticPropertyUsageRuleTest.php b/tests/PHPStan/Rules/RestrictedUsage/RestrictedStaticPropertyUsageRuleTest.php index 9d8fa01f6d7..e5a0b3c745f 100644 --- a/tests/PHPStan/Rules/RestrictedUsage/RestrictedStaticPropertyUsageRuleTest.php +++ b/tests/PHPStan/Rules/RestrictedUsage/RestrictedStaticPropertyUsageRuleTest.php @@ -5,6 +5,7 @@ use PHPStan\Rules\Rule as TRule; use PHPStan\Rules\RuleLevelHelper; use PHPStan\Testing\RuleTestCase; +use PHPUnit\Framework\Attributes\RequiresPhp; /** * @extends RuleTestCase @@ -52,6 +53,33 @@ public function testBug12951(): void ]); } + #[RequiresPhp('>= 8.1.0')] + public function testBug15681(): void + { + $this->analyse([__DIR__ . '/data/bug-15681.php'], [ + [ + 'Cannot access $foo', + 72, + ], + [ + 'Cannot access $foo', + 84, + ], + [ + 'Cannot access $foo', + 89, + ], + [ + 'Cannot access $foo', + 117, + ], + [ + 'Cannot access $foo', + 122, + ], + ]); + } + public static function getAdditionalConfigFiles(): array { return [ diff --git a/tests/PHPStan/Rules/RestrictedUsage/RestrictedUsageOfDeprecatedStringCastRuleTest.php b/tests/PHPStan/Rules/RestrictedUsage/RestrictedUsageOfDeprecatedStringCastRuleTest.php index d6ed16a496b..8fbc13b40c9 100644 --- a/tests/PHPStan/Rules/RestrictedUsage/RestrictedUsageOfDeprecatedStringCastRuleTest.php +++ b/tests/PHPStan/Rules/RestrictedUsage/RestrictedUsageOfDeprecatedStringCastRuleTest.php @@ -4,6 +4,7 @@ use PHPStan\Rules\Rule; use PHPStan\Testing\RuleTestCase; +use PHPUnit\Framework\Attributes\RequiresPhp; /** * @extends RuleTestCase @@ -29,6 +30,21 @@ public function testRule(): void ]); } + #[RequiresPhp('>= 8.1.0')] + public function testBug15681(): void + { + $this->analyse([__DIR__ . '/data/bug-15681.php'], [ + [ + 'Cannot call __toString', + 67, + ], + [ + 'Cannot call __toString', + 68, + ], + ]); + } + public static function getAdditionalConfigFiles(): array { return [ diff --git a/tests/PHPStan/Rules/RestrictedUsage/data/bug-15681.php b/tests/PHPStan/Rules/RestrictedUsage/data/bug-15681.php new file mode 100644 index 00000000000..87fc0b49126 --- /dev/null +++ b/tests/PHPStan/Rules/RestrictedUsage/data/bug-15681.php @@ -0,0 +1,124 @@ += 8.1 + +namespace Bug15681RestrictedUsage; + +class Request +{ + + public string $foo = ''; + + public function doFoo(): void + { + } + + public function __toString(): string + { + return ''; + } + +} + +class RequestStack +{ + + public function getCurrentRequest(): ?Request + { + return null; + } + +} + +class Other +{ + +} + +class StaticRequest +{ + + public static string $foo = ''; + + public const FOO = 1; + + public static function doFoo(): void + { + } + +} + +function test(RequestStack $stack, Request $request, ?Request $nullable, Request|false $falsy, Request|Other|null $multi, Request|int $int, ?StaticRequest $static): void +{ + $request->doFoo(); + $nullable?->doFoo(); + $nullable->doFoo(); + $stack->getCurrentRequest()->doFoo(); + $falsy->doFoo(); + $multi->doFoo(); + $int->doFoo(); + + echo $request->foo; + echo $nullable->foo; + echo $stack->getCurrentRequest()->foo; + echo $falsy->foo; + + $nullable->doFoo(...); + $falsy->doFoo(...); + + (string) $nullable; + (string) $falsy; + + $static::doFoo(); + $static::doFoo(...); + echo $static::$foo; + echo $static::FOO; +} + +/** + * @param class-string $className + * @param class-string|null $nullableClassName + */ +function testClassString(string $className, ?string $nullableClassName): void +{ + $className::doFoo(); + $className::doFoo(...); + echo $className::$foo; + echo $className::FOO; + + $nullableClassName::doFoo(); + $nullableClassName::doFoo(...); + echo $nullableClassName::$foo; + echo $nullableClassName::FOO; +} + +/** + * @template T + */ +class GenericStaticRequest +{ + + public static string $foo = ''; + + public const FOO = 1; + + public static function doFoo(): void + { + } + +} + +/** + * @param GenericStaticRequest|null $generic + * @param class-string> $genericClassName + */ +function testGeneric(?GenericStaticRequest $generic, string $genericClassName): void +{ + $generic::doFoo(); + $generic::doFoo(...); + echo $generic::$foo; + echo $generic::FOO; + + $genericClassName::doFoo(); + $genericClassName::doFoo(...); + echo $genericClassName::$foo; + echo $genericClassName::FOO; +}