Skip to content

Commit fa5f8ec

Browse files
committed
Support @var above global keyword
1 parent e501091 commit fa5f8ec

6 files changed

Lines changed: 209 additions & 0 deletions

File tree

src/Analyser/NodeScopeResolver.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ private function processStmtNode(
343343
} elseif (
344344
!$stmt instanceof Static_
345345
&& !$stmt instanceof Foreach_
346+
&& !$stmt instanceof Node\Stmt\Global_
346347
) {
347348
$scope = $this->processStmtVarAnnotation($scope, $stmt, null);
348349
}
@@ -1152,6 +1153,7 @@ private function processStmtNode(
11521153
}
11531154
} elseif ($stmt instanceof Node\Stmt\Global_) {
11541155
$hasYield = false;
1156+
$vars = [];
11551157
foreach ($stmt->vars as $var) {
11561158
if (!$var instanceof Variable) {
11571159
throw new \PHPStan\ShouldNotHappenException();
@@ -1165,6 +1167,11 @@ private function processStmtNode(
11651167
}
11661168

11671169
$scope = $scope->assignVariable($var->name, new MixedType());
1170+
$vars[] = $var->name;
1171+
}
1172+
$comment = CommentHelper::getDocComment($stmt);
1173+
if ($comment !== null) {
1174+
$scope = $this->processVarAnnotation($scope, $vars, $comment);
11681175
}
11691176
} elseif ($stmt instanceof Static_) {
11701177
$hasYield = false;

src/Rules/PhpDoc/WrongVariableNameInVarTagRule.php

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ public function processNode(Node $node, Scope $scope): array
7373
return $this->processStmt($scope, $varTags, $node->expr);
7474
}
7575

76+
if ($node instanceof Node\Stmt\Global_) {
77+
return $this->processGlobal($scope, $node, $varTags);
78+
}
79+
7680
return $this->processStmt($scope, $varTags, null);
7781
}
7882

@@ -285,4 +289,52 @@ private function processStmt(Scope $scope, array $varTags, ?Expr $defaultExpr):
285289
return $errors;
286290
}
287291

292+
/**
293+
* @param \PHPStan\Analyser\Scope $scope
294+
* @param \PHPStan\PhpDoc\Tag\VarTag[] $varTags
295+
* @return \PHPStan\Rules\RuleError[]
296+
*/
297+
private function processGlobal(Scope $scope, Node\Stmt\Global_ $node, array $varTags): array
298+
{
299+
$variableNames = [];
300+
foreach ($node->vars as $var) {
301+
if (!$var instanceof Expr\Variable) {
302+
continue;
303+
}
304+
if (!is_string($var->name)) {
305+
continue;
306+
}
307+
308+
$variableNames[$var->name] = true;
309+
}
310+
311+
$errors = [];
312+
foreach (array_keys($varTags) as $name) {
313+
if (is_int($name)) {
314+
if (count($variableNames) === 1) {
315+
continue;
316+
}
317+
318+
$errors[] = RuleErrorBuilder::message(
319+
'PHPDoc tag @var above multiple global variables does not specify variable name.'
320+
)->build();
321+
continue;
322+
}
323+
324+
if (isset($variableNames[$name])) {
325+
continue;
326+
}
327+
328+
$errors[] = RuleErrorBuilder::message(sprintf(
329+
'Variable $%s in PHPDoc tag @var does not match any global variable: %s',
330+
$name,
331+
implode(', ', array_map(static function (string $name): string {
332+
return sprintf('$%s', $name);
333+
}, array_keys($variableNames)))
334+
))->build();
335+
}
336+
337+
return $errors;
338+
}
339+
288340
}

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10753,6 +10753,11 @@ public function dataCompact(): array
1075310753
return $this->gatherAssertTypes(__DIR__ . '/data/compact.php');
1075410754
}
1075510755

