1
0
mirror of https://github.com/danog/psalm.git synced 2024-12-12 01:09:38 +01:00

refactor big methods away

This commit is contained in:
orklah 2021-09-12 21:06:57 +02:00
parent 6b5e6eeecc
commit e75e390449

View File

@ -916,65 +916,7 @@ class ArithmeticOpAnalyzer
}
if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Mod) {
//result of Mod is not directly dependant on the bounds of the range
if ($right_type_part->min_bound !== null && $right_type_part->min_bound === $right_type_part->max_bound) {
//if the second operand is a literal, we can be pretty detailed
if ($right_type_part->min_bound === 0) {
$new_result_type = Type::getEmpty();
} else {
if ($left_type_part->isPositiveOrZero()) {
if ($right_type_part->isPositive()) {
$max = $right_type_part->min_bound - 1;
$new_result_type = new Type\Union([new TIntRange(0, $max)]);
} else {
$max = $right_type_part->min_bound + 1;
$new_result_type = new Type\Union([new TIntRange($max, 0)]);
}
} elseif ($left_type_part->isNegativeOrZero()) {
if ($right_type_part->isPositive()) {
$max = $right_type_part->min_bound - 1;
$new_result_type = new Type\Union([new TIntRange(-$max, 0)]);
} else {
$max = $right_type_part->min_bound + 1;
$new_result_type = new Type\Union([new TIntRange(-$max, 0)]);
}
} else {
if ($right_type_part->isPositive()) {
$max = $right_type_part->min_bound - 1;
} else {
$max = -$right_type_part->min_bound - 1;
}
$new_result_type = new Type\Union([new TIntRange(-$max, $max)]);
}
}
} elseif ($right_type_part->isPositive()) {
if ($left_type_part->isPositiveOrZero()) {
$new_result_type = new Type\Union([new TIntRange(0, null)]);
} elseif ($left_type_part->isNegativeOrZero()) {
$new_result_type = new Type\Union([new TIntRange(null, 0)]);
} else {
$new_result_type = Type::getInt(true);
}
} elseif ($right_type_part->isNegative()) {
if ($left_type_part->isPositiveOrZero()) {
$new_result_type = new Type\Union([new TIntRange(null, 0)]);
} elseif ($left_type_part->isNegativeOrZero()) {
$new_result_type = new Type\Union([new TIntRange(null, 0)]);
} else {
$new_result_type = Type::getInt(true);
}
} else {
$new_result_type = Type::getInt(true);
}
if (!$result_type) {
$result_type = $new_result_type;
} else {
$result_type = Type::combineUnionTypes(
$new_result_type,
$result_type
);
}
self::analyzeModBetweenIntRange($right_type_part, $left_type_part, $result_type);
return;
}
@ -1010,6 +952,101 @@ class ArithmeticOpAnalyzer
}
if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Mul) {
self::analyzeMulBetweenIntRange($right_type_part, $left_type_part, $parent, $result_type);
return;
}
if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Pow) {
self::analyzePowBetweenIntRange($left_type_part, $right_type_part, $result_type);
return;
}
if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Minus) {
//for Minus, we have to assume the min is the min from first range minus the max from the second
$min_operand1 = $left_type_part->min_bound;
$min_operand2 = $right_type_part->max_bound;
//and the max is the max from first range minus the min from the second
$max_operand1 = $left_type_part->max_bound;
$max_operand2 = $right_type_part->min_bound;
} else {
$min_operand1 = $left_type_part->min_bound;
$min_operand2 = $right_type_part->min_bound;
$max_operand1 = $left_type_part->max_bound;
$max_operand2 = $right_type_part->max_bound;
}
$calculated_min_type = null;
if ($min_operand1 !== null && $min_operand2 !== null) {
// when there are two valid numbers, make any operation
$calculated_min_type = self::arithmeticOperation(
$parent,
$min_operand1,
$min_operand2,
false
);
}
$calculated_max_type = null;
if ($max_operand1 !== null && $max_operand2 !== null) {
// when there are two valid numbers, make any operation
$calculated_max_type = self::arithmeticOperation(
$parent,
$max_operand1,
$max_operand2,
false
);
}
$min_value = $calculated_min_type !== null ? $calculated_min_type->getSingleIntLiteral()->value : null;
$max_value = $calculated_max_type !== null ? $calculated_max_type->getSingleIntLiteral()->value : null;
$new_result_type = new Type\Union([new Type\Atomic\TIntRange($min_value, $max_value)]);
if (!$result_type) {
$result_type = $new_result_type;
} else {
$result_type = Type::combineUnionTypes($new_result_type, $result_type);
}
}
/**
* @param TIntRange|TInt $left_type_part
* @param TIntRange|TInt $right_type_part
*/
private static function analyzeOperandsBetweenIntRangeAndInt(
PhpParser\Node $parent,
?Type\Union &$result_type,
Atomic $left_type_part,
Atomic $right_type_part
): void {
if ($left_type_part instanceof Type\Atomic\TIntRange) {
$left_is_range = true;
$range_operand = $left_type_part;
$other_operand = $right_type_part;
} elseif ($right_type_part instanceof Type\Atomic\TIntRange) {
$left_is_range = false;
$range_operand = $right_type_part;
$other_operand = $left_type_part;
} else {
//this can't happen
return;
}
$new_range = TIntRange::convertToIntRange($other_operand);
if ($left_is_range) {
self::analyzeOperandsBetweenIntRange($parent, $result_type, $range_operand, $new_range);
} else {
self::analyzeOperandsBetweenIntRange($parent, $result_type, $new_range, $range_operand);
}
}
private static function analyzeMulBetweenIntRange(
TIntRange $right_type_part,
TIntRange $left_type_part,
PhpParser\Node\Expr\BinaryOp\Mul $parent,
?Type\Union &$result_type
): void {
//Mul is a special case because of double negatives. We can only infer when we know both signs strictly
if ($right_type_part->min_bound !== null
&& $right_type_part->max_bound !== null
@ -1183,11 +1220,13 @@ class ArithmeticOpAnalyzer
} else {
$result_type = Type::combineUnionTypes($new_result_type, $result_type);
}
return;
}
if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Pow) {
private static function analyzePowBetweenIntRange(
TIntRange $left_type_part,
TIntRange $right_type_part,
?Type\Union &$result_type
): void {
//If Pow first operand is negative, the result will be negative, else it will be positive
//If Pow second operand is negative, the result will be float, if it's 0, it will be 1/-1, else positive
if ($left_type_part->isPositive()) {
@ -1239,87 +1278,71 @@ class ArithmeticOpAnalyzer
} else {
$result_type = Type::combineUnionTypes($new_result_type, $result_type);
}
return;
}
if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Minus) {
//for Minus, we have to assume the min is the min from first range minus the max from the second
$min_operand1 = $left_type_part->min_bound;
$min_operand2 = $right_type_part->max_bound;
//and the max is the max from first range minus the min from the second
$max_operand1 = $left_type_part->max_bound;
$max_operand2 = $right_type_part->min_bound;
private static function analyzeModBetweenIntRange(
TIntRange $right_type_part,
TIntRange $left_type_part,
?Type\Union &$result_type
): void {
//result of Mod is not directly dependant on the bounds of the range
if ($right_type_part->min_bound !== null && $right_type_part->min_bound === $right_type_part->max_bound) {
//if the second operand is a literal, we can be pretty detailed
if ($right_type_part->min_bound === 0) {
$new_result_type = Type::getEmpty();
} else {
$min_operand1 = $left_type_part->min_bound;
$min_operand2 = $right_type_part->min_bound;
$max_operand1 = $left_type_part->max_bound;
$max_operand2 = $right_type_part->max_bound;
if ($left_type_part->isPositiveOrZero()) {
if ($right_type_part->isPositive()) {
$max = $right_type_part->min_bound - 1;
$new_result_type = new Type\Union([new TIntRange(0, $max)]);
} else {
$max = $right_type_part->min_bound + 1;
$new_result_type = new Type\Union([new TIntRange($max, 0)]);
}
$calculated_min_type = null;
if ($min_operand1 !== null && $min_operand2 !== null) {
// when there are two valid numbers, make any operation
$calculated_min_type = self::arithmeticOperation(
$parent,
$min_operand1,
$min_operand2,
false
);
} elseif ($left_type_part->isNegativeOrZero()) {
if ($right_type_part->isPositive()) {
$max = $right_type_part->min_bound - 1;
$new_result_type = new Type\Union([new TIntRange(-$max, 0)]);
} else {
$max = $right_type_part->min_bound + 1;
$new_result_type = new Type\Union([new TIntRange(-$max, 0)]);
}
$calculated_max_type = null;
if ($max_operand1 !== null && $max_operand2 !== null) {
// when there are two valid numbers, make any operation
$calculated_max_type = self::arithmeticOperation(
$parent,
$max_operand1,
$max_operand2,
false
);
} else {
if ($right_type_part->isPositive()) {
$max = $right_type_part->min_bound - 1;
} else {
$max = -$right_type_part->min_bound - 1;
}
$new_result_type = new Type\Union([new TIntRange(-$max, $max)]);
}
}
} elseif ($right_type_part->isPositive()) {
if ($left_type_part->isPositiveOrZero()) {
$new_result_type = new Type\Union([new TIntRange(0, null)]);
} elseif ($left_type_part->isNegativeOrZero()) {
$new_result_type = new Type\Union([new TIntRange(null, 0)]);
} else {
$new_result_type = Type::getInt(true);
}
} elseif ($right_type_part->isNegative()) {
if ($left_type_part->isPositiveOrZero()) {
$new_result_type = new Type\Union([new TIntRange(null, 0)]);
} elseif ($left_type_part->isNegativeOrZero()) {
$new_result_type = new Type\Union([new TIntRange(null, 0)]);
} else {
$new_result_type = Type::getInt(true);
}
} else {
$new_result_type = Type::getInt(true);
}
$min_value = $calculated_min_type !== null ? $calculated_min_type->getSingleIntLiteral()->value : null;
$max_value = $calculated_max_type !== null ? $calculated_max_type->getSingleIntLiteral()->value : null;
$new_result_type = new Type\Union([new Type\Atomic\TIntRange($min_value, $max_value)]);
if (!$result_type) {
$result_type = $new_result_type;
} else {
$result_type = Type::combineUnionTypes($new_result_type, $result_type);
}
}
/**
* @param TIntRange|TInt $left_type_part
* @param TIntRange|TInt $right_type_part
*/
private static function analyzeOperandsBetweenIntRangeAndInt(
PhpParser\Node $parent,
?Type\Union &$result_type,
Atomic $left_type_part,
Atomic $right_type_part
): void {
if ($left_type_part instanceof Type\Atomic\TIntRange) {
$left_is_range = true;
$range_operand = $left_type_part;
$other_operand = $right_type_part;
} elseif ($right_type_part instanceof Type\Atomic\TIntRange) {
$left_is_range = false;
$range_operand = $right_type_part;
$other_operand = $left_type_part;
} else {
//this can't happen
return;
}
$new_range = TIntRange::convertToIntRange($other_operand);
if ($left_is_range) {
self::analyzeOperandsBetweenIntRange($parent, $result_type, $range_operand, $new_range);
} else {
self::analyzeOperandsBetweenIntRange($parent, $result_type, $new_range, $range_operand);
$result_type = Type::combineUnionTypes(
$new_result_type,
$result_type
);
}
}
}