Skip to content

Commit

Permalink
Detect unknown classes in @var tags (bleeding edge)
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Jun 29, 2019
1 parent f151ad9 commit 42a8618
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 8 deletions.
2 changes: 2 additions & 0 deletions conf/config.level2.neon
Expand Up @@ -36,3 +36,5 @@ services:
- phpstan.rules.rule
-
class: PHPStan\Rules\PhpDoc\InvalidPhpDocVarTagTypeRule
arguments:
checkClassCaseSensitivity: %checkClassCaseSensitivity%
60 changes: 53 additions & 7 deletions src/Rules/PhpDoc/InvalidPhpDocVarTagTypeRule.php
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand Down
15 changes: 14 additions & 1 deletion tests/PHPStan/Rules/PhpDoc/InvalidPhpDocVarTagTypeRuleTest.php
Expand Up @@ -2,6 +2,7 @@

namespace PHPStan\Rules\PhpDoc;

use PHPStan\Rules\ClassCaseSensitivityCheck;
use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;
use PHPStan\Type\FileTypeMapper;
Expand All @@ -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
);
}

Expand All @@ -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,
Expand All @@ -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,
],
]);
}

Expand Down
3 changes: 3 additions & 0 deletions tests/PHPStan/Rules/PhpDoc/data/invalid-var-tag-type.php
Expand Up @@ -26,6 +26,9 @@ public function doFoo()

/** @var self&\stdClass $staticVar */
static $staticVar = 1;

/** @var foo $test */
$test = new self();
}

}

0 comments on commit 42a8618

Please sign in to comment.