Skip to content

Commit 9a3c88c

Browse files
committed
Fix method call error reported on wrong line for chained calls
- FunctionCallParametersCheck used $funcCall->getStartLine() which returns the line of the entire expression (e.g. $this), not the method name line - For MethodCall, StaticCall, and FuncCall, now use $funcCall->name->getStartLine() to report errors on the line where the actual method/function name appears - New regression test in tests/PHPStan/Rules/Methods/data/bug-9820.php Closes phpstan/phpstan#9820
1 parent 106fc93 commit 9a3c88c

File tree

3 files changed

+58
-8
lines changed

3 files changed

+58
-8
lines changed

src/Rules/FunctionCallParametersCheck.php

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ public function check(
8989
string $namedArgumentMessage,
9090
): array
9191
{
92+
if ($funcCall instanceof Node\Expr\MethodCall || $funcCall instanceof Node\Expr\StaticCall || $funcCall instanceof Node\Expr\FuncCall) {
93+
$funcCallLine = $funcCall->name->getStartLine();
94+
} else {
95+
$funcCallLine = $funcCall->getStartLine();
96+
}
97+
9298
$functionParametersMinCount = 0;
9399
$functionParametersMaxCount = 0;
94100
foreach ($parametersAcceptor->getParameters() as $parameter) {
@@ -225,7 +231,7 @@ public function check(
225231
if ($hasNamedArguments && !$scope->getPhpVersion()->supportsNamedArguments()->yes() && !(bool) $funcCall->getAttribute('isAttribute', false)) {
226232
$errors[] = RuleErrorBuilder::message('Named arguments are supported only on PHP 8.0 and later.')
227233
->identifier('argument.namedNotSupported')
228-
->line($funcCall->getStartLine())
234+
->line($funcCallLine)
229235
->nonIgnorable()
230236
->build();
231237
}
@@ -250,7 +256,7 @@ public function check(
250256
$functionParametersMinCount,
251257
))
252258
->identifier('arguments.count')
253-
->line($funcCall->getStartLine())
259+
->line($funcCallLine)
254260
->build();
255261
} elseif ($functionParametersMaxCount === -1 && $invokedParametersCount < $functionParametersMinCount) {
256262
$errors[] = RuleErrorBuilder::message(sprintf(
@@ -259,7 +265,7 @@ public function check(
259265
$functionParametersMinCount,
260266
))
261267
->identifier('arguments.count')
262-
->line($funcCall->getStartLine())
268+
->line($funcCallLine)
263269
->build();
264270
} elseif ($functionParametersMaxCount !== -1) {
265271
$errors[] = RuleErrorBuilder::message(sprintf(
@@ -269,7 +275,7 @@ public function check(
269275
$functionParametersMaxCount,
270276
))
271277
->identifier('arguments.count')
272-
->line($funcCall->getStartLine())
278+
->line($funcCallLine)
273279
->build();
274280
}
275281
}
@@ -282,11 +288,11 @@ public function check(
282288
) {
283289
$errors[] = RuleErrorBuilder::message($voidReturnTypeUsed)
284290
->identifier(sprintf('%s.void', $nodeType))
285-
->line($funcCall->getStartLine())
291+
->line($funcCallLine)
286292
->build();
287293
}
288294

289-
[$addedErrors, $argumentsWithParameters] = $this->processArguments($parametersAcceptor, $funcCall->getStartLine(), $isBuiltin, $arguments, $hasNamedArguments, $missingParameterMessage, $unknownParameterMessage);
295+
[$addedErrors, $argumentsWithParameters] = $this->processArguments($parametersAcceptor, $funcCallLine, $isBuiltin, $arguments, $hasNamedArguments, $missingParameterMessage, $unknownParameterMessage);
290296
foreach ($addedErrors as $error) {
291297
$errors[] = $error;
292298
}
@@ -528,7 +534,7 @@ static function (Type $type, callable $traverse) use (&$returnTemplateTypes): Ty
528534

529535
$errors[] = RuleErrorBuilder::message(sprintf($unresolvableTemplateTypeMessage, $name))
530536
->identifier('argument.templateType')
531-
->line($funcCall->getStartLine())
537+
->line($funcCallLine)
532538
->tip('See: https://phpstan.org/blog/solving-phpstan-error-unable-to-resolve-template-type')
533539
->build();
534540
}
@@ -540,7 +546,7 @@ static function (Type $type, callable $traverse) use (&$returnTemplateTypes): Ty
540546
) {
541547
$errors[] = RuleErrorBuilder::message($unresolvableReturnTypeMessage)
542548
->identifier(sprintf('%s.unresolvableReturnType', $nodeType))
543-
->line($funcCall->getStartLine())
549+
->line($funcCallLine)
544550
->build();
545551
}
546552
}

tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3853,4 +3853,21 @@ public function testBug13805(): void
38533853
$this->analyse([__DIR__ . '/data/bug-13805.php'], []);
38543854
}
38553855

3856+
public function testBug9820(): void
3857+
{
3858+
$this->checkThisOnly = false;
3859+
$this->checkNullables = true;
3860+
$this->checkUnionTypes = true;
3861+
$this->analyse([__DIR__ . '/data/bug-9820.php'], [
3862+
[
3863+
'Method Bug9820\HelloWorld::x() invoked with 1 parameter, 0 required.',
3864+
18,
3865+
],
3866+
[
3867+
'Method Bug9820\HelloWorld::x() invoked with 1 parameter, 0 required.',
3868+
25,
3869+
],
3870+
]);
3871+
}
3872+
38563873
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug9820;
4+
5+
class HelloWorld
6+
{
7+
/**
8+
* @return $this
9+
*/
10+
public function x(): static
11+
{
12+
return $this;
13+
}
14+
15+
public function test(): void
16+
{
17+
$this
18+
->x(1);
19+
}
20+
21+
public function test2(): void
22+
{
23+
$this
24+
->x()
25+
->x(1);
26+
}
27+
}

0 commit comments

Comments
 (0)