Skip to content

Commit 322de33

Browse files
authored
Fix param castable to string rule
1 parent 7d9800c commit 322de33

21 files changed

+940
-375
lines changed

Diff for: conf/config.level5.neon

+10-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ conditionalTags:
1212
phpstan.rules.rule: %featureToggles.arrayValues%
1313
PHPStan\Rules\Functions\CallUserFuncRule:
1414
phpstan.rules.rule: %featureToggles.callUserFunc%
15-
PHPStan\Rules\Functions\ParameterCastableToStringFunctionRule:
15+
PHPStan\Rules\Functions\ParameterCastableToStringRule:
16+
phpstan.rules.rule: %featureToggles.checkParameterCastableToStringFunctions%
17+
PHPStan\Rules\Functions\ImplodeParameterCastableToStringRule:
18+
phpstan.rules.rule: %featureToggles.checkParameterCastableToStringFunctions%
19+
PHPStan\Rules\Functions\SortParameterCastableToStringRule:
1620
phpstan.rules.rule: %featureToggles.checkParameterCastableToStringFunctions%
1721

1822
rules:
@@ -45,4 +49,8 @@ services:
4549
tags:
4650
- phpstan.rules.rule
4751
-
48-
class: PHPStan\Rules\Functions\ParameterCastableToStringFunctionRule
52+
class: PHPStan\Rules\Functions\ParameterCastableToStringRule
53+
-
54+
class: PHPStan\Rules\Functions\ImplodeParameterCastableToStringRule
55+
-
56+
class: PHPStan\Rules\Functions\SortParameterCastableToStringRule

Diff for: conf/config.neon

+2
Original file line numberDiff line numberDiff line change
@@ -948,6 +948,8 @@ services:
948948

949949
-
950950
class: PHPStan\Rules\FunctionReturnTypeCheck
951+
-
952+
class: PHPStan\Rules\ParameterCastableToStringCheck
951953

952954
-
953955
class: PHPStan\Rules\Generics\CrossCheckInterfacesHelper

Diff for: phpstan-baseline.neon

+2-2
Original file line numberDiff line numberDiff line change
@@ -1823,15 +1823,15 @@ parameters:
18231823
-
18241824
message: """
18251825
#^Instantiation of deprecated class PHPStan\\\\Rules\\\\Functions\\\\ImplodeFunctionRule\\:
1826-
Replaced by PHPStan\\\\Rules\\\\Functions\\\\ParameterCastableToStringFunctionRule$#
1826+
Replaced by PHPStan\\\\Rules\\\\Functions\\\\ImplodeParameterCastableToStringRuleTest$#
18271827
"""
18281828
count: 1
18291829
path: tests/PHPStan/Rules/Functions/ImplodeFunctionRuleTest.php
18301830

18311831
-
18321832
message: """
18331833
#^Return type of method PHPStan\\\\Rules\\\\Functions\\\\ImplodeFunctionRuleTest\\:\\:getRule\\(\\) has typehint with deprecated class PHPStan\\\\Rules\\\\Functions\\\\ImplodeFunctionRule\\:
1834-
Replaced by PHPStan\\\\Rules\\\\Functions\\\\ParameterCastableToStringFunctionRule$#
1834+
Replaced by PHPStan\\\\Rules\\\\Functions\\\\ImplodeParameterCastableToStringRuleTest$#
18351835
"""
18361836
count: 1
18371837
path: tests/PHPStan/Rules/Functions/ImplodeFunctionRuleTest.php

Diff for: src/Rules/Functions/ImplodeFunctionRule.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
use function sprintf;
1818

