From 42a8618b10c3f49bea6a60f395b5885ee1d79a7c Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Sat, 29 Jun 2019 21:12:12 +0200 Subject: [PATCH] Detect unknown classes in @var tags (bleeding edge) --- conf/config.level2.neon | 2 + .../PhpDoc/InvalidPhpDocVarTagTypeRule.php | 60 ++++++++++++++++--- .../InvalidPhpDocVarTagTypeRuleTest.php | 15 ++++- .../PhpDoc/data/invalid-var-tag-type.php | 3 + 4 files changed, 72 insertions(+), 8 deletions(-) diff --git a/conf/config.level2.neon b/conf/config.level2.neon index 59133e0394..6ad5a18e15 100644 --- a/conf/config.level2.neon +++ b/conf/config.level2.neon @@ -36,3 +36,5 @@ services: - phpstan.rules.rule - class: PHPStan\Rules\PhpDoc\InvalidPhpDocVarTagTypeRule + arguments: + checkClassCaseSensitivity: %checkClassCaseSensitivity% diff --git a/src/Rules/PhpDoc/InvalidPhpDocVarTagTypeRule.php b/src/Rules/PhpDoc/InvalidPhpDocVarTagTypeRule.php index c6d17ea47a..2d9f90d165 100644 --- a/src/Rules/PhpDoc/InvalidPhpDocVarTagTypeRule.php +++ b/src/Rules/PhpDoc/InvalidPhpDocVarTagTypeRule.php @@ -4,6 +4,9 @@ use PhpParser\Node; use PHPStan\Analyser\Scope; +use PHPStan\Broker\Broker; +use PHPStan\Rules\ClassCaseSensitivityCheck; +use PHPStan\Rules\ClassNameNodePair; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Type\ErrorType; @@ -17,9 +20,26 @@ class InvalidPhpDocVarTagTypeRule implements Rule /** @var FileTypeMapper */ private $fileTypeMapper; - public function __construct(FileTypeMapper $fileTypeMapper) + /** @var \PHPStan\Broker\Broker */ + private $broker; + + /** @var \PHPStan\Rules\ClassCaseSensitivityCheck */ + private $classCaseSensitivityCheck; + + /** @var bool */ + private $checkClassCaseSensitivity; + + public function __construct( + FileTypeMapper $fileTypeMapper, + Broker $broker, + ClassCaseSensitivityCheck $classCaseSensitivityCheck, + bool $checkClassCaseSensitivity + ) { $this->fileTypeMapper = $fileTypeMapper; + $this->broker = $broker; + $this->classCaseSensitivityCheck = $classCaseSensitivityCheck; + $this->checkClassCaseSensitivity = $checkClassCaseSensitivity; } public function getNodeType(): string @@ -58,19 +78,45 @@ public function processNode(Node $node, Scope $scope): array $errors = []; foreach ($resolvedPhpDoc->getVarTags() as $name => $varTag) { $varTagType = $varTag->getType(); + $identifier = 'PHPDoc tag @var'; + if (is_string($name)) { + $identifier .= sprintf(' for variable $%s', $name); + } if ( - !$varTagType instanceof ErrorType - && !$varTagType instanceof NeverType + $varTagType instanceof ErrorType || $varTagType instanceof NeverType ) { + $errors[] = RuleErrorBuilder::message(sprintf('%s contains unresolvable type.', $identifier))->line($docComment->getLine())->build(); continue; } - $identifier = 'PHPDoc tag @var'; - if (is_string($name)) { - $identifier .= sprintf(' for variable $%s', $name); + $referencedClasses = $varTagType->getReferencedClasses(); + foreach ($referencedClasses as $referencedClass) { + if ($this->broker->hasClass($referencedClass)) { + if ($this->broker->getClass($referencedClass)->isTrait()) { + $errors[] = RuleErrorBuilder::message(sprintf( + sprintf('%s has invalid type %%s.', $identifier), + $referencedClass + ))->build(); + } + continue; + } + + $errors[] = RuleErrorBuilder::message(sprintf( + sprintf('%s contains unknown class %%s.', $identifier), + $referencedClass + ))->build(); + } + + if (!$this->checkClassCaseSensitivity) { + continue; } - $errors[] = RuleErrorBuilder::message(sprintf('%s contains unresolvable type.', $identifier))->line($docComment->getLine())->build(); + $errors = array_merge( + $errors, + $this->classCaseSensitivityCheck->checkClassNames(array_map(static function (string $class) use ($node): ClassNameNodePair { + return new ClassNameNodePair($class, $node); + }, $referencedClasses)) + ); } return $errors; diff --git a/tests/PHPStan/Rules/PhpDoc/InvalidPhpDocVarTagTypeRuleTest.php b/tests/PHPStan/Rules/PhpDoc/InvalidPhpDocVarTagTypeRuleTest.php index 606d53e016..3636455623 100644 --- a/tests/PHPStan/Rules/PhpDoc/InvalidPhpDocVarTagTypeRuleTest.php +++ b/tests/PHPStan/Rules/PhpDoc/InvalidPhpDocVarTagTypeRuleTest.php @@ -2,6 +2,7 @@ namespace PHPStan\Rules\PhpDoc; +use PHPStan\Rules\ClassCaseSensitivityCheck; use PHPStan\Rules\Rule; use PHPStan\Testing\RuleTestCase; use PHPStan\Type\FileTypeMapper; @@ -11,8 +12,12 @@ class InvalidPhpDocVarTagTypeRuleTest extends RuleTestCase protected function getRule(): Rule { + $broker = $this->createBroker(); return new InvalidPhpDocVarTagTypeRule( - self::getContainer()->getByType(FileTypeMapper::class) + self::getContainer()->getByType(FileTypeMapper::class), + $broker, + new ClassCaseSensitivityCheck($broker), + true ); } @@ -27,6 +32,10 @@ public function testRule(): void 'PHPDoc tag @var contains unresolvable type.', 16, ], + [ + 'PHPDoc tag @var for variable $test contains unknown class InvalidVarTagType\aray.', + 20, + ], [ 'PHPDoc tag @var for variable $value contains unresolvable type.', 22, @@ -35,6 +44,10 @@ public function testRule(): void 'PHPDoc tag @var for variable $staticVar contains unresolvable type.', 27, ], + [ + 'Class InvalidVarTagType\Foo referenced with incorrect case: InvalidVarTagType\foo.', + 31, + ], ]); } diff --git a/tests/PHPStan/Rules/PhpDoc/data/invalid-var-tag-type.php b/tests/PHPStan/Rules/PhpDoc/data/invalid-var-tag-type.php index 1f24c4488d..5f9df1a4cd 100644 --- a/tests/PHPStan/Rules/PhpDoc/data/invalid-var-tag-type.php +++ b/tests/PHPStan/Rules/PhpDoc/data/invalid-var-tag-type.php @@ -26,6 +26,9 @@ public function doFoo() /** @var self&\stdClass $staticVar */ static $staticVar = 1; + + /** @var foo $test */ + $test = new self(); } }