Skip to content

Commit

Permalink
Fix resolving mixed + array
Browse files Browse the repository at this point in the history
  • Loading branch information
Richard van Velzen authored and staabm committed Aug 1, 2022
1 parent 39380cd commit 4277cf4
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 13 deletions.
21 changes: 21 additions & 0 deletions src/Reflection/InitializerExprTypeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -924,6 +924,27 @@ public function getPlusType(Expr $left, Expr $right, callable $getTypeCallback):
]);
}

$mixedArray = new ArrayType(new MixedType(), new MixedType());
if (
($leftType->isArray()->yes() && $rightType->isSuperTypeOf($mixedArray)->no())
|| ($rightType->isArray()->yes() && $leftType->isSuperTypeOf($mixedArray)->no())
) {
return new ErrorType();
}

if (
($leftType->isArray()->yes() && $rightType->isSuperTypeOf($mixedArray)->yes())
|| ($rightType->isArray()->yes() && $leftType->isSuperTypeOf($mixedArray)->yes())
) {
$arrayType = new ArrayType(new MixedType(), new MixedType());

if ($leftType->isIterableAtLeastOnce()->yes() || $rightType->isIterableAtLeastOnce()->yes()) {
return TypeCombinator::intersect($arrayType, new NonEmptyArrayType());
}

return $arrayType;
}

return $this->resolveCommonMath(new BinaryOp\Plus($left, $right), $leftType, $rightType);
}

Expand Down
31 changes: 19 additions & 12 deletions src/Rules/Operators/InvalidBinaryOperationRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use PHPStan\Rules\RuleLevelHelper;
use PHPStan\ShouldNotHappenException;
use PHPStan\Type\ErrorType;
use PHPStan\Type\MixedType;
use PHPStan\Type\Type;
use PHPStan\Type\VerbosityLevel;
use function sprintf;
Expand Down Expand Up @@ -71,22 +72,28 @@ public function processNode(Node $node, Scope $scope): array
$callback = static fn (Type $type): bool => !$type->toNumber() instanceof ErrorType;
}

$leftType = $this->ruleLevelHelper->findTypeToCheck(
$scope,
$left,
'',
$callback,
)->getType();
$leftType = $scope->getType($left);
if (!$leftType instanceof MixedType) {
$leftType = $this->ruleLevelHelper->findTypeToCheck(
$scope,
$left,
'',
$callback,
)->getType();
}
if ($leftType instanceof ErrorType) {
return [];
}

$rightType = $this->ruleLevelHelper->findTypeToCheck(
$scope,
$right,
'',
$callback,
)->getType();
$rightType = $scope->getType($right);
if (!$rightType instanceof MixedType) {
$rightType = $this->ruleLevelHelper->findTypeToCheck(
$scope,
$right,
'',
$callback,
)->getType();
}
if ($rightType instanceof ErrorType) {
return [];
}
Expand Down
10 changes: 9 additions & 1 deletion tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2696,9 +2696,17 @@ public function dataBinaryOperations(): array
'$mixed - $mixed',
],
[
'*ERROR*',
'array',
'$mixed + []',
],
[
'*ERROR*',
'$mixedNoArray + []',
],
[
'*ERROR*',
'$integer + []',
],
[
'124',
'1 + "123"',
Expand Down
1 change: 1 addition & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -962,6 +962,7 @@ public function dataFileAsserts(): iterable
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Arrays/data/bug-5758.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Functions/data/bug-3931.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-5223.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7492.php');
}

/**
Expand Down
4 changes: 4 additions & 0 deletions tests/PHPStan/Analyser/data/binary.php
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,10 @@ public function doFoo(array $generalArray)
$severalSumWithStaticConst2 = 1 + static::INT_CONST + 1;
$severalSumWithStaticConst3 = 1 + 1 + static::INT_CONST;

if (!is_array($mixed)) {
$mixedNoArray = $mixed;
}

die;
}

Expand Down
14 changes: 14 additions & 0 deletions tests/PHPStan/Analyser/data/bug-7492.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php declare(strict_types = 1);

namespace Bug7492;

use function PHPStan\Testing\assertType;

class HelloWorld
{
public function sayHello(array $config): void
{
$x = ($config['db'][1] ?? []) + ['host' => '', 'login' => '', 'password' => '', 'name' => ''];
assertType('non-empty-array', $x);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,14 @@ public function testRule(): void
'Binary operation "/" between 10 and literal-string results in an error.',
222,
],
[
'Binary operation "+" between int and array{} results in an error.',
259,
],
[
'Binary operation "+" between mixed and array results in an error.',
266,
],
]);
}

Expand Down
15 changes: 15 additions & 0 deletions tests/PHPStan/Rules/Operators/data/invalid-binary.php
Original file line number Diff line number Diff line change
Expand Up @@ -254,3 +254,18 @@ function benevolentPlus(array $a, int $i): void {
echo $k + $i;
}
};

function (int $int) {
$int + [];
};

function ($mixed, array $arr) {
if (is_array($mixed)) {
return;
}
$mixed + $arr; // mixed~array + array
};

function noErrorOnMixedPlusArray($mixed, array $arr) {
$mixed + $arr;
};

0 comments on commit 4277cf4

Please sign in to comment.