Skip to content

Commit

Permalink
OverridingPropertyRule - relax checking PHPDoc types
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Aug 21, 2021
1 parent 9762d37 commit 24f6264
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 7 deletions.
1 change: 1 addition & 0 deletions conf/config.level0.neon
Expand Up @@ -220,6 +220,7 @@ services:
class: PHPStan\Rules\Properties\OverridingPropertyRule
arguments:
checkPhpDocMethodSignatures: %checkPhpDocMethodSignatures%
reportMaybes: %reportMaybesInPropertyPhpDocTypes%

-
class: PHPStan\Rules\Properties\UninitializedPropertyRule
Expand Down
2 changes: 2 additions & 0 deletions conf/config.neon
Expand Up @@ -82,6 +82,7 @@ parameters:
implicitThrows: true
reportMaybes: false
reportMaybesInMethodSignatures: false
reportMaybesInPropertyPhpDocTypes: false
reportStaticMethodSignatures: false
mixinExcludeClasses: []
scanFiles: []
Expand Down Expand Up @@ -267,6 +268,7 @@ parametersSchema:
tipsOfTheDay: bool()
reportMaybes: bool()
reportMaybesInMethodSignatures: bool()
reportMaybesInPropertyPhpDocTypes: bool()
reportStaticMethodSignatures: bool()
parallel: structure([
jobSize: int(),
Expand Down
44 changes: 39 additions & 5 deletions src/Rules/Properties/OverridingPropertyRule.php
Expand Up @@ -20,9 +20,15 @@ class OverridingPropertyRule implements Rule

private bool $checkPhpDocMethodSignatures;

public function __construct(bool $checkPhpDocMethodSignatures)
private bool $reportMaybes;

public function __construct(
bool $checkPhpDocMethodSignatures,
bool $reportMaybes
)
{
$this->checkPhpDocMethodSignatures = $checkPhpDocMethodSignatures;
$this->reportMaybes = $reportMaybes;
}

public function getNodeType(): string
Expand Down Expand Up @@ -152,21 +158,49 @@ public function processNode(Node $node, Scope $scope): array
}

$propertyReflection = $classReflection->getNativeProperty($node->getName());
if ($propertyReflection->getReadableType()->equals($prototype->getReadableType())) {
if ($prototype->getReadableType()->equals($propertyReflection->getReadableType())) {
return $errors;
}

$verbosity = VerbosityLevel::getRecommendedLevelByType($prototype->getReadableType(), $propertyReflection->getReadableType());

$errors[] = RuleErrorBuilder::message(sprintf(
'Type %s of property %s::$%s is not the same as type %s of overridden property %s::$%s.',
$isSuperType = $prototype->getReadableType()->isSuperTypeOf($propertyReflection->getReadableType());
$canBeTurnedOffError = RuleErrorBuilder::message(sprintf(
'PHPDoc type %s of property %s::$%s is not the same as PHPDoc type %s of overridden property %s::$%s.',
$propertyReflection->getReadableType()->describe($verbosity),
$classReflection->getDisplayName(),
$node->getName(),
$prototype->getReadableType()->describe($verbosity),
$prototype->getDeclaringClass()->getDisplayName(),
$node->getName()
))->tip(sprintf(
"You can fix 3rd party PHPDoc types with stub files:\n %s\n This error can be turned off by setting\n %s",
'<fg=cyan>https://phpstan.org/user-guide/stub-files</>',
'<fg=cyan>reportMaybesInPropertyPhpDocTypes: false</> in your <fg=cyan>%configurationFile%</>.'
))->build();
$cannotBeTurnedOffError = RuleErrorBuilder::message(sprintf(
'PHPDoc type %s of property %s::$%s is %s PHPDoc type %s of overridden property %s::$%s.',
$propertyReflection->getReadableType()->describe($verbosity),
$classReflection->getDisplayName(),
$node->getName(),
$this->reportMaybes ? 'not the same as' : 'not covariant with',
$prototype->getReadableType()->describe($verbosity),
$prototype->getDeclaringClass()->getDisplayName(),
$node->getName()
))->tip(sprintf(
"You can fix 3rd party PHPDoc types with stub files:\n %s",
'<fg=cyan>https://phpstan.org/user-guide/stub-files</>'
))->build();
if ($this->reportMaybes) {
if (!$isSuperType->yes()) {
$errors[] = $cannotBeTurnedOffError;
} else {
$errors[] = $canBeTurnedOffError;
}
} else {
if (!$isSuperType->yes()) {
$errors[] = $cannotBeTurnedOffError;
}
}

return $errors;
}
Expand Down
74 changes: 72 additions & 2 deletions tests/PHPStan/Rules/Properties/OverridingPropertyRuleTest.php
Expand Up @@ -11,9 +11,12 @@
class OverridingPropertyRuleTest extends RuleTestCase
{

/** @var bool */
private $reportMaybes;

protected function getRule(): Rule
{
return new OverridingPropertyRule(true);
return new OverridingPropertyRule(true, $this->reportMaybes);
}

public function testRule(): void
Expand All @@ -22,6 +25,7 @@ public function testRule(): void
$this->markTestSkipped('Test requires static reflection.');
}

$this->reportMaybes = true;
$this->analyse([__DIR__ . '/data/overriding-property.php'], [
[
'Static property OverridingProperty\Bar::$protectedFoo overrides non-static property OverridingProperty\Foo::$protectedFoo.',
Expand Down Expand Up @@ -88,10 +92,76 @@ public function testRule(): void
142,
],
[
'Type 4 of property OverridingProperty\Typed2WithPhpDoc::$foo is not the same as type 1|2|3 of overridden property OverridingProperty\TypedWithPhpDoc::$foo.',
'PHPDoc type 4 of property OverridingProperty\Typed2WithPhpDoc::$foo is not the same as PHPDoc type 1|2|3 of overridden property OverridingProperty\TypedWithPhpDoc::$foo.',
158,
sprintf(
"You can fix 3rd party PHPDoc types with stub files:\n %s",
'<fg=cyan>https://phpstan.org/user-guide/stub-files</>'
),
],
]);
}

public function dataRulePHPDocTypes(): array
{
$tip = sprintf(
"You can fix 3rd party PHPDoc types with stub files:\n %s",
'<fg=cyan>https://phpstan.org/user-guide/stub-files</>',
);
$tipWithOption = sprintf(
"You can fix 3rd party PHPDoc types with stub files:\n %s\n This error can be turned off by setting\n %s",
'<fg=cyan>https://phpstan.org/user-guide/stub-files</>',
'<fg=cyan>reportMaybesInPropertyPhpDocTypes: false</> in your <fg=cyan>%configurationFile%</>.'
);

return [
[
false,
[
[
'PHPDoc type array of property OverridingPropertyPhpDoc\Bar::$arrayClassStrings is not covariant with PHPDoc type array<class-string> of overridden property OverridingPropertyPhpDoc\Foo::$arrayClassStrings.',
26,
$tip,
],
[
'PHPDoc type int of property OverridingPropertyPhpDoc\Bar::$string is not covariant with PHPDoc type string of overridden property OverridingPropertyPhpDoc\Foo::$string.',
29,
$tip,
],
],
],
[
true,
[
[
'PHPDoc type array<class-string> of property OverridingPropertyPhpDoc\Bar::$array is not the same as PHPDoc type array of overridden property OverridingPropertyPhpDoc\Foo::$array.',
23,
$tipWithOption,
],
[
'PHPDoc type array of property OverridingPropertyPhpDoc\Bar::$arrayClassStrings is not the same as PHPDoc type array<class-string> of overridden property OverridingPropertyPhpDoc\Foo::$arrayClassStrings.',
26,
$tip,
],
[
'PHPDoc type int of property OverridingPropertyPhpDoc\Bar::$string is not the same as PHPDoc type string of overridden property OverridingPropertyPhpDoc\Foo::$string.',
29,
$tip,
],
],
],
];
}

/**
* @dataProvider dataRulePHPDocTypes
* @param bool $reportMaybes
* @param mixed[] $errors
*/
public function testRulePHPDocTypes(bool $reportMaybes, array $errors): void
{
$this->reportMaybes = $reportMaybes;
$this->analyse([__DIR__ . '/data/overriding-property-phpdoc.php'], $errors);
}

}
31 changes: 31 additions & 0 deletions tests/PHPStan/Rules/Properties/data/overriding-property-phpdoc.php
@@ -0,0 +1,31 @@
<?php

namespace OverridingPropertyPhpDoc;

class Foo
{

/** @var array */
protected $array;

/** @var array<class-string> */
protected $arrayClassStrings;

/** @var string */
protected $string;

}

class Bar extends Foo
{

/** @var array<class-string> */
protected $array;

/** @var array */
protected $arrayClassStrings;

/** @var int */
protected $string;

}

0 comments on commit 24f6264

Please sign in to comment.