Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix check generic mixed type based on config v2 #2885

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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]);
}