diff --git a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php index d5b2a67aa..deb809480 100644 --- a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php @@ -110,6 +110,13 @@ abstract class FunctionLikeAnalyzer extends SourceAnalyzer */ public $inferred_has_mutation = false; + /** + * Holds param nodes for functions with func_get_args calls + * + * @var array + */ + public $param_nodes = []; + /** * @var FunctionLikeStorage */ @@ -1231,6 +1238,10 @@ abstract class FunctionLikeAnalyzer extends SourceAnalyzer ); } + if ($storage->variadic) { + $this->param_nodes += [$param_assignment->id => $param_assignment]; + } + $var_type->parent_nodes += [$param_assignment->id => $param_assignment]; } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php index 2a78bd3ba..f297dc066 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php @@ -285,6 +285,7 @@ class ArgumentAnalyzer if (!$arg_type_param) { $arg_type_param = Type::getMixed(); + $arg_type_param->parent_nodes = $arg_type->parent_nodes; } } @@ -1329,11 +1330,9 @@ class ArgumentAnalyzer $statements_analyzer->control_flow_graph->addSink($sink); } - if ($input_type->parent_nodes) { - foreach ($input_type->parent_nodes as $parent_node) { - $statements_analyzer->control_flow_graph->addNode($method_node); - $statements_analyzer->control_flow_graph->addPath($parent_node, $argument_value_node, 'arg'); - } + foreach ($input_type->parent_nodes as $parent_node) { + $statements_analyzer->control_flow_graph->addNode($method_node); + $statements_analyzer->control_flow_graph->addPath($parent_node, $argument_value_node, 'arg'); } if ($function_param->assert_untainted) { diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php index 06b3df1e5..832705001 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php @@ -1515,6 +1515,22 @@ class FunctionCallAnalyzer extends CallAnalyzer $statements_analyzer->node_data->setType($stmt, $arr_type); } } + } elseif ($function_name->parts === ['func_get_args']) { + $source = $statements_analyzer->getSource(); + + if ($statements_analyzer->control_flow_graph + && $source instanceof \Psalm\Internal\Analyzer\FunctionLikeAnalyzer + ) { + if ($statements_analyzer->control_flow_graph instanceof \Psalm\Internal\Codebase\VariableUseGraph) { + foreach ($source->param_nodes as $param_node) { + $statements_analyzer->control_flow_graph->addPath( + $param_node, + new ControlFlowNode('variable-use', 'variable use', null), + 'variable-use' + ); + } + } + } } elseif (strtolower($function_name->parts[0]) === 'var_dump' || strtolower($function_name->parts[0]) === 'shell_exec') { if (IssueBuffer::accepts( diff --git a/tests/UnusedVariableTest.php b/tests/UnusedVariableTest.php index 4394902e5..c0be5aaf6 100644 --- a/tests/UnusedVariableTest.php +++ b/tests/UnusedVariableTest.php @@ -2174,6 +2174,19 @@ class UnusedVariableTest extends TestCase print_r($source); }' ], + 'implicitSpread' => [ + ' [ + '