Skip to content

Commit

Permalink
Bleeding edge - check invalid PHPDocs in previously unchecked stateme…
Browse files Browse the repository at this point in the history
…nt types
  • Loading branch information
ondrejmirtes committed Mar 9, 2023
1 parent 694f392 commit 9780d35
Show file tree
Hide file tree
Showing 10 changed files with 185 additions and 29 deletions.
1 change: 1 addition & 0 deletions conf/bleedingEdge.neon
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ parameters:
newRuleLevelHelper: true
instanceofType: true
paramOutVariance: true
allInvalidPhpDocs: true
14 changes: 12 additions & 2 deletions conf/config.level2.neon
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ rules:
- PHPStan\Rules\PhpDoc\IncompatibleClassConstantPhpDocTypeRule
- PHPStan\Rules\PhpDoc\IncompatiblePhpDocTypeRule
- PHPStan\Rules\PhpDoc\IncompatiblePropertyPhpDocTypeRule
- PHPStan\Rules\PhpDoc\InvalidPhpDocTagValueRule
- PHPStan\Rules\PhpDoc\InvalidPHPStanDocTagRule
- PHPStan\Rules\PhpDoc\InvalidThrowsPhpDocValueRule
- PHPStan\Rules\Properties\AccessPrivatePropertyThroughStaticRule

Expand Down Expand Up @@ -77,13 +75,25 @@ services:
class: PHPStan\Rules\Methods\IllegalConstructorMethodCallRule
-
class: PHPStan\Rules\Methods\IllegalConstructorStaticCallRule
-
class: PHPStan\Rules\PhpDoc\InvalidPhpDocTagValueRule
arguments:
checkAllInvalidPhpDocs: %featureToggles.allInvalidPhpDocs%
tags:
- phpstan.rules.rule
-
class: PHPStan\Rules\PhpDoc\InvalidPhpDocVarTagTypeRule
arguments:
checkClassCaseSensitivity: %checkClassCaseSensitivity%
checkMissingVarTagTypehint: %checkMissingVarTagTypehint%
tags:
- phpstan.rules.rule
-
class: PHPStan\Rules\PhpDoc\InvalidPHPStanDocTagRule
arguments:
checkAllInvalidPhpDocs: %featureToggles.allInvalidPhpDocs%
tags:
- phpstan.rules.rule
-
class: PHPStan\Rules\PhpDoc\VarTagChangedExpressionTypeRule
-
Expand Down
2 changes: 2 additions & 0 deletions conf/config.neon
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ parameters:
newRuleLevelHelper: false
instanceofType: false
paramOutVariance: false
allInvalidPhpDocs: false
fileExtensions:
- php
checkAdvancedIsset: false
Expand Down Expand Up @@ -291,6 +292,7 @@ parametersSchema:
newRuleLevelHelper: bool()
instanceofType: bool()
paramOutVariance: bool()
allInvalidPhpDocs: bool()
])
fileExtensions: listOf(string())
checkAdvancedIsset: bool()
Expand Down
1 change: 1 addition & 0 deletions src/PhpDoc/StubValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ private function getRuleRegistry(Container $container): RuleRegistry
new InvalidPhpDocTagValueRule(
$container->getByType(Lexer::class),
$container->getByType(PhpDocParser::class),
$container->getParameter('featureToggles')['allInvalidPhpDocs'],
),
new InvalidThrowsPhpDocValueRule($fileTypeMapper),

Expand Down
38 changes: 28 additions & 10 deletions src/Rules/PhpDoc/InvalidPHPStanDocTagRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Node\VirtualNode;
use PHPStan\PhpDocParser\Lexer\Lexer;
use PHPStan\PhpDocParser\Parser\PhpDocParser;
use PHPStan\PhpDocParser\Parser\TokenIterator;
Expand Down Expand Up @@ -53,7 +54,11 @@ class InvalidPHPStanDocTagRule implements Rule
'@phpstan-readonly-allow-private-mutation',
];

public function __construct(private Lexer $phpDocLexer, private PhpDocParser $phpDocParser)
public function __construct(
private Lexer $phpDocLexer,
private PhpDocParser $phpDocParser,
private bool $checkAllInvalidPhpDocs,
)
{
}

Expand All @@ -64,15 +69,28 @@ public function getNodeType(): string

