1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-22 05:41:20 +01:00

Fix issue evaluating elseif empty assertions

This commit is contained in:
Matthew Brown 2017-03-14 15:48:52 -04:00
parent 9dd94d099f
commit 6cfe540c98
4 changed files with 94 additions and 19 deletions

View File

@ -440,6 +440,8 @@ class IfChecker
$elseif_context->vars_in_scope = $elseif_vars_reconciled;
}
$pre_conditional_context = clone $elseif_context;
$elseif_context->inside_conditional = true;
// check the elseif
@ -591,25 +593,43 @@ class IfChecker
}
if ($negated_elseif_types) {
$negated_keys = [];
if ($has_leaving_statements) {
$changed_vars = [];
foreach ($negated_elseif_types as $var_id => $type) {
if (!$has_leaving_statements ||
$type !== '!empty' ||
!isset($elseif_context->vars_in_scope[$var_id]) ||
$elseif_context->vars_in_scope[$var_id]->hasObjectType()
) {
$negated_keys[] = $var_id;
$leaving_vars_reconciled = TypeChecker::reconcileKeyedTypes(
$negated_elseif_types,
$pre_conditional_context->vars_in_scope,
$changed_vars,
$statements_checker->getFileChecker(),
new CodeLocation($statements_checker->getSource(), $elseif),
$statements_checker->getSuppressedIssues()
);
if ($leaving_vars_reconciled === false) {
return false;
}
}
$outer_context->update(
$old_elseif_context,
$elseif_context,
$has_leaving_statements,
$negated_keys,
$if_scope->updated_vars
);
$implied_outer_context = clone $elseif_context;
$implied_outer_context->vars_in_scope = $leaving_vars_reconciled;
$outer_context->update(
$elseif_context,
$implied_outer_context,
false,
array_keys($negated_elseif_types),
$if_scope->updated_vars
);
} else {
$negated_keys = [];
$outer_context->update(
$old_elseif_context,
$elseif_context,
false,
array_keys($negated_elseif_types),
$if_scope->updated_vars
);
}
}
if (!$has_ending_statements) {

View File

@ -339,7 +339,7 @@ class StatementsChecker extends SourceChecker implements StatementsSource
Context $outer_context
) {
// record all the vars that existed before we did the first pass through the loop
$pre_loop_vars_in_scope = $loop_context->vars_in_scope;
$pre_loop_context = clone $loop_context;
IssueBuffer::startRecording();
$this->analyze($stmts, $loop_context, $outer_context);
@ -352,10 +352,10 @@ class StatementsChecker extends SourceChecker implements StatementsSource
// widen the foreach context type with the initial context type
foreach ($loop_context->vars_in_scope as $var_id => $type) {
if (isset($pre_loop_vars_in_scope[$var_id])) {
if (isset($pre_loop_context->vars_in_scope[$var_id])) {
$loop_context->vars_in_scope[$var_id] = Type::combineUnionTypes(
$type,
$pre_loop_vars_in_scope[$var_id]
$pre_loop_context->vars_in_scope[$var_id]
);
if (isset($outer_context->vars_in_scope[$var_id])) {

View File

@ -441,6 +441,37 @@ class LoopScopeTest extends PHPUnit_Framework_TestCase
$file_checker->visitAndAnalyzeMethods();
}
/**
* @return void
*/
public function testSecondLoopWithReturnInElseif()
{
$stmts = self::$parser->parse('<?php
class A {}
class B extends A {}
class C extends A {}
$b = null;
foreach ([new A, new A] as $a) {
if ($a instanceof B) {
} elseif (!$a instanceof C) {
return "goodbye";
}
if ($b instanceof C) {
return "hello";
}
$b = $a;
}
');
$file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts);
$file_checker->visitAndAnalyzeMethods();
}
/**
* @return void
*/

View File

@ -883,6 +883,30 @@ class TypeReconciliationTest extends PHPUnit_Framework_TestCase
$file_checker->visitAndAnalyzeMethods();
}
/**
* @return void
*/
public function testEmptyExceptionReconciliationAfterIf()
{
$stmts = self::$parser->parse('<?php
/**
* @param Exception|null $a
*/
function foo($a) : string {
if ($a && $a->getMessage() === "hello") {
return "hello";
} elseif (empty($a)) {
return "goodbye";
}
return $a->getMessage();
}
');
$file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts);
$file_checker->visitAndAnalyzeMethods();
}
/**
* @return void
*/