Skip to content

Commit 289fed3

Browse files
phpstan-botstaabmclaude
committed
Address review comments
- Remove unnecessary toArrayKey() calls in resolveFiniteScalarKeyTypes() - Use ConstantArrayTypeBuilder instead of recursion in setOffsetValueType() - Remove CLAUDE.md changes - Add test case for int<0, 5>|int<10, 15> union of integer ranges Co-authored-by: Markus Staab <staabm@users.noreply.github.com> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 8bc6aab commit 289fed3

File tree

3 files changed

+16
-10
lines changed

3 files changed

+16
-10
lines changed

CLAUDE.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -412,10 +412,6 @@ When adding or editing PHPDoc comments in this codebase, follow these guidelines
412412
- `symfony/console` - CLI interface
413413
- `hoa/compiler` - Used for regex type parsing
414414

415-
### ConstantArrayType::setOffsetValueType union expansion for finite offsets
416-
417-
When `ConstantArrayType::setOffsetValueType()` receives a union of constant string keys (e.g., `'a'|'b'`) or a finite `IntegerRangeType` (e.g., `int<1,5>`) and at least one key is new (not already in the array), it creates a union of constant arrays — one for each possible key — instead of degrading to a general `ArrayType`. This is controlled by `CHUNK_FINITE_TYPES_LIMIT` (5) to avoid combinatorial explosion in loops. Integer constant unions (e.g., `0|1` from loop fixpoint analysis) are excluded to prevent regression in loop analysis; only string constant unions and `IntegerRangeType` expansions are supported. The `resolveFiniteScalarKeyTypes()` helper method resolves the offset type to individual constant keys.
418-
419415
### Ternary expression type narrowing in TypeSpecifier
420416

421417
`TypeSpecifier::specifyTypesInCondition()` handles ternary expressions (`$cond ? $a : $b`) for type narrowing. In a truthy context (e.g., inside `assert()`), the ternary is semantically equivalent to `($cond && $a) || (!$cond && $b)` — meaning if the condition is true, the "if" branch must be truthy, and if false, the "else" branch must be truthy. The fix converts ternary expressions to this `BooleanOr(BooleanAnd(...), BooleanAnd(...))` form so the existing OR/AND narrowing logic handles both branches correctly. This enables `assert($cond ? $x instanceof A : $x instanceof B)` to narrow `$x` to `A|B`. The `AssertFunctionTypeSpecifyingExtension` calls `specifyTypesInCondition` with `TypeSpecifierContext::createTruthy()` context for the assert argument.

src/Type/Constant/ConstantArrayType.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,9 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $uni
722722
if ($hasNewKey) {
723723
$arrayTypes = [];
724724
foreach ($scalarKeyTypes as $scalarKeyType) {
725-
$arrayTypes[] = $this->setOffsetValueType($scalarKeyType, $valueType, $unionValues);
725+
$builder = ConstantArrayTypeBuilder::createFromConstantArray($this);
726+
$builder->setOffsetValueType($scalarKeyType, $valueType);
727+
$arrayTypes[] = $builder->getArray();
726728
}
727729

728730
return TypeCombinator::union(...$arrayTypes);
@@ -756,8 +758,7 @@ private function resolveFiniteScalarKeyTypes(Type $offsetType): ?array
756758
if (count($constantStrings) >= 2 && count($constantStrings) <= self::CHUNK_FINITE_TYPES_LIMIT) {
757759
$result = [];
758760
foreach ($constantStrings as $constantString) {
759-
$arrayKeyType = $constantString->toArrayKey();
760-
$scalarValues = $arrayKeyType->getConstantScalarValues();
761+
$scalarValues = $constantString->getConstantScalarValues();
761762
if (count($scalarValues) !== 1) {
762763
return null;
763764
}
@@ -793,11 +794,10 @@ private function resolveFiniteScalarKeyTypes(Type $offsetType): ?array
793794

794795
$result = [];
795796
foreach ($finiteScalarTypes as $scalarType) {
796-
$arrayKeyType = $scalarType->toArrayKey();
797-
if (!$arrayKeyType instanceof ConstantIntegerType) {
797+
if (!$scalarType instanceof ConstantIntegerType) {
798798
return null;
799799
}
800-
$result[] = $arrayKeyType;
800+
$result[] = $scalarType;
801801
}
802802
return $result;
803803
}

tests/PHPStan/Analyser/nsrt/set-constant-union-offset-on-constant-array.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,16 @@ public function doInfiniteRange(array $a, $intRange): void
3737
assertType('non-empty-array<\'foo\'|int<0, max>, int>', $a);
3838
}
3939

40+
/**
41+
* @param array{foo: int} $a
42+
* @param int<0, 5>|int<10, 15> $intRange
43+
*/
44+
public function doUnionOfRanges(array $a, $intRange): void
45+
{
46+
$a[$intRange] = 256;
47+
assertType('non-empty-array<\'foo\'|int<0, 5>|int<10, 15>, int>', $a);
48+
}
49+
4050
/**
4151
* @param array{0: 'a', 1: 'b'} $a
4252
* @param int<0,1> $intRange

0 commit comments

Comments
 (0)