Skip to content

Commit b2ebd76

Browse files
committed
[Php71] Skip AssignArrayToStringRector on a variable re-assigned as string
PHPStan 2.2.8 infers a possibly undefined variable as ErrorType, where it used to be a union of its possible types. AssignArrayToStringRector leaned on that union to skip: if (empty($where)) { $where = ''; } else { $where = 'WHERE ' . implode(' AND ', $where); } With ErrorType, neither the isArray() nor the UnionType guard matches, and the empty string turned into an array, breaking the concat that follows. The variable being filled as a string later on is what makes it a string here, so that is now checked directly. An empty string assign does not count, as it is a candidate for the very same re-type.
1 parent 006fdc1 commit b2ebd76

1 file changed

Lines changed: 59 additions & 0 deletions

File tree

‎rules/Php71/Rector/Assign/AssignArrayToStringRector.php‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,61 @@ private function isReAssignedAsArray(Assign $assign, string $variableName, Varia
237237
return false;
238238
}
239239

240+
/**
241+
* The variable is filled as a string later on, so it has to stay a string here as well
242+
*/
243+
private function isReAssignedAsString(
244+
Variable $variable,
245+
Namespace_|FileNode|ClassMethod|Function_|Closure $node
246+
): bool {
247+
if ($node->stmts === null) {
248+
return false;
249+
}
250+
251+
$variableName = $this->getName($variable);
252+
if ($variableName === null) {
253+
return false;
254+
}
255+
256+
$isReAssignedAsString = false;
257+
258+
$this->traverseNodesWithCallable($node->stmts, function (Node $node) use (
259+
$variable,
260+
$variableName,
261+
&$isReAssignedAsString
262+
): ?int {
263+
if (! $node instanceof Assign) {
264+
return null;
265+
}
266+
267+
if (! $node->var instanceof Variable) {
268+
return null;
269+
}
270+
271+
if (! $this->isName($node->var, $variableName)) {
272+
return null;
273+
}
274+
275+
if ($node->var->getStartTokenPos() <= $variable->getStartTokenPos()) {
276+
return null;
277+
}
278+
279+
// an empty string assign is a candidate for the very same re-type, so it is no proof of a string
280+
if ($this->isEmptyString($node->expr)) {
281+
return null;
282+
}
283+
284+
if (! $this->nodeTypeResolver->getNativeType($node->expr)->isString()->yes()) {
285+
return null;
286+
}
287+
288+
$isReAssignedAsString = true;
289+
return NodeVisitor::STOP_TRAVERSAL;
290+
});
291+
292+
return $isReAssignedAsString;
293+
}
294+
240295
private function refactorAssign(
241296
Assign $assign,
242297
Namespace_|FileNode|ClassMethod|Function_|Closure $node
@@ -258,6 +313,10 @@ private function refactorAssign(
258313
return null;
259314
}
260315

316+
if ($this->isReAssignedAsString($assign->var, $node)) {
317+
return null;
318+
}
319+
261320
$variableAssignArrayDimFetches = $this->findSameNamedVariableAssigns($assign->var, $node);
262321

263322
$shouldRetype = false;

0 commit comments

Comments
 (0)