public function processNode(Node $node, Scope $scope): array
{
if (
!$node instanceof Node\Stmt\ClassLike
&& !$node instanceof Node\FunctionLike
&& !$node instanceof Node\Stmt\Foreach_
&& !$node instanceof Node\Stmt\Property
&& !$node instanceof Node\Expr\Assign
&& !$node instanceof Node\Expr\AssignRef
) {
return [];
if (!$this->checkAllInvalidPhpDocs) {
if (
!$node instanceof Node\Stmt\ClassLike
&& !$node instanceof Node\FunctionLike
&& !$node instanceof Node\Stmt\Foreach_
&& !$node instanceof Node\Stmt\Property
&& !$node instanceof Node\Expr\Assign
&& !$node instanceof Node\Expr\AssignRef
) {
return [];
}
} else {
// mirrored with InvalidPhpDocTagValueRule
if ($node instanceof VirtualNode) {
return [];
}
if ($node instanceof Node\Stmt\Expression) {
return [];
}
if ($node instanceof Node\Expr && !$node instanceof Node\Expr\Assign && !$node instanceof Node\Expr\AssignRef) {
return [];
}
}

$docComment = $node->getDocComment();
Expand Down
40 changes: 29 additions & 11 deletions src/Rules/PhpDoc/InvalidPhpDocTagValueRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Node\VirtualNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\InvalidTagValueNode;
use PHPStan\PhpDocParser\Lexer\Lexer;
use PHPStan\PhpDocParser\Parser\PhpDocParser;
Expand All @@ -19,7 +20,11 @@
class InvalidPhpDocTagValueRule implements Rule
{

public function __construct(private Lexer $phpDocLexer, private PhpDocParser $phpDocParser)
public function __construct(
private Lexer $phpDocLexer,
private PhpDocParser $phpDocParser,
private bool $checkAllInvalidPhpDocs,
)
{
}

Expand All @@ -30,16 +35,29 @@ public function getNodeType(): string

public function processNode(Node $node, Scope $scope): array
{
if (
!$node instanceof Node\Stmt\ClassLike
&& !$node instanceof Node\FunctionLike
&& !$node instanceof Node\Stmt\Foreach_
&& !$node instanceof Node\Stmt\Property
&& !$node instanceof Node\Expr\Assign
&& !$node instanceof Node\Expr\AssignRef
&& !$node instanceof Node\Stmt\ClassConst
) {
return [];
if (!$this->checkAllInvalidPhpDocs) {
if (
!$node instanceof Node\Stmt\ClassLike
&& !$node instanceof Node\FunctionLike
&& !$node instanceof Node\Stmt\Foreach_
&& !$node instanceof Node\Stmt\Property
&& !$node instanceof Node\Expr\Assign
&& !$node instanceof Node\Expr\AssignRef
&& !$node instanceof Node\Stmt\ClassConst
) {
return [];
}
} else {
// mirrored with InvalidPHPStanDocTagRule
if ($node instanceof VirtualNode) {
return [];
}
if ($node instanceof Node\Stmt\Expression) {
return [];
}
if ($node instanceof Node\Expr && !$node instanceof Node\Expr\Assign && !$node instanceof Node\Expr\AssignRef) {
return [];
}
}

$docComment = $node->getDocComment();
Expand Down
36 changes: 33 additions & 3 deletions tests/PHPStan/Rules/PhpDoc/InvalidPHPStanDocTagRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,28 @@
use PHPStan\PhpDocParser\Parser\PhpDocParser;
use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;
use function array_merge;

/**
* @extends RuleTestCase<InvalidPHPStanDocTagRule>
*/
class InvalidPHPStanDocTagRuleTest extends RuleTestCase
{

private bool $checkAllInvalidPhpDocs;

protected function getRule(): Rule
{
return new InvalidPHPStanDocTagRule(
self::getContainer()->getByType(Lexer::class),
self::getContainer()->getByType(PhpDocParser::class),
$this->checkAllInvalidPhpDocs,
);
}

public function testRule(): void
public function dataRule(): iterable
{
$this->analyse([__DIR__ . '/data/invalid-phpstan-doc.php'], [
$errors = [
[
'Unknown PHPDoc tag: @phpstan-extens',
7,
Expand All @@ -32,11 +36,37 @@ public function testRule(): void
'Unknown PHPDoc tag: @phpstan-pararm',
14,
],
]);
[
'Unknown PHPDoc tag: @phpstan-varr',
44,
],
];
yield [false, $errors];
yield [true, array_merge($errors, [
[
'Unknown PHPDoc tag: @phpstan-varr',
47,
],
[
'Unknown PHPDoc tag: @phpstan-varr',
57,
],
])];
}

/**
* @dataProvider dataRule
* @param list<array{0: string, 1: int, 2?: string}> $expectedErrors
*/
public function testRule(bool $checkAllInvalidPhpDocs, array $expectedErrors): void
{
$this->checkAllInvalidPhpDocs = $checkAllInvalidPhpDocs;
$this->analyse([__DIR__ . '/data/invalid-phpstan-doc.php'], $expectedErrors);
}

public function testBug8697(): void
{
$this->checkAllInvalidPhpDocs = true;
$this->analyse([__DIR__ . '/data/bug-8697.php'], []);
}

Expand Down
38 changes: 35 additions & 3 deletions tests/PHPStan/Rules/PhpDoc/InvalidPhpDocTagValueRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,28 @@
use PHPStan\PhpDocParser\Parser\PhpDocParser;
use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;
use function array_merge;

/**
* @extends RuleTestCase<InvalidPhpDocTagValueRule>
*/
class InvalidPhpDocTagValueRuleTest extends RuleTestCase
{

private bool $checkAllInvalidPhpDocs;

protected function getRule(): Rule
{
return new InvalidPhpDocTagValueRule(
self::getContainer()->getByType(Lexer::class),
self::getContainer()->getByType(PhpDocParser::class),
$this->checkAllInvalidPhpDocs,
);
}

public function testRule(): void
public function dataRule(): iterable
{
$this->analyse([__DIR__ . '/data/invalid-phpdoc.php'], [
$errors = [
[
'PHPDoc tag @param has invalid value (): Unexpected token "\n * ", expected type at offset 13',
25,
Expand Down Expand Up @@ -84,16 +88,44 @@ public function testRule(): void
'PHPDoc tag @var has invalid value ((Foo|Bar): Unexpected token "*/", expected \')\' at offset 18',
81,
],
]);
[
'PHPDoc tag @var has invalid value ((Foo&): Unexpected token "*/", expected type at offset 15',
89,
],
[
'PHPDoc tag @var has invalid value ((Foo&): Unexpected token "*/", expected type at offset 15',
92,
],
];

yield [false, $errors];
yield [true, array_merge($errors, [
[
'PHPDoc tag @var has invalid value ((Foo&): Unexpected token "*/", expected type at offset 15',
102,
],
])];
}

/**
* @dataProvider dataRule
* @param list<array{0: string, 1: int, 2?: string}> $expectedErrors
*/
public function testRule(bool $checkAllInvalidPhpDocs, array $expectedErrors): void
{
$this->checkAllInvalidPhpDocs = $checkAllInvalidPhpDocs;
$this->analyse([__DIR__ . '/data/invalid-phpdoc.php'], $expectedErrors);
}

public function testBug4731(): void
{
$this->checkAllInvalidPhpDocs = true;
$this->analyse([__DIR__ . '/data/bug-4731.php'], []);
}

public function testBug4731WithoutFirstTag(): void
{
$this->checkAllInvalidPhpDocs = true;
$this->analyse([__DIR__ . '/data/bug-4731-no-first-tag.php'], []);
}

Expand Down
22 changes: 22 additions & 0 deletions tests/PHPStan/Rules/PhpDoc/data/invalid-phpdoc.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,25 @@ class ClassConstant
const FOO = 1;

}

class AboveProperty
{

/** @var (Foo& */
private $foo;

/** @var (Foo& */
private const TEST = 1;

}

class AboveReturn
{

public function doFoo(): string
{
/** @var (Foo& */
return doFoo();
}

}
22 changes: 22 additions & 0 deletions tests/PHPStan/Rules/PhpDoc/data/invalid-phpstan-doc.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,25 @@ function any()

}
}

class AboveProperty
{

/** @phpstan-varr 1 */
private $foo;

/** @phpstan-varr 1 */
private const TEST = 1;

}

class AboveReturn
{

public function doFoo(): string
{
/** @phpstan-varr string */
return doFoo();
}

}

0 comments on commit 9780d35

Please sign in to comment.