Skip to content

Commit

Permalink
IncompatiblePropertyPhpDocTypeRule - fix inconsistency for stubbed PH…
Browse files Browse the repository at this point in the history
…PDocs
  • Loading branch information
ondrejmirtes committed Oct 13, 2022
1 parent 02f0f89 commit 32b3c67
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 11 deletions.
2 changes: 1 addition & 1 deletion src/PhpDoc/StubValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ private function getRuleRegistry(Container $container): RuleRegistry
$genericObjectTypeCheck,
$unresolvableTypeHelper,
),
new IncompatiblePropertyPhpDocTypeRule($genericObjectTypeCheck, $unresolvableTypeHelper),
new IncompatiblePropertyPhpDocTypeRule($genericObjectTypeCheck, $unresolvableTypeHelper, $fileTypeMapper),
new InvalidPhpDocTagValueRule(
$container->getByType(Lexer::class),
$container->getByType(PhpDocParser::class),
Expand Down
40 changes: 31 additions & 9 deletions src/Rules/PhpDoc/IncompatiblePropertyPhpDocTypeRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\ShouldNotHappenException;
use PHPStan\Type\FileTypeMapper;
use PHPStan\Type\Generic\TemplateType;
use PHPStan\Type\ParserNodeTypeToPHPStanType;
use PHPStan\Type\VerbosityLevel;
use function array_merge;
use function count;
use function sprintf;

/**
Expand All @@ -24,6 +27,7 @@ class IncompatiblePropertyPhpDocTypeRule implements Rule
public function __construct(
private GenericObjectTypeCheck $genericObjectTypeCheck,
private UnresolvableTypeHelper $unresolvableTypeHelper,
private FileTypeMapper $fileTypeMapper,
)
{
}
Expand All @@ -40,15 +44,33 @@ public function processNode(Node $node, Scope $scope): array
}

$propertyName = $node->getName();
$propertyReflection = $scope->getClassReflection()->getNativeProperty($propertyName);
$phpDoc = $node->getPhpDoc();
if ($phpDoc === null) {
return [];
}

$resolvedPhpDoc = $this->fileTypeMapper->getResolvedPhpDoc(
$scope->getFile(),
$scope->isInClass() ? $scope->getClassReflection()->getName() : null,
$scope->isInTrait() ? $scope->getTraitReflection()->getName() : null,
null,
$phpDoc,
);

$varTags = $resolvedPhpDoc->getVarTags();
$phpDocType = null;
if (isset($varTags[0]) && count($varTags) === 1) {
$phpDocType = $varTags[0]->getType();
} elseif (isset($varTags[$propertyName])) {
$phpDocType = $varTags[$propertyName]->getType();
}

if (!$propertyReflection->hasPhpDocType()) {
if ($phpDocType === null) {
return [];
}

$phpDocType = $propertyReflection->getPhpDocType();
$description = 'PHPDoc tag @var';
if ($propertyReflection->isPromoted()) {
if ($node->isPromoted()) {
$description = 'PHPDoc type';
}

Expand All @@ -59,18 +81,18 @@ public function processNode(Node $node, Scope $scope): array
$messages[] = RuleErrorBuilder::message(sprintf(
'%s for property %s::$%s contains unresolvable type.',
$description,
$propertyReflection->getDeclaringClass()->getName(),
$scope->getClassReflection()->getDisplayName(),
$propertyName,
))->build();
}

$nativeType = $propertyReflection->getNativeType();
$nativeType = ParserNodeTypeToPHPStanType::resolve($node->getNativeType(), $scope->getClassReflection());
$isSuperType = $nativeType->isSuperTypeOf($phpDocType);
if ($isSuperType->no()) {
$messages[] = RuleErrorBuilder::message(sprintf(
'%s for property %s::$%s with type %s is incompatible with native type %s.',
$description,
$propertyReflection->getDeclaringClass()->getDisplayName(),
$scope->getClassReflection()->getDisplayName(),
$propertyName,
$phpDocType->describe(VerbosityLevel::typeOnly()),
$nativeType->describe(VerbosityLevel::typeOnly()),
Expand All @@ -80,7 +102,7 @@ public function processNode(Node $node, Scope $scope): array
$errorBuilder = RuleErrorBuilder::message(sprintf(
'%s for property %s::$%s with type %s is not subtype of native type %s.',
$description,
$propertyReflection->getDeclaringClass()->getDisplayName(),
$scope->getClassReflection()->getDisplayName(),
$propertyName,
$phpDocType->describe(VerbosityLevel::typeOnly()),
$nativeType->describe(VerbosityLevel::typeOnly()),
Expand All @@ -93,7 +115,7 @@ public function processNode(Node $node, Scope $scope): array
$messages[] = $errorBuilder->build();
}

$className = SprintfHelper::escapeFormatString($propertyReflection->getDeclaringClass()->getDisplayName());
$className = SprintfHelper::escapeFormatString($scope->getClassReflection()->getDisplayName());
$escapedPropertyName = SprintfHelper::escapeFormatString($propertyName);

$messages = array_merge($messages, $this->genericObjectTypeCheck->check(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use PHPStan\Rules\Generics\GenericObjectTypeCheck;
use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;
use PHPStan\Type\FileTypeMapper;

/**
* @extends RuleTestCase<IncompatiblePropertyPhpDocTypeRule>
Expand All @@ -14,7 +15,11 @@ class IncompatiblePropertyPhpDocTypeRuleTest extends RuleTestCase

protected function getRule(): Rule
{
return new IncompatiblePropertyPhpDocTypeRule(new GenericObjectTypeCheck(), new UnresolvableTypeHelper());
return new IncompatiblePropertyPhpDocTypeRule(
new GenericObjectTypeCheck(),
new UnresolvableTypeHelper(),
self::getContainer()->getByType(FileTypeMapper::class),
);
}

public function testRule(): void
Expand Down

0 comments on commit 32b3c67

Please sign in to comment.