Skip to content

Commit ecd6811

Browse files
phpstan-botclaude
authored andcommitted
De-duplicate ConditionalExpressionHolder creation for array dim fetch tracking
Extract createArrayDimFetchConditionalExpressionHolder() helper method and merge the separate array_search/array_find_key blocks into one, reducing three instances of the same ~12-line pattern to single-line calls. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 25d0b66 commit ecd6811

1 file changed

Lines changed: 57 additions & 87 deletions

File tree

src/Analyser/TypeSpecifier.php

Lines changed: 57 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -854,113 +854,62 @@ public function specifyTypesInCondition(
854854
$this->create($dimFetch, $arrayType->getIterableValueType(), TypeSpecifierContext::createTrue(), $scope),
855855
);
856856
} elseif ($expr->var instanceof Expr\Variable && is_string($expr->var->name)) {
857-
// The array might be empty here, so we cannot register
858-
// $arr[$key] unconditionally. Attach a conditional holder
859-
// that fires once the user narrows $key to non-null
860-
// (e.g. `if ($key !== null)`), giving the deep-write
861-
// path the same shape information `isset($arr[$key])`
862-
// would have provided.
863857
$keyType = $scope->getType($expr->expr);
864858
$nonNullKeyType = TypeCombinator::removeNull($keyType);
865-
if (!$nonNullKeyType instanceof NeverType && !$keyType->isNull()->yes()) {
866-
$dimFetch = new ArrayDimFetch($arrayArg, $expr->var);
867-
$dimFetchString = $this->exprPrinter->printExpr($dimFetch);
868-
$keyExprString = $this->exprPrinter->printExpr($expr->var);
869-
870-
$holder = new ConditionalExpressionHolder(
871-
[$keyExprString => ExpressionTypeHolder::createYes($expr->var, $nonNullKeyType)],
872-
ExpressionTypeHolder::createYes($dimFetch, $arrayType->getIterableValueType()),
873-
);
874-
875-
$specifiedTypes = $specifiedTypes->unionWith(
876-
(new SpecifiedTypes([], []))->setNewConditionalExpressionHolders([
877-
$dimFetchString => [$holder->getKey() => $holder],
878-
]),
879-
);
880-
}
881-
}
882-
}
883-
}
884-
885-
// infer $arr[$key] after $key = array_search($needle, $arr)
886-
if (
887-
$expr->expr instanceof FuncCall
888-
&& $expr->expr->name instanceof Name
889-
&& $expr->expr->name->toLowerString() === 'array_search'
890-
&& count($expr->expr->getArgs()) >= 2
891-
) {
892-
$arrayArg = $expr->expr->getArgs()[1]->value;
893-
$arrayType = $scope->getType($arrayArg);
894-
895-
if ($arrayType->isArray()->yes()) {
896-
if ($context->true()) {
897-
$dimFetch = new ArrayDimFetch($arrayArg, $expr->var);
898-
899-
$specifiedTypes = $specifiedTypes->unionWith(
900-
$this->create($dimFetch, $arrayType->getIterableValueType(), TypeSpecifierContext::createTrue(), $scope),
901-
);
902-
} elseif ($expr->var instanceof Expr\Variable && is_string($expr->var->name)) {
903-
$keyType = $scope->getType($expr->expr);
904-
$nonFalseKeyType = TypeCombinator::remove($keyType, new ConstantBooleanType(false));
905-
if (!$nonFalseKeyType instanceof NeverType && !$keyType->isFalse()->yes()) {
906-
$dimFetch = new ArrayDimFetch($arrayArg, $expr->var);
907-
$dimFetchString = $this->exprPrinter->printExpr($dimFetch);
908-
$keyExprString = $this->exprPrinter->printExpr($expr->var);
909-
910-
$holder = new ConditionalExpressionHolder(
911-
[$keyExprString => ExpressionTypeHolder::createYes($expr->var, $nonFalseKeyType)],
912-
ExpressionTypeHolder::createYes($dimFetch, $arrayType->getIterableValueType()),
913-
);
914-
859+
if (!$nonNullKeyType instanceof NeverType) {
915860
$specifiedTypes = $specifiedTypes->unionWith(
916-
(new SpecifiedTypes([], []))->setNewConditionalExpressionHolders([
917-
$dimFetchString => [$holder->getKey() => $holder],
918-
]),
861+
$this->createArrayDimFetchConditionalExpressionHolder($expr->var, $arrayArg, $arrayType, $nonNullKeyType),
919862
);
920863
}
921864
}
922865
}
923866
}
924867

925-
// infer $arr[$key] after $key = array_find_key($arr, $callback)
868+
// infer $arr[$key] after $key = array_search($needle, $arr) or $key = array_find_key($arr, $callback)
926869
if (
927870
$expr->expr instanceof FuncCall
928871
&& $expr->expr->name instanceof Name
929-
&& $expr->expr->name->toLowerString() === 'array_find_key'
930872
&& count($expr->expr->getArgs()) >= 2
931873
) {
932-
$arrayArg = $expr->expr->getArgs()[0]->value;
933-
$arrayType = $scope->getType($arrayArg);
934-
935-
if ($arrayType->isArray()->yes()) {
936-
if ($context->true()) {
937-
$specifiedTypes = $specifiedTypes->unionWith(
938-
$this->create($arrayArg, new NonEmptyArrayType(), TypeSpecifierContext::createTrue(), $scope),
939-
);
874+
$funcName = $expr->expr->name->toLowerString();
875+
$arrayArgIndex = null;
876+
$sentinelType = null;
877+
$narrowToNonEmpty = false;
878+
879+
if ($funcName === 'array_search') {
880+
$arrayArgIndex = 1;
881+
$sentinelType = new ConstantBooleanType(false);
882+
} elseif ($funcName === 'array_find_key') {
883+
$arrayArgIndex = 0;
884+
$sentinelType = new NullType();
885+
$narrowToNonEmpty = true;
886+
}
887+
888+
if ($arrayArgIndex !== null && $sentinelType !== null) {
889+
$arrayArg = $expr->expr->getArgs()[$arrayArgIndex]->value;
890+
$arrayType = $scope->getType($arrayArg);
940891

941-
$dimFetch = new ArrayDimFetch($arrayArg, $expr->var);
892+
if ($arrayType->isArray()->yes()) {
893+
if ($context->true()) {
894+
if ($narrowToNonEmpty) {
895+
$specifiedTypes = $specifiedTypes->unionWith(
896+
$this->create($arrayArg, new NonEmptyArrayType(), TypeSpecifierContext::createTrue(), $scope),
897+
);
898+
}
942899

943-
$specifiedTypes = $specifiedTypes->unionWith(
944-
$this->create($dimFetch, $arrayType->getIterableValueType(), TypeSpecifierContext::createTrue(), $scope),
945-
);
946-
} elseif ($expr->var instanceof Expr\Variable && is_string($expr->var->name)) {
947-
$keyType = $scope->getType($expr->expr);
948-
$nonNullKeyType = TypeCombinator::removeNull($keyType);
949-
if (!$nonNullKeyType instanceof NeverType && !$keyType->isNull()->yes()) {
950900
$dimFetch = new ArrayDimFetch($arrayArg, $expr->var);
951-
$dimFetchString = $this->exprPrinter->printExpr($dimFetch);
952-
$keyExprString = $this->exprPrinter->printExpr($expr->var);
953-
954-
$holder = new ConditionalExpressionHolder(
955-
[$keyExprString => ExpressionTypeHolder::createYes($expr->var, $nonNullKeyType)],
956-
ExpressionTypeHolder::createYes($dimFetch, $arrayType->getIterableValueType()),
957-
);
958901

959902
$specifiedTypes = $specifiedTypes->unionWith(
960-
(new SpecifiedTypes([], []))->setNewConditionalExpressionHolders([
961-
$dimFetchString => [$holder->getKey() => $holder],
962-
]),
903+
$this->create($dimFetch, $arrayType->getIterableValueType(), TypeSpecifierContext::createTrue(), $scope),
963904
);
905+
} elseif ($expr->var instanceof Expr\Variable && is_string($expr->var->name)) {
906+
$keyType = $scope->getType($expr->expr);
907+
$narrowedKeyType = TypeCombinator::remove($keyType, $sentinelType);
908+
if (!$narrowedKeyType instanceof NeverType) {
909+
$specifiedTypes = $specifiedTypes->unionWith(
910+
$this->createArrayDimFetchConditionalExpressionHolder($expr->var, $arrayArg, $arrayType, $narrowedKeyType),
911+
);
912+
}
964913
}
965914
}
966915
}
@@ -2466,6 +2415,27 @@ public function create(
24662415
return $types;
24672416
}
24682417

2418+
private function createArrayDimFetchConditionalExpressionHolder(
2419+
Expr\Variable $keyVar,
2420+
Expr $arrayArg,
2421+
Type $arrayType,
2422+
Type $narrowedKeyType,
2423+
): SpecifiedTypes
2424+
{
2425+
$dimFetch = new ArrayDimFetch($arrayArg, $keyVar);
2426+
$dimFetchString = $this->exprPrinter->printExpr($dimFetch);
2427+
$keyExprString = $this->exprPrinter->printExpr($keyVar);
2428+
2429+
$holder = new ConditionalExpressionHolder(
2430+
[$keyExprString => ExpressionTypeHolder::createYes($keyVar, $narrowedKeyType)],
2431+
ExpressionTypeHolder::createYes($dimFetch, $arrayType->getIterableValueType()),
2432+
);
2433+
2434+
return (new SpecifiedTypes([], []))->setNewConditionalExpressionHolders([
2435+
$dimFetchString => [$holder->getKey() => $holder],
2436+
]);
2437+
}
2438+
24692439
private function createForExpr(
24702440
Expr $expr,
24712441
Type $type,

0 commit comments

Comments
 (0)