1919
/**
20-
* @deprecated Replaced by PHPStan\Rules\Functions\ParameterCastableToStringFunctionRule
20+
* @deprecated Replaced by PHPStan\Rules\Functions\ImplodeParameterCastableToStringRuleTest
2121
* @implements Rule<Node\Expr\FuncCall>
2222
*/
2323
class ImplodeFunctionRule implements Rule
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Functions;
4+
5+
use PhpParser\Node;
6+
use PhpParser\Node\Expr\FuncCall;
7+
use PHPStan\Analyser\ArgumentsNormalizer;
8+
use PHPStan\Analyser\Scope;
9+
use PHPStan\Reflection\ParametersAcceptorSelector;
10+
use PHPStan\Reflection\ReflectionProvider;
11+
use PHPStan\Rules\ParameterCastableToStringCheck;
12+
use PHPStan\Rules\Rule;
13+
use PHPStan\Type\Type;
14+
use function array_key_exists;
15+
use function count;
16+
use function in_array;
17+
use function sprintf;
18+
19+
/**
20+
* @implements Rule<Node\Expr\FuncCall>
21+
*/
22+
class ImplodeParameterCastableToStringRule implements Rule
23+
{
24+
25+
public function __construct(
26+
private ReflectionProvider $reflectionProvider,
27+
private ParameterCastableToStringCheck $parameterCastableToStringCheck,
28+
)
29+
{
30+
}
31+
32+
public function getNodeType(): string
33+
{
34+
return FuncCall::class;
35+
}
36+
37+
public function processNode(Node $node, Scope $scope): array
38+
{
39+
if (!($node->name instanceof Node\Name)) {
40+
return [];
41+
}
42+
43+
if (!$this->reflectionProvider->hasFunction($node->name, $scope)) {
44+
return [];
45+
}
46+
47+
$functionReflection = $this->reflectionProvider->getFunction($node->name, $scope);
48+
$functionName = $functionReflection->getName();
49+
if (!in_array($functionName, ['implode', 'join'], true)) {
50+
return [];
51+
}
52+
53+
$origArgs = $node->getArgs();
54+
$parametersAcceptor = ParametersAcceptorSelector::selectFromArgs(
55+
$scope,
56+
$origArgs,
57+
$functionReflection->getVariants(),
58+
$functionReflection->getNamedArgumentsVariants(),
59+
);
60+
61+
$normalizedFuncCall = ArgumentsNormalizer::reorderFuncArguments($parametersAcceptor, $node);
62+
63+
if ($normalizedFuncCall === null) {
64+
return [];
65+
}
66+
67+
$normalizedArgs = $normalizedFuncCall->getArgs();
68+
$errorMessage = 'Parameter %s of function %s expects array<string>, %s given.';
69+
if (count($normalizedArgs) === 1) {
70+
$argsToCheck = [0 => $normalizedArgs[0]];
71+
} elseif (count($normalizedArgs) === 2) {
72+
$argsToCheck = [1 => $normalizedArgs[1]];
73+
} else {
74+
return [];
75+
}
76+
77+
$origNamedArgs = [];
78+
foreach ($origArgs as $arg) {
79+
if ($arg->unpack || $arg->name === null) {
80+
continue;
81+
}
82+
83+
$origNamedArgs[$arg->name->toString()] = $arg;
84+
}
85+
86+
$errors = [];
87+
88+
foreach ($argsToCheck as $argIdx => $arg) {
89+
// implode has weird variants, so $array has to be fixed. It's especially weird with named arguments.
90+
if (array_key_exists('array', $origNamedArgs)) {
91+
$argName = '$array';
92+
} elseif (array_key_exists('separator', $origNamedArgs) && count($origArgs) === 1) {
93+
$argName = '$separator';
94+
} else {
95+
$argName = sprintf('#%d $array', $argIdx + 1);
96+
}
97+
98+
$error = $this->parameterCastableToStringCheck->checkParameter(
99+
$arg,
100+
$scope,
101+
$errorMessage,
102+
static fn (Type $t) => $t->toString(),
103+
$functionName,
104+
$argName,
105+
);
106+
107+
if ($error === null) {
108+
continue;
109+
}
110+
111+
$errors[] = $error;
112+
}
113+
114+
return $errors;
115+
}
116+
117+
}

Diff for: src/Rules/Functions/ParameterCastableToStringFunctionRule.php

-170
This file was deleted.

0 commit comments

Comments
 (0)