Skip to content

Commit 987fb32

Browse files
committed
Refactor Identical handling in TypeSpecifier
1 parent 8329c64 commit 987fb32

File tree

1 file changed

+185
-178
lines changed

1 file changed

+185
-178
lines changed

src/Analyser/TypeSpecifier.php

Lines changed: 185 additions & 178 deletions
Original file line numberDiff line numberDiff line change
@@ -181,184 +181,7 @@ public function specifyTypesInCondition(
181181
return $this->create($exprNode, new ObjectWithoutClassType(), $context, false, $scope, $rootExpr);
182182
}
183183
} elseif ($expr instanceof Node\Expr\BinaryOp\Identical) {
184-
$leftExpr = $expr->left;
185-
$rightExpr = $expr->right;
186-
if ($rightExpr instanceof FuncCall && !$leftExpr instanceof FuncCall) {
187-
[$leftExpr, $rightExpr] = [$rightExpr, $leftExpr];
188-
}
189-
$unwrappedLeftExpr = $leftExpr;
190-
if ($leftExpr instanceof AlwaysRememberedExpr) {
191-
$unwrappedLeftExpr = $leftExpr->getExpr();
192-
}
193-
$rightType = $scope->getType($rightExpr);
194-
if (
195-
$context->true()
196-
&& $unwrappedLeftExpr instanceof FuncCall
197-
&& $unwrappedLeftExpr->name instanceof Name
198-
&& strtolower($unwrappedLeftExpr->name->toString()) === 'get_class'
199-
&& isset($unwrappedLeftExpr->getArgs()[0])
200-
) {
201-
if ($rightType->getClassStringObjectType()->isObject()->yes()) {
202-
return $this->create(
203-
$unwrappedLeftExpr->getArgs()[0]->value,
204-
$rightType->getClassStringObjectType(),
205-
$context,
206-
false,
207-
$scope,
208-
$rootExpr,
209-
)->unionWith($this->create($leftExpr, $rightType, $context, false, $scope, $rootExpr));
210-
}
211-
}
212-
213-
if (count($rightType->getConstantStrings()) > 0) {
214-
$types = null;
215-
foreach ($rightType->getConstantStrings() as $constantString) {
216-
$specifiedType = $this->specifyTypesForConstantStringBinaryExpression($unwrappedLeftExpr, $constantString, $context, $scope, $rootExpr);
217-
if ($specifiedType === null) {
218-
continue;
219-
}
220-
if ($types === null) {
221-
$types = $specifiedType;
222-
continue;
223-
}
224-
225-
$types = $types->intersectWith($specifiedType);
226-
}
227-
228-
if ($types !== null) {
229-
if ($leftExpr !== $unwrappedLeftExpr) {
230-
$types = $types->unionWith($this->create($leftExpr, $rightType, $context, false, $scope, $rootExpr));
231-
}
232-
return $types;
233-
}
234-
}
235-
236-
$expressions = $this->findTypeExpressionsFromBinaryOperation($scope, $expr);
237-
if ($expressions !== null) {
238-
$exprNode = $expressions[0];
239-
$constantType = $expressions[1];
240-
241-
$specifiedType = $this->specifyTypesForConstantBinaryExpression($exprNode, $constantType, $context, $scope, $rootExpr);
242-
if ($specifiedType !== null) {
243-
return $specifiedType;
244-
}
245-
}
246-
247-
if ($rightExpr instanceof AlwaysRememberedExpr) {
248-
$rightExpr = $rightExpr->getExpr();
249-
}
250-
251-
if ($leftExpr instanceof AlwaysRememberedExpr) {
252-
$leftExpr = $leftExpr->getExpr();
253-
}
254-
255-
if (
256-
$context->true() &&
257-
$leftExpr instanceof ClassConstFetch &&
258-
$leftExpr->class instanceof Expr &&
259-
$leftExpr->name instanceof Node\Identifier &&
260-
$rightExpr instanceof ClassConstFetch &&
261-
$rightType instanceof ConstantStringType &&
262-
strtolower($leftExpr->name->toString()) === 'class'
263-
) {
264-
return $this->specifyTypesInCondition(
265-
$scope,
266-
new Instanceof_(
267-
$leftExpr->class,
268-
new Name($rightType->getValue()),
269-
),
270-
$context,
271-
$rootExpr,
272-
)->unionWith($this->create($expr->left, $rightType, $context, false, $scope, $rootExpr));
273-
}
274-
275-
$leftType = $scope->getType($leftExpr);
276-
if (
277-
$context->true() &&
278-
$rightExpr instanceof ClassConstFetch &&
279-
$rightExpr->class instanceof Expr &&
280-
$rightExpr->name instanceof Node\Identifier &&
281-
$leftExpr instanceof ClassConstFetch &&
282-
$leftType instanceof ConstantStringType &&
283-
strtolower($rightExpr->name->toString()) === 'class'
284-
) {
285-
return $this->specifyTypesInCondition(
286-
$scope,
287-
new Instanceof_(
288-
$rightExpr->class,
289-
new Name($leftType->getValue()),
290-
),
291-
$context,
292-
$rootExpr,
293-
)->unionWith($this->create($expr->right, $leftType, $context, false, $scope, $rootExpr));
294-
}
295-
296-
if ($context->false()) {
297-
$identicalType = $scope->getType($expr);
298-
if ($identicalType instanceof ConstantBooleanType) {
299-
$never = new NeverType();
300-
$contextForTypes = $identicalType->getValue() ? $context->negate() : $context;
301-
$leftTypes = $this->create($expr->left, $never, $contextForTypes, false, $scope, $rootExpr);
302-
$rightTypes = $this->create($expr->right, $never, $contextForTypes, false, $scope, $rootExpr);
303-
return $leftTypes->unionWith($rightTypes);
304-
}
305-
}
306-
307-
$types = null;
308-
$exprLeftType = $scope->getType($expr->left);
309-
$exprRightType = $scope->getType($expr->right);
310-
if (
311-
count($exprLeftType->getFiniteTypes()) === 1
312-
|| ($exprLeftType->isConstantValue()->yes() && !$exprRightType->equals($exprLeftType) && $exprRightType->isSuperTypeOf($exprLeftType)->yes())
313-
) {
314-
$types = $this->create(
315-
$expr->right,
316-
$exprLeftType,
317-
$context,
318-
false,
319-
$scope,
320-
$rootExpr,
321-
);
322-
}
323-
if (
324-
count($exprRightType->getFiniteTypes()) === 1
325-
|| ($exprRightType->isConstantValue()->yes() && !$exprLeftType->equals($exprRightType) && $exprLeftType->isSuperTypeOf($exprRightType)->yes())
326-
) {
327-
$leftType = $this->create(
328-
$expr->left,
329-
$exprRightType,
330-
$context,
331-
false,
332-
$scope,
333-
$rootExpr,
334-
);
335-
if ($types !== null) {
336-
$types = $types->unionWith($leftType);
337-
} else {
338-
$types = $leftType;
339-
}
340-
}
341-
342-
if ($types !== null) {
343-
return $types;
344-
}
345-
346-
$leftExprString = $this->exprPrinter->printExpr($expr->left);
347-
$rightExprString = $this->exprPrinter->printExpr($expr->right);
348-
if ($leftExprString === $rightExprString) {
349-
if (!$expr->left instanceof Expr\Variable || !$expr->right instanceof Expr\Variable) {
350-
return new SpecifiedTypes([], [], false, [], $rootExpr);
351-
}
352-
}
353-
354-
if ($context->true()) {
355-
$leftTypes = $this->create($expr->left, $exprRightType, $context, false, $scope, $rootExpr);
356-
$rightTypes = $this->create($expr->right, $exprLeftType, $context, false, $scope, $rootExpr);
357-
return $leftTypes->unionWith($rightTypes);
358-
} elseif ($context->false()) {
359-
return $this->create($expr->left, $exprLeftType, $context, false, $scope, $rootExpr)->normalize($scope)
360-
->intersectWith($this->create($expr->right, $exprRightType, $context, false, $scope, $rootExpr)->normalize($scope));
361-
}
184+
return $this->resolveIdentical($expr, $scope, $context, $rootExpr);
362185

363186
} elseif ($expr instanceof Node\Expr\BinaryOp\NotIdentical) {
364187
return $this->specifyTypesInCondition(
@@ -1811,4 +1634,188 @@ private function getTypeSpecifyingExtensionsForType(array $extensions, string $c
18111634
return array_merge(...$extensionsForClass);
18121635
}
18131636

1637+
public function resolveIdentical(Expr\BinaryOp\Identical $expr, Scope $scope, TypeSpecifierContext $context, Expr $rootExpr): SpecifiedTypes
1638+
{
1639+
$leftExpr = $expr->left;
1640+
$rightExpr = $expr->right;
1641+
if ($rightExpr instanceof FuncCall && !$leftExpr instanceof FuncCall) {
1642+
[$leftExpr, $rightExpr] = [$rightExpr, $leftExpr];
1643+
}
1644+
$unwrappedLeftExpr = $leftExpr;
1645+
if ($leftExpr instanceof AlwaysRememberedExpr) {
1646+
$unwrappedLeftExpr = $leftExpr->getExpr();
1647+
}
1648+
$rightType = $scope->getType($rightExpr);
1649+
if (
1650+
$context->true()
1651+
&& $unwrappedLeftExpr instanceof FuncCall
1652+
&& $unwrappedLeftExpr->name instanceof Name
1653+
&& strtolower($unwrappedLeftExpr->name->toString()) === 'get_class'
1654+
&& isset($unwrappedLeftExpr->getArgs()[0])
1655+
) {
1656+
if ($rightType->getClassStringObjectType()->isObject()->yes()) {
1657+
return $this->create(
1658+
$unwrappedLeftExpr->getArgs()[0]->value,
1659+
$rightType->getClassStringObjectType(),
1660+
$context,
1661+
false,
1662+
$scope,
1663+
$rootExpr,
1664+
)->unionWith($this->create($leftExpr, $rightType, $context, false, $scope, $rootExpr));
1665+
}
1666+
}
1667+
1668+
if (count($rightType->getConstantStrings()) > 0) {
1669+
$types = null;
1670+
foreach ($rightType->getConstantStrings() as $constantString) {
1671+
$specifiedType = $this->specifyTypesForConstantStringBinaryExpression($unwrappedLeftExpr, $constantString, $context, $scope, $rootExpr);
1672+
if ($specifiedType === null) {
1673+
continue;
1674+
}
1675+
if ($types === null) {
1676+
$types = $specifiedType;
1677+
continue;
1678+
}
1679+
1680+
$types = $types->intersectWith($specifiedType);
1681+
}
1682+
1683+
if ($types !== null) {
1684+
if ($leftExpr !== $unwrappedLeftExpr) {
1685+
$types = $types->unionWith($this->create($leftExpr, $rightType, $context, false, $scope, $rootExpr));
1686+
}
1687+
return $types;
1688+
}
1689+
}
1690+
1691+
$expressions = $this->findTypeExpressionsFromBinaryOperation($scope, $expr);
1692+
if ($expressions !== null) {
1693+
$exprNode = $expressions[0];
1694+
$constantType = $expressions[1];
1695+
1696+
$specifiedType = $this->specifyTypesForConstantBinaryExpression($exprNode, $constantType, $context, $scope, $rootExpr);
1697+
if ($specifiedType !== null) {
1698+
return $specifiedType;
1699+
}
1700+
}
1701+
1702+
if ($rightExpr instanceof AlwaysRememberedExpr) {
1703+
$rightExpr = $rightExpr->getExpr();
1704+
}
1705+
1706+
if ($leftExpr instanceof AlwaysRememberedExpr) {
1707+
$leftExpr = $leftExpr->getExpr();
1708+
}
1709+
1710+
if (
1711+
$context->true() &&
1712+
$leftExpr instanceof ClassConstFetch &&
1713+
$leftExpr->class instanceof Expr &&
1714+
$leftExpr->name instanceof Node\Identifier &&
1715+
$rightExpr instanceof ClassConstFetch &&
1716+
$rightType instanceof ConstantStringType &&
1717+
strtolower($leftExpr->name->toString()) === 'class'
1718+
) {
1719+
return $this->specifyTypesInCondition(
1720+
$scope,
1721+
new Instanceof_(
1722+
$leftExpr->class,
1723+
new Name($rightType->getValue()),
1724+
),
1725+
$context,
1726+
$rootExpr,
1727+
)->unionWith($this->create($expr->left, $rightType, $context, false, $scope, $rootExpr));
1728+
}
1729+
1730+
$leftType = $scope->getType($leftExpr);
1731+
if (
1732+
$context->true() &&
1733+
$rightExpr instanceof ClassConstFetch &&
1734+
$rightExpr->class instanceof Expr &&
1735+
$rightExpr->name instanceof Node\Identifier &&
1736+
$leftExpr instanceof ClassConstFetch &&
1737+
$leftType instanceof ConstantStringType &&
1738+
strtolower($rightExpr->name->toString()) === 'class'
1739+
) {
1740+
return $this->specifyTypesInCondition(
1741+
$scope,
1742+
new Instanceof_(
1743+
$rightExpr->class,
1744+
new Name($leftType->getValue()),
1745+
),
1746+
$context,
1747+
$rootExpr,
1748+
)->unionWith($this->create($expr->right, $leftType, $context, false, $scope, $rootExpr));
1749+
}
1750+
1751+
if ($context->false()) {
1752+
$identicalType = $scope->getType($expr);
1753+
if ($identicalType instanceof ConstantBooleanType) {
1754+
$never = new NeverType();
1755+
$contextForTypes = $identicalType->getValue() ? $context->negate() : $context;
1756+
$leftTypes = $this->create($expr->left, $never, $contextForTypes, false, $scope, $rootExpr);
1757+
$rightTypes = $this->create($expr->right, $never, $contextForTypes, false, $scope, $rootExpr);
1758+
return $leftTypes->unionWith($rightTypes);
1759+
}
1760+
}
1761+
1762+
$types = null;
1763+
$exprLeftType = $scope->getType($expr->left);
1764+
$exprRightType = $scope->getType($expr->right);
1765+
if (
1766+
count($exprLeftType->getFiniteTypes()) === 1
1767+
|| ($exprLeftType->isConstantValue()->yes() && !$exprRightType->equals($exprLeftType) && $exprRightType->isSuperTypeOf($exprLeftType)->yes())
1768+
) {
1769+
$types = $this->create(
1770+
$expr->right,
1771+
$exprLeftType,
1772+
$context,
1773+
false,
1774+
$scope,
1775+
$rootExpr,
1776+
);
1777+
}
1778+
if (
1779+
count($exprRightType->getFiniteTypes()) === 1
1780+
|| ($exprRightType->isConstantValue()->yes() && !$exprLeftType->equals($exprRightType) && $exprLeftType->isSuperTypeOf($exprRightType)->yes())
1781+
) {
1782+
$leftType = $this->create(
1783+
$expr->left,
1784+
$exprRightType,
1785+
$context,
1786+
false,
1787+
$scope,
1788+
$rootExpr,
1789+
);
1790+
if ($types !== null) {
1791+
$types = $types->unionWith($leftType);
1792+
} else {
1793+
$types = $leftType;
1794+
}
1795+
}
1796+
1797+
if ($types !== null) {
1798+
return $types;
1799+
}
1800+
1801+
$leftExprString = $this->exprPrinter->printExpr($expr->left);
1802+
$rightExprString = $this->exprPrinter->printExpr($expr->right);
1803+
if ($leftExprString === $rightExprString) {
1804+
if (!$expr->left instanceof Expr\Variable || !$expr->right instanceof Expr\Variable) {
1805+
return new SpecifiedTypes([], [], false, [], $rootExpr);
1806+
}
1807+
}
1808+
1809+
if ($context->true()) {
1810+
$leftTypes = $this->create($expr->left, $exprRightType, $context, false, $scope, $rootExpr);
1811+
$rightTypes = $this->create($expr->right, $exprLeftType, $context, false, $scope, $rootExpr);
1812+
return $leftTypes->unionWith($rightTypes);
1813+
} elseif ($context->false()) {
1814+
return $this->create($expr->left, $exprLeftType, $context, false, $scope, $rootExpr)->normalize($scope)
1815+
->intersectWith($this->create($expr->right, $exprRightType, $context, false, $scope, $rootExpr)->normalize($scope));
1816+
}
1817+
1818+
return new SpecifiedTypes([], [], false, [], $rootExpr);
1819+
}
1820+
18141821
}

0 commit comments

Comments
 (0)