diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 02b0bb3538..23b4baa7a4 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -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 - @@ -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 - diff --git a/src/Reflection/InitializerExprTypeResolver.php b/src/Reflection/InitializerExprTypeResolver.php index 0c5ede18b5..499f504a7a 100644 --- a/src/Reflection/InitializerExprTypeResolver.php +++ b/src/Reflection/InitializerExprTypeResolver.php @@ -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); } @@ -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); } @@ -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 + { if ($leftType instanceof IntegerRangeType) { $leftTypes = $leftType->getFiniteTypes(); } else { @@ -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); }