Skip to content

Commit

Permalink
move certainty into IssetExpr
Browse files Browse the repository at this point in the history
  • Loading branch information
staabm committed Oct 31, 2023
1 parent 4c4caab commit 2f75d65
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 34 deletions.
59 changes: 34 additions & 25 deletions src/Analyser/MutatingScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -3517,7 +3517,7 @@ public function specifyExpressionType(Expr $expr, Type $type, Type $nativeType,
);
}

public function assignExpression(Expr $expr, Type $type, ?Type $nativeType = null): self
public function assignExpression(Expr $expr, Type $type, ?Type $nativeType = null, ?TrinaryLogic $certainty = null): self
{
if ($nativeType === null) {
$nativeType = new MixedType();
Expand All @@ -3532,7 +3532,7 @@ public function assignExpression(Expr $expr, Type $type, ?Type $nativeType = nul
$scope = $this->invalidateExpression($expr);
}

return $scope->specifyExpressionType($expr, $type, $nativeType);
return $scope->specifyExpressionType($expr, $type, $nativeType, $certainty);
}

public function assignInitializedProperty(Type $fetchedOnType, string $propertyName): self
Expand Down Expand Up @@ -3712,7 +3712,7 @@ private function invalidateMethodsOnExpression(Expr $expressionToInvalidate): se
);
}

private function addTypeToExpression(Expr $expr, Type $type): self
private function addTypeToExpression(Expr $expr, Type $type, ?TrinaryLogic $certainty = null): self
{
$originalExprType = $this->getType($expr);
$nativeType = $this->getNativeType($expr);
Expand All @@ -3726,10 +3726,11 @@ private function addTypeToExpression(Expr $expr, Type $type): self
$expr,
TypeCombinator::intersect($type, $originalExprType),
TypeCombinator::intersect($type, $nativeType),
$certainty,
);
}

public function removeTypeFromExpression(Expr $expr, Type $typeToRemove): self
public function removeTypeFromExpression(Expr $expr, Type $typeToRemove, ?TrinaryLogic $certainty = null): self
{
$exprType = $this->getType($expr);
if (
Expand All @@ -3742,6 +3743,7 @@ public function removeTypeFromExpression(Expr $expr, Type $typeToRemove): self
$expr,
TypeCombinator::remove($exprType, $typeToRemove),
TypeCombinator::remove($this->getNativeType($expr), $typeToRemove),
$certainty,
);
}

