Skip to content

Commit

Permalink
Fix checking generic mixed type based on config
Browse files Browse the repository at this point in the history
  • Loading branch information
schlndh committed Jan 22, 2024
1 parent 8f82b79 commit a3d0910
Show file tree
Hide file tree
Showing 12 changed files with 633 additions and 25 deletions.
55 changes: 39 additions & 16 deletions src/Rules/RuleLevelHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,13 @@ private function transformCommonType(Type $type): Type

return TypeTraverser::map($type, function (Type $type, callable $traverse) {
if ($type instanceof TemplateMixedType) {
return $type->toStrictMixedType();
if (!$this->newRuleLevelHelper) {
return $type->toStrictMixedType();
}

if ($this->checkExplicitMixed) {
return $type->toStrictMixedType();
}
}
if (
$type instanceof MixedType
Expand Down Expand Up @@ -301,22 +307,39 @@ public function findTypeToCheck(
$type = TypeCombinator::removeNull($type);
}

if (
$this->checkExplicitMixed
&& $type instanceof MixedType
&& !$type instanceof TemplateMixedType
&& $type->isExplicitMixed()
) {
return new FoundTypeResult(new StrictMixedType(), [], [], null);
}
if ($this->newRuleLevelHelper) {
if (
($this->checkExplicitMixed || $this->checkImplicitMixed)
&& $type instanceof MixedType
&& ($type->isExplicitMixed() ? $this->checkExplicitMixed : $this->checkImplicitMixed)
) {
return new FoundTypeResult(
$type instanceof TemplateMixedType
? $type->toStrictMixedType()
: new StrictMixedType(),
[],
[],
null,
);
}
} else {
if (
$this->checkExplicitMixed
&& $type instanceof MixedType
&& !$type instanceof TemplateMixedType
&& $type->isExplicitMixed()
) {
return new FoundTypeResult(new StrictMixedType(), [], [], null);
}

if (
$this->checkImplicitMixed
&& $type instanceof MixedType
&& !$type instanceof TemplateMixedType
&& !$type->isExplicitMixed()
) {
return new FoundTypeResult(new StrictMixedType(), [], [], null);
if (
$this->checkImplicitMixed
&& $type instanceof MixedType
&& !$type instanceof TemplateMixedType
&& !$type->isExplicitMixed()
) {
return new FoundTypeResult(new StrictMixedType(), [], [], null);
}
}

if ($type instanceof MixedType || $type instanceof NeverType) {
Expand Down
62 changes: 61 additions & 1 deletion tests/PHPStan/Rules/Arrays/IterableInForeachRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleLevelHelper;
use PHPStan\Testing\RuleTestCase;
use function array_merge;
use function usort;
use const PHP_VERSION_ID;

/**
Expand All @@ -15,9 +17,11 @@ class IterableInForeachRuleTest extends RuleTestCase

private bool $checkExplicitMixed = false;

private bool $checkImplicitMixed = false;

protected function getRule(): Rule
{
return new IterableInForeachRule(new RuleLevelHelper($this->createReflectionProvider(), true, false, true, $this->checkExplicitMixed, false, true, false));
return new IterableInForeachRule(new RuleLevelHelper($this->createReflectionProvider(), true, false, true, $this->checkExplicitMixed, $this->checkImplicitMixed, true, false));
}

public function testCheckWithMaybes(): void
Expand Down Expand Up @@ -80,4 +84,60 @@ public function testBug4335(): void
$this->analyse([__DIR__ . '/data/bug-4335.php'], []);
}

public function dataMixed(): array
{
$explicitOnlyErrors = [
[
'Argument of an invalid type T of mixed supplied for foreach, only iterables are supported.',
11,
],
[
'Argument of an invalid type mixed supplied for foreach, only iterables are supported.',
14,
],
];
$implicitOnlyErrors = [
[
'Argument of an invalid type mixed supplied for foreach, only iterables are supported.',
17,
],
];
$combinedErrors = array_merge($explicitOnlyErrors, $implicitOnlyErrors);
usort($combinedErrors, static fn (array $a, array $b): int => $a[1] <=> $b[1]);

return [
[
true,
false,
$explicitOnlyErrors,
],
[
false,
true,
$implicitOnlyErrors,
],
[
true,
true,
$combinedErrors,
],
[
false,
false,
[],
],
];
}

/**
* @dataProvider dataMixed
* @param list<array{0: string, 1: int, 2?: string}> $errors
*/
public function testMixed(bool $checkExplicitMixed, bool $checkImplicitMixed, array $errors): void
{
$this->checkExplicitMixed = $checkExplicitMixed;
$this->checkImplicitMixed = $checkImplicitMixed;
$this->analyse([__DIR__ . '/data/foreach-mixed.php'], $errors);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ class NonexistentOffsetInArrayDimFetchRuleTest extends RuleTestCase

private bool $checkExplicitMixed = false;

private bool $checkImplicitMixed = false;

private bool $bleedingEdge = false;

protected function getRule(): Rule
{
$ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, true, $this->checkExplicitMixed, false, true, false);
$ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, true, $this->checkExplicitMixed, $this->checkImplicitMixed, true, false);

return new NonexistentOffsetInArrayDimFetchRule(
$ruleLevelHelper,
Expand Down Expand Up @@ -747,4 +749,24 @@ public function testBug8166(): void
]);
}

public function testMixed(): void
{
$this->checkExplicitMixed = true;
$this->checkImplicitMixed = true;
$this->analyse([__DIR__ . '/data/offset-access-mixed.php'], [
[
'Cannot access offset 5 on T of mixed.',
11,
],
[
'Cannot access offset 5 on mixed.',
16,
],
[
'Cannot access offset 5 on mixed.',
21,
],
]);
}

}
64 changes: 63 additions & 1 deletion tests/PHPStan/Rules/Arrays/UnpackIterableInArrayRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleLevelHelper;
use PHPStan\Testing\RuleTestCase;
use function array_merge;
use function usort;
use const PHP_VERSION_ID;

/**
Expand All @@ -13,9 +15,13 @@
class UnpackIterableInArrayRuleTest extends RuleTestCase
{

private bool $checkExplicitMixed = false;

private bool $checkImplicitMixed = false;

protected function getRule(): Rule
{
return new UnpackIterableInArrayRule(new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false, false, true, false));
return new UnpackIterableInArrayRule(new RuleLevelHelper($this->createReflectionProvider(), true, false, true, $this->checkExplicitMixed, $this->checkImplicitMixed, true, false));
}

public function testRule(): void
Expand Down Expand Up @@ -50,4 +56,60 @@ public function testRuleWithNullsafeVariant(): void
]);
}

public function dataMixed(): array
{
$explicitOnlyErrors = [
[
'Only iterables can be unpacked, T of mixed given.',
11,
],
[
'Only iterables can be unpacked, mixed given.',
12,
],
];
$implicitOnlyErrors = [
[
'Only iterables can be unpacked, mixed given.',
13,
],
];
$combinedErrors = array_merge($explicitOnlyErrors, $implicitOnlyErrors);
usort($combinedErrors, static fn (array $a, array $b): int => $a[1] <=> $b[1]);

return [
[
true,
false,
$explicitOnlyErrors,
],
[
false,
true,
$implicitOnlyErrors,
],
[
true,
true,
$combinedErrors,
],
[
false,
false,
[],
],
];
}

/**
* @dataProvider dataMixed
* @param list<array{0: string, 1: int, 2?: string}> $errors
*/
public function testMixed(bool $checkExplicitMixed, bool $checkImplicitMixed, array $errors): void
{
$this->checkExplicitMixed = $checkExplicitMixed;
$this->checkImplicitMixed = $checkImplicitMixed;
$this->analyse([__DIR__ . '/data/unpack-mixed.php'], $errors);
}

}
19 changes: 19 additions & 0 deletions tests/PHPStan/Rules/Arrays/data/foreach-mixed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php declare(strict_types=1); // lint >= 8.0

namespace ForeachMixed;

/**
* @template T
* @param T $t
*/
function foo(mixed $t, mixed $explicit, $implicit): void
{
foreach ($t as $v) {
}

foreach ($explicit as $v) {
}

foreach ($implicit as $v) {
}
}
22 changes: 22 additions & 0 deletions tests/PHPStan/Rules/Arrays/data/offset-access-mixed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php declare(strict_types=1); // lint >= 8.0

namespace OffsetAccessMixed;

/**
* @template T
* @param T $a
*/
function foo(mixed $a): void
{
var_dump($a[5]);
}

function foo2(mixed $a): void
{
var_dump($a[5]);
}

function foo3($a): void
{
var_dump($a[5]);
}
14 changes: 14 additions & 0 deletions tests/PHPStan/Rules/Arrays/data/unpack-mixed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php declare(strict_types=1); // lint >= 8.0

namespace UnpackMixed;

/**
* @template T
* @param T $t
*/
function foo(mixed $t, mixed $explicit, $implicit): void
{
var_dump([...$t]);
var_dump([...$explicit]);
var_dump([...$implicit]);
}

0 comments on commit a3d0910

Please sign in to comment.