Skip to content

Commit b4ac49d

Browse files
committed
Support CallLike with variable method name
1 parent 9d19cd0 commit b4ac49d

File tree

6 files changed

+151
-6
lines changed

6 files changed

+151
-6
lines changed

src/Rules/Methods/CallMethodsRule.php

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
use PHPStan\Internal\SprintfHelper;
99
use PHPStan\Reflection\ParametersAcceptorSelector;
1010
use PHPStan\Rules\FunctionCallParametersCheck;
11+
use PHPStan\Rules\IdentifierRuleError;
1112
use PHPStan\Rules\Rule;
13+
use PHPStan\Type\Constant\ConstantStringType;
14+
use function array_map;
1215
use function array_merge;
1316

1417
/**
@@ -31,12 +34,33 @@ public function getNodeType(): string
3134

3235
public function processNode(Node $node, Scope $scope): array
3336
{
34-
if (!$node->name instanceof Node\Identifier) {
35-
return [];
37+
if ($node->name instanceof Node\Identifier) {
38+
$methodNames = [$node->name->name];
39+
} else {
40+
$methodNames = array_map(
41+
static fn (ConstantStringType $constantString): string => $constantString->getValue(),
42+
$scope->getType($node->name)->getConstantStrings(),
43+
);
44+
if ($methodNames === []) {
45+
return [];
46+
}
3647
}
3748

38-
$methodName = $node->name->name;
49+
foreach ($methodNames as $methodName) {
50+
$errors = $this->processMethod($methodName, $node, $scope);
51+
if ($errors !== []) {
52+
return $errors;
53+
}
54+
}
55+
56+
return [];
57+
}
3958

59+
/**
60+
* @return list<IdentifierRuleError>
61+
*/
62+
private function processMethod(string $methodName, MethodCall $node, Scope $scope): array
63+
{
4064
[$errors, $methodReflection] = $this->methodCallCheck->check($scope, $methodName, $node->var);
4165
if ($methodReflection === null) {
4266
return $errors;

src/Rules/Methods/CallStaticMethodsRule.php

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
use PHPStan\Internal\SprintfHelper;
99
use PHPStan\Reflection\ParametersAcceptorSelector;
1010
use PHPStan\Rules\FunctionCallParametersCheck;
11+
use PHPStan\Rules\IdentifierRuleError;
1112
use PHPStan\Rules\Rule;
13+
use PHPStan\Type\Constant\ConstantStringType;
14+
use function array_map;
1215
use function array_merge;
1316
use function sprintf;
1417

@@ -32,11 +35,33 @@ public function getNodeType(): string
3235

3336
public function processNode(Node $node, Scope $scope): array
3437
{
35-
if (!$node->name instanceof Node\Identifier) {
36-
return [];
38+
if ($node->name instanceof Node\Identifier) {
39+
$methodNames = [$node->name->name];
40+
} else {
41+
$methodNames = array_map(
42+
static fn (ConstantStringType $constantString): string => $constantString->getValue(),
43+
$scope->getType($node->name)->getConstantStrings(),
44+
);
45+
if ($methodNames === []) {
46+
return [];
47+
}
3748
}
38-
$methodName = $node->name->name;
3949

50+
foreach ($methodNames as $methodName) {
51+
$errors = $this->processMethod($methodName, $node, $scope);
52+
if ($errors !== []) {
53+
return $errors;
54+
}
55+
}
56+
57+
return [];
58+
}
59+
60+
/**
61+
* @return list<IdentifierRuleError>
62+
*/
63+
private function processMethod(string $methodName, StaticCall $node, Scope $scope): array
64+
{
4065
[$errors, $method] = $this->methodCallCheck->check($scope, $methodName, $node->class);
4166
if ($method === null) {
4267
return $errors;

tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3412,4 +3412,26 @@ public function testBug1953(): void
34123412
]);
34133413
}
34143414

3415+
public function testBug2920(): void
3416+
{
3417+
$this->checkThisOnly = false;
3418+
$this->checkNullables = true;
3419+
$this->checkUnionTypes = true;
3420+
$this->checkExplicitMixed = true;
3421+
$this->analyse([__DIR__ . '/data/bug-2920.php'], [
3422+
[
3423+
'Call to an undefined method Bug2920MethodCall\HelloWorld::a().',
3424+
13,
3425+
],
3426+
[
3427+
'Parameter #1 $s of method Bug2920MethodCall\HelloWorld::b() expects string, int given.',
3428+
14,
3429+
],
3430+
[
3431+
'Parameter #1 $s of method Bug2920MethodCall\HelloWorld::b() expects string, int given.',
3432+
17,
3433+
],
3434+
]);
3435+
}
3436+
34153437
}

tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -852,4 +852,26 @@ public function testBug10872(): void
852852
$this->analyse([__DIR__ . '/data/bug-10872.php'], []);
853853
}
854854

855+
public function testBug2920(): void
856+
{
857+
$this->checkThisOnly = false;
858+
$this->checkExplicitMixed = true;
859+
$this->checkImplicitMixed = true;
860+
861+
$this->analyse([__DIR__ . '/data/bug-2920b.php'], [
862+
[
863+
'Call to an undefined static method Bug2920bStaticCall\HelloWorld::a().',
864+
13,
865+
],
866+
[
867+
'Parameter #1 $s of static method Bug2920bStaticCall\HelloWorld::b() expects string, int given.',
868+
14,
869+
],
870+
[
871+
'Parameter #1 $s of static method Bug2920bStaticCall\HelloWorld::b() expects string, int given.',
872+
17,
873+
],
874+
]);
875+
}
876+
855877
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace Bug2920MethodCall;
4+
5+
6+
class HelloWorld
7+
{
8+
public function sayHello(): void
9+
{
10+
$a = 'a';
11+
$b = 'b';
12+
13+
$this->$a();
14+
$this->$b(1);
15+
$this->$b("1");
16+
17+
$this->b(1);
18+
$this->b("1");
19+
}
20+
21+
public function b(string $s): void
22+
{
23+
}
24+
25+
}
26+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace Bug2920bStaticCall;
4+
5+
6+
class HelloWorld
7+
{
8+
public function sayHello(): void
9+
{
10+
$a = 'a';
11+
$b = 'b';
12+
13+
self::$a();
14+
self::$b(1);
15+
self::$b("1");
16+
17+
self::b(1);
18+
self::b("1");
19+
}
20+
21+
static public function b(string $s): void
22+
{
23+
}
24+
25+
}
26+

0 commit comments

Comments
 (0)