Skip to content

Commit 3255013

Browse files
phpstan-botclaude
andauthored
Fix phpstan/phpstan#14351: Missing errors arround use of $this (#5278)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 6895e54 commit 3255013

10 files changed

+219
-0
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ lint:
135135
--exclude tests/PHPStan/Rules/Functions/data/bug-14241.php \
136136
--exclude tests/PHPStan/Rules/Variables/data/bug-14349.php \
137137
--exclude tests/PHPStan/Rules/Variables/data/bug-14352.php \
138+
--exclude tests/PHPStan/Rules/Variables/data/bug-14351.php \
138139
src tests
139140

140141
install-paratest:

src/Analyser/NodeScopeResolver.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1974,6 +1974,7 @@ public function processStmtNode(
19741974
}
19751975

19761976
$variableName = $catchNode->var->name;
1977+
$this->callNodeCallback($nodeCallback, new VariableAssignNode($catchNode->var, new TypeExpr($catchType)), $scope, $storage);
19771978
}
19781979

19791980
$catchScopeResult = $this->processStmtNodesInternal($catchNode, $catchNode->stmts, $catchScope->enterCatchType($catchType, $variableName), $storage, $nodeCallback, $context);
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Variables;
4+
5+
use PhpParser\Node;
6+
use PhpParser\Node\Expr\Variable;
7+
use PHPStan\Analyser\Scope;
8+
use PHPStan\DependencyInjection\RegisteredRule;
9+
use PHPStan\Rules\Rule;
10+
use PHPStan\Rules\RuleErrorBuilder;
11+
use function is_string;
12+
13+
/**
14+
* @implements Rule<Node\Stmt\Global_>
15+
*/
16+
#[RegisteredRule(level: 0)]
17+
final class ThisInGlobalStatementRule implements Rule
18+
{
19+
20+
public function getNodeType(): string
21+
{
22+
return Node\Stmt\Global_::class;
23+
}
24+
25+
public function processNode(Node $node, Scope $scope): array
26+
{
27+
$errors = [];
28+
foreach ($node->vars as $var) {
29+
if (!$var instanceof Variable) {
30+
continue;
31+
}
32+
if (!is_string($var->name)) {
33+
continue;
34+
}
35+
if ($var->name !== 'this') {
36+
continue;
37+
}
38+
39+
$errors[] = RuleErrorBuilder::message('Cannot use $this as global variable.')
40+
->identifier('global.this')
41+
->nonIgnorable()
42+
->build();
43+
}
44+
45+
return $errors;
46+
}
47+
48+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Variables;
4+
5+
use PhpParser\Node;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\DependencyInjection\RegisteredRule;
8+
use PHPStan\Rules\Rule;
9+
use PHPStan\Rules\RuleErrorBuilder;
10+
use function is_string;
11+
12+
/**
13+
* @implements Rule<Node\Stmt\Static_>
14+
*/
15+
#[RegisteredRule(level: 0)]
16+
final class ThisInStaticStatementRule implements Rule
17+
{
18+
19+
public function getNodeType(): string
20+
{
21+
return Node\Stmt\Static_::class;
22+
}
23+
24+
public function processNode(Node $node, Scope $scope): array
25+
{
26+
$errors = [];
27+
foreach ($node->vars as $var) {
28+
if (!is_string($var->var->name)) {
29+
continue;
30+
}
31+
if ($var->var->name !== 'this') {
32+
continue;
33+
}
34+
35+
$errors[] = RuleErrorBuilder::message('Cannot use $this as static variable.')
36+
->identifier('static.this')
37+
->nonIgnorable()
38+
->build();
39+
}
40+
41+
return $errors;
42+
}
43+
44+
}

tests/PHPStan/Rules/Variables/InvalidVariableAssignRuleTest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,16 @@ public function testBug14352(): void
8888
]);
8989
}
9090

91+
public function testBug14351(): void
92+
{
93+
$this->analyse([__DIR__ . '/data/bug-14351.php'], [
94+
[
95+
'Cannot re-assign $this.',
96+
9,
97+
],
98+
]);
99+
}
100+
91101
public function testBug14349(): void
92102
{
93103
$this->analyse([__DIR__ . '/data/bug-14349.php'], [

tests/PHPStan/Rules/Variables/ParameterOutAssignedTypeRuleTest.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,19 @@ public function testBug14124b(): void
9191
$this->analyse([__DIR__ . '/data/bug-14124b.php'], []);
9292
}
9393

94+
public function testCatchVariable(): void
95+
{
96+
$this->analyse([__DIR__ . '/data/parameter-out-catch-variable.php'], [
97+
[
98+
'Parameter &$p @param-out type of function ParameterOutCatchVariable\foo() expects int, Exception given.',
99+
11,
100+
],
101+
[
102+
'Parameter &$p by-ref type of function ParameterOutCatchVariable\bar() expects int, Exception given.',
103+
19,
104+
'You can change the parameter out type with @param-out PHPDoc tag.',
105+
],
106+
]);
107+
}
108+
94109
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Variables;
4+
5+
use PHPStan\Rules\Rule;
6+
use PHPStan\Testing\RuleTestCase;
7+
8+
/**
9+
* @extends RuleTestCase<ThisInGlobalStatementRule>
10+
*/
11+
class ThisInGlobalStatementRuleTest extends RuleTestCase
12+
{
13+
14+
protected function getRule(): Rule
15+
{
16+
return new ThisInGlobalStatementRule();
17+
}
18+
19+
public function testBug14351(): void
20+
{
21+
$this->analyse([__DIR__ . '/data/bug-14351.php'], [
22+
[
23+
'Cannot use $this as global variable.',
24+
15,
25+
],
26+
]);
27+
}
28+
29+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Variables;
4+
5+
use PHPStan\Rules\Rule;
6+
use PHPStan\Testing\RuleTestCase;
7+
8+
/**
9+
* @extends RuleTestCase<ThisInStaticStatementRule>
10+
*/
11+
class ThisInStaticStatementRuleTest extends RuleTestCase
12+
{
13+
14+
protected function getRule(): Rule
15+
{
16+
return new ThisInStaticStatementRule();
17+
}
18+
19+
public function testBug14351(): void
20+
{
21+
$this->analyse([__DIR__ . '/data/bug-14351.php'], [
22+
[
23+
'Cannot use $this as static variable.',
24+
19,
25+
],
26+
]);
27+
}
28+
29+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug14351;
4+
5+
class C {
6+
function foo(): void {
7+
try {
8+
throw new \Exception();
9+
} catch (\Exception $this) { // should report: Cannot re-assign $this
10+
}
11+
}
12+
}
13+
14+
function foo(): void {
15+
global $this; // should report: Cannot use $this as global variable
16+
}
17+
18+
function bar(): void {
19+
static $this; // should report: Cannot use $this as static variable
20+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace ParameterOutCatchVariable;
4+
5+
/**
6+
* @param-out int $p
7+
*/
8+
function foo(&$p): void {
9+
try {
10+
throw new \Exception();
11+
} catch (\Exception $p) {
12+
13+
}
14+
}
15+
16+
function bar(int &$p): void {
17+
try {
18+
throw new \Exception();
19+
} catch (\Exception $p) {
20+
21+
}
22+
}

0 commit comments

Comments
 (0)