Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/Type/InspectorTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,16 @@ private function specifyAssertAll(MethodReflection $staticMethodReflection, Stat
return new SpecifiedTypes();
}

$traversable = $node->getArgs()[1]->value;
$traversableInfo = $scope->getType($traversable);

// If it is already not mixed (narrowed by other code, like
// '::assertAllArray()'), we could not provide any additional
// information. We can only narrow this method to 'array<mixed, mixed>'.
if (!$traversableInfo->equals(new MixedType())) {
return new SpecifiedTypes();
}

// In a negation context, we cannot precisely narrow types because we do
// not know the exact logic of the callable function. This means we
// cannot safely return 'mixed~iterable' since the value might still be
Expand Down
52 changes: 52 additions & 0 deletions tests/src/Rules/ImpossibleCheckTypeStaticMethodCallRuleTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

declare(strict_types=1);

namespace mglaman\PHPStanDrupal\Tests\Rules;

use mglaman\PHPStanDrupal\Rules\Drupal\Tests\TestClassSuffixNameRule;
use mglaman\PHPStanDrupal\Tests\DrupalRuleTestCase;
use PHPStan\Rules\Comparison\ImpossibleCheckTypeHelper;
use PHPStan\Rules\Comparison\ImpossibleCheckTypeStaticMethodCallRule;
use PHPStan\Rules\Rule;

class ImpossibleCheckTypeStaticMethodCallRuleTest extends DrupalRuleTestCase
{

private bool $treatPhpDocTypesAsCertain = false;

private bool $reportAlwaysTrueInLastCondition = false;

protected function getRule(): Rule
{
/** @phpstan-ignore phpstanApi.constructor */
return new ImpossibleCheckTypeStaticMethodCallRule(
/** @phpstan-ignore phpstanApi.constructor */
new ImpossibleCheckTypeHelper(
$this->createReflectionProvider(),
$this->getTypeSpecifier(),
[],
$this->treatPhpDocTypesAsCertain,
false,
),
true,
$this->treatPhpDocTypesAsCertain,
$this->reportAlwaysTrueInLastCondition,
true,
);
}

protected function shouldTreatPhpDocTypesAsCertain(): bool
{
return $this->treatPhpDocTypesAsCertain;
}


public function testBug857(): void
{
$this->treatPhpDocTypesAsCertain = true;
// It shouldn't report anything.
$this->analyse([__DIR__ . '/data/bug-857.php'], []);
}

}
11 changes: 11 additions & 0 deletions tests/src/Rules/data/bug-857.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

use Drupal\Component\Assertion\Inspector;
use Drupal\Core\Url;

function foo(array $images) {
// This call is crucial for the bug, because it narrows type to
// 'array<array>'.
Inspector::assertAllArrays($images);
Inspector::assertAll(fn (array $i): bool => $i['file'] instanceof Url, $images);
}
1 change: 1 addition & 0 deletions tests/src/Type/InspectorTypeExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ final class InspectorTypeExtensionTest extends TypeInferenceTestCase {
public static function dataFileAsserts(): iterable {
yield from self::gatherAssertTypes(__DIR__ . '/data/inspector.php');
yield from self::gatherAssertTypes(__DIR__ . '/data/bug-852.php');
yield from self::gatherAssertTypes(__DIR__ . '/data/bug-857.php');
}

/**
Expand Down
21 changes: 21 additions & 0 deletions tests/src/Type/data/bug-857.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

use Drupal\Component\Assertion\Inspector;
use function PHPStan\Testing\assertType;

function mixed_function(): mixed {}

// If the type is not narrowed from 'mixed' before call, it is narrowed to
// 'iterable'.
$array = mixed_function();
assertType('mixed', $array);
\assert(Inspector::assertAll(fn (array $i): bool => TRUE, $array));
assertType('iterable', $array);

// If the type is narrowed before '::assertAll()', it shouldn't change it.
$array = mixed_function();
assertType('mixed', $array);
\assert(Inspector::assertAllArrays($array));
assertType('iterable<array>', $array);
\assert(Inspector::assertAll(fn (array $i): bool => TRUE, $array));
assertType('iterable<array>', $array);
Loading