mirror of
https://github.com/danog/psalm.git
synced 2025-01-22 05:41:20 +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 &&
|
if (!$context->inside_assignment &&
|
||||||
!$context->inside_call &&
|
!$context->inside_call &&
|
||||||
!$context->inside_use &&
|
!$context->inside_use &&
|
||||||
|
!self::callUsesByReferenceArguments($function_call_info, $stmt) &&
|
||||||
!(
|
!(
|
||||||
$function_call_info->function_storage &&
|
$function_call_info->function_storage &&
|
||||||
$function_call_info->function_storage->return_type &&
|
$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
|
* @var array
|
||||||
*/
|
*/
|
||||||
public $byref_uses = [];
|
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);
|
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',
|
'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