Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Fix check generic mixed type based on config v2
  • Loading branch information
schlndh authored and ondrejmirtes committed Jan 22, 2024
commit 0bf864d8a13882415b86e3faec9bf78f552ecb97
26 changes: 12 additions & 14 deletions src/Rules/RuleLevelHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ private function transformCommonType(Type $type): Type
}

return TypeTraverser::map($type, function (Type $type, callable $traverse) {
if ($type instanceof TemplateMixedType) {
if ($type instanceof TemplateMixedType && ($this->checkExplicitMixed || !$this->newRuleLevelHelper)) {
return $type->toStrictMixedType();
}
if (
Expand Down Expand Up @@ -302,21 +302,19 @@ public function findTypeToCheck(
}

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

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