Skip to content

Commit

Permalink
Add coalesce rule to level one, handle known nullable offsets
Browse files Browse the repository at this point in the history
  • Loading branch information
leongersen committed Jan 15, 2020
1 parent 90c901f commit 5702089
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 2 deletions.
1 change: 1 addition & 0 deletions conf/config.level1.neon
Expand Up @@ -12,3 +12,4 @@ rules:
- PHPStan\Rules\Constants\ConstantRule
- PHPStan\Rules\Functions\UnusedClosureUsesRule
- PHPStan\Rules\Variables\VariableCertaintyInIssetRule
- PHPStan\Rules\Variables\CoalesceRule
20 changes: 18 additions & 2 deletions src/Rules/Variables/CoalesceRule.php
Expand Up @@ -45,7 +45,8 @@ private function canBeCoalesced(Node $node, Scope $scope): ?RuleError

$variableType = $scope->getVariableType($node->name);

if ($variableType->isSuperTypeOf(new NullType())->no()) {
// For hasVariable->maybe a coalesce is valid
if ($hasVariable->yes() && $variableType->isSuperTypeOf(new NullType())->no()) {
return RuleErrorBuilder::message(
sprintf('Coalesce of variable $%s, which cannot be null.', $node->name)
)->line($node->getLine())->build();
Expand All @@ -54,8 +55,9 @@ private function canBeCoalesced(Node $node, Scope $scope): ?RuleError
} elseif ($node instanceof Node\Expr\ArrayDimFetch && $node->dim !== null) {
$type = $scope->getType($node->var);
$dimType = $scope->getType($node->dim);
$hasOffsetValue = $type->hasOffsetValueType($dimType);

if ($type->isOffsetAccessible()->no() || $type->hasOffsetValueType($dimType)->no()) {
if ($type->isOffsetAccessible()->no() || $hasOffsetValue->no()) {
return RuleErrorBuilder::message(
sprintf(
'Coalesce of invalid offset %s on %s.',
Expand All @@ -65,6 +67,20 @@ private function canBeCoalesced(Node $node, Scope $scope): ?RuleError
)->line($node->getLine())->build();
}

if ($hasOffsetValue->maybe()) {
return null;
}

if ($hasOffsetValue->yes() && $type->isSuperTypeOf(new NullType())->no()) {
return RuleErrorBuilder::message(
sprintf(
'Coalesce of offset %s on %s, which cannot be null.',
$dimType->describe(VerbosityLevel::value()),
$type->describe(VerbosityLevel::value())
)
)->line($node->getLine())->build();
}

return $this->canBeCoalesced($node->var, $scope);
}

Expand Down
4 changes: 4 additions & 0 deletions tests/PHPStan/Rules/Variables/CoalesceRuleTest.php
Expand Up @@ -33,6 +33,10 @@ public function testUnsetRule(): void
'Coalesce of undefined variable $doesNotExist.',
17,
],
[
'Coalesce of offset \'dim\' on array(\'dim\' => 1), which cannot be null.',
27,
],
]);
}

Expand Down
17 changes: 17 additions & 0 deletions tests/PHPStan/Rules/Variables/data/coalesce.php
Expand Up @@ -16,4 +16,21 @@ function coalesce () {

echo $doesNotExist ?? 0;

if(rand() > 0.5) {
$maybeVariable = 3;
}

echo $maybeVariable ?? 0;

$fixedDimArray = ['dim' => 1];

echo $fixedDimArray['dim'] ?? 0;
}

/**
* @param array<string, int> $array
*/
function coalesceStringOffset(array $array)
{
echo $array['string'] ?? 0;
}

0 comments on commit 5702089

Please sign in to comment.