mirror of
https://github.com/danog/psalm.git
synced 2025-01-22 05:41:20 +01:00
Fix #5640 - improve handling of assignments in conditional
This commit is contained in:
parent
bb0bfda7c5
commit
642f2f435c
@ -147,12 +147,24 @@ class IfAnalyzer
|
||||
if (!$has_break_statement) {
|
||||
$if_scope->reasonable_clauses = [];
|
||||
|
||||
// If we're assigning inside
|
||||
if ($if_conditional_scope->assigned_in_conditional_var_ids
|
||||
&& $if_scope->post_leaving_if_context
|
||||
) {
|
||||
self::addConditionallyAssignedVarsToContext(
|
||||
$statements_analyzer,
|
||||
$stmt->cond,
|
||||
$if_scope->post_leaving_if_context,
|
||||
$outer_context,
|
||||
$if_conditional_scope->assigned_in_conditional_var_ids
|
||||
);
|
||||
}
|
||||
|
||||
if (!$stmt->else && !$stmt->elseifs) {
|
||||
$mic_drop = self::handleMicDrop(
|
||||
$statements_analyzer,
|
||||
$stmt->cond,
|
||||
$if_scope,
|
||||
$if_conditional_scope,
|
||||
$outer_context,
|
||||
$new_assigned_var_ids
|
||||
);
|
||||
@ -246,23 +258,9 @@ class IfAnalyzer
|
||||
StatementsAnalyzer $statements_analyzer,
|
||||
PhpParser\Node\Expr $cond,
|
||||
IfScope $if_scope,
|
||||
IfConditionalScope $if_conditional_scope,
|
||||
Context $post_if_context,
|
||||
array $new_assigned_var_ids
|
||||
) : bool {
|
||||
// If we're assigning inside
|
||||
if ($if_conditional_scope->assigned_in_conditional_var_ids
|
||||
&& $if_scope->post_leaving_if_context
|
||||
) {
|
||||
self::addConditionallyAssignedVarsToContext(
|
||||
$statements_analyzer,
|
||||
$cond,
|
||||
$if_scope->post_leaving_if_context,
|
||||
$post_if_context,
|
||||
$if_conditional_scope->assigned_in_conditional_var_ids
|
||||
);
|
||||
}
|
||||
|
||||
if (!$if_scope->negated_types) {
|
||||
return false;
|
||||
}
|
||||
|
@ -68,10 +68,9 @@ class IfElseAnalyzer
|
||||
$if_scope = new IfScope();
|
||||
|
||||
// We need to clone the original context for later use if we're exiting in this if conditional
|
||||
if (!$stmt->else && !$stmt->elseifs
|
||||
&& ($stmt->cond instanceof PhpParser\Node\Expr\BinaryOp
|
||||
|| ($stmt->cond instanceof PhpParser\Node\Expr\BooleanNot
|
||||
&& $stmt->cond->expr instanceof PhpParser\Node\Expr\BinaryOp))
|
||||
if ($stmt->cond instanceof PhpParser\Node\Expr\BinaryOp
|
||||
|| ($stmt->cond instanceof PhpParser\Node\Expr\BooleanNot
|
||||
&& $stmt->cond->expr instanceof PhpParser\Node\Expr\BinaryOp)
|
||||
) {
|
||||
$final_actions = ScopeAnalyzer::getControlActions(
|
||||
$stmt->stmts,
|
||||
@ -368,7 +367,7 @@ class IfElseAnalyzer
|
||||
}
|
||||
|
||||
// check the else
|
||||
$else_context = clone $post_if_context;
|
||||
$else_context = clone ($if_scope->post_leaving_if_context ?? $post_if_context);
|
||||
|
||||
// check the elseifs
|
||||
foreach ($stmt->elseifs as $elseif) {
|
||||
|
@ -486,6 +486,41 @@ class AssignmentInConditionalTest extends \Psalm\Tests\TestCase
|
||||
}',
|
||||
'error_message' => 'InvalidReturnStatement',
|
||||
],
|
||||
'assignmentInBranchOfAndReferencedAfterIf' => [
|
||||
'<?php
|
||||
function bar(bool $result): bool {
|
||||
if ($result && ($result = rand(0, 1))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}',
|
||||
'error_message' => 'InvalidReturnStatement',
|
||||
],
|
||||
'assignmentInBranchOfAndReferencedInElse' => [
|
||||
'<?php
|
||||
function bar(bool $result): bool {
|
||||
if ($result && ($result = rand(0, 1))) {
|
||||
return true;
|
||||
} else {
|
||||
return $result;
|
||||
}
|
||||
}',
|
||||
'error_message' => 'InvalidReturnStatement',
|
||||
],
|
||||
'assignmentInBranchOfAndReferencedInElseIf' => [
|
||||
'<?php
|
||||
function bar(bool $result): bool {
|
||||
if ($result && ($result = rand(0, 1))) {
|
||||
return true;
|
||||
} elseif (rand(0, 1)) {
|
||||
return $result;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}',
|
||||
'error_message' => 'InvalidReturnStatement',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -2348,6 +2348,23 @@ class UnusedVariableTest extends TestCase
|
||||
}
|
||||
}'
|
||||
],
|
||||
'noUnusedVariableDefinedInBranchOfIf' => [
|
||||
'<?php
|
||||
abstract class Foo {
|
||||
abstract function validate(): bool|string;
|
||||
abstract function save(): bool|string;
|
||||
|
||||
function bar(): int {
|
||||
if (($result = $this->validate()) && ($result = $this->save())) {
|
||||
return 0;
|
||||
} elseif (is_string($result)) {
|
||||
return 1;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
}'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user