Skip to content

Commit

Permalink
Fix options created from callbacks that return void
Browse files Browse the repository at this point in the history
  • Loading branch information
axlon committed Sep 28, 2024
1 parent 6c176dd commit fd5d233
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 22 deletions.
4 changes: 3 additions & 1 deletion src/Type/PhpOption/EnsureReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ static function (Type $type, callable $traverse) use ($noneValueType, $scope): T

if ($type->isCallable()->yes()) {
$type = TypeCombinator::union(...array_map(
static fn (CallableParametersAcceptor $variant) => $variant->getReturnType(),
static function (CallableParametersAcceptor $variant) {
return TypeUtil::replaceVoid($variant->getReturnType());
},
$type->getCallableParametersAcceptors($scope),
));
}
Expand Down
2 changes: 1 addition & 1 deletion src/Type/PhpOption/FromReturnReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public function getTypeFromStaticMethodCall(
: [];

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

return new GenericObjectType('PhpOption\LazyOption', [
$valueType->generalize(GeneralizePrecision::templateArgument()),
Expand Down
34 changes: 18 additions & 16 deletions src/Type/PhpOption/LiftReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@
use PHPStan\Reflection\ParameterReflection;
use PHPStan\Type\ClosureType;
use PHPStan\Type\DynamicStaticMethodReturnTypeExtension;
use PHPStan\Type\GeneralizePrecision;
use PHPStan\Type\Generic\GenericObjectType;
use PHPStan\Type\NeverType;
use PHPStan\Type\NullType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\TypeTraverser;
use PHPStan\Type\UnionType;
use Resolve\PHPStan\Reflection\PhpOption\LiftedParameterReflection;

/**
Expand Down Expand Up @@ -61,24 +63,24 @@ static function (ParameterReflection $parameterReflection) {
$parametersAcceptor->getParameters(),
);

$returnTypeIsOption = (new ObjectType('PhpOption\Option'))->isSuperTypeOf(
$returnType = TypeTraverser::map(
$parametersAcceptor->getReturnType(),
);
static function (Type $type, callable $traverse) use ($noneValueType): Type {
if ($type instanceof UnionType) {
return $traverse($type);
}

if ((new ObjectType('PhpOption\Option'))->isSuperTypeOf($type)->yes()) {
return $type;
}

if ($returnTypeIsOption->yes() || $returnTypeIsOption->maybe()) {
$returnType = $parametersAcceptor->getReturnType();
} else {
$returnType = new NeverType();
}
$type = TypeCombinator::remove(TypeUtil::replaceVoid($type), $noneValueType);

if ($returnTypeIsOption->no() || $returnTypeIsOption->maybe()) {
$returnType = TypeCombinator::union(
$returnType,
new GenericObjectType('PhpOption\Option', [
TypeCombinator::remove($parametersAcceptor->getReturnType(), $noneValueType),
]),
);
}
return new GenericObjectType('PhpOption\Option', [
$type->generalize(GeneralizePrecision::templateArgument()),
]);
},
);

return new ClosureType($parameters, $returnType);
},
Expand Down
12 changes: 12 additions & 0 deletions src/Type/PhpOption/TypeUtil.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Resolve\PHPStan\Type\PhpOption;

use PHPStan\Type\NeverType;
use PHPStan\Type\NullType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeTraverser;
use PHPStan\Type\UnionType;
Expand All @@ -28,4 +29,15 @@ public static function forShallowComparison(Type $type): Type
return new NeverType();
});
}

public static function replaceVoid(Type $type): Type
{
return TypeTraverser::map($type, static function (Type $type, callable $traverse): Type {
if ($type instanceof UnionType) {
return $traverse($type);
}

return $type->isVoid()->yes() ? new NullType() : $type;
});
}
}
13 changes: 11 additions & 2 deletions tests/Type/PhpOption/data/option-ensure.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
* @var string|null $value
*/
assertType('PhpOption\Option<string>', Option::ensure($value));
assertType('PhpOption\Option<string>', Option::ensure($value, null));
assertType('PhpOption\Option<null>|PhpOption\Option<string>', Option::ensure($value, 123));

/**
Expand All @@ -22,5 +21,15 @@
* @var \PhpOption\Option<string>|123|(callable(): float)|null $option
*/
assertType('PhpOption\Option<float>|PhpOption\Option<int>|PhpOption\Option<string>', Option::ensure($option));
assertType('PhpOption\Option<float>|PhpOption\Option<int>|PhpOption\Option<string>', Option::ensure($option, null));
assertType('PhpOption\Option<float>|PhpOption\Option<null>|PhpOption\Option<string>', Option::ensure($option, 123));

/**
* @var callable(): string $value
*/
assertType('PhpOption\Option<string>', Option::ensure($value));

/**
* @var callable(): void $value
*/
assertType('PhpOption\Option<*NEVER*>', Option::ensure($value));
assertType('PhpOption\Option<null>', Option::ensure($value, 123));
9 changes: 7 additions & 2 deletions tests/Type/PhpOption/data/option-from-return.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,10 @@
* @var callable(): (string|null) $fn
*/
assertType('PhpOption\LazyOption<string>', Option::fromReturn($fn));
assertType('PhpOption\LazyOption<string>', Option::fromReturn($fn, [], null));
assertType('PhpOption\LazyOption<string|null>', Option::fromReturn($fn, [], 123));
assertType('PhpOption\LazyOption<string|null>', Option::fromReturn($fn, noneValue: 123));

/**
* @var callable(): void $fn
*/
assertType('PhpOption\LazyOption<*NEVER*>', Option::fromReturn($fn));
assertType('PhpOption\LazyOption<null>', Option::fromReturn($fn, noneValue: 123));
7 changes: 7 additions & 0 deletions tests/Type/PhpOption/data/option-lift.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,10 @@
* @var callable(int ...): string $fn
*/
assertType('Closure(PhpOption\Option<int> ...): PhpOption\Option<string>', Option::lift($fn));


/**
* @var callable(): void $fn
*/
assertType('Closure(): PhpOption\Option<never>', Option::lift($fn));
assertType('Closure(): PhpOption\Option<null>', Option::lift($fn, 123));

0 comments on commit fd5d233

Please sign in to comment.