diff --git a/src/Type/CallableType.php b/src/Type/CallableType.php index 98a9ba3ef5..af281fbead 100644 --- a/src/Type/CallableType.php +++ b/src/Type/CallableType.php @@ -218,7 +218,7 @@ public function inferTemplateTypes(Type $receivedType): TemplateTypeMap return $receivedType->inferTemplateTypesOn($this); } - if ($receivedType->isCallable()->no()) { + if (! $receivedType->isCallable()->yes()) { return TemplateTypeMap::createEmpty(); } diff --git a/src/Type/ClosureType.php b/src/Type/ClosureType.php index 5c9ebacb01..d6747060e4 100644 --- a/src/Type/ClosureType.php +++ b/src/Type/ClosureType.php @@ -334,7 +334,7 @@ public function inferTemplateTypes(Type $receivedType): TemplateTypeMap return $receivedType->inferTemplateTypesOn($this); } - if ($receivedType->isCallable()->no()) { + if ($receivedType->isCallable()->no() || ! $receivedType instanceof self) { return TemplateTypeMap::createEmpty(); } diff --git a/tests/PHPStan/Analyser/data/generic-unions.php b/tests/PHPStan/Analyser/data/generic-unions.php index 7ebc5bb89b..4fe7aac9f5 100644 --- a/tests/PHPStan/Analyser/data/generic-unions.php +++ b/tests/PHPStan/Analyser/data/generic-unions.php @@ -61,3 +61,75 @@ public function foo( } } + +class InvokableClass +{ + public function __invoke(): string + { + return 'foo'; + } +} + +/** + * + * @template TGetDefault + * @template TKey + * + * @param TKey $key + * @param TGetDefault|(\Closure(): TGetDefault) $default + * @return TKey|TGetDefault + */ +function getWithDefault($key, $default = null) +{ + if(rand(0,10) > 5) { + return $key; + } + + if (is_callable($default)) { + return $default(); + } + + return $default; +} + +/** + * + * @template TGetDefault + * @template TKey + * + * @param TKey $key + * @param TGetDefault|(callable(): TGetDefault) $default + * @return TKey|TGetDefault + */ +function getWithDefaultCallable($key, $default = null) +{ + if(rand(0,10) > 5) { + return $key; + } + + if (is_callable($default)) { + return $default(); + } + + return $default; +} + +assertType('int|null', getWithDefault(3)); +assertType('int|null', getWithDefaultCallable(3)); +assertType('int|string', getWithDefault(3, 'foo')); +assertType('int|string', getWithDefaultCallable(3, 'foo')); +assertType('int|string', getWithDefault(3, function () { + return 'foo'; +})); +assertType('int|string', getWithDefaultCallable(3, function () { + return 'foo'; +})); +assertType('GenericUnions\Foo|int', getWithDefault(3, function () { + return new Foo; +})); +assertType('GenericUnions\Foo|int', getWithDefaultCallable(3, function () { + return new Foo; +})); +assertType('GenericUnions\Foo|int', getWithDefault(3, new Foo)); +assertType('GenericUnions\Foo|int', getWithDefaultCallable(3, new Foo)); +assertType('int|string', getWithDefaultCallable(3, new InvokableClass));