From c45d42dd1d031016668099957d2b77706f208d67 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Thu, 5 Oct 2023 15:43:33 +0200 Subject: [PATCH] Do not influence types of arguments before the function is called --- src/Analyser/NodeScopeResolver.php | 72 ++++++++++++------- .../Variables/DefinedVariableRuleTest.php | 4 ++ 2 files changed, 52 insertions(+), 24 deletions(-) diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index 699b11770c..f2c55160b2 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -3580,43 +3580,27 @@ private function processArgs( $hasYield = false; $throwPoints = []; foreach ($args as $i => $arg) { - $originalArg = $arg->getAttribute(ArgumentsNormalizer::ORIGINAL_ARG_ATTRIBUTE) ?? $arg; - $nodeCallback($originalArg, $scope); + $assignByReference = false; if (isset($parameters) && $parametersAcceptor !== null) { - $byRefType = new MixedType(); - $assignByReference = false; if (isset($parameters[$i])) { $assignByReference = $parameters[$i]->passedByReference()->createsNewVariable(); $parameterType = $parameters[$i]->getType(); - - if (isset($paramOutTypes[$parameters[$i]->getName()])) { - $byRefType = $paramOutTypes[$parameters[$i]->getName()]; - } } elseif (count($parameters) > 0 && $parametersAcceptor->isVariadic()) { $lastParameter = $parameters[count($parameters) - 1]; $assignByReference = $lastParameter->passedByReference()->createsNewVariable(); $parameterType = $lastParameter->getType(); - - if (isset($paramOutTypes[$lastParameter->getName()])) { - $byRefType = $paramOutTypes[$lastParameter->getName()]; - } } + } - if ($assignByReference) { - $argValue = $arg->value; - if ($argValue instanceof Variable && is_string($argValue->name)) { - $scope = $scope->assignVariable($argValue->name, $byRefType, new MixedType()); - } else { - $scope = $scope->invalidateExpression($argValue); - } - } elseif ($calleeReflection !== null && $calleeReflection->hasSideEffects()->yes()) { - $argType = $scope->getType($arg->value); - if (!$argType->isObject()->no() || !(new ResourceType())->isSuperTypeOf($argType)->no()) { - $scope = $scope->invalidateExpression($arg->value, true); - } + if ($assignByReference) { + if ($arg->value instanceof Variable) { + $scope = $this->lookForSetAllowedUndefinedExpressions($scope, $arg->value); } } + $originalArg = $arg->getAttribute(ArgumentsNormalizer::ORIGINAL_ARG_ATTRIBUTE) ?? $arg; + $nodeCallback($originalArg, $scope); + $originalScope = $scope; $scopeToPass = $scope; if ($i === 0 && $closureBindScope !== null) { @@ -3633,6 +3617,11 @@ private function processArgs( $result = $this->processExprNode($arg->value, $scopeToPass, $nodeCallback, $context->enterDeep()); } $scope = $result->getScope(); + if ($assignByReference) { + if ($arg->value instanceof Variable) { + $scope = $this->lookForUnsetAllowedUndefinedExpressions($scope, $arg->value); + } + } $hasYield = $hasYield || $result->hasYield(); $throwPoints = array_merge($throwPoints, $result->getThrowPoints()); if ($i !== 0 || $closureBindScope === null) { @@ -3646,6 +3635,41 @@ private function processArgs( $scope = $scope->popInFunctionCall(); } + foreach ($args as $i => $arg) { + if (!isset($parameters) || $parametersAcceptor === null) { + continue; + } + + $byRefType = new MixedType(); + $assignByReference = false; + if (isset($parameters[$i])) { + $assignByReference = $parameters[$i]->passedByReference()->createsNewVariable(); + if (isset($paramOutTypes[$parameters[$i]->getName()])) { + $byRefType = $paramOutTypes[$parameters[$i]->getName()]; + } + } elseif (count($parameters) > 0 && $parametersAcceptor->isVariadic()) { + $lastParameter = $parameters[count($parameters) - 1]; + $assignByReference = $lastParameter->passedByReference()->createsNewVariable(); + if (isset($paramOutTypes[$lastParameter->getName()])) { + $byRefType = $paramOutTypes[$lastParameter->getName()]; + } + } + + if ($assignByReference) { + $argValue = $arg->value; + if ($argValue instanceof Variable && is_string($argValue->name)) { + $scope = $scope->assignVariable($argValue->name, $byRefType, new MixedType()); + } else { + $scope = $scope->invalidateExpression($argValue); + } + } elseif ($calleeReflection !== null && $calleeReflection->hasSideEffects()->yes()) { + $argType = $scope->getType($arg->value); + if (!$argType->isObject()->no() || !(new ResourceType())->isSuperTypeOf($argType)->no()) { + $scope = $scope->invalidateExpression($arg->value, true); + } + } + } + return new ExpressionResult($scope, $hasYield, $throwPoints); } diff --git a/tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php b/tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php index 9d84ab9035..96a41c3f74 100644 --- a/tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php +++ b/tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php @@ -66,6 +66,10 @@ public function testDefinedVariables(): void 'Undefined variable: $parseStrParameter', 34, ], + [ + 'Undefined variable: $parseStrParameter', + 36, + ], [ 'Undefined variable: $foo', 39,