diff --git a/src/Type/Php/GetClassDynamicReturnTypeExtension.php b/src/Type/Php/GetClassDynamicReturnTypeExtension.php index 05b9f3e6e40..cbbc45f049b 100644 --- a/src/Type/Php/GetClassDynamicReturnTypeExtension.php +++ b/src/Type/Php/GetClassDynamicReturnTypeExtension.php @@ -17,7 +17,7 @@ use PHPStan\Type\ObjectType; use PHPStan\Type\ObjectWithoutClassType; use PHPStan\Type\StaticType; -use PHPStan\Type\StringType; +use PHPStan\Type\ThisType; use PHPStan\Type\Type; use PHPStan\Type\TypeTraverser; use PHPStan\Type\TypeWithClassName; @@ -37,6 +37,10 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, $args = $functionCall->getArgs(); if (count($args) === 0) { + if ($scope->isInTrait()) { + return new ClassStringType(); + } + if ($scope->isInClass()) { return new ConstantStringType($scope->getClassReflection()->getName(), true); } @@ -44,12 +48,12 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, return new ConstantBooleanType(false); } - if ($scope->isInTrait()) { - return new StringType(); - } - $argType = $scope->getType($args[0]->value); + if ($scope->isInTrait() && $argType instanceof ThisType) { + return new ClassStringType(); + } + return TypeTraverser::map( $argType, static function (Type $type, callable $traverse): Type { diff --git a/tests/PHPStan/Rules/Comparison/StrictComparisonOfDifferentTypesRuleTest.php b/tests/PHPStan/Rules/Comparison/StrictComparisonOfDifferentTypesRuleTest.php index b79534b21ef..6683505ed96 100644 --- a/tests/PHPStan/Rules/Comparison/StrictComparisonOfDifferentTypesRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/StrictComparisonOfDifferentTypesRuleTest.php @@ -625,23 +625,55 @@ public function testBug3633(): void $this->analyse([__DIR__ . '/data/bug-3633.php'], [ [ 'Strict comparison using === between class-string and \'Bug3633\\\OtherClass\' will always evaluate to false.', - 23, + 37, + ], + [ + 'Strict comparison using === between \'Bug3633\\\HelloWorld\' and \'Bug3633\\\HelloWorld\' will always evaluate to true.', + 41, + ], + [ + 'Strict comparison using === between \'Bug3633\\\HelloWorld\' and \'Bug3633\\\OtherClass\' will always evaluate to false.', + 44, ], [ 'Strict comparison using === between class-string and \'Bug3633\\\HelloWorld\' will always evaluate to false.', - 35, + 64, + ], + [ + 'Strict comparison using === between \'Bug3633\\\OtherClass\' and \'Bug3633\\\HelloWorld\' will always evaluate to false.', + 71, + ], + [ + 'Strict comparison using === between \'Bug3633\\\OtherClass\' and \'Bug3633\\\OtherClass\' will always evaluate to true.', + 74, ], [ 'Strict comparison using === between class-string and \'Bug3633\\\HelloWorld\' will always evaluate to false.', - 50, + 93, ], [ 'Strict comparison using === between class-string and \'Bug3633\\\OtherClass\' will always evaluate to false.', - 53, + 96, + ], + [ + 'Strict comparison using === between \'Bug3633\\\FinalClass\' and \'Bug3633\\\FinalClass\' will always evaluate to true.', + 102, + ], + [ + 'Strict comparison using === between \'Bug3633\\\FinalClass\' and \'Bug3633\\\HelloWorld\' will always evaluate to false.', + 106, + ], + [ + 'Strict comparison using === between \'Bug3633\\\FinalClass\' and \'Bug3633\\\OtherClass\' will always evaluate to false.', + 109, + ], + [ + 'Strict comparison using !== between \'Bug3633\\\FinalClass\' and \'Bug3633\\\FinalClass\' will always evaluate to false.', + 112, ], [ 'Strict comparison using === between \'Bug3633\\\FinalClass\' and \'Bug3633\\\FinalClass\' will always evaluate to true.', - 59, + 115, ], ]); } diff --git a/tests/PHPStan/Rules/Comparison/data/bug-3633.php b/tests/PHPStan/Rules/Comparison/data/bug-3633.php index f4f2ad637f0..95aedbcf12e 100644 --- a/tests/PHPStan/Rules/Comparison/data/bug-3633.php +++ b/tests/PHPStan/Rules/Comparison/data/bug-3633.php @@ -3,20 +3,34 @@ namespace Bug3633; trait Foo { - public function test(): void { + public function test($obj): void { if (get_class($this) === HelloWorld::class) { echo "OK"; } if (get_class($this) === OtherClass::class) { echo "OK"; } + + if (get_class() === HelloWorld::class) { + echo "OK"; + } + if (get_class() === OtherClass::class) { + echo "OK"; + } + + if (get_class($obj) === HelloWorld::class) { + echo "OK"; + } + if (get_class($obj) === OtherClass::class) { + echo "OK"; + } } } class HelloWorld { use Foo; - public function bar(): void { + public function bar($obj): void { if (get_class($this) === HelloWorld::class) { echo "OK"; } @@ -24,6 +38,21 @@ public function bar(): void { echo "OK"; } + if (get_class() === HelloWorld::class) { + echo "OK"; + } + if (get_class() === OtherClass::class) { + echo "OK"; + } + + if (get_class($obj) === HelloWorld::class) { + echo "OK"; + } + if (get_class($obj) === OtherClass::class) { + echo "OK"; + } + + $this->test(); } } @@ -31,7 +60,7 @@ public function bar(): void { class OtherClass { use Foo; - public function bar(): void { + public function bar($obj): void { if (get_class($this) === HelloWorld::class) { echo "OK"; } @@ -39,6 +68,20 @@ public function bar(): void { echo "OK"; } + if (get_class() === HelloWorld::class) { + echo "OK"; + } + if (get_class() === OtherClass::class) { + echo "OK"; + } + + if (get_class($obj) === HelloWorld::class) { + echo "OK"; + } + if (get_class($obj) === OtherClass::class) { + echo "OK"; + } + $this->test(); } } @@ -46,7 +89,7 @@ public function bar(): void { final class FinalClass { use Foo; - public function bar(): void { + public function bar($obj): void { if (get_class($this) === HelloWorld::class) { echo "OK"; } @@ -60,6 +103,26 @@ public function bar(): void { echo "OK"; } + if (get_class() === HelloWorld::class) { + echo "OK"; + } + if (get_class() === OtherClass::class) { + echo "OK"; + } + if (get_class() !== FinalClass::class) { + echo "OK"; + } + if (get_class() === FinalClass::class) { + echo "OK"; + } + + if (get_class($obj) === HelloWorld::class) { + echo "OK"; + } + if (get_class($obj) === OtherClass::class) { + echo "OK"; + } + $this->test(); } }