1
0
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:
Saif Eddin Gmati 2021-03-25 14:05:59 +01:00 committed by GitHub
parent b627962e63
commit 9f74676524
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 220 additions and 0 deletions

View File

@ -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;
}
} }

View File

@ -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;
}
} }

View File

@ -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',
],
]; ];
} }
} }