Skip to content
Permalink
Browse files

Fix #2578 - improve inference of functions beginning with assert

  • Loading branch information
muglug committed Jan 9, 2020
1 parent bd9142f commit 2c7197ab4bf1ff897409e894c1d39f2748e4f0ee
Showing with 73 additions and 47 deletions.
  1. +54 −47 src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php
  2. +19 −0 tests/AssertAnnotationTest.php
@@ -440,21 +440,15 @@ private static function scrapeEqualityAssertions(
bool $inside_negation = false,
bool $cache = true
) {
if (!$source instanceof StatementsAnalyzer) {
return [];
}

$if_types = [];

$null_position = self::hasNullVariable($conditional);
$false_position = self::hasFalseVariable($conditional);
$true_position = self::hasTrueVariable($conditional);
$empty_array_position = self::hasEmptyArrayVariable($conditional);
$gettype_position = self::hasGetTypeCheck($conditional);
$getclass_position = self::hasGetClassCheck($conditional, $source);
$min_count = null;
$count_equality_position = self::hasNonEmptyCountEqualityCheck($conditional, $min_count);
$typed_value_position = self::hasTypedValueComparison($conditional, $source);

if ($null_position !== null) {
if ($null_position === self::ASSIGNMENT_TO_RIGHT) {
@@ -475,8 +469,6 @@ private static function scrapeEqualityAssertions(
$var_name = '=' . $var_name;
}

$var_type = $source->node_data->getType($base_conditional);

if ($var_name) {
if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical) {
$if_types[$var_name] = [['null']];
@@ -486,7 +478,8 @@ private static function scrapeEqualityAssertions(
}

if ($codebase
&& $var_type
&& $source instanceof StatementsAnalyzer
&& ($var_type = $source->node_data->getType($base_conditional))
&& $conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical
) {
$null_type = Type::getNull();
@@ -536,8 +529,6 @@ private static function scrapeEqualityAssertions(
throw new \UnexpectedValueException('Unrecognised position');
}

$var_type = $source->node_data->getType($base_conditional);

if ($base_conditional instanceof PhpParser\Node\Expr\FuncCall) {
$if_types = self::processFunctionCall(
$base_conditional,
@@ -561,7 +552,7 @@ private static function scrapeEqualityAssertions(
} else {
$base_assertions = null;

if ($cache) {
if ($source instanceof StatementsAnalyzer && $cache) {
$base_assertions = $source->node_data->getAssertions($base_conditional);
}

@@ -575,7 +566,7 @@ private static function scrapeEqualityAssertions(
$cache
);

if ($cache) {
if ($source instanceof StatementsAnalyzer && $cache) {
$source->node_data->setAssertions($base_conditional, $base_assertions);
}
}
@@ -588,7 +579,10 @@ private static function scrapeEqualityAssertions(
}
}

if ($codebase && $var_type) {
if ($codebase
&& $source instanceof StatementsAnalyzer
&& ($var_type = $source->node_data->getType($base_conditional))
) {
if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical) {
$true_type = Type::getTrue();

@@ -638,8 +632,6 @@ private static function scrapeEqualityAssertions(
throw new \UnexpectedValueException('$false_position value');
}

$var_type = $source->node_data->getType($base_conditional);

if ($base_conditional instanceof PhpParser\Node\Expr\FuncCall) {
$if_types = self::processFunctionCall(
$base_conditional,
@@ -660,10 +652,10 @@ private static function scrapeEqualityAssertions(
} else {
$if_types[$var_name] = [['falsy']];
}
} elseif ($var_type) {
} else {
$base_assertions = null;

if ($cache) {
if ($source instanceof StatementsAnalyzer && $cache) {
$base_assertions = $source->node_data->getAssertions($base_conditional);
}

@@ -677,7 +669,7 @@ private static function scrapeEqualityAssertions(
$cache
);

if ($cache) {
if ($source instanceof StatementsAnalyzer && $cache) {
$source->node_data->setAssertions($base_conditional, $base_assertions);
}
}
@@ -694,7 +686,10 @@ private static function scrapeEqualityAssertions(
}
}

if ($codebase && $var_type) {
if ($codebase
&& $source instanceof StatementsAnalyzer
&& ($var_type = $source->node_data->getType($base_conditional))
) {
if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical) {
$false_type = Type::getFalse();

@@ -750,8 +745,6 @@ private static function scrapeEqualityAssertions(
$source
);

$var_type = $source->node_data->getType($base_conditional);

if ($var_name) {
if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical) {
$if_types[$var_name] = [['!non-empty-countable']];
@@ -761,7 +754,8 @@ private static function scrapeEqualityAssertions(
}

if ($codebase
&& $var_type
&& $source instanceof StatementsAnalyzer
&& ($var_type = $source->node_data->getType($base_conditional))
&& $conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical
) {
$null_type = Type::getEmptyArray();
@@ -868,6 +862,12 @@ private static function scrapeEqualityAssertions(
return $if_types;
}

if (!$source instanceof StatementsAnalyzer) {
return [];
}

$getclass_position = self::hasGetClassCheck($conditional, $source);

if ($getclass_position) {
if ($getclass_position === self::ASSIGNMENT_TO_RIGHT) {
$whichclass_expr = $conditional->left;
@@ -934,6 +934,8 @@ private static function scrapeEqualityAssertions(
return $if_types;
}

$typed_value_position = self::hasTypedValueComparison($conditional, $source);

if ($typed_value_position) {
if ($typed_value_position === self::ASSIGNMENT_TO_RIGHT) {
/** @var PhpParser\Node\Expr $conditional->right */
@@ -1059,19 +1061,13 @@ private static function scrapeInequalityAssertions(
bool $inside_negation = false,
bool $cache = true
) {
if (!$source instanceof StatementsAnalyzer) {
return [];
}

$if_types = [];

$null_position = self::hasNullVariable($conditional);
$false_position = self::hasFalseVariable($conditional);
$true_position = self::hasTrueVariable($conditional);
$empty_array_position = self::hasEmptyArrayVariable($conditional);
$gettype_position = self::hasGetTypeCheck($conditional);
$getclass_position = self::hasGetClassCheck($conditional, $source);
$typed_value_position = self::hasTypedValueComparison($conditional, $source);

if ($null_position !== null) {
if ($null_position === self::ASSIGNMENT_TO_RIGHT) {
@@ -1082,8 +1078,6 @@ private static function scrapeInequalityAssertions(
throw new \UnexpectedValueException('Bad null variable position');
}

$var_type = $source->node_data->getType($base_conditional);

$var_name = ExpressionAnalyzer::getArrayVarId(
$base_conditional,
$this_class_name,
@@ -1102,7 +1096,10 @@ private static function scrapeInequalityAssertions(
}
}

if ($codebase && $var_type) {
if ($codebase
&& $source instanceof StatementsAnalyzer
&& ($var_type = $source->node_data->getType($base_conditional))
) {
if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical) {
$null_type = Type::getNull();

@@ -1158,18 +1155,16 @@ private static function scrapeInequalityAssertions(
$source
);

$var_type = $source->node_data->getType($base_conditional);

if ($var_name) {
if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical) {
$if_types[$var_name] = [['!false']];
} else {
$if_types[$var_name] = [['!falsy']];
}
} elseif ($var_type) {
} else {
$base_assertions = null;

if ($cache) {
if ($source instanceof StatementsAnalyzer && $cache) {
$base_assertions = $source->node_data->getAssertions($base_conditional);
}

@@ -1183,7 +1178,7 @@ private static function scrapeInequalityAssertions(
$cache
);

if ($cache) {
if ($source instanceof StatementsAnalyzer && $cache) {
$source->node_data->setAssertions($base_conditional, $base_assertions);
}
}
@@ -1199,7 +1194,10 @@ private static function scrapeInequalityAssertions(
}
}

if ($codebase && $var_type) {
if ($codebase
&& $source instanceof StatementsAnalyzer
&& ($var_type = $source->node_data->getType($base_conditional))
) {
if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical) {
$false_type = Type::getFalse();

@@ -1249,8 +1247,6 @@ private static function scrapeInequalityAssertions(
throw new \UnexpectedValueException('Bad null variable position');
}

$var_type = $source->node_data->getType($base_conditional);

if ($base_conditional instanceof PhpParser\Node\Expr\FuncCall) {
$if_types = self::processFunctionCall(
$base_conditional,
@@ -1271,10 +1267,10 @@ private static function scrapeInequalityAssertions(
} else {
$if_types[$var_name] = [['falsy']];
}
} elseif ($var_type) {
} else {
$base_assertions = null;

if ($cache) {
if ($source instanceof StatementsAnalyzer && $cache) {
$base_assertions = $source->node_data->getAssertions($base_conditional);
}

@@ -1288,7 +1284,7 @@ private static function scrapeInequalityAssertions(
$cache
);

if ($cache) {
if ($source instanceof StatementsAnalyzer && $cache) {
$source->node_data->setAssertions($base_conditional, $base_assertions);
}
}
@@ -1305,7 +1301,10 @@ private static function scrapeInequalityAssertions(
}
}

if ($codebase && $var_type) {
if ($codebase
&& $source instanceof StatementsAnalyzer
&& ($var_type = $source->node_data->getType($base_conditional))
) {
if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical) {
$true_type = Type::getTrue();

@@ -1355,8 +1354,6 @@ private static function scrapeInequalityAssertions(
throw new \UnexpectedValueException('Bad empty array variable position');
}

$var_type = $source->node_data->getType($base_conditional);

$var_name = ExpressionAnalyzer::getArrayVarId(
$base_conditional,
$this_class_name,
@@ -1371,7 +1368,10 @@ private static function scrapeInequalityAssertions(
}
}

if ($codebase && $var_type) {
if ($codebase
&& $source instanceof StatementsAnalyzer
&& ($var_type = $source->node_data->getType($base_conditional))
) {
if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical) {
$empty_array_type = Type::getEmptyArray();

@@ -1461,6 +1461,13 @@ private static function scrapeInequalityAssertions(
return $if_types;
}

if (!$source instanceof StatementsAnalyzer) {
return [];
}

$getclass_position = self::hasGetClassCheck($conditional, $source);
$typed_value_position = self::hasTypedValueComparison($conditional, $source);

if ($getclass_position) {
if ($getclass_position === self::ASSIGNMENT_TO_RIGHT) {
$whichclass_expr = $conditional->left;
@@ -32,6 +32,25 @@ function takesA(A $a): void {
$a->foo();
}',
],
'implicitAssertEqualsNull' => [
'<?php
function takesInt(int $int): void { echo $int; }
function getIntOrNull(): ?int {
return rand(0,1) === 0 ? null : 1;
}
/** @param mixed $value */
function assertNotNull($value): void {
if (null === $value) {
throw new Exception();
}
}
$value = getIntOrNull();
assertNotNull($value);
takesInt($value);',
],
'dropInReplacementForAssert' => [
'<?php
namespace Bar;

0 comments on commit 2c7197a

Please sign in to comment.
You can’t perform that action at this time.