Skip to content

Commit 5886053

Browse files
committed
Bleeding edge - detect wrong usage of @var PHPDoc tag
1 parent 4f7ac53 commit 5886053

6 files changed

+80
-6
lines changed

conf/bleedingEdge.neon

+1
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ parameters:
1212
checkLogicalAndConstantCondition: true
1313
checkLogicalOrConstantCondition: true
1414
checkMissingTemplateTypeInParameter: true
15+
wrongVarUsage: true

conf/config.level2.neon

+6-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ rules:
3030
- PHPStan\Rules\PhpDoc\InvalidPhpDocTagValueRule
3131
- PHPStan\Rules\PhpDoc\InvalidPHPStanDocTagRule
3232
- PHPStan\Rules\PhpDoc\InvalidThrowsPhpDocValueRule
33-
- PHPStan\Rules\PhpDoc\WrongVariableNameInVarTagRule
3433

3534
services:
3635
-
@@ -52,3 +51,9 @@ services:
5251
checkMissingVarTagTypehint: %checkMissingVarTagTypehint%
5352
tags:
5453
- phpstan.rules.rule
54+
-
55+
class: PHPStan\Rules\PhpDoc\WrongVariableNameInVarTagRule
56+
arguments:
57+
checkWrongVarUsage: %featureToggles.wrongVarUsage%
58+
tags:
59+
- phpstan.rules.rule

conf/config.neon

+3-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ parameters:
2525
checkLogicalAndConstantCondition: false
2626
checkLogicalOrConstantCondition: false
2727
checkMissingTemplateTypeInParameter: false
28+
wrongVarUsage: false
2829
fileExtensions:
2930
- php
3031
checkAlwaysTrueCheckTypeFunctionCall: false
@@ -176,7 +177,8 @@ parametersSchema:
176177
detectDuplicateStubFiles: bool(),
177178
checkLogicalAndConstantCondition: bool(),
178179
checkLogicalOrConstantCondition: bool(),
179-
checkMissingTemplateTypeInParameter: bool()
180+
checkMissingTemplateTypeInParameter: bool(),
181+
wrongVarUsage: bool()
180182
])
181183
fileExtensions: listOf(string())
182184
checkAlwaysTrueCheckTypeFunctionCall: bool()

src/Rules/PhpDoc/WrongVariableNameInVarTagRule.php

+36-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66
use PhpParser\Node;
77
use PhpParser\Node\Expr;
88
use PHPStan\Analyser\Scope;
9-
use PHPStan\Node\UnreachableStatementNode;
9+
use PHPStan\Node\InClassMethodNode;
10+
use PHPStan\Node\InClassNode;
11+
use PHPStan\Node\InFunctionNode;
12+
use PHPStan\Node\VirtualNode;
1013
use PHPStan\Rules\Rule;
1114
use PHPStan\Rules\RuleErrorBuilder;
1215
use PHPStan\Type\FileTypeMapper;
@@ -19,9 +22,15 @@ class WrongVariableNameInVarTagRule implements Rule
1922

2023
private FileTypeMapper $fileTypeMapper;
2124

22-
public function __construct(FileTypeMapper $fileTypeMapper)
25+
private bool $checkWrongVarUsage;
26+
27+
public function __construct(
28+
FileTypeMapper $fileTypeMapper,
29+
bool $checkWrongVarUsage = false
30+
)
2331
{
2432
$this->fileTypeMapper = $fileTypeMapper;
33+
$this->checkWrongVarUsage = $checkWrongVarUsage;
2534
}
2635

2736
public function getNodeType(): string
@@ -36,7 +45,7 @@ public function processNode(Node $node, Scope $scope): array
3645
|| $node instanceof Node\Stmt\PropertyProperty
3746
|| $node instanceof Node\Stmt\ClassConst
3847
|| $node instanceof Node\Stmt\Const_
39-
|| $node instanceof UnreachableStatementNode
48+
|| ($node instanceof VirtualNode && !$node instanceof InFunctionNode && !$node instanceof InClassMethodNode && !$node instanceof InClassNode)
4049
) {
4150
return [];
4251
}
@@ -83,6 +92,30 @@ public function processNode(Node $node, Scope $scope): array
8392
return $this->processGlobal($scope, $node, $varTags);
8493
}
8594

95+
if ($node instanceof InClassNode || $node instanceof InClassMethodNode || $node instanceof InFunctionNode) {
96+
if ($this->checkWrongVarUsage) {
97+
$description = 'a function';
98+
$originalNode = $node->getOriginalNode();
99+
if ($originalNode instanceof Node\Stmt\Interface_) {
100+
$description = 'an interface';
101+
} elseif ($originalNode instanceof Node\Stmt\Class_) {
102+
$description = 'a class';
103+
} elseif ($originalNode instanceof Node\Stmt\Trait_) {
104+
throw new \PHPStan\ShouldNotHappenException();
105+
} elseif ($originalNode instanceof Node\Stmt\ClassMethod) {
106+
$description = 'a method';
107+
}
108+
return [
109+
RuleErrorBuilder::message(sprintf(
110+
'PHPDoc tag @var above %s has no effect.',
111+
$description
112+
))->build(),
113+
];
114+
}
115+
116+
return [];
117+
}
118+
86119
return $this->processStmt($scope, $varTags, null);
87120
}
88121

tests/PHPStan/Rules/PhpDoc/WrongVariableNameInVarTagRuleTest.php

+14-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ class WrongVariableNameInVarTagRuleTest extends RuleTestCase
1515
protected function getRule(): Rule
1616
{
1717
return new WrongVariableNameInVarTagRule(
18-
self::getContainer()->getByType(FileTypeMapper::class)
18+
self::getContainer()->getByType(FileTypeMapper::class),
19+
true
1920
);
2021
}
2122

@@ -110,6 +111,18 @@ public function testRule(): void
110111
'Variable $slots in PHPDoc tag @var does not match assigned variable $itemSlots.',
111112
280,
112113
],
114+
[
115+
'PHPDoc tag @var above a class has no effect.',
116+
300,
117+
],
118+
[
119+
'PHPDoc tag @var above a method has no effect.',
120+
304,
121+
],
122+
[
123+
'PHPDoc tag @var above a function has no effect.',
124+
312,
125+
],
113126
]);
114127
}
115128

tests/PHPStan/Rules/PhpDoc/data/wrong-variable-name-var.php

+20
Original file line numberDiff line numberDiff line change
@@ -293,3 +293,23 @@ public function doSit(): void
293293
}
294294

295295
}
296+
297+
/**
298+
* @var string
299+
*/
300+
class VarInWrongPlaces
301+
{
302+
303+
/** @var int $a */
304+
public function doFoo($a)
305+
{
306+
307+
}
308+
309+
}
310+
311+
/** @var int */
312+
function doFoo(): void
313+
{
314+
315+
}

0 commit comments

Comments
 (0)