Skip to content

Commit

Permalink
Fixed nullsafe operator with null coalesce
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Feb 12, 2021
1 parent 46103ad commit 990ba51
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 50 deletions.
100 changes: 50 additions & 50 deletions src/Analyser/MutatingScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -1007,56 +1007,6 @@ private function resolveType(Expr $node): Type
return IntegerRangeType::fromInterval(-1, 1);
}

if ($node instanceof Expr\AssignOp\Coalesce) {
return $this->getType(new BinaryOp\Coalesce($node->var, $node->expr, $node->getAttributes()));
}

if ($node instanceof Expr\BinaryOp\Coalesce) {
if ($node->left instanceof Expr\ArrayDimFetch && $node->left->dim !== null) {
$dimType = $this->getType($node->left->dim);
$varType = $this->getType($node->left->var);
$hasOffset = $varType->hasOffsetValueType($dimType);
$leftType = $this->getType($node->left);
$rightType = $this->getType($node->right);
if ($hasOffset->no()) {
return $rightType;
} elseif ($hasOffset->yes()) {
$offsetValueType = $varType->getOffsetValueType($dimType);
if ($offsetValueType->isSuperTypeOf(new NullType())->no()) {
return TypeCombinator::removeNull($leftType);
}
}

return TypeCombinator::union(
TypeCombinator::removeNull($leftType),
$rightType
);
}

$leftType = $this->getType($node->left);
$rightType = $this->getType($node->right);
if ($leftType instanceof ErrorType || $leftType instanceof NullType) {
return $rightType;
}

if (
TypeCombinator::containsNull($leftType)
|| $node->left instanceof PropertyFetch
|| (
$node->left instanceof Variable
&& is_string($node->left->name)
&& !$this->hasVariableType($node->left->name)->yes()
)
) {
return TypeCombinator::union(
TypeCombinator::removeNull($leftType),
$rightType
);
}

return TypeCombinator::removeNull($leftType);
}

if ($node instanceof Expr\Clone_) {
return $this->getType($node->expr);
}
Expand Down Expand Up @@ -1663,6 +1613,56 @@ private function resolveType(Expr $node): Type
return $this->moreSpecificTypes[$exprString]->getType();
}

if ($node instanceof Expr\AssignOp\Coalesce) {
return $this->getType(new BinaryOp\Coalesce($node->var, $node->expr, $node->getAttributes()));
}

if ($node instanceof Expr\BinaryOp\Coalesce) {
if ($node->left instanceof Expr\ArrayDimFetch && $node->left->dim !== null) {
$dimType = $this->getType($node->left->dim);
$varType = $this->getType($node->left->var);
$hasOffset = $varType->hasOffsetValueType($dimType);
$leftType = $this->getType($node->left);
$rightType = $this->getType($node->right);
if ($hasOffset->no()) {
return $rightType;
} elseif ($hasOffset->yes()) {
$offsetValueType = $varType->getOffsetValueType($dimType);
if ($offsetValueType->isSuperTypeOf(new NullType())->no()) {
return TypeCombinator::removeNull($leftType);
}
}

return TypeCombinator::union(
TypeCombinator::removeNull($leftType),
$rightType
);
}

$leftType = $this->getType($node->left);
$rightType = $this->getType($node->right);
if ($leftType instanceof ErrorType || $leftType instanceof NullType) {
return $rightType;
}

if (
TypeCombinator::containsNull($leftType)
|| $node->left instanceof PropertyFetch
|| (
$node->left instanceof Variable
&& is_string($node->left->name)
&& !$this->hasVariableType($node->left->name)->yes()
)
) {
return TypeCombinator::union(
TypeCombinator::removeNull($leftType),
$rightType
);
}

return TypeCombinator::removeNull($leftType);
}

if ($node instanceof ConstFetch) {
$constName = (string) $node->name;
$loweredConstName = strtolower($constName);
Expand Down
11 changes: 11 additions & 0 deletions tests/PHPStan/Rules/Properties/AccessPropertiesRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -473,4 +473,15 @@ public function testBug3371(): void
$this->analyse([__DIR__ . '/data/bug-3371.php'], []);
}

public function testBug4527(): void
{
if (PHP_VERSION_ID < 80000 && !self::$useStaticReflectionProvider) {
$this->markTestSkipped('Test requires PHP 8.0.');
}

$this->checkThisOnly = false;
$this->checkUnionTypes = true;
$this->analyse([__DIR__ . '/data/bug-4527.php'], []);
}

}
19 changes: 19 additions & 0 deletions tests/PHPStan/Rules/Properties/data/bug-4527.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php // lint >= 8.0

namespace Bug4527;

class Foo
{

/**
* @param Bar[] $bars
*/
public function foo(array $bars): void
{
($bars['randomKey'] ?? null)?->bar;
}
}

class Bar {
public $bar;
}

0 comments on commit 990ba51

Please sign in to comment.