Skip to content

Commit b78be62

Browse files
phpstan-botclaude
andcommitted
Refactor: extract computeNeedleNarrowingType to unify true/false context logic
Consolidates the needle narrowing logic into a single computeNeedleNarrowingType method that returns the narrowing type or null. This eliminates duplicated typeSpecifier->create + AlwaysRememberedExpr handling for both contexts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 264f473 commit b78be62

File tree

1 file changed

+33
-25
lines changed

1 file changed

+33
-25
lines changed

src/Type/Php/InArrayFunctionTypeSpecifyingExtension.php

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -116,42 +116,21 @@ public function specifyTypes(FunctionReflection $functionReflection, FuncCall $n
116116
}
117117

118118
$specifiedTypes = new SpecifiedTypes();
119-
if ($context->true()) {
119+
$narrowingValueType = $this->computeNeedleNarrowingType($context, $needleType, $arrayType, $arrayValueType);
120+
if ($narrowingValueType !== null) {
120121
$specifiedTypes = $this->typeSpecifier->create(
121122
$needleExpr,
122-
$arrayValueType,
123+
$narrowingValueType,
123124
$context,
124125
$scope,
125126
);
126127
if ($needleExpr instanceof AlwaysRememberedExpr) {
127128
$specifiedTypes = $specifiedTypes->unionWith($this->typeSpecifier->create(
128129
$needleExpr->getExpr(),
129-
$arrayValueType,
130-
$context,
131-
$scope,
132-
));
133-
}
134-
} elseif (
135-
$context->false()
136-
&& count($needleType->getFiniteTypes()) > 0
137-
&& $arrayType->isIterableAtLeastOnce()->yes()
138-
) {
139-
$narrowingValueType = $this->computeGuaranteedValueType($arrayType, $arrayValueType);
140-
if (count($narrowingValueType->getFiniteTypes()) > 0) {
141-
$specifiedTypes = $this->typeSpecifier->create(
142-
$needleExpr,
143130
$narrowingValueType,
144131
$context,
145132
$scope,
146-
);
147-
if ($needleExpr instanceof AlwaysRememberedExpr) {
148-
$specifiedTypes = $specifiedTypes->unionWith($this->typeSpecifier->create(
149-
$needleExpr->getExpr(),
150-
$narrowingValueType,
151-
$context,
152-
$scope,
153-
));
154-
}
133+
));
155134
}
156135
}
157136

@@ -188,6 +167,35 @@ public function specifyTypes(FunctionReflection $functionReflection, FuncCall $n
188167
return $specifiedTypes;
189168
}
190169

170+
/**
171+
* Computes the type to narrow the needle against, or null if no narrowing
172+
* should occur. In true context, returns the array value type directly.
173+
* In false context, returns only the values guaranteed to be in every
174+
* possible variant of the array.
175+
*/
176+
private function computeNeedleNarrowingType(TypeSpecifierContext $context, Type $needleType, Type $arrayType, Type $arrayValueType): ?Type
177+
{
178+
if ($context->true()) {
179+
return $arrayValueType;
180+
}
181+
182+
if (
183+
!$context->false()
184+
|| count($needleType->getFiniteTypes()) === 0
185+
|| !$arrayType->isIterableAtLeastOnce()->yes()
186+
) {
187+
return null;
188+
}
189+
190+
$guaranteedValueType = $this->computeGuaranteedValueType($arrayType, $arrayValueType);
191+
192+
if (count($guaranteedValueType->getFiniteTypes()) === 0) {
193+
return null;
194+
}
195+
196+
return $guaranteedValueType;
197+
}
198+
191199
/**
192200
* Computes the type of values guaranteed to be in every possible variant
193201
* of the array. For union types like array{A}|array{B}, we intersect the

0 commit comments

Comments
 (0)