Skip to content

Commit

Permalink
MethodAssertRule - do not report implicitly inherited assert tags
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Feb 19, 2024
1 parent d2c30d1 commit 299df51
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 8 deletions.
6 changes: 3 additions & 3 deletions src/PhpDoc/PhpDocNodeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -515,19 +515,19 @@ private function resolveAssertTagsFor(PhpDocNode $phpDocNode, NameScope $nameSco
foreach ($phpDocNode->getAssertTagValues($tagName) as $assertTagValue) {
$type = $this->typeNodeResolver->resolve($assertTagValue->type, $nameScope);
$parameter = new AssertTagParameter($assertTagValue->parameter, null, null);
$resolved[] = new AssertTag($if, $type, $parameter, $assertTagValue->isNegated, $assertTagValue->isEquality ?? false);
$resolved[] = new AssertTag($if, $type, $parameter, $assertTagValue->isNegated, $assertTagValue->isEquality ?? false, true);
}

foreach ($phpDocNode->getAssertPropertyTagValues($tagName) as $assertTagValue) {
$type = $this->typeNodeResolver->resolve($assertTagValue->type, $nameScope);
$parameter = new AssertTagParameter($assertTagValue->parameter, $assertTagValue->property, null);
$resolved[] = new AssertTag($if, $type, $parameter, $assertTagValue->isNegated, $assertTagValue->isEquality ?? false);
$resolved[] = new AssertTag($if, $type, $parameter, $assertTagValue->isNegated, $assertTagValue->isEquality ?? false, true);
}

foreach ($phpDocNode->getAssertMethodTagValues($tagName) as $assertTagValue) {
$type = $this->typeNodeResolver->resolve($assertTagValue->type, $nameScope);
$parameter = new AssertTagParameter($assertTagValue->parameter, null, $assertTagValue->method);
$resolved[] = new AssertTag($if, $type, $parameter, $assertTagValue->isNegated, $assertTagValue->isEquality ?? false);
$resolved[] = new AssertTag($if, $type, $parameter, $assertTagValue->isNegated, $assertTagValue->isEquality ?? false, true);
}

return $resolved;
Expand Down
2 changes: 1 addition & 1 deletion src/PhpDoc/ResolvedPhpDocBlock.php
Original file line number Diff line number Diff line change
Expand Up @@ -934,7 +934,7 @@ private static function mergeAssertTags(array $assertTags, array $parents, array
static fn (AssertTag $assertTag) => self::resolveTemplateTypeInTag(
$assertTag->withParameter(
$phpDocBlock->transformAssertTagParameterWithParameterNameMapping($assertTag->getParameter()),
),
)->toImplicit(),
$phpDocBlock,
TemplateTypeVariance::createCovariant(),
),
Expand Down
18 changes: 14 additions & 4 deletions src/PhpDoc/Tag/AssertTag.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ final class AssertTag implements TypedTag
/**
* @param self::NULL|self::IF_TRUE|self::IF_FALSE $if
*/
public function __construct(private string $if, private Type $type, private AssertTagParameter $parameter, private bool $negated, private bool $equality)
public function __construct(private string $if, private Type $type, private AssertTagParameter $parameter, private bool $negated, private bool $equality, private bool $isExplicit)
{
}

Expand Down Expand Up @@ -60,14 +60,14 @@ public function isEquality(): bool
*/
public function withType(Type $type): TypedTag
{
$tag = new self($this->if, $type, $this->parameter, $this->negated, $this->equality);
$tag = new self($this->if, $type, $this->parameter, $this->negated, $this->equality, $this->isExplicit);
$tag->originalType = $this->getOriginalType();
return $tag;
}

public function withParameter(AssertTagParameter $parameter): self
{
$tag = new self($this->if, $this->type, $parameter, $this->negated, $this->equality);
$tag = new self($this->if, $this->type, $parameter, $this->negated, $this->equality, $this->isExplicit);
$tag->originalType = $this->getOriginalType();
return $tag;
}
Expand All @@ -78,9 +78,19 @@ public function negate(): self
throw new ShouldNotHappenException();
}

$tag = new self($this->if, $this->type, $this->parameter, !$this->negated, $this->equality);
$tag = new self($this->if, $this->type, $this->parameter, !$this->negated, $this->equality, $this->isExplicit);
$tag->originalType = $this->getOriginalType();
return $tag;
}

public function isExplicit(): bool
{
return $this->isExplicit;
}

public function toImplicit(): self
{
return new self($this->if, $this->type, $this->parameter, $this->negated, $this->equality, false);
}

}
4 changes: 4 additions & 0 deletions src/Rules/PhpDoc/AssertRuleHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ public function check(ExtendedMethodReflection|FunctionReflection $reflection, P
continue;
}

if (!$assert->isExplicit()) {
continue;
}

$assertedExpr = $assert->getParameter()->getExpr(new TypeExpr($parametersByName[$parameterName]));
$assertedExprType = $this->initializerExprTypeResolver->getType($assertedExpr, $context);
if ($assertedExprType instanceof ErrorType) {
Expand Down
5 changes: 5 additions & 0 deletions tests/PHPStan/Rules/PhpDoc/MethodAssertRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,9 @@ public function testRule(): void
]);
}

public function testBug10573(): void
{
$this->analyse([__DIR__ . '/data/bug-10573.php'], []);
}

}
35 changes: 35 additions & 0 deletions tests/PHPStan/Rules/PhpDoc/data/bug-10573.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace Bug10573;

/**
* @template TAttribute of string
* @template TSubject of mixed
*/
abstract class Voter
{
/**
* Determines if the attribute and subject are supported by this voter.
*
* @param string $attribute
* @param mixed $subject
*
* @phpstan-assert-if-true TSubject $subject
* @phpstan-assert-if-true TAttribute $attribute
*
* @return bool
*/
abstract protected function supports(string $attribute, $subject);
}

class Post {}

/**
* @extends Voter<string, Post>
*/
class PostVoter extends Voter {
function supports($attribute, $subject): bool
{
return $attribute === 'POST_READ' && $subject instanceof Post;
}
}

0 comments on commit 299df51

Please sign in to comment.