Skip to content

Commit fd5d233

Browse files
committed
Fix options created from callbacks that return void
1 parent 6c176dd commit fd5d233

File tree

7 files changed

+59
-22
lines changed

7 files changed

+59
-22
lines changed

src/Type/PhpOption/EnsureReturnTypeExtension.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,9 @@ static function (Type $type, callable $traverse) use ($noneValueType, $scope): T
6060

6161
if ($type->isCallable()->yes()) {
6262
$type = TypeCombinator::union(...array_map(
63-
static fn (CallableParametersAcceptor $variant) => $variant->getReturnType(),
63+
static function (CallableParametersAcceptor $variant) {
64+
return TypeUtil::replaceVoid($variant->getReturnType());
65+
},
6466
$type->getCallableParametersAcceptors($scope),
6567
));
6668
}

src/Type/PhpOption/FromReturnReturnTypeExtension.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public function getTypeFromStaticMethodCall(
5454
: [];
5555

5656
$returnType = ParametersAcceptorSelector::selectFromArgs($scope, $args, $parametersAcceptors)->getReturnType();
57-
$valueType = TypeCombinator::remove($returnType, $noneValueType);
57+
$valueType = TypeCombinator::remove(TypeUtil::replaceVoid($returnType), $noneValueType);
5858

5959
return new GenericObjectType('PhpOption\LazyOption', [
6060
$valueType->generalize(GeneralizePrecision::templateArgument()),

src/Type/PhpOption/LiftReturnTypeExtension.php

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@
1111
use PHPStan\Reflection\ParameterReflection;
1212
use PHPStan\Type\ClosureType;
1313
use PHPStan\Type\DynamicStaticMethodReturnTypeExtension;
14+
use PHPStan\Type\GeneralizePrecision;
1415
use PHPStan\Type\Generic\GenericObjectType;
15-
use PHPStan\Type\NeverType;
1616
use PHPStan\Type\NullType;
1717
use PHPStan\Type\ObjectType;
1818
use PHPStan\Type\Type;
1919
use PHPStan\Type\TypeCombinator;
20+
use PHPStan\Type\TypeTraverser;
21+
use PHPStan\Type\UnionType;
2022
use Resolve\PHPStan\Reflection\PhpOption\LiftedParameterReflection;
2123

2224
/**
@@ -61,24 +63,24 @@ static function (ParameterReflection $parameterReflection) {
6163
$parametersAcceptor->getParameters(),
6264
);
6365

64-
$returnTypeIsOption = (new ObjectType('PhpOption\Option'))->isSuperTypeOf(
66+
$returnType = TypeTraverser::map(
6567
$parametersAcceptor->getReturnType(),
66-
);
68+
static function (Type $type, callable $traverse) use ($noneValueType): Type {
69+
if ($type instanceof UnionType) {
70+
return $traverse($type);
71+
}
72+
73+
if ((new ObjectType('PhpOption\Option'))->isSuperTypeOf($type)->yes()) {
74+
return $type;
75+
}
6776

68-
if ($returnTypeIsOption->yes() || $returnTypeIsOption->maybe()) {
69-
$returnType = $parametersAcceptor->getReturnType();
70-
} else {
71-
$returnType = new NeverType();
72-
}
77+
$type = TypeCombinator::remove(TypeUtil::replaceVoid($type), $noneValueType);
7378

74-
if ($returnTypeIsOption->no() || $returnTypeIsOption->maybe()) {
75-
$returnType = TypeCombinator::union(
76-
$returnType,
77-
new GenericObjectType('PhpOption\Option', [
78-
TypeCombinator::remove($parametersAcceptor->getReturnType(), $noneValueType),
79-
]),
80-
);
81-
}
79+
return new GenericObjectType('PhpOption\Option', [
80+
$type->generalize(GeneralizePrecision::templateArgument()),
81+
]);
82+
},
83+
);
8284

8385
return new ClosureType($parameters, $returnType);
8486
},

src/Type/PhpOption/TypeUtil.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Resolve\PHPStan\Type\PhpOption;
66

77
use PHPStan\Type\NeverType;
8+
use PHPStan\Type\NullType;
89
use PHPStan\Type\Type;
910
use PHPStan\Type\TypeTraverser;
1011
use PHPStan\Type\UnionType;
@@ -28,4 +29,15 @@ public static function forShallowComparison(Type $type): Type
2829
return new NeverType();
2930
});
3031
}
32+
33+
public static function replaceVoid(Type $type): Type
34+
{
35+
return TypeTraverser::map($type, static function (Type $type, callable $traverse): Type {
36+
if ($type instanceof UnionType) {
37+
return $traverse($type);
38+
}
39+
40+
return $type->isVoid()->yes() ? new NullType() : $type;
41+
});
42+
}
3143
}

tests/Type/PhpOption/data/option-ensure.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
* @var string|null $value
1111
*/
1212
assertType('PhpOption\Option<string>', Option::ensure($value));
13-
assertType('PhpOption\Option<string>', Option::ensure($value, null));
1413
assertType('PhpOption\Option<null>|PhpOption\Option<string>', Option::ensure($value, 123));
1514

1615
/**
@@ -22,5 +21,15 @@
2221
* @var \PhpOption\Option<string>|123|(callable(): float)|null $option
2322
*/
2423
assertType('PhpOption\Option<float>|PhpOption\Option<int>|PhpOption\Option<string>', Option::ensure($option));
25-
assertType('PhpOption\Option<float>|PhpOption\Option<int>|PhpOption\Option<string>', Option::ensure($option, null));
2624
assertType('PhpOption\Option<float>|PhpOption\Option<null>|PhpOption\Option<string>', Option::ensure($option, 123));
25+
26+
/**
27+
* @var callable(): string $value
28+
*/
29+
assertType('PhpOption\Option<string>', Option::ensure($value));
30+
31+
/**
32+
* @var callable(): void $value
33+
*/
34+
assertType('PhpOption\Option<*NEVER*>', Option::ensure($value));
35+
assertType('PhpOption\Option<null>', Option::ensure($value, 123));

tests/Type/PhpOption/data/option-from-return.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,10 @@
1010
* @var callable(): (string|null) $fn
1111
*/
1212
assertType('PhpOption\LazyOption<string>', Option::fromReturn($fn));
13-
assertType('PhpOption\LazyOption<string>', Option::fromReturn($fn, [], null));
14-
assertType('PhpOption\LazyOption<string|null>', Option::fromReturn($fn, [], 123));
13+
assertType('PhpOption\LazyOption<string|null>', Option::fromReturn($fn, noneValue: 123));
14+
15+
/**
16+
* @var callable(): void $fn
17+
*/
18+
assertType('PhpOption\LazyOption<*NEVER*>', Option::fromReturn($fn));
19+
assertType('PhpOption\LazyOption<null>', Option::fromReturn($fn, noneValue: 123));

tests/Type/PhpOption/data/option-lift.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,10 @@
2020
* @var callable(int ...): string $fn
2121
*/
2222
assertType('Closure(PhpOption\Option<int> ...): PhpOption\Option<string>', Option::lift($fn));
23+
24+
25+
/**
26+
* @var callable(): void $fn
27+
*/
28+
assertType('Closure(): PhpOption\Option<never>', Option::lift($fn));
29+
assertType('Closure(): PhpOption\Option<null>', Option::lift($fn, 123));

0 commit comments

Comments
 (0)