From 0f1efa90797438163cc8a5e09a8f32de70424adb Mon Sep 17 00:00:00 2001 From: orklah Date: Wed, 9 Feb 2022 21:15:22 +0100 Subject: [PATCH 1/4] documentation --- .../Internal/Analyzer/Statements/Expression/AssertionFinder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php b/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php index a561525b348..8ed75d22740 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php @@ -1782,7 +1782,7 @@ protected static function hasReconcilableNonEmptyCountEqualityCheck( } /** - * @param Identical|Equal|Smaller|SmallerOrEqual|NotIdentical|NotEqual $conditional + * @param Identical|Equal|NotIdentical|NotEqual $conditional * @return false|int */ protected static function hasTypedValueComparison( From 1c74774c40e430671af04ea90ab5f9b4808c76df Mon Sep 17 00:00:00 2001 From: orklah Date: Wed, 9 Feb 2022 21:20:49 +0100 Subject: [PATCH 2/4] consistency --- .../Statements/Expression/AssertionFinder.php | 169 ++++++++++-------- 1 file changed, 98 insertions(+), 71 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php b/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php index 8ed75d22740..5c3d8ec96ef 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php @@ -378,30 +378,31 @@ private static function scrapeEqualityAssertions( ); } - $true_position = self::hasTrueVariable($conditional); + $false_position = self::hasFalseVariable($conditional); - if ($true_position) { - return self::getTrueEqualityAssertions( + if ($false_position) { + return self::getFalseEqualityAssertions( $conditional, $this_class_name, $source, $codebase, + $false_position, $cache, - $true_position + $inside_conditional ); } - $false_position = self::hasFalseVariable($conditional); + $true_position = self::hasTrueVariable($conditional); - if ($false_position) { - return self::getFalseEqualityAssertions( + if ($true_position) { + return self::getTrueEqualityAssertions( $conditional, $this_class_name, $source, $codebase, + $true_position, $cache, - $inside_conditional, - $false_position + $inside_conditional ); } @@ -439,9 +440,7 @@ private static function scrapeEqualityAssertions( ); } - if (!$source instanceof StatementsAnalyzer) { - return []; - } + $count = null; $count_equality_position = self::hasCountEqualityCheck($conditional, $count); @@ -464,22 +463,24 @@ private static function scrapeEqualityAssertions( $source ); - $var_type = $source->node_data->getType($conditional->left); - $other_type = $source->node_data->getType($conditional->right); + if ($source instanceof StatementsAnalyzer) { + $var_type = $source->node_data->getType($conditional->left); + $other_type = $source->node_data->getType($conditional->right); - if ($codebase - && $other_type - && $var_type - && $conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical - ) { - self::handleParadoxicalAssertions( - $source, - $var_type, - $this_class_name, - $other_type, - $codebase, - $conditional - ); + if ($codebase + && $other_type + && $var_type + && $conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical + ) { + self::handleParadoxicalAssertions( + $source, + $var_type, + $this_class_name, + $other_type, + $codebase, + $conditional + ); + } } if ($var_name) { @@ -493,6 +494,10 @@ private static function scrapeEqualityAssertions( return $if_types ? [$if_types] : []; } + if (!$source instanceof StatementsAnalyzer) { + return []; + } + $getclass_position = self::hasGetClassCheck($conditional, $source); if ($getclass_position) { @@ -601,12 +606,12 @@ private static function scrapeInequalityAssertions( if ($false_position) { return self::getFalseInequalityAssertions( $conditional, - $cache, $this_class_name, $source, - $inside_conditional, $codebase, - $false_position + $false_position, + $cache, + $inside_conditional ); } @@ -614,48 +619,16 @@ private static function scrapeInequalityAssertions( if ($true_position) { return self::getTrueInequalityAssertions( - $true_position, $conditional, $this_class_name, $source, $codebase, + $true_position, $cache, $inside_conditional ); } - $count = null; - $count_inequality_position = self::hasCountEqualityCheck($conditional, $count); - - if ($count_inequality_position) { - $if_types = []; - - if ($count_inequality_position === self::ASSIGNMENT_TO_RIGHT) { - $count_expr = $conditional->left; - } elseif ($count_inequality_position === self::ASSIGNMENT_TO_LEFT) { - $count_expr = $conditional->right; - } else { - throw new UnexpectedValueException('$count_equality_position value'); - } - - /** @var PhpParser\Node\Expr\FuncCall $count_expr */ - $var_name = ExpressionIdentifier::getExtendedVarId( - $count_expr->getArgs()[0]->value, - $this_class_name, - $source - ); - - if ($var_name) { - if ($count > 0) { - $if_types[$var_name] = [[new DoesNotHaveExactCount($count)]]; - } else { - $if_types[$var_name] = [[new NonEmptyCountable(true)]]; - } - } - - return $if_types ? [$if_types] : []; - } - $empty_array_position = self::hasEmptyArrayVariable($conditional); if ($empty_array_position !== null) { @@ -690,6 +663,58 @@ private static function scrapeInequalityAssertions( ); } + $count = null; + $count_inequality_position = self::hasCountEqualityCheck($conditional, $count); + + if ($count_inequality_position) { + $if_types = []; + + if ($count_inequality_position === self::ASSIGNMENT_TO_RIGHT) { + $count_expr = $conditional->left; + } elseif ($count_inequality_position === self::ASSIGNMENT_TO_LEFT) { + $count_expr = $conditional->right; + } else { + throw new UnexpectedValueException('$count_inequality_position value'); + } + + /** @var PhpParser\Node\Expr\FuncCall $count_expr */ + $var_name = ExpressionIdentifier::getExtendedVarId( + $count_expr->getArgs()[0]->value, + $this_class_name, + $source + ); + + if ($source instanceof StatementsAnalyzer) { + $var_type = $source->node_data->getType($conditional->left); + $other_type = $source->node_data->getType($conditional->right); + + if ($codebase + && $other_type + && $var_type + && $conditional instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical + ) { + self::handleParadoxicalAssertions( + $source, + $var_type, + $this_class_name, + $other_type, + $codebase, + $conditional + ); + } + } + + if ($var_name) { + if ($count > 0) { + $if_types[$var_name] = [[new DoesNotHaveExactCount($count)]]; + } else { + $if_types[$var_name] = [[new NonEmptyCountable(true)]]; + } + } + + return $if_types ? [$if_types] : []; + } + if (!$source instanceof StatementsAnalyzer) { return []; } @@ -2118,12 +2143,12 @@ private static function getNullInequalityAssertions( */ private static function getFalseInequalityAssertions( PhpParser\Node\Expr\BinaryOp $conditional, - bool $cache, ?string $this_class_name, FileSource $source, - bool $inside_conditional, ?Codebase $codebase, - int $false_position + int $false_position, + bool $cache, + bool $inside_conditional ): array { $if_types = []; @@ -2235,11 +2260,11 @@ private static function getFalseInequalityAssertions( * @return list>>> */ private static function getTrueInequalityAssertions( - int $true_position, PhpParser\Node\Expr\BinaryOp $conditional, ?string $this_class_name, FileSource $source, ?Codebase $codebase, + int $true_position, bool $cache, bool $inside_conditional ): array { @@ -2811,8 +2836,9 @@ private static function getTrueEqualityAssertions( ?string $this_class_name, FileSource $source, ?Codebase $codebase, + int $true_position, bool $cache, - int $true_position + bool $inside_conditional ): array { $if_types = []; @@ -2861,7 +2887,8 @@ private static function getTrueEqualityAssertions( $source, $codebase, false, - $cache + $cache, + $inside_conditional ); if ($source instanceof StatementsAnalyzer && $cache) { @@ -2935,9 +2962,9 @@ private static function getFalseEqualityAssertions( ?string $this_class_name, FileSource $source, ?Codebase $codebase, + int $false_position, bool $cache, - bool $inside_conditional, - int $false_position + bool $inside_conditional ): array { $if_types = []; From a236aa2a415e9a3a34d4ccb5ce2900b49004b909 Mon Sep 17 00:00:00 2001 From: orklah Date: Wed, 9 Feb 2022 22:44:26 +0100 Subject: [PATCH 3/4] only remove int from array-key if we're dealing with a general Int --- src/Psalm/Internal/Type/NegatedAssertionReconciler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Psalm/Internal/Type/NegatedAssertionReconciler.php b/src/Psalm/Internal/Type/NegatedAssertionReconciler.php index 2a112e94e8d..6b56bb1d2a2 100644 --- a/src/Psalm/Internal/Type/NegatedAssertionReconciler.php +++ b/src/Psalm/Internal/Type/NegatedAssertionReconciler.php @@ -179,7 +179,7 @@ public static function reconcile( clone $iterable->type_params[1], ] )); - } elseif ($assertion_type instanceof TInt + } elseif (get_class($assertion_type) === TInt::class && isset($existing_var_type->getAtomicTypes()['array-key']) ) { $existing_var_type->removeType('array-key'); From 511177556b61d9a98176f434c212f18c15e49dcb Mon Sep 17 00:00:00 2001 From: orklah Date: Wed, 9 Feb 2022 22:46:41 +0100 Subject: [PATCH 4/4] only remove int from array-key if we're dealing with a general Int --- src/Psalm/Internal/Type/NegatedAssertionReconciler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Psalm/Internal/Type/NegatedAssertionReconciler.php b/src/Psalm/Internal/Type/NegatedAssertionReconciler.php index 6b56bb1d2a2..3e500ab1d6b 100644 --- a/src/Psalm/Internal/Type/NegatedAssertionReconciler.php +++ b/src/Psalm/Internal/Type/NegatedAssertionReconciler.php @@ -179,7 +179,7 @@ public static function reconcile( clone $iterable->type_params[1], ] )); - } elseif (get_class($assertion_type) === TInt::class + } elseif ($assertion_type !== null && get_class($assertion_type) === TInt::class && isset($existing_var_type->getAtomicTypes()['array-key']) ) { $existing_var_type->removeType('array-key');