Skip to content

Commit

Permalink
Let TypeCombiner return disjoint int ranges
Browse files Browse the repository at this point in the history
  • Loading branch information
danog committed Oct 17, 2022
1 parent 150be5c commit 1e55256
Show file tree
Hide file tree
Showing 7 changed files with 47 additions and 28 deletions.
Expand Up @@ -778,7 +778,7 @@ public static function getGlobalType(string $var_id, int $codebase_analysis_php_
]),
];

if ($codebase_analysis_php_version_id >= 81000) {
if ($codebase_analysis_php_version_id >= 8_10_00) {
$values['full_path'] = new Union([
new TString(),
new TNonEmptyList(Type::getString()),
Expand Down
5 changes: 3 additions & 2 deletions src/Psalm/Internal/Provider/ParserCacheProvider.php
Expand Up @@ -11,6 +11,8 @@
use UnexpectedValueException;

use function clearstatcache;
use function error_log;
use function file_get_contents;
use function file_put_contents;
use function filemtime;
use function gettype;
Expand All @@ -31,7 +33,6 @@
use function unserialize;

use const DIRECTORY_SEPARATOR;
use const E_USER_ERROR;
use const JSON_THROW_ON_ERROR;
use const LOCK_EX;
use const PHP_VERSION_ID;
Expand Down Expand Up @@ -348,7 +349,7 @@ public function deleteOldParserCaches(float $time_before): int

private function getParserCacheKey(string $file_path): string
{
if (PHP_VERSION_ID >= 80100) {
if (PHP_VERSION_ID >= 8_01_00) {
$hash = hash('xxh128', $file_path);
} else {
$hash = hash('md4', $file_path);
Expand Down
2 changes: 1 addition & 1 deletion src/Psalm/Internal/Provider/Providers.php
Expand Up @@ -90,7 +90,7 @@ public static function safeFileGetContents(string $path): string
break;
}
$max_wait_cycles--;
usleep(50000);
usleep(50_000);
}

if (!$has_lock) {
Expand Down
Expand Up @@ -21,6 +21,7 @@
use function array_filter;
use function assert;
use function count;
use function get_class;
use function in_array;
use function max;
use function min;
Expand Down
2 changes: 1 addition & 1 deletion src/Psalm/Internal/Provider/StatementsProvider.php
Expand Up @@ -139,7 +139,7 @@ public function getStatementsForFile(

$config = Config::getInstance();

if (PHP_VERSION_ID >= 80100) {
if (PHP_VERSION_ID >= 8_01_00) {
$file_content_hash = hash('xxh128', $version . $file_contents);
} else {
$file_content_hash = hash('md4', $version . $file_contents);
Expand Down
61 changes: 38 additions & 23 deletions src/Psalm/Internal/Type/TypeCombiner.php
Expand Up @@ -1168,7 +1168,16 @@ private static function scrapeIntProperties(

if ($type instanceof TLiteralInt) {
if ($combination->ints !== null && count($combination->ints) < $literal_limit) {
$combination->ints[$type_key] = $type;
$contains_literal = false;
if (isset($combination->value_types['int'])
&& $combination->value_types['int'] instanceof TIntRange
&& $combination->value_types['int']->contains($type->value)
) {
$contains_literal = true;
}
if (!$contains_literal) {
$combination->ints[$type_key] = $type;
}
} else {
$combination->ints[$type_key] = $type;

Expand All @@ -1177,20 +1186,20 @@ private static function scrapeIntProperties(
static fn($int): bool => $int->value < 0
);

if (isset($combination->value_types['int'])) {
if (isset($combination->value_types['int'])
&& $combination->value_types['int'] instanceof TIntRange
) {
$current_int_type = $combination->value_types['int'];
if ($current_int_type instanceof TIntRange) {
foreach ($combination->ints as $int) {
if (!$current_int_type->contains($int->value)) {
$current_int_type->min_bound = TIntRange::getNewLowestBound(
$current_int_type->min_bound,
$int->value
);
$current_int_type->max_bound = TIntRange::getNewHighestBound(
$current_int_type->max_bound,
$int->value
);
}
foreach ($combination->ints as $int) {
if (!$current_int_type->contains($int->value)) {
$current_int_type->min_bound = TIntRange::getNewLowestBound(
$current_int_type->min_bound,
$int->value
);
$current_int_type->max_bound = TIntRange::getNewHighestBound(
$current_int_type->max_bound,
$int->value
);
}
}
}
Expand All @@ -1213,34 +1222,40 @@ private static function scrapeIntProperties(
) {
$combination->value_types['int'] = new TInt();
}
$combination->ints = null;
} elseif ($type instanceof TIntRange) {
$type = clone $type;
if ($combination->ints) {
foreach ($combination->ints as $int) {
if (!$type->contains($int->value)) {
$type->min_bound = TIntRange::getNewLowestBound($type->min_bound, $int->value);
$type->max_bound = TIntRange::getNewHighestBound($type->max_bound, $int->value);
foreach ($combination->ints as $k => $int) {
if ($type->contains($int->value)) {
unset($combination->ints[$k]);
} elseif ($type->max_bound === $int->value-1) {
$type->max_bound++;
unset($combination->ints[$k]);
} elseif ($type->min_bound === $int->value+1) {
$type->min_bound--;
unset($combination->ints[$k]);
}
}

$combination->value_types['int'] = $type;
} elseif (!isset($combination->value_types['int'])) {
$combination->value_types['int'] = $type;
} else {
} elseif (isset($combination->value_types['int'])) {
$old_type = $combination->value_types['int'];
if ($old_type instanceof TIntRange) {
$type->min_bound = TIntRange::getNewLowestBound($old_type->min_bound, $type->min_bound);
$type->max_bound = TIntRange::getNewHighestBound($old_type->max_bound, $type->max_bound);
} else {
$type = new TInt();
$combination->ints = null;
}
$combination->value_types['int'] = $type;
} else {
$combination->value_types['int'] = $type;
}
} else {
$combination->value_types['int'] = $type;
$combination->ints = null;
}

$combination->ints = null;
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/Psalm/Type/Reconciler.php
Expand Up @@ -549,6 +549,8 @@ public static function breakUpPathIntoParts(string $path): array
$escape_char = !$escape_char;
}

// TODO: Add support for disjoint int ranges in offset checks
/** @psalm-suppress PossiblyUndefinedIntArrayOffset */
$parts[$parts_offset] .= $char;
continue;
}
Expand Down

0 comments on commit 1e55256

Please sign in to comment.