From 3a8efec728fa254c83e1e0211a8ace9c2025aa4a Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Sat, 16 Sep 2017 13:16:21 -0400 Subject: [PATCH] Add error for pass by reference --- config.xsd | 1 + .../Statements/Expression/CallChecker.php | 27 ++++++++++++++++--- src/Psalm/Issue/InvalidPassByReference.php | 6 +++++ tests/FunctionCallTest.php | 18 +++++++++++-- 4 files changed, 46 insertions(+), 6 deletions(-) create mode 100644 src/Psalm/Issue/InvalidPassByReference.php diff --git a/config.xsd b/config.xsd index 409697374..c68b05bb2 100644 --- a/config.xsd +++ b/config.xsd @@ -110,6 +110,7 @@ + diff --git a/src/Psalm/Checker/Statements/Expression/CallChecker.php b/src/Psalm/Checker/Statements/Expression/CallChecker.php index 3a285eb3b..181e919aa 100644 --- a/src/Psalm/Checker/Statements/Expression/CallChecker.php +++ b/src/Psalm/Checker/Statements/Expression/CallChecker.php @@ -23,6 +23,7 @@ use Psalm\Issue\ImplicitToStringCast; use Psalm\Issue\InvalidArgument; use Psalm\Issue\InvalidFunctionCall; use Psalm\Issue\InvalidMethodCall; +use Psalm\Issue\InvalidPassByReference; use Psalm\Issue\InvalidScalarArgument; use Psalm\Issue\InvalidScope; use Psalm\Issue\MixedArgument; @@ -1697,11 +1698,29 @@ class CallChecker } foreach ($args as $argument_offset => $arg) { - if (isset($arg->value->inferredType)) { - $function_param = count($function_params) > $argument_offset - ? $function_params[$argument_offset] - : ($last_param && $last_param->is_variadic ? $last_param : null); + $function_param = count($function_params) > $argument_offset + ? $function_params[$argument_offset] + : ($last_param && $last_param->is_variadic ? $last_param : null); + if ($function_param + && $function_param->by_ref + && !($arg->value instanceof PhpParser\Node\Expr\Variable) + && !($arg->value instanceof PhpParser\Node\Expr\PropertyFetch) + ) { + if (IssueBuffer::accepts( + new InvalidPassByReference( + 'Parameter ' . ($argument_offset + 1) . ' of ' . $method_id . ' expects a variable', + $code_location + ), + $statements_checker->getSuppressedIssues() + )) { + return false; + } + + break; + } + + if (isset($arg->value->inferredType)) { if ($function_param && $function_param->type) { $param_type = clone $function_param->type; diff --git a/src/Psalm/Issue/InvalidPassByReference.php b/src/Psalm/Issue/InvalidPassByReference.php new file mode 100644 index 000000000..bd0ec490e --- /dev/null +++ b/src/Psalm/Issue/InvalidPassByReference.php @@ -0,0 +1,6 @@ + [ + 'foo); + fooFoo($b);', + ], ]; } @@ -359,8 +374,7 @@ class FunctionCallTest extends TestCase function f($p = false) {}', 'error_message' => 'InvalidParamDefault', ], - // Skipped. Does not throw an error. - 'SKIPPED-badByRef' => [ + 'badByRef' => [ '