1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-21 21:31:13 +01:00

Make the length of possible string unions unlimited in conditionals

Fixes #1103
This commit is contained in:
Brown 2018-11-28 15:12:08 -05:00
parent 04b3fd2bb5
commit c378079db8
6 changed files with 96 additions and 20 deletions

View File

@ -306,9 +306,14 @@ class SwitchAnalyzer
}
}
$case_stmts = $case->stmts;
$continue_case_equality_expr = false;
$case_stmts = array_merge($switch_scope->leftover_statements, $case_stmts);
if ($case->stmts) {
$case_stmts = array_merge($switch_scope->leftover_statements, $case->stmts);
} else {
$continue_case_equality_expr = count($switch_scope->leftover_statements) === 1;
$case_stmts = $switch_scope->leftover_statements;
}
if (!$has_leaving_statements && !$is_last) {
if (!$case_equality_expr) {
@ -330,12 +335,19 @@ class SwitchAnalyzer
)
: $case_equality_expr;
$case_if_stmt = new PhpParser\Node\Stmt\If_(
$switch_scope->leftover_case_equality_expr,
['stmts' => $case_stmts]
);
if ($continue_case_equality_expr
&& $switch_scope->leftover_statements[0] instanceof PhpParser\Node\Stmt\If_
) {
$case_if_stmt = $switch_scope->leftover_statements[0];
$case_if_stmt->cond = $switch_scope->leftover_case_equality_expr;
} else {
$case_if_stmt = new PhpParser\Node\Stmt\If_(
$switch_scope->leftover_case_equality_expr,
['stmts' => $case_stmts]
);
$switch_scope->leftover_statements = [$case_if_stmt];
$switch_scope->leftover_statements = [$case_if_stmt];
}
return;
}

View File

@ -69,7 +69,7 @@ class ArrayAnalyzer
}
if ($item_key_type) {
$item_key_type = Type::combineUnionTypes($key_type, $item_key_type);
$item_key_type = Type::combineUnionTypes($key_type, $item_key_type, false, 30);
} else {
$item_key_type = $key_type;
}
@ -126,7 +126,12 @@ class ArrayAnalyzer
}
if ($item_value_type) {
$item_value_type = Type::combineUnionTypes($item->value->inferredType, clone $item_value_type);
$item_value_type = Type::combineUnionTypes(
$item->value->inferredType,
clone $item_value_type,
false,
30
);
} else {
$item_value_type = $item->value->inferredType;
}

View File

@ -1056,7 +1056,12 @@ class StatementsAnalyzer extends SourceAnalyzer implements StatementsSource
if ($single_item_key_type) {
if ($item_key_type) {
$item_key_type = Type::combineUnionTypes($single_item_key_type, $item_key_type);
$item_key_type = Type::combineUnionTypes(
$single_item_key_type,
$item_key_type,
false,
30
);
} else {
$item_key_type = $single_item_key_type;
}
@ -1120,7 +1125,12 @@ class StatementsAnalyzer extends SourceAnalyzer implements StatementsSource
}
if ($item_value_type) {
$item_value_type = Type::combineUnionTypes($single_item_value_type, $item_value_type);
$item_value_type = Type::combineUnionTypes(
$single_item_value_type,
$item_value_type,
false,
30
);
} else {
$item_value_type = $single_item_value_type;
}

View File

@ -73,12 +73,17 @@ class TypeCombination
* - and `array + array<string> = array<mixed>`
*
* @param array<Atomic> $types
* @param int $literal_limit any greater number of literal types than this
* will be merged to a scalar
*
* @return Union
* @psalm-suppress TypeCoercion
*/
public static function combineTypes(array $types, bool $overwrite_empty_array = false)
{
public static function combineTypes(
array $types,
bool $overwrite_empty_array = false,
int $literal_limit = 500
) {
if (in_array(null, $types, true)) {
return Type::getMixed();
}
@ -108,7 +113,7 @@ class TypeCombination
foreach ($types as $type) {
$from_docblock = $from_docblock || $type->from_docblock;
$result = self::scrapeTypeProperties($type, $combination, $overwrite_empty_array);
$result = self::scrapeTypeProperties($type, $combination, $overwrite_empty_array, $literal_limit);
if ($type instanceof TNull) {
$has_null = true;
@ -308,7 +313,8 @@ class TypeCombination
private static function scrapeTypeProperties(
Atomic $type,
TypeCombination $combination,
bool $overwrite_empty_array
bool $overwrite_empty_array,
int $literal_limit
) {
if ($type instanceof TMixed) {
if ($type->from_isset || $type instanceof TEmptyMixed) {
@ -395,7 +401,7 @@ class TypeCombination
} else {
if ($type instanceof TString) {
if ($type instanceof TLiteralString) {
if ($combination->strings !== null && count($combination->strings) < 30) {
if ($combination->strings !== null && count($combination->strings) < $literal_limit) {
$combination->strings[] = $type;
} else {
$combination->strings = null;
@ -426,7 +432,7 @@ class TypeCombination
}
} elseif ($type instanceof TInt) {
if ($type instanceof TLiteralInt) {
if ($combination->ints !== null && count($combination->ints) < 30) {
if ($combination->ints !== null && count($combination->ints) < $literal_limit) {
$combination->ints[] = $type;
} else {
$combination->ints = null;
@ -438,7 +444,7 @@ class TypeCombination
}
} elseif ($type instanceof TFloat) {
if ($type instanceof TLiteralFloat) {
if ($combination->floats !== null && count($combination->floats) < 30) {
if ($combination->floats !== null && count($combination->floats) < $literal_limit) {
$combination->floats[] = $type;
} else {
$combination->floats = null;

View File

@ -944,13 +944,16 @@ abstract class Type
*
* @param Union $type_1
* @param Union $type_2
* @param int $literal_limit any greater number of literal types than this
* will be merged to a scalar
*
* @return Union
*/
public static function combineUnionTypes(
Union $type_1,
Union $type_2,
bool $overwrite_empty_array = false
bool $overwrite_empty_array = false,
int $literal_limit = 500
) {
if ($type_1->isVanillaMixed() || $type_2->isVanillaMixed()) {
$combined_type = Type::getMixed();
@ -972,7 +975,8 @@ abstract class Type
array_values($type_1->getTypes()),
array_values($type_2->getTypes())
),
$overwrite_empty_array
$overwrite_empty_array,
$literal_limit
);
if (!$type_1->initialized || !$type_2->initialized) {

View File

@ -517,6 +517,45 @@ class SwitchTypeTest extends TestCase
'$a' => 'bool',
],
],
'moreThan30Cases' => [
'<?php
function f(string $a) : void {
switch ($a) {
case "a":
case "b":
case "c":
case "d":
case "e":
case "f":
case "g":
case "h":
case "i":
case "j":
case "k":
case "l":
case "m":
case "n":
case "o":
case "p":
case "q":
case "r":
case "s":
case "t":
case "u":
case "v":
case "w":
case "x":
case "y":
case "z":
case "A":
case "B":
case "C":
case "D":
case "E":
return;
}
}',
],
];
}