Skip to content

Commit 2ea33ca

Browse files
phpstan-botgithub-actions[bot]staabm
authored
Fix #9820: Method call phpstan error is reported on wrong line (#4967)
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: Markus Staab <staabm@users.noreply.github.com>
1 parent 40b3f24 commit 2ea33ca

File tree

3 files changed

+81
-8
lines changed

3 files changed

+81
-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: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3853,4 +3853,29 @@ 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+
20,
3865+
],
3866+
[
3867+
'Method Bug9820\HelloWorld::x() invoked with 1 parameter, 0 required.',
3868+
27,
3869+
],
3870+
[
3871+
'Method Bug9820\HelloWorld::x() invoked with 1 parameter, 0 required.',
3872+
33,
3873+
],
3874+
[
3875+
'Method Bug9820\HelloWorld::x() invoked with 1 parameter, 0 required.',
3876+
40,
3877+
],
3878+
]);
3879+
}
3880+
38563881
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php // lint >= 8.0
2+
3+
declare(strict_types = 1);
4+
5+
namespace Bug9820;
6+
7+
class HelloWorld
8+
{
9+
/**
10+
* @return $this
11+
*/
12+
public function x(): static
13+
{
14+
return $this;
15+
}
16+
17+
public function test(): void
18+
{
19+
$this
20+
->x(1);
21+
}
22+
23+
public function test2(): void
24+
{
25+
$this
26+
->x()
27+
->x(1);
28+
}
29+
30+
public function test3(?self $selfOrNull): void
31+
{
32+
$selfOrNull
33+
?->x(1);
34+
}
35+
36+
public function test4(?self $selfOrNull): void
37+
{
38+
$selfOrNull
39+
?->x()
40+
?->x(1);
41+
}
42+
}

0 commit comments

Comments
 (0)