diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php index e062c2f00ba..19e7fd3fac2 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php @@ -860,6 +860,45 @@ public static function analyzeAssignmentOperation( PhpParser\Node\Expr\AssignOp $stmt, Context $context ) { + $array_var_id = ExpressionAnalyzer::getArrayVarId( + $stmt->var, + $statements_analyzer->getFQCLN(), + $statements_analyzer + ); + + if ($stmt instanceof PhpParser\Node\Expr\AssignOp\Coalesce) { + $old_data_provider = $statements_analyzer->node_data; + + $statements_analyzer->node_data = clone $statements_analyzer->node_data; + + $fake_coalesce_expr = new PhpParser\Node\Expr\BinaryOp\Coalesce( + $stmt->var, + $stmt->expr, + $stmt->getAttributes() + ); + + $fake_coalesce_type = AssignmentAnalyzer::analyze( + $statements_analyzer, + $stmt->var, + $fake_coalesce_expr, + null, + $context, + $stmt->getDocComment() + ); + + $statements_analyzer->node_data = $old_data_provider; + + if ($fake_coalesce_type) { + if ($array_var_id) { + $context->vars_in_scope[$array_var_id] = $fake_coalesce_type; + } + + $statements_analyzer->node_data->setType($stmt, $fake_coalesce_type); + } + + return; + } + $was_in_assignment = $context->inside_assignment; $context->inside_assignment = true; @@ -872,12 +911,6 @@ public static function analyzeAssignmentOperation( return false; } - $array_var_id = ExpressionAnalyzer::getArrayVarId( - $stmt->var, - $statements_analyzer->getFQCLN(), - $statements_analyzer - ); - if ($array_var_id && $context->mutation_free && $stmt->var instanceof PhpParser\Node\Expr\PropertyFetch @@ -1030,38 +1063,6 @@ public static function analyzeAssignmentOperation( ); } - if ($stmt instanceof PhpParser\Node\Expr\AssignOp\Coalesce) { - $old_data_provider = $statements_analyzer->node_data; - - $statements_analyzer->node_data = clone $statements_analyzer->node_data; - - $fake_coalesce_expr = new PhpParser\Node\Expr\BinaryOp\Coalesce( - $stmt->var, - $stmt->expr, - $stmt->getAttributes() - ); - - if (BinaryOpAnalyzer::analyze( - $statements_analyzer, - $fake_coalesce_expr, - $context - ) === false) { - return false; - } - - $fake_coalesce_type = $statements_analyzer->node_data->getType($fake_coalesce_expr); - - $statements_analyzer->node_data = $old_data_provider; - - if ($fake_coalesce_type) { - if ($array_var_id) { - $context->vars_in_scope[$array_var_id] = $fake_coalesce_type; - } - - $statements_analyzer->node_data->setType($stmt, $fake_coalesce_type); - } - } - if (!$was_in_assignment) { $context->inside_assignment = false; } diff --git a/tests/BinaryOperationTest.php b/tests/BinaryOperationTest.php index ccce99c9751..987331404e9 100644 --- a/tests/BinaryOperationTest.php +++ b/tests/BinaryOperationTest.php @@ -229,6 +229,19 @@ function foo(?string $s): string { return $s; }' ], + 'nullCoalescingArrayAssignment' => [ + ' $arr + */ + function foo(array $arr) : void { + $b = []; + + foreach ($arr as $a) { + $b[0] ??= $a; + } + }' + ], ]; }