Skip to content

Commit d3513ec

Browse files
phpstan-botclaude
andcommitted
Refactor: defer narrowing computation to avoid unnecessary work in !in_array
Move the guaranteed value type computation inside the guard condition that checks for finite types and non-empty array, so it's only performed when actually needed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d3148e6 commit d3513ec

File tree

1 file changed

+35
-25
lines changed

1 file changed

+35
-25
lines changed

src/Type/Php/InArrayFunctionTypeSpecifyingExtension.php

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,26 @@ public function specifyTypes(FunctionReflection $functionReflection, FuncCall $n
115115
}
116116

117117
$specifiedTypes = new SpecifiedTypes();
118-
$narrowingValueType = $arrayValueType;
119-
if ($context->false()) {
118+
if ($context->true()) {
119+
$specifiedTypes = $this->typeSpecifier->create(
120+
$needleExpr,
121+
$arrayValueType,
122+
$context,
123+
$scope,
124+
);
125+
if ($needleExpr instanceof AlwaysRememberedExpr) {
126+
$specifiedTypes = $specifiedTypes->unionWith($this->typeSpecifier->create(
127+
$needleExpr->getExpr(),
128+
$arrayValueType,
129+
$context,
130+
$scope,
131+
));
132+
}
133+
} elseif (
134+
$context->false()
135+
&& count($needleType->getFiniteTypes()) > 0
136+
&& $arrayType->isIterableAtLeastOnce()->yes()
137+
) {
120138
// In false context (!in_array), we can only remove values guaranteed
121139
// to be in every possible array variant. For union types like
122140
// array{A}|array{B}, getIterableValueType() returns A|B but neither
@@ -145,32 +163,24 @@ public function specifyTypes(FunctionReflection $functionReflection, FuncCall $n
145163
$innerValueTypes[] = $innerType->getIterableValueType();
146164
}
147165
}
148-
if (count($innerValueTypes) > 0) {
149-
$narrowingValueType = TypeCombinator::intersect(...$innerValueTypes);
150-
}
151-
}
152-
if (
153-
$context->true()
154-
|| (
155-
$context->false()
156-
&& count($narrowingValueType->getFiniteTypes()) > 0
157-
&& count($needleType->getFiniteTypes()) > 0
158-
&& $arrayType->isIterableAtLeastOnce()->yes()
159-
)
160-
) {
161-
$specifiedTypes = $this->typeSpecifier->create(
162-
$needleExpr,
163-
$narrowingValueType,
164-
$context,
165-
$scope,
166-
);
167-
if ($needleExpr instanceof AlwaysRememberedExpr) {
168-
$specifiedTypes = $specifiedTypes->unionWith($this->typeSpecifier->create(
169-
$needleExpr->getExpr(),
166+
$narrowingValueType = count($innerValueTypes) > 0
167+
? TypeCombinator::intersect(...$innerValueTypes)
168+
: $arrayValueType;
169+
if (count($narrowingValueType->getFiniteTypes()) > 0) {
170+
$specifiedTypes = $this->typeSpecifier->create(
171+
$needleExpr,
170172
$narrowingValueType,
171173
$context,
172174
$scope,
173-
));
175+
);
176+
if ($needleExpr instanceof AlwaysRememberedExpr) {
177+
$specifiedTypes = $specifiedTypes->unionWith($this->typeSpecifier->create(
178+
$needleExpr->getExpr(),
179+
$narrowingValueType,
180+
$context,
181+
$scope,
182+
));
183+
}
174184
}
175185
}
176186

0 commit comments

Comments
 (0)