10756+
public function dataBug4500(): array
10757+
{
10758+
return $this->gatherAssertTypes(__DIR__ . '/data/bug-4500.php');
10759+
}
10760+
1075610761
/**
1075710762
* @param string $file
1075810763
* @return array<string, mixed[]>
@@ -10966,6 +10971,7 @@ private function gatherAssertTypes(string $file): array
1096610971
* @dataProvider dataBug4398
1096710972
* @dataProvider dataBug4415
1096810973
* @dataProvider dataCompact
10974+
* @dataProvider dataBug4500
1096910975
* @param string $assertType
1097010976
* @param string $file
1097110977
* @param mixed ...$args
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
namespace Bug4500TypeInference;
4+
5+
use function PHPStan\Analyser\assertType;
6+
7+
class Foo
8+
{
9+
10+
public function doFirst(): void
11+
{
12+
global $foo;
13+
assertType('mixed', $foo);
14+
}
15+
16+
public function doFoo(): void
17+
{
18+
/** @var int */
19+
global $foo;
20+
assertType('int', $foo);
21+
}
22+
23+
public function doBar(): void
24+
{
25+
/** @var int $foo */
26+
global $foo;
27+
assertType('int', $foo);
28+
}
29+
30+
public function doBaz(): void
31+
{
32+
/** @var int */
33+
global $foo, $bar;
34+
assertType('mixed', $foo);
35+
assertType('mixed', $bar);
36+
}
37+
38+
public function doLorem(): void
39+
{
40+
/** @var int $foo */
41+
global $foo, $bar;
42+
assertType('int', $foo);
43+
assertType('mixed', $bar);
44+
45+
$baz = 'foo';
46+
47+
/** @var int $baz */
48+
global $lorem;
49+
assertType('mixed', $lorem);
50+
assertType('\'foo\'', $baz);
51+
}
52+
53+
public function doIpsum(): void
54+
{
55+
/**
56+
* @var int $foo
57+
* @var string $bar
58+
*/
59+
global $foo, $bar;
60+
61+
assertType('int', $foo);
62+
assertType('string', $bar);
63+
}
64+
65+
public function doDolor(): void
66+
{
67+
/** @var int $baz */
68+
global $lorem;
69+
70+
assertType('mixed', $lorem);
71+
assertType('*ERROR*', $baz);
72+
}
73+
74+
}

tests/PHPStan/Rules/PhpDoc/WrongVariableNameInVarTagRuleTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,22 @@ public function testBug3515(): void
109109
$this->analyse([__DIR__ . '/data/bug-3515.php'], []);
110110
}
111111

112+
public function testBug4500(): void
113+
{
114+
$this->analyse([__DIR__ . '/data/bug-4500.php'], [
115+
[
116+
'PHPDoc tag @var above multiple global variables does not specify variable name.',
117+
23,
118+
],
119+
[
120+
'Variable $baz in PHPDoc tag @var does not match any global variable: $lorem',
121+
43,
122+
],
123+
[
124+
'Variable $baz in PHPDoc tag @var does not match any global variable: $lorem',
125+
49,
126+
],
127+
]);
128+
}
129+
112130
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
namespace Bug4500;
4+
5+
class Foo
6+
{
7+
8+
public function doFoo(): void
9+
{
10+
/** @var int */
11+
global $foo;
12+
}
13+
14+
public function doBar(): void
15+
{
16+
/** @var int $foo */
17+
global $foo;
18+
}
19+
20+
public function doBaz(): void
21+
{
22+
/** @var int */
23+
global $foo, $bar;
24+
}
25+
26+
public function doLorem(): void
27+
{
28+
/** @var int $foo */
29+
global $foo, $bar;
30+
}
31+
32+
public function doIpsum(): void
33+
{
34+
/**
35+
* @var int $foo
36+
* @var string $bar
37+
*/
38+
global $foo, $bar;
39+
40+
$baz = 'foo';
41+
42+
/** @var int $baz */
43+
global $lorem;
44+
}
45+
46+
public function doDolor(): void
47+
{
48+
/** @var int $baz */
49+
global $lorem;
50+
}
51+
52+
}

0 commit comments

Comments
 (0)