diff --git a/src/Rules/Methods/CallStaticMethodsRule.php b/src/Rules/Methods/CallStaticMethodsRule.php index 0037608fec..8cdb97de1b 100644 --- a/src/Rules/Methods/CallStaticMethodsRule.php +++ b/src/Rules/Methods/CallStaticMethodsRule.php @@ -19,6 +19,7 @@ use PHPStan\Type\ErrorType; use PHPStan\Type\Generic\GenericClassStringType; use PHPStan\Type\ObjectType; +use PHPStan\Type\ObjectWithoutClassType; use PHPStan\Type\StringType; use PHPStan\Type\ThisType; use PHPStan\Type\Type; @@ -170,6 +171,9 @@ static function (Type $type) use ($methodName): bool { if ($classType instanceof GenericClassStringType) { $classType = $classType->getGenericType(); + if (!(new ObjectWithoutClassType())->isSuperTypeOf($classType)->yes()) { + return []; + } } elseif ((new StringType())->isSuperTypeOf($classType)->yes()) { return []; } diff --git a/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php b/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php index e08a3ad0b1..0ddb435f49 100644 --- a/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php +++ b/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php @@ -403,4 +403,19 @@ public function testBug577(): void $this->analyse([__DIR__ . '/data/bug-577.php'], []); } + public function testBug4550(): void + { + $this->checkThisOnly = false; + $this->analyse([__DIR__ . '/data/bug-4550.php'], [ + [ + 'Parameter #1 $class of static method Bug4550\Test::valuesOf() expects class-string, string given.', + 34, + ], + [ + 'Parameter #1 $class of static method Bug4550\Test::valuesOf() expects class-string, string given.', + 44, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Methods/data/bug-4550.php b/tests/PHPStan/Rules/Methods/data/bug-4550.php new file mode 100644 index 0000000000..d0c4139957 --- /dev/null +++ b/tests/PHPStan/Rules/Methods/data/bug-4550.php @@ -0,0 +1,46 @@ + $class + */ + public static function valuesOf(string $class): void + { + (new $class())->values(); + assert(method_exists($class, 'values')); + $class::values(); + } + + /** + * @template T + * @param class-string $class + */ + public static function doBar(string $class): void + { + (new $class())->values(); + $class::values(); + } + + /** + * @param class-string $s + */ + public function doBaz(string $s): void + { + $s::valuesOf(\stdClass::class); + $s::valuesOf('Person'); + } + + /** + * @template T of self + * @param class-string $s + */ + public function doLorem(string $s): void + { + $s::valuesOf(\stdClass::class); + $s::valuesOf('Person'); + } +}