Skip to content

Commit d81b07f

Browse files
phpstan-botclaude
andcommitted
Normalize the sealed unsealed sentinel value type in the constructor
Centralize protection of the sealed `[never, never]` unsealed sentinel: whenever a ConstantArrayType is sealed (its unsealed key is the explicit `never`), force the unsealed value slot back to the explicit-never sentinel. This prevents any caller from planting a projected/transformed type (e.g. an ErrorType produced by mapping the sentinel as if it were a real element) into the slot, where UnresolvableTypeHelper would later flag it as a bogus "contains unresolvable type" error. This supersedes the per-call-site guard in mapValueType, which is now reverted to its simpler form since recreate() routes every transform through the constructor normalization. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 6ee0856 commit d81b07f

1 file changed

Lines changed: 10 additions & 2 deletions

File tree

src/Type/Constant/ConstantArrayType.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,14 @@ public function __construct(
181181
if ($unsealed[0] instanceof StrictMixedType && !$unsealed[0] instanceof TemplateStrictMixedType) {
182182
$unsealed[0] = (new UnionType([new StringType(), new IntegerType()]))->toArrayKey();
183183
}
184+
if ($unsealed[0] instanceof NeverType && $unsealed[0]->isExplicit()) {
185+
// Sealed sentinel (`isUnsealed()->no()`): there are no extra
186+
// elements, so the value-type slot is not a real element type.
187+
// Force it back to the explicit-never sentinel so no caller can
188+
// plant a projected/transformed type (e.g. an ErrorType produced
189+
// by mapping the sentinel as if it were an element) into it.
190+
$unsealed[1] = new NeverType(true);
191+
}
184192
} elseif (BleedingEdgeToggle::isBleedingEdge()) {
185193
$never = new NeverType(true);
186194
$unsealed = [$never, $never];
@@ -3186,8 +3194,8 @@ public function mapValueType(callable $cb): Type
31863194
$newValueTypes[] = $cb($valueType);
31873195
}
31883196

3189-
$newUnsealed = $this->unsealed === null || $this->isUnsealed()->no()
3190-
? $this->unsealed
3197+
$newUnsealed = $this->unsealed === null
3198+
? null
31913199
: [$this->unsealed[0], $cb($this->unsealed[1])];
31923200

31933201
return $this->recreate(

0 commit comments

Comments
 (0)