Skip to content

Commit 91cb386

Browse files
authored
1.x: Fix assertAll causes staticMethod.alreadyNarrowedType (#863)
* Fix assertAll causes staticMethod.alreadyNarrowedType * Fix duplicated part from wrong conflict resolve
1 parent 0a73fb2 commit 91cb386

File tree

5 files changed

+95
-0
lines changed

5 files changed

+95
-0
lines changed

src/Type/InspectorTypeExtension.php

+10
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,16 @@ private function specifyAssertAll(MethodReflection $staticMethodReflection, Stat
107107
return new SpecifiedTypes();
108108
}
109109

110+
$traversable = $node->getArgs()[1]->value;
111+
$traversableInfo = $scope->getType($traversable);
112+
113+
// If it is already not mixed (narrowed by other code, like
114+
// '::assertAllArray()'), we could not provide any additional
115+
// information. We can only narrow this method to 'array<mixed, mixed>'.
116+
if (!$traversableInfo->equals(new MixedType())) {
117+
return new SpecifiedTypes();
118+
}
119+
110120
// In a negation context, we cannot precisely narrow types because we do
111121
// not know the exact logic of the callable function. This means we
112122
// cannot safely return 'mixed~iterable' since the value might still be
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace mglaman\PHPStanDrupal\Tests\Rules;
6+
7+
use mglaman\PHPStanDrupal\Rules\Drupal\Tests\TestClassSuffixNameRule;
8+
use mglaman\PHPStanDrupal\Tests\DrupalRuleTestCase;
9+
use PHPStan\Rules\Comparison\ImpossibleCheckTypeHelper;
10+
use PHPStan\Rules\Comparison\ImpossibleCheckTypeStaticMethodCallRule;
11+
use PHPStan\Rules\Rule;
12+
13+
class ImpossibleCheckTypeStaticMethodCallRuleTest extends DrupalRuleTestCase
14+
{
15+
16+
private bool $treatPhpDocTypesAsCertain = false;
17+
18+
private bool $reportAlwaysTrueInLastCondition = false;
19+
20+
protected function getRule(): Rule
21+
{
22+
/** @phpstan-ignore phpstanApi.constructor */
23+
return new ImpossibleCheckTypeStaticMethodCallRule(
24+
/** @phpstan-ignore phpstanApi.constructor */
25+
new ImpossibleCheckTypeHelper(
26+
$this->createReflectionProvider(),
27+
$this->getTypeSpecifier(),
28+
[],
29+
$this->treatPhpDocTypesAsCertain,
30+
false,
31+
),
32+
true,
33+
$this->treatPhpDocTypesAsCertain,
34+
$this->reportAlwaysTrueInLastCondition,
35+
true,
36+
);
37+
}
38+
39+
protected function shouldTreatPhpDocTypesAsCertain(): bool
40+
{
41+
return $this->treatPhpDocTypesAsCertain;
42+
}
43+
44+
45+
public function testBug857(): void
46+
{
47+
$this->treatPhpDocTypesAsCertain = true;
48+
// It shouldn't report anything.
49+
$this->analyse([__DIR__ . '/data/bug-857.php'], []);
50+
}
51+
52+
}

tests/src/Rules/data/bug-857.php

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
use Drupal\Component\Assertion\Inspector;
4+
use Drupal\Core\Url;
5+
6+
function foo(array $images) {
7+
// This call is crucial for the bug, because it narrows type to
8+
// 'array<array>'.
9+
Inspector::assertAllArrays($images);
10+
Inspector::assertAll(fn (array $i): bool => $i['file'] instanceof Url, $images);
11+
}

tests/src/Type/InspectorTypeExtensionTest.php

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ final class InspectorTypeExtensionTest extends TypeInferenceTestCase {
1414
public static function dataFileAsserts(): iterable {
1515
yield from self::gatherAssertTypes(__DIR__ . '/data/inspector.php');
1616
yield from self::gatherAssertTypes(__DIR__ . '/data/bug-852.php');
17+
yield from self::gatherAssertTypes(__DIR__ . '/data/bug-857.php');
1718
}
1819

1920
/**

tests/src/Type/data/bug-857.php

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
use Drupal\Component\Assertion\Inspector;
4+
use function PHPStan\Testing\assertType;
5+
6+
function mixed_function(): mixed {}
7+
8+
// If the type is not narrowed from 'mixed' before call, it is narrowed to
9+
// 'iterable'.
10+
$array = mixed_function();
11+
assertType('mixed', $array);
12+
\assert(Inspector::assertAll(fn (array $i): bool => TRUE, $array));
13+
assertType('iterable', $array);
14+
15+
// If the type is narrowed before '::assertAll()', it shouldn't change it.
16+
$array = mixed_function();
17+
assertType('mixed', $array);
18+
\assert(Inspector::assertAllArrays($array));
19+
assertType('iterable<array>', $array);
20+
\assert(Inspector::assertAll(fn (array $i): bool => TRUE, $array));
21+
assertType('iterable<array>', $array);

0 commit comments

Comments
 (0)