Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 47 additions & 17 deletions src/Analyser/MutatingScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use PhpParser\Node\ComplexType;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\Cast\Unset_;
use PhpParser\Node\Expr\ConstFetch;
Expand Down Expand Up @@ -797,6 +798,21 @@ public function getAnonymousFunctionReturnType(): ?Type
/** @api */
public function getType(Expr $node): Type
{
if ($node instanceof Node\Scalar\Int_) {
return $this->initializerExprTypeResolver->getType($node, InitializerExprContext::fromScope($this));
} elseif ($node instanceof String_) {
return $this->initializerExprTypeResolver->getType($node, InitializerExprContext::fromScope($this));
} elseif ($node instanceof Node\Scalar\Float_) {
return $this->initializerExprTypeResolver->getType($node, InitializerExprContext::fromScope($this));
} elseif ($node instanceof Expr\UnaryMinus && $node->expr instanceof Node\Scalar) {
return $this->initializerExprTypeResolver->getType($node, InitializerExprContext::fromScope($this));
} elseif ($node instanceof ConstFetch) {
$loweredConstName = strtolower($node->name->toString());
if (in_array($loweredConstName, ['true', 'false', 'null'], true)) {
return $this->initializerExprTypeResolver->getType($node, InitializerExprContext::fromScope($this));
}
}

if ($node instanceof GetIterableKeyTypeExpr) {
return $this->getIterableKeyType($this->getType($node->getExpr()));
}
Expand Down Expand Up @@ -1295,11 +1311,7 @@ private function resolveType(string $exprString, Expr $node): Type
});
}

if ($node instanceof Node\Scalar\Int_) {
return $this->initializerExprTypeResolver->getType($node, InitializerExprContext::fromScope($this));
} elseif ($node instanceof String_) {
return $this->initializerExprTypeResolver->getType($node, InitializerExprContext::fromScope($this));
} elseif ($node instanceof Node\Scalar\InterpolatedString) {
if ($node instanceof Node\Scalar\InterpolatedString) {
$resultType = null;
foreach ($node->parts as $part) {
if ($part instanceof InterpolatedStringPart) {
Expand All @@ -1316,8 +1328,6 @@ private function resolveType(string $exprString, Expr $node): Type
}

return $resultType ?? new ConstantStringType('');
} elseif ($node instanceof Node\Scalar\Float_) {
return $this->initializerExprTypeResolver->getType($node, InitializerExprContext::fromScope($this));
} elseif ($node instanceof Expr\CallLike && $node->isFirstClassCallable()) {
if ($node instanceof FuncCall && $node->name instanceof Expr) {
$callableType = $this->getType($node->name);
Expand Down Expand Up @@ -2039,16 +2049,6 @@ static function (Node $node, Scope $scope) use ($arrowScope, &$arrowFunctionImpu
}

if ($node instanceof ConstFetch) {
$constName = (string) $node->name;
$loweredConstName = strtolower($constName);
if ($loweredConstName === 'true') {
return new ConstantBooleanType(true);
} elseif ($loweredConstName === 'false') {
return new ConstantBooleanType(false);
} elseif ($loweredConstName === 'null') {
return new NullType();
}

$namespacedName = null;
if (!$node->name->isFullyQualified() && $this->getNamespace() !== null) {
$namespacedName = new FullyQualified([$this->getNamespace(), $node->name->toString()]);
Expand Down Expand Up @@ -4358,6 +4358,7 @@ public function specifyExpressionType(Expr $expr, Type $type, Type $nativeType,
$this->parentScope,
$this->nativeTypesPromoted,
);
$scope->resolvedTypes = $this->preserveResolvedTypes([$exprString => $expr]);

if ($expr instanceof AlwaysRememberedExpr) {
return $scope->specifyExpressionType($expr->expr, $type, $nativeType, $certainty);
Expand Down Expand Up @@ -4406,6 +4407,35 @@ public function assignInitializedProperty(Type $fetchedOnType, string $propertyN
return $this->assignExpression(new PropertyInitializationExpr($propertyName), new MixedType(), new MixedType());
}

/**
* @param array<string, Expr> $changedExpressions
*
* @return array<string, ExpressionTypeHolder>
*/
private function preserveResolvedTypes(array $changedExpressions): array
{
$preservedTypes = $this->resolvedTypes;
foreach($preservedTypes as $exprString => $exprTypeHolder) {
$exprExpr = $exprTypeHolder->getExpr();

foreach ($changedExpressions as $exprStringToInvalidate => $expressionToInvalidate) {
while ($expressionToInvalidate instanceof Expr\ArrayDimFetch) {
$expressionToInvalidate = $expressionToInvalidate->var;
$exprStringToInvalidate = $this->getNodeKey($expressionToInvalidate);
}

if (!$this->shouldInvalidateExpression($exprStringToInvalidate, $expressionToInvalidate, $exprExpr)) {
continue;
}

unset($preservedTypes[$exprString]);
continue 2;
}
}

return $preservedTypes;
}

public function invalidateExpression(Expr $expressionToInvalidate, bool $requireMoreCharacters = false): self
{
$expressionTypes = $this->expressionTypes;
Expand Down
Loading