Expand Down Expand Up @@ -3822,43 +3824,50 @@ public function filterBySpecifiedTypes(SpecifiedTypes $specifiedTypes): self
foreach ($typeSpecifications as $typeSpecification) {
$expr = $typeSpecification['expr'];
$type = $typeSpecification['type'];
$certainty = TrinaryLogic::createYes();

if ($expr instanceof IssetExpr) {
$unsetExpr = $expr->getExpr();
$scope = $scope->unsetExpression($unsetExpr);

if ($unsetExpr instanceof Variable) {
$exprString = $this->getNodeKey($unsetExpr);
$issetExpr = $expr;
$expr = $issetExpr->getExpr();
$certainty = $issetExpr->getCertainty();

unset($scope->expressionTypes[$exprString]);
unset($scope->nativeExpressionTypes[$exprString]);
}
if ($certainty->no()) {
$scope = $scope->unsetExpression($expr);

if ($unsetExpr instanceof Expr\ArrayDimFetch) {
$type = $scope->getType($unsetExpr->var);
$arraySize = $type->getArraySize();

if (
$arraySize instanceof ConstantIntegerType
&& $arraySize->getValue() === 0
) {
$exprString = $this->getNodeKey($unsetExpr->var);
if ($expr instanceof Variable) {
$exprString = $this->getNodeKey($expr);

unset($scope->expressionTypes[$exprString]);
unset($scope->nativeExpressionTypes[$exprString]);
}

if ($expr instanceof Expr\ArrayDimFetch) {
$type = $scope->getType($expr->var);
$arraySize = $type->getArraySize();

if (
$arraySize instanceof ConstantIntegerType
&& $arraySize->getValue() === 0
) {
$exprString = $this->getNodeKey($expr->var);

unset($scope->expressionTypes[$exprString]);
unset($scope->nativeExpressionTypes[$exprString]);
}
}

continue;
}
continue;
}

if ($typeSpecification['sure']) {
if ($specifiedTypes->shouldOverwrite()) {
$scope = $scope->assignExpression($expr, $type, $type);
$scope = $scope->assignExpression($expr, $type, $type, $certainty);
} else {
$scope = $scope->addTypeToExpression($expr, $type);
$scope = $scope->addTypeToExpression($expr, $type, $certainty);
}
} else {
$scope = $scope->removeTypeFromExpression($expr, $type);
$scope = $scope->removeTypeFromExpression($expr, $type, $certainty);
}
$specifiedExpressions[$this->getNodeKey($expr)] = ExpressionTypeHolder::createYes($expr, $scope->getType($expr));
}
Expand Down
10 changes: 5 additions & 5 deletions src/Analyser/TypeSpecifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -688,9 +688,9 @@ public function specifyTypesInCondition(
if ($var instanceof Expr\Variable) {
$type = $scope->getType($var);

if ($isset === true && !TypeCombinator::containsNull($type)) {
if ($isset !== false && !$type instanceof MixedType && !TypeCombinator::containsNull($type)) {
$specifiedTypes = $specifiedTypes->unionWith($this->create(
new IssetExpr($var),
new IssetExpr($var, TrinaryLogic::createNo()),
new NullType(),
$context,
false,
Expand All @@ -713,7 +713,7 @@ public function specifyTypesInCondition(
}

$specifiedTypes = $specifiedTypes->unionWith($this->create(
$var,
new IssetExpr($var, $scope->hasExpressionType($var)),
new NullType(),
$context->negate(),
true,
Expand Down Expand Up @@ -757,7 +757,7 @@ public function specifyTypesInCondition(
$offsetType = $type->getOffsetValueType($dimType);
if ($hasOffsetType->yes() && !TypeCombinator::containsNull($offsetType)) {
$specifiedTypes = $specifiedTypes->unionWith($this->create(
new IssetExpr($var),
new IssetExpr($var, $scope->hasExpressionType($var)),
new NullType(),
$context,
false,
Expand Down Expand Up @@ -791,7 +791,7 @@ public function specifyTypesInCondition(
});

$specifiedTypes = $specifiedTypes->unionWith($this->create(
$var->var,
new IssetExpr($var->var, $scope->hasExpressionType($var->var)),
$newType,
$context->negate(),
true,
Expand Down
7 changes: 7 additions & 0 deletions src/Node/IssetExpr.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
namespace PHPStan\Node;

use PhpParser\Node\Expr;
use PHPStan\TrinaryLogic;

class IssetExpr extends Expr implements VirtualNode
{

public function __construct(
private Expr $expr,
private TrinaryLogic $certainty,
)
{
parent::__construct([]);
Expand All @@ -19,6 +21,11 @@ public function getExpr(): Expr
return $this->expr;
}

public function getCertainty(): TrinaryLogic
{
return $this->certainty;
}

public function getType(): string
{
return 'PHPStan_Node_IssetExpr';
Expand Down
2 changes: 1 addition & 1 deletion tests/PHPStan/Analyser/data/falsey-isset-certainty.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ function falseyIssetVariable(): void
if (isset($a)) {
assertVariableCertainty(TrinaryLogic::createYes(), $a);
} else {
assertVariableCertainty(TrinaryLogic::createMaybe(), $a);
assertVariableCertainty(TrinaryLogic::createNo(), $a);
}

assertVariableCertainty(TrinaryLogic::createMaybe(), $a);
Expand Down
7 changes: 4 additions & 3 deletions tests/PHPStan/Analyser/data/falsy-isset.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,11 @@ public function maybeCertainNull(): void
assertType("array{bar: 1}", $a);
} else {
assertVariableCertainty(TrinaryLogic::createMaybe(), $a);
assertType('array{bar: null}', $a);
assertType('array{bar?: null}', $a);
}

assertVariableCertainty(TrinaryLogic::createMaybe(), $a);
assertType("array{bar: 1}|array{bar: null}", $a);
assertType("array{bar: 1}|array{bar?: null}", $a);
}

public function maybeCertainNonNull(): void
Expand Down Expand Up @@ -204,7 +204,8 @@ function maybeNonNullableVariable(): void
if (isset($a)) {
assertType("'hello'", $a);
} else {
assertType("null", $a);
assertVariableCertainty(TrinaryLogic::createNo(), $a);
assertType("*ERROR*", $a);
}
}

Expand Down

0 comments on commit 2f75d65

Please sign in to comment.