mirror of
https://github.com/danog/psalm.git
synced 2025-01-21 21:31:13 +01:00
allow dismissing return value of pure functions with by-reference arguments (#5463)
This commit is contained in:
parent
b627962e63
commit
9f74676524
@ -1011,6 +1011,7 @@ class FunctionCallAnalyzer extends CallAnalyzer
|
||||
if (!$context->inside_assignment &&
|
||||
!$context->inside_call &&
|
||||
!$context->inside_use &&
|
||||
!self::callUsesByReferenceArguments($function_call_info, $stmt) &&
|
||||
!(
|
||||
$function_call_info->function_storage &&
|
||||
$function_call_info->function_storage->return_type &&
|
||||
@ -1034,4 +1035,43 @@ class FunctionCallAnalyzer extends CallAnalyzer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function callUsesByReferenceArguments(
|
||||
FunctionCallInfo $function_call_info,
|
||||
PhpParser\Node\Expr\FuncCall $stmt
|
||||
): bool {
|
||||
// If the function doesn't have any by-reference parameters
|
||||
// we shouldn't look any further.
|
||||
if (!$function_call_info->hasByReferenceParameters() || null === $function_call_info->function_params) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$parameters = $function_call_info->function_params;
|
||||
|
||||
// If no arguments were passed
|
||||
if (0 === \count($stmt->args)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($stmt->args as $index => $argument) {
|
||||
$parameter = null;
|
||||
if (null !== $argument->name) {
|
||||
$argument_name = $argument->name->toString();
|
||||
foreach ($parameters as $param) {
|
||||
if ($param->name === $argument_name) {
|
||||
$parameter = $param;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$parameter = $parameters[$index] ?? null;
|
||||
}
|
||||
|
||||
if ($parameter && $parameter->by_ref) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -63,4 +63,22 @@ class FunctionCallInfo
|
||||
* @var array
|
||||
*/
|
||||
public $byref_uses = [];
|
||||
|
||||
/**
|
||||
* @mutation-free
|
||||
*/
|
||||
public function hasByReferenceParameters(): bool
|
||||
{
|
||||
if (null === $this->function_params) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($this->function_params as $value) {
|
||||
if ($value->by_ref) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -779,6 +779,120 @@ class UnusedCodeTest extends TestCase
|
||||
return strrev($string);
|
||||
}'
|
||||
],
|
||||
'unusedByReferenceFunctionCall' => [
|
||||
'<?php
|
||||
/**
|
||||
* @pure
|
||||
*/
|
||||
function bar(string &$str): string
|
||||
{
|
||||
$str .= "foo";
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* @pure
|
||||
*/
|
||||
function baz(): string
|
||||
{
|
||||
$f = "foo";
|
||||
bar($f);
|
||||
|
||||
return $f;
|
||||
}'
|
||||
],
|
||||
'unusedVoidByReferenceFunctionCall' => [
|
||||
'<?php
|
||||
/**
|
||||
* @pure
|
||||
*/
|
||||
function bar(string &$str): void
|
||||
{
|
||||
$str .= "foo";
|
||||
}
|
||||
|
||||
/**
|
||||
* @pure
|
||||
*/
|
||||
function baz(): string
|
||||
{
|
||||
$f = "foo";
|
||||
bar($f);
|
||||
|
||||
return $f;
|
||||
}'
|
||||
],
|
||||
'unusedNamedByReferenceFunctionCall' => [
|
||||
'<?php
|
||||
/**
|
||||
* @pure
|
||||
*/
|
||||
function bar(string $c = "", string &$str = ""): string
|
||||
{
|
||||
$c .= $str;
|
||||
$str .= $c;
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @pure
|
||||
*/
|
||||
function baz(): string
|
||||
{
|
||||
$f = "foo";
|
||||
bar(str: $f);
|
||||
|
||||
return $f;
|
||||
}'
|
||||
],
|
||||
'unusedNamedByReferenceFunctionCallV2' => [
|
||||
'<?php
|
||||
/**
|
||||
* @pure
|
||||
*/
|
||||
function bar(string &$st, string &$str = ""): string
|
||||
{
|
||||
$st .= $str;
|
||||
|
||||
return $st;
|
||||
}
|
||||
|
||||
/**
|
||||
* @pure
|
||||
*/
|
||||
function baz(): string
|
||||
{
|
||||
$f = "foo";
|
||||
bar(st: $f);
|
||||
|
||||
return $f;
|
||||
}',
|
||||
],
|
||||
'unusedNamedByReferenceFunctionCallV3' => [
|
||||
'<?php
|
||||
/**
|
||||
* @pure
|
||||
*/
|
||||
function bar(string &$st, ?string &$str = ""): string
|
||||
{
|
||||
$st .= (string) $str;
|
||||
|
||||
return $st;
|
||||
}
|
||||
|
||||
/**
|
||||
* @pure
|
||||
*/
|
||||
function baz(): string
|
||||
{
|
||||
$f = "foo";
|
||||
bar(st: $f, str: $c);
|
||||
|
||||
return $f;
|
||||
}',
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
@ -1093,6 +1207,54 @@ class UnusedCodeTest extends TestCase
|
||||
}',
|
||||
'error_message' => 'UnevaluatedCode',
|
||||
],
|
||||
'UnusedFunctionCallWithOptionalByReferenceParameter' => [
|
||||
'<?php
|
||||
/**
|
||||
* @pure
|
||||
*/
|
||||
function bar(string $c, string &$str = ""): string
|
||||
{
|
||||
$c .= $str;
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @pure
|
||||
*/
|
||||
function baz(): string
|
||||
{
|
||||
$f = "foo";
|
||||
bar($f);
|
||||
|
||||
return $f;
|
||||
}',
|
||||
'error_message' => 'UnusedFunctionCall',
|
||||
],
|
||||
'UnusedFunctionCallWithOptionalByReferenceParameterV2' => [
|
||||
'<?php
|
||||
/**
|
||||
* @pure
|
||||
*/
|
||||
function bar(string $st, string &$str = ""): string
|
||||
{
|
||||
$st .= $str;
|
||||
|
||||
return $st;
|
||||
}
|
||||
|
||||
/**
|
||||
* @pure
|
||||
*/
|
||||
function baz(): string
|
||||
{
|
||||
$f = "foo";
|
||||
bar(st: $f);
|
||||
|
||||
return $f;
|
||||
}',
|
||||
'error_message' => 'UnusedFunctionCall',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user