diff --git a/src/Rules/RuleLevelHelper.php b/src/Rules/RuleLevelHelper.php index 1b46ccfbe7..9c05539935 100644 --- a/src/Rules/RuleLevelHelper.php +++ b/src/Rules/RuleLevelHelper.php @@ -95,7 +95,7 @@ private function transformAcceptedType(Type $acceptingType, Type $acceptedType): return new CallableType( $acceptedType->getParameters(), - $traverse($this->transformCommonType($acceptedType->getReturnType())), + $traverse($acceptedType->getReturnType()), $acceptedType->isVariadic(), $acceptedType->getTemplateTypeMap(), $acceptedType->getResolvedTemplateTypeMap(), @@ -111,7 +111,7 @@ private function transformAcceptedType(Type $acceptingType, Type $acceptedType): return new ClosureType( $acceptedType->getParameters(), - $traverse($this->transformCommonType($acceptedType->getReturnType())), + $traverse($acceptedType->getReturnType()), $acceptedType->isVariadic(), $acceptedType->getTemplateTypeMap(), $acceptedType->getResolvedTemplateTypeMap(), @@ -142,10 +142,10 @@ private function transformAcceptedType(Type $acceptingType, Type $acceptedType): } } - return $traverse($this->transformCommonType($acceptedType)); + return $traverse($acceptedType); }); - return [$acceptedType, $checkForUnion]; + return [$this->transformCommonType($acceptedType), $checkForUnion]; } /** @api */ diff --git a/tests/PHPStan/Rules/Classes/InstantiationRuleTest.php b/tests/PHPStan/Rules/Classes/InstantiationRuleTest.php index 511bf6136d..a1db3f755a 100644 --- a/tests/PHPStan/Rules/Classes/InstantiationRuleTest.php +++ b/tests/PHPStan/Rules/Classes/InstantiationRuleTest.php @@ -20,6 +20,8 @@ class InstantiationRuleTest extends RuleTestCase { + private bool $checkExplicitMixed = false; + protected function getRule(): Rule { $reflectionProvider = self::createReflectionProvider(); @@ -27,7 +29,7 @@ protected function getRule(): Rule return new InstantiationRule( $container, $reflectionProvider, - new FunctionCallParametersCheck(new RuleLevelHelper($reflectionProvider, true, false, true, false, false, false, true), new NullsafeCheck(), new UnresolvableTypeHelper(), new PropertyReflectionFinder(), $reflectionProvider, true, true, true, true), + new FunctionCallParametersCheck(new RuleLevelHelper($reflectionProvider, true, false, true, $this->checkExplicitMixed, false, false, true), new NullsafeCheck(), new UnresolvableTypeHelper(), new PropertyReflectionFinder(), $reflectionProvider, true, true, true, true), new ClassNameCheck( new ClassCaseSensitivityCheck($reflectionProvider, true), new ClassForbiddenNameCheck($container), @@ -570,6 +572,13 @@ public function testBug14097(): void $this->analyse([__DIR__ . '/data/bug-14097.php'], []); } + #[RequiresPhp('>= 8.0')] + public function testBug13440(): void + { + $this->checkExplicitMixed = true; + $this->analyse([__DIR__ . '/data/bug-13440.php'], []); + } + public function testNewStaticWithConsistentConstructor(): void { $this->analyse([__DIR__ . '/data/instantiation-new-static-consistent-constructor.php'], [ diff --git a/tests/PHPStan/Rules/Classes/data/bug-13440.php b/tests/PHPStan/Rules/Classes/data/bug-13440.php new file mode 100644 index 0000000000..dedd1b08fb --- /dev/null +++ b/tests/PHPStan/Rules/Classes/data/bug-13440.php @@ -0,0 +1,64 @@ += 8.0 + +declare(strict_types = 1); + +namespace Bug13440; + +use Closure; + +/** @template T */ +interface Foo {} + +/** + * @template TVal + * @template TReturn + */ +class Box +{ + /** + * @param TVal $val + * @param Closure(Foo): TReturn $cb + */ + public function __construct( + private mixed $val, + private Closure $cb, + ) { + } + + /** + * @template TNewReturn + * @param Closure(Foo): TNewReturn $cb + * @return self + */ + public function test(Closure $cb): self + { + return new self($this->val, $cb); + } +} + +/** + * @template TVal + * @template TReturn + */ +class Box2 +{ + /** + * @param TVal $val + * @param callable(Foo): TReturn $cb + */ + public function __construct( + private mixed $val, + private $cb, + ) { + } + + /** + * @template TNewReturn + * @param callable(Foo): TNewReturn $cb + * @return self + */ + public function test($cb): self + { + return new self($this->val, $cb); + } +} diff --git a/tests/PHPStan/Rules/Properties/TypesAssignedToPropertiesRuleTest.php b/tests/PHPStan/Rules/Properties/TypesAssignedToPropertiesRuleTest.php index cf7136dd1f..6c03bc86c3 100644 --- a/tests/PHPStan/Rules/Properties/TypesAssignedToPropertiesRuleTest.php +++ b/tests/PHPStan/Rules/Properties/TypesAssignedToPropertiesRuleTest.php @@ -1025,6 +1025,14 @@ public function testBug12250(): void $this->analyse([__DIR__ . '/data/bug-12250.php'], []); } + #[RequiresPhp('>= 8.0')] + public function testBug12688(): void + { + $this->checkExplicitMixed = true; + $this->checkImplicitMixed = true; + $this->analyse([__DIR__ . '/data/bug-12688.php'], []); + } + public function testBug4525(): void { $this->analyse([__DIR__ . '/data/bug-4525.php'], []); diff --git a/tests/PHPStan/Rules/Properties/data/bug-12688.php b/tests/PHPStan/Rules/Properties/data/bug-12688.php new file mode 100644 index 0000000000..6d3b01c64b --- /dev/null +++ b/tests/PHPStan/Rules/Properties/data/bug-12688.php @@ -0,0 +1,54 @@ += 8.1 + +namespace Bug12688; + +/** + * @template T = mixed + */ +interface I {} + +/** + * @implements I + */ +enum E implements I +{ + case E; +} + +/** + * @template T + */ +final class TemplateWithoutDefaultWorks +{ + /** + * @var I + */ + public readonly I $i; + + /** + * @param I $i + */ + public function __construct(I $i = E::E) + { + $this->i = $i; + } +} + +/** + * @template T = mixed + */ +final class TemplateWithDefaultDoesNotWork +{ + /** + * @var I + */ + public readonly I $i; + + /** + * @param I $i + */ + public function __construct(I $i = E::E) + { + $this->i = $i; + } +}