Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ parameters:
-
rawMessage: 'Doing instanceof PHPStan\Type\ConstantScalarType is error-prone and deprecated. Use Type::isConstantScalarValue() or Type::getConstantScalarTypes() or Type::getConstantScalarValues() instead.'
identifier: phpstanApi.instanceofType
count: 22
count: 18
path: src/Reflection/InitializerExprTypeResolver.php

-
Expand All @@ -474,7 +474,7 @@ parameters:
-
rawMessage: 'Doing instanceof PHPStan\Type\Constant\ConstantStringType is error-prone and deprecated. Use Type::getConstantStrings() instead.'
identifier: phpstanApi.instanceofType
count: 10
count: 6
path: src/Reflection/InitializerExprTypeResolver.php

-
Expand Down
165 changes: 57 additions & 108 deletions src/Reflection/InitializerExprTypeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -976,47 +976,10 @@ public function getBitwiseAndTypeFromTypes(Type $leftType, Type $rightType): Typ
return $this->getNeverType($leftType, $rightType);
}

if ($leftType instanceof IntegerRangeType) {
$leftTypes = $leftType->getFiniteTypes();
} else {
$leftTypes = $leftType->getConstantScalarTypes();
}
if ($rightType instanceof IntegerRangeType) {
$rightTypes = $rightType->getFiniteTypes();
} else {
$rightTypes = $rightType->getConstantScalarTypes();
}

$leftTypesCount = count($leftTypes);
$rightTypesCount = count($rightTypes);
if ($leftTypesCount > 0 && $rightTypesCount > 0) {
$resultTypes = [];
$generalize = $leftTypesCount * $rightTypesCount > self::CALCULATE_SCALARS_LIMIT;
if (!$generalize) {
foreach ($leftTypes as $leftTypeInner) {
foreach ($rightTypes as $rightTypeInner) {
if ($leftTypeInner instanceof ConstantStringType && $rightTypeInner instanceof ConstantStringType) {
$resultType = $this->getTypeFromValue($leftTypeInner->getValue() & $rightTypeInner->getValue());
} else {
$leftNumberType = $leftTypeInner->toNumber();
$rightNumberType = $rightTypeInner->toNumber();

if ($leftNumberType instanceof ErrorType || $rightNumberType instanceof ErrorType) {
return new ErrorType();
}

if (!$leftNumberType instanceof ConstantScalarType || !$rightNumberType instanceof ConstantScalarType) {
throw new ShouldNotHappenException();
}

$resultType = $this->getTypeFromValue($leftNumberType->getValue() & $rightNumberType->getValue());
}
$resultTypes[] = $resultType;
}
}
return TypeCombinator::union(...$resultTypes);
}

$result = $this->getFiniteOrConstantScalarTypes($leftType, $rightType, static fn ($a, $b) => $a & $b);
if ($result instanceof Type) {
return $result;
} elseif ($result === self::IS_SCALAR_TYPE) {
$leftType = $this->optimizeScalarType($leftType);
$rightType = $this->optimizeScalarType($rightType);
}
Expand Down Expand Up @@ -1071,47 +1034,10 @@ public function getBitwiseOrTypeFromTypes(Type $leftType, Type $rightType): Type
return $this->getNeverType($leftType, $rightType);
}

if ($leftType instanceof IntegerRangeType) {
$leftTypes = $leftType->getFiniteTypes();
} else {
$leftTypes = $leftType->getConstantScalarTypes();
}
if ($rightType instanceof IntegerRangeType) {
$rightTypes = $rightType->getFiniteTypes();
} else {
$rightTypes = $rightType->getConstantScalarTypes();
}

$leftTypesCount = count($leftTypes);
$rightTypesCount = count($rightTypes);
if ($leftTypesCount > 0 && $rightTypesCount > 0) {
$resultTypes = [];
$generalize = $leftTypesCount * $rightTypesCount > self::CALCULATE_SCALARS_LIMIT;
if (!$generalize) {
foreach ($leftTypes as $leftTypeInner) {
foreach ($rightTypes as $rightTypeInner) {
if ($leftTypeInner instanceof ConstantStringType && $rightTypeInner instanceof ConstantStringType) {
$resultType = $this->getTypeFromValue($leftTypeInner->getValue() | $rightTypeInner->getValue());
} else {
$leftNumberType = $leftTypeInner->toNumber();
$rightNumberType = $rightTypeInner->toNumber();

if ($leftNumberType instanceof ErrorType || $rightNumberType instanceof ErrorType) {
return new ErrorType();
}

if (!$leftNumberType instanceof ConstantScalarType || !$rightNumberType instanceof ConstantScalarType) {
throw new ShouldNotHappenException();
}

$resultType = $this->getTypeFromValue($leftNumberType->getValue() | $rightNumberType->getValue());
}
$resultTypes[] = $resultType;
}
}
return TypeCombinator::union(...$resultTypes);
}

$result = $this->getFiniteOrConstantScalarTypes($leftType, $rightType, static fn ($a, $b) => $a | $b);
if ($result instanceof Type) {
return $result;
} elseif ($result === self::IS_SCALAR_TYPE) {
$leftType = $this->optimizeScalarType($leftType);
$rightType = $this->optimizeScalarType($rightType);
}
Expand Down Expand Up @@ -1150,12 +1076,16 @@ public function getBitwiseXorType(Expr $left, Expr $right, callable $getTypeCall
return $this->getBitwiseXorTypeFromTypes($leftType, $rightType);
}

public function getBitwiseXorTypeFromTypes(Type $leftType, Type $rightType): Type
{
if ($leftType instanceof NeverType || $rightType instanceof NeverType) {
return $this->getNeverType($leftType, $rightType);
}
private const IS_SCALAR_TYPE = 1;
private const IS_UNKNOWN = 2;

/**
* @param callable(bool|float|int|string|null, bool|float|int|string|null):string $operationCallable
*
* @return self::IS_UNKNOWN|self::IS_SCALAR_TYPE|Type
*/
private function getFiniteOrConstantScalarTypes(Type $leftType, Type $rightType, callable $operationCallable): int|Type
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method might be easier to read with early return ?

if ($leftTypesCount === 0 || $rightTypesCount === 0) {
     return self::IS_UNKNOWN;
}
...
if ($generalize) {
     return self::IS_SCALAR_TYPE;
}
...
return TypeCombinator::union(...$resultTypes);
```

I also wonder if a Type|bool isn't enough (and avoid introducing two constant). True when it need to be generalized.
Another solution could be including the generalize call inside this method and returning `[$leftType, $rightType, $result]` but I'm not sure it should be preferred.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method might be easier to read with early return ?

done.

I also wonder if a Type|bool isn't enough (and avoid introducing two constant).

I had it like that before I introduced constants. was not happy with the client-side code of this method as it was pretty unreadable.

{
if ($leftType instanceof IntegerRangeType) {
$leftTypes = $leftType->getFiniteTypes();
} else {
Expand All @@ -1169,34 +1099,53 @@ public function getBitwiseXorTypeFromTypes(Type $leftType, Type $rightType): Typ

$leftTypesCount = count($leftTypes);
$rightTypesCount = count($rightTypes);
if ($leftTypesCount > 0 && $rightTypesCount > 0) {
$resultTypes = [];
$generalize = $leftTypesCount * $rightTypesCount > self::CALCULATE_SCALARS_LIMIT;
if (!$generalize) {
foreach ($leftTypes as $leftTypeInner) {
foreach ($rightTypes as $rightTypeInner) {
if ($leftTypeInner instanceof ConstantStringType && $rightTypeInner instanceof ConstantStringType) {
$resultType = $this->getTypeFromValue($leftTypeInner->getValue() ^ $rightTypeInner->getValue());
} else {
$leftNumberType = $leftTypeInner->toNumber();
$rightNumberType = $rightTypeInner->toNumber();

if ($leftNumberType instanceof ErrorType || $rightNumberType instanceof ErrorType) {
return new ErrorType();
}
if ($leftTypesCount === 0 || $rightTypesCount === 0) {
return self::IS_UNKNOWN;
}

if (!$leftNumberType instanceof ConstantScalarType || !$rightNumberType instanceof ConstantScalarType) {
throw new ShouldNotHappenException();
}
$generalize = $leftTypesCount * $rightTypesCount > self::CALCULATE_SCALARS_LIMIT;
if ($generalize) {
return self::IS_SCALAR_TYPE;
}

$resultType = $this->getTypeFromValue($leftNumberType->getValue() ^ $rightNumberType->getValue());
}
$resultTypes[] = $resultType;
$resultTypes = [];
foreach ($leftTypes as $leftTypeInner) {
foreach ($rightTypes as $rightTypeInner) {
if ($leftTypeInner instanceof ConstantStringType && $rightTypeInner instanceof ConstantStringType) {
$resultValue = $operationCallable($leftTypeInner->getValue(), $rightTypeInner->getValue());
$resultType = $this->getTypeFromValue($resultValue);
} else {
$leftNumberType = $leftTypeInner->toNumber();
$rightNumberType = $rightTypeInner->toNumber();

if ($leftNumberType instanceof ErrorType || $rightNumberType instanceof ErrorType) {
return new ErrorType();
}

if (!$leftNumberType instanceof ConstantScalarType || !$rightNumberType instanceof ConstantScalarType) {
throw new ShouldNotHappenException();
}

$resultValue = $operationCallable($leftNumberType->getValue(), $rightNumberType->getValue());
$resultType = $this->getTypeFromValue($resultValue);
}
return TypeCombinator::union(...$resultTypes);
$resultTypes[] = $resultType;
}
}
return TypeCombinator::union(...$resultTypes);
}

public function getBitwiseXorTypeFromTypes(Type $leftType, Type $rightType): Type
{
if ($leftType instanceof NeverType || $rightType instanceof NeverType) {
return $this->getNeverType($leftType, $rightType);
}

$result = $this->getFiniteOrConstantScalarTypes($leftType, $rightType, static fn ($a, $b) => $a ^ $b);
if ($result instanceof Type) {
return $result;
} elseif ($result === self::IS_SCALAR_TYPE) {
$leftType = $this->optimizeScalarType($leftType);
$rightType = $this->optimizeScalarType($rightType);
}
Expand Down
Loading