diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php index b0b4e1413..4e36c777a 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php @@ -792,17 +792,33 @@ class ArrayAssignmentAnalyzer && ($statements_analyzer->data_flow_graph instanceof VariableUseGraph || !\in_array('TaintedInput', $statements_analyzer->getSuppressedIssues())) ) { - if (!$stmt_type->parent_nodes) { - $var_location = new \Psalm\CodeLocation($statements_analyzer->getSource(), $expr->var); + $var_location = new \Psalm\CodeLocation($statements_analyzer->getSource(), $expr->var); - $parent_node = \Psalm\Internal\DataFlow\DataFlowNode::getForAssignment( - $var_var_id ?: 'assignment', - $var_location + $parent_node = \Psalm\Internal\DataFlow\DataFlowNode::getForAssignment( + $var_var_id ?: 'assignment', + $var_location + ); + + $statements_analyzer->data_flow_graph->addNode($parent_node); + + $old_parent_nodes = $stmt_type->parent_nodes; + + $stmt_type->parent_nodes = [$parent_node->id => $parent_node]; + + foreach ($old_parent_nodes as $old_parent_node) { + $statements_analyzer->data_flow_graph->addPath( + $old_parent_node, + $parent_node, + '=' ); - $statements_analyzer->data_flow_graph->addNode($parent_node); - - $stmt_type->parent_nodes = [$parent_node->id => $parent_node]; + if ($stmt_type->by_ref) { + $statements_analyzer->data_flow_graph->addPath( + $parent_node, + $old_parent_node, + '=' + ); + } } foreach ($stmt_type->parent_nodes as $parent_node) { diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/InstancePropertyAssignmentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/InstancePropertyAssignmentAnalyzer.php index ba9942c01..90ee101a8 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/InstancePropertyAssignmentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/InstancePropertyAssignmentAnalyzer.php @@ -1235,13 +1235,16 @@ class InstancePropertyAssignmentAnalyzer return; } - $code_location = new CodeLocation($statements_analyzer->getSource(), $stmt); + $var_property_id = ExpressionIdentifier::getArrayVarId( + $stmt, + null, + $statements_analyzer + ); - $localized_property_node = new DataFlowNode( - $property_id . '-' . $code_location->file_name . ':' . $code_location->raw_file_start, - $property_id, - $code_location, - null + $localized_property_node = DataFlowNode::getForAssignment( + $var_property_id + ?: $property_id . '-' . $property_location->file_name . ':' . $property_location->raw_file_start, + $property_location ); $data_flow_graph->addNode($localized_property_node); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php index 3ac817033..4d6316d7b 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php @@ -1267,7 +1267,7 @@ class ArgumentAnalyzer $function_param->location ); - if (strpos($cased_method_id, '::')) { + if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph && strpos($cased_method_id, '::')) { [$fq_classlike_name, $cased_method_name] = explode('::', $cased_method_id); $method_name = strtolower($cased_method_name); $class_storage = $codebase->classlike_storage_provider->get($fq_classlike_name); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php index 349a49560..8f7556d20 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php @@ -1700,6 +1700,8 @@ class FunctionCallAnalyzer extends CallAnalyzer ]) ); } + + return; } if ($function_name->parts === ['method_exists']) { @@ -1714,7 +1716,11 @@ class FunctionCallAnalyzer extends CallAnalyzer } else { $context->check_methods = false; } - } elseif ($function_name->parts === ['class_exists']) { + + return; + } + + if ($function_name->parts === ['class_exists']) { if ($first_arg) { if ($first_arg->value instanceof PhpParser\Node\Scalar\String_) { if (!$codebase->classlikes->classExists($first_arg->value->value)) { @@ -1732,7 +1738,11 @@ class FunctionCallAnalyzer extends CallAnalyzer } } } - } elseif ($function_name->parts === ['interface_exists']) { + + return; + } + + if ($function_name->parts === ['interface_exists']) { if ($first_arg) { if ($first_arg->value instanceof PhpParser\Node\Scalar\String_) { $context->phantom_classes[strtolower($first_arg->value->value)] = true; @@ -1748,13 +1758,21 @@ class FunctionCallAnalyzer extends CallAnalyzer } } } - } elseif ($function_name->parts === ['file_exists'] && $first_arg) { + + return; + } + + if ($function_name->parts === ['file_exists'] && $first_arg) { $var_id = ExpressionIdentifier::getArrayVarId($first_arg->value, null); if ($var_id) { $context->phantom_files[$var_id] = true; } - } elseif ($function_name->parts === ['extension_loaded']) { + + return; + } + + if ($function_name->parts === ['extension_loaded']) { if ($first_arg && $first_arg->value instanceof PhpParser\Node\Scalar\String_ ) { @@ -1764,14 +1782,27 @@ class FunctionCallAnalyzer extends CallAnalyzer $context->check_classes = false; } } - } elseif ($function_name->parts === ['function_exists']) { + + return; + } + + if ($function_name->parts === ['function_exists']) { $context->check_functions = false; - } elseif ($function_name->parts === ['is_callable']) { + return; + } + + if ($function_name->parts === ['is_callable']) { $context->check_methods = false; $context->check_functions = false; - } elseif ($function_name->parts === ['defined']) { + return; + } + + if ($function_name->parts === ['defined']) { $context->check_consts = false; - } elseif ($function_name->parts === ['extract']) { + return; + } + + if ($function_name->parts === ['extract']) { $context->check_variables = false; foreach ($context->vars_in_scope as $var_id => $_) { @@ -1786,7 +1817,11 @@ class FunctionCallAnalyzer extends CallAnalyzer $context->assigned_var_ids[$var_id] = (int) $stmt->getAttribute('startFilePos'); $context->possibly_assigned_var_ids[$var_id] = true; } - } elseif ($function_name->parts === ['compact']) { + + return; + } + + if ($function_name->parts === ['compact']) { $all_args_string_literals = true; $new_items = []; @@ -1823,7 +1858,11 @@ class FunctionCallAnalyzer extends CallAnalyzer $statements_analyzer->node_data->setType($stmt, $arr_type); } } - } elseif ($function_name->parts === ['func_get_args']) { + + return; + } + + if ($function_name->parts === ['func_get_args']) { $source = $statements_analyzer->getSource(); if ($statements_analyzer->data_flow_graph @@ -1839,7 +1878,11 @@ class FunctionCallAnalyzer extends CallAnalyzer } } } - } elseif (strtolower($function_name->parts[0]) === 'var_dump' + + return; + } + + if (strtolower($function_name->parts[0]) === 'var_dump' || strtolower($function_name->parts[0]) === 'shell_exec') { if (IssueBuffer::accepts( new ForbiddenCode( @@ -1850,7 +1893,9 @@ class FunctionCallAnalyzer extends CallAnalyzer )) { // continue } - } elseif (isset($codebase->config->forbidden_functions[strtolower((string) $function_name)])) { + } + + if (isset($codebase->config->forbidden_functions[strtolower((string) $function_name)])) { if (IssueBuffer::accepts( new ForbiddenCode( 'You have forbidden the use of ' . $function_name, @@ -1860,7 +1905,11 @@ class FunctionCallAnalyzer extends CallAnalyzer )) { // continue } - } elseif ($function_name->parts === ['define']) { + + return; + } + + if ($function_name->parts === ['define']) { if ($first_arg) { $fq_const_name = ConstFetchAnalyzer::getConstName( $first_arg->value, @@ -1886,7 +1935,11 @@ class FunctionCallAnalyzer extends CallAnalyzer } else { $context->check_consts = false; } - } elseif ($function_name->parts === ['constant']) { + + return; + } + + if ($function_name->parts === ['constant']) { if ($first_arg) { $fq_const_name = ConstFetchAnalyzer::getConstName( $first_arg->value, @@ -1910,7 +1963,9 @@ class FunctionCallAnalyzer extends CallAnalyzer } else { $context->check_consts = false; } - } elseif ($first_arg + } + + if ($first_arg && $function_id && strpos($function_id, 'is_') === 0 && $function_id !== 'is_a' @@ -1951,7 +2006,11 @@ class FunctionCallAnalyzer extends CallAnalyzer new CodeLocation($statements_analyzer->getSource(), $stmt) ); } - } elseif ($first_arg && $function_id === 'strtolower') { + + return; + } + + if ($first_arg && $function_id === 'strtolower') { $first_arg_type = $statements_analyzer->node_data->getType($first_arg->value); if ($first_arg_type diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php index 2badfdd80..e45fe6099 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php @@ -840,13 +840,16 @@ class AtomicPropertyFetchAnalyzer $type->parent_nodes = [$property_node->id => $property_node]; } } else { - $code_location = new CodeLocation($statements_analyzer, $stmt->name); + $var_property_id = ExpressionIdentifier::getArrayVarId( + $stmt, + null, + $statements_analyzer + ); - $localized_property_node = new DataFlowNode( - $property_id . '-' . $code_location->file_name . ':' . $code_location->raw_file_start, - $property_id, - $code_location, - null + $localized_property_node = DataFlowNode::getForAssignment( + $var_property_id + ?: $property_id . '-' . $property_location->file_name . ':' . $property_location->raw_file_start, + $property_location ); $data_flow_graph->addNode($localized_property_node); @@ -866,7 +869,7 @@ class AtomicPropertyFetchAnalyzer $data_flow_graph->addPath($property_node, $localized_property_node, 'property-fetch'); } - $type->parent_nodes[$localized_property_node->id] = $localized_property_node; + $type->parent_nodes = [$localized_property_node->id => $localized_property_node]; } }