diff --git a/src/Rules/PhpDoc/AssertRuleHelper.php b/src/Rules/PhpDoc/AssertRuleHelper.php index 40551504fa..728a7f39b0 100644 --- a/src/Rules/PhpDoc/AssertRuleHelper.php +++ b/src/Rules/PhpDoc/AssertRuleHelper.php @@ -2,6 +2,7 @@ namespace PHPStan\Rules\PhpDoc; +use PHPStan\PhpDoc\Tag\AssertTag; use PHPStan\Node\Expr\TypeExpr; use PHPStan\Reflection\ExtendedMethodReflection; use PHPStan\Reflection\FunctionReflection; @@ -20,7 +21,10 @@ final class AssertRuleHelper { - public function __construct(private InitializerExprTypeResolver $initializerExprTypeResolver) + public function __construct( + private InitializerExprTypeResolver $initializerExprTypeResolver, + private UnresolvableTypeHelper $unresolvableTypeHelper, + ) { } @@ -55,9 +59,25 @@ public function check(ExtendedMethodReflection|FunctionReflection $reflection, P continue; } + if ($this->unresolvableTypeHelper->containsUnresolvableType($assert->getType())) { + $tagName = [ + AssertTag::NULL => '@phpstan-assert', + AssertTag::IF_TRUE => '@phpstan-assert-if-true', + AssertTag::IF_FALSE => '@phpstan-assert-if-false', + ][$assert->getIf()]; + + $errors[] = RuleErrorBuilder::message(sprintf( + 'PHPDoc tag %s for parameter $%s contains unresolvable type.', + $tagName, + $parameterName, + ))->identifier('parameter.unresolvableType')->build(); + + continue; + } + $assertedExpr = $assert->getParameter()->getExpr(new TypeExpr($parametersByName[$parameterName])); $assertedExprType = $this->initializerExprTypeResolver->getType($assertedExpr, $context); - if ($assertedExprType instanceof ErrorType) { + if ($this->unresolvableTypeHelper->containsUnresolvableType($assertedExprType)) { continue; } diff --git a/tests/PHPStan/Rules/PhpDoc/FunctionAssertRuleTest.php b/tests/PHPStan/Rules/PhpDoc/FunctionAssertRuleTest.php index 3bfd8ae7a6..946528f150 100644 --- a/tests/PHPStan/Rules/PhpDoc/FunctionAssertRuleTest.php +++ b/tests/PHPStan/Rules/PhpDoc/FunctionAssertRuleTest.php @@ -54,6 +54,18 @@ public function testRule(): void 'Asserted negated type string for $i with type int does not narrow down the type.', 70, ], + [ + 'PHPDoc tag @phpstan-assert for parameter $str contains unresolvable type.', + 83, + ], + [ + 'PHPDoc tag @phpstan-assert-if-true for parameter $str contains unresolvable type.', + 90, +], + [ + 'PHPDoc tag @phpstan-assert-if-false for parameter $str contains unresolvable type.', + 97, + ], ]); } diff --git a/tests/PHPStan/Rules/PhpDoc/MethodAssertRuleTest.php b/tests/PHPStan/Rules/PhpDoc/MethodAssertRuleTest.php index 932a09c299..cf5d540bb3 100644 --- a/tests/PHPStan/Rules/PhpDoc/MethodAssertRuleTest.php +++ b/tests/PHPStan/Rules/PhpDoc/MethodAssertRuleTest.php @@ -54,6 +54,18 @@ public function testRule(): void 'Asserted negated type string for $i with type int does not narrow down the type.', 72, ], + [ + 'PHPDoc tag @phpstan-assert for parameter $str contains unresolvable type.', + 86, + ], + [ + 'PHPDoc tag @phpstan-assert-if-true for parameter $str contains unresolvable type.', + 93, +], + [ + 'PHPDoc tag @phpstan-assert-if-false for parameter $str contains unresolvable type.', + 100, + ], ]); } diff --git a/tests/PHPStan/Rules/PhpDoc/data/function-assert.php b/tests/PHPStan/Rules/PhpDoc/data/function-assert.php index e1b25eb6c0..494a47c20f 100644 --- a/tests/PHPStan/Rules/PhpDoc/data/function-assert.php +++ b/tests/PHPStan/Rules/PhpDoc/data/function-assert.php @@ -76,3 +76,24 @@ function negate1(int $i): void function negate2(int $i): void { } + +/** + * @phpstan-assert empty-str $str + */ +function unresolvableAssert(string $str): void +{ +} + +/** + * @phpstan-assert-if-true empty-str $str + */ +function unresolvableAssertIfTrue(string $str): void +{ +} + +/** + * @phpstan-assert-if-false empty-str $str + */ +function unresolvableAssertIfFalse(string $str): void +{ +} diff --git a/tests/PHPStan/Rules/PhpDoc/data/method-assert.php b/tests/PHPStan/Rules/PhpDoc/data/method-assert.php index fa603eb85f..93cc988363 100644 --- a/tests/PHPStan/Rules/PhpDoc/data/method-assert.php +++ b/tests/PHPStan/Rules/PhpDoc/data/method-assert.php @@ -79,4 +79,26 @@ public function negate1(int $i): void public function negate2(int $i): void { } + + /** + * @phpstan-assert empty-str $str + */ + public function unresolvableAssert(string $str): void + { + } + + /** + * @phpstan-assert-if-true empty-str $str + */ + public function unresolvableAssertIfTrue(string $str): void + { + } + + /** + * @phpstan-assert-if-false empty-str $str + */ + public function unresolvableAssertIfFalse(string $str): void + { + } + }