From 497fb7cec8b87a2a18dce09de4e38276f21afc40 Mon Sep 17 00:00:00 2001 From: Matt Brown Date: Sun, 18 Oct 2020 00:39:43 -0400 Subject: [PATCH] Be more explicit about negation --- .../Internal/Type/AssertionReconciler.php | 24 +++++--- .../Type/NegatedAssertionReconciler.php | 10 +++- .../Type/SimpleAssertionReconciler.php | 57 ++++++++++++------- .../Type/SimpleNegatedAssertionReconciler.php | 36 ++++++++---- src/Psalm/Type/Reconciler.php | 36 ++++++++---- tests/TypeReconciliation/ConditionalTest.php | 2 +- 6 files changed, 111 insertions(+), 54 deletions(-) diff --git a/src/Psalm/Internal/Type/AssertionReconciler.php b/src/Psalm/Internal/Type/AssertionReconciler.php index 1eb7123fde9..af74d7833bf 100644 --- a/src/Psalm/Internal/Type/AssertionReconciler.php +++ b/src/Psalm/Internal/Type/AssertionReconciler.php @@ -547,7 +547,8 @@ private static function refine( $old_var_type_string, $key, $assertion, - !$negated, + true, + $negated, $code_location, $suppressed_issues ); @@ -574,7 +575,8 @@ private static function refine( $old_var_type_string, $key, $assertion, - !$negated, + true, + $negated, $code_location, $suppressed_issues ); @@ -1025,7 +1027,8 @@ private static function handleLiteralEquality( $old_var_type_string, $var_id, $assertion, - $negated xor $can_be_equal, + $can_be_equal, + $negated, $code_location, $suppressed_issues ); @@ -1039,6 +1042,7 @@ private static function handleLiteralEquality( $old_var_type_string, $var_id, $assertion, + true, $negated, $code_location, $suppressed_issues @@ -1072,7 +1076,8 @@ private static function handleLiteralEquality( $old_var_type_string, $var_id, $assertion, - $negated xor $can_be_equal, + $can_be_equal, + $negated, $code_location, $suppressed_issues ); @@ -1166,7 +1171,8 @@ private static function handleLiteralEquality( $old_var_type_string, $var_id, $assertion, - $negated xor $can_be_equal, + $can_be_equal, + $negated, $code_location, $suppressed_issues ); @@ -1187,6 +1193,7 @@ private static function handleLiteralEquality( $old_var_type_string, $var_id, $assertion, + false, $negated, $code_location, $suppressed_issues @@ -1228,7 +1235,8 @@ private static function handleLiteralEquality( $old_var_type_string, $var_id, $assertion, - $negated xor $can_be_equal, + $can_be_equal, + $negated, $code_location, $suppressed_issues ); @@ -1242,6 +1250,7 @@ private static function handleLiteralEquality( $old_var_type_string, $var_id, $assertion, + false, $negated, $code_location, $suppressed_issues @@ -1275,7 +1284,8 @@ private static function handleLiteralEquality( $old_var_type_string, $var_id, $assertion, - $negated xor $can_be_equal, + $can_be_equal, + $negated, $code_location, $suppressed_issues ); diff --git a/src/Psalm/Internal/Type/NegatedAssertionReconciler.php b/src/Psalm/Internal/Type/NegatedAssertionReconciler.php index b588c4549db..8b440d30b19 100644 --- a/src/Psalm/Internal/Type/NegatedAssertionReconciler.php +++ b/src/Psalm/Internal/Type/NegatedAssertionReconciler.php @@ -259,7 +259,8 @@ public static function reconcile( $old_var_type_string, $key, '!=' . $assertion, - !$negated, + true, + $negated, $code_location, $suppressed_issues ); @@ -276,6 +277,7 @@ public static function reconcile( $old_var_type_string, $key, '!' . $assertion, + false, $negated, $code_location, $suppressed_issues @@ -381,7 +383,8 @@ private static function handleLiteralNegatedEquality( $old_var_type_string, $key, '!' . $assertion, - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); @@ -400,7 +403,8 @@ private static function handleLiteralNegatedEquality( $old_var_type_string, $key, '!=' . $assertion, - !$negated, + true, + $negated, $code_location, $suppressed_issues ); diff --git a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php index b6ddb244b32..2329d4ed257 100644 --- a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php +++ b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php @@ -494,7 +494,8 @@ private static function reconcileNonEmptyCountable( $old_var_type_string, $key, 'non-empty-countable', - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); @@ -596,7 +597,8 @@ private static function reconcilePositiveNumeric( $old_var_type_string, $key, 'positive-numeric', - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); @@ -700,7 +702,8 @@ private static function reconcileHasMethod( $old_var_type_string, $key, 'object with method ' . $method_name, - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); @@ -791,7 +794,8 @@ private static function reconcileString( $old_var_type_string, $key, 'string', - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); @@ -885,7 +889,8 @@ private static function reconcileInt( $old_var_type_string, $key, 'int', - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); @@ -963,7 +968,8 @@ private static function reconcileBool( $old_var_type_string, $key, 'bool', - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); @@ -1037,7 +1043,8 @@ private static function reconcileScalar( $old_var_type_string, $key, 'scalar', - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); @@ -1128,7 +1135,8 @@ private static function reconcileNumeric( $old_var_type_string, $key, 'numeric', - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); @@ -1212,7 +1220,8 @@ private static function reconcileObject( $old_var_type_string, $key, 'object', - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); @@ -1268,7 +1277,8 @@ private static function reconcileResource( $old_var_type_string, $key, 'resource', - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); @@ -1338,7 +1348,8 @@ private static function reconcileCountable( $old_var_type_string, $key, 'countable', - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); @@ -1397,7 +1408,8 @@ private static function reconcileIterable( $old_var_type_string, $key, 'iterable', - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); @@ -1541,7 +1553,8 @@ private static function reconcileTraversable( $old_var_type_string, $key, 'Traversable', - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); @@ -1613,7 +1626,8 @@ private static function reconcileArray( $old_var_type_string, $key, 'array', - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); @@ -1731,7 +1745,8 @@ private static function reconcileList( $old_var_type_string, $key, 'array', - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); @@ -1801,7 +1816,8 @@ private static function reconcileStringArrayAccess( $old_var_type_string, $key, 'string-array-access', - !$negated, + true, + $negated, $code_location, $suppressed_issues ); @@ -1860,7 +1876,8 @@ private static function reconcileIntArrayAccess( $old_var_type_string, $key, 'int-or-string-array-access', - !$negated, + true, + $negated, $code_location, $suppressed_issues ); @@ -1956,7 +1973,8 @@ private static function reconcileCallable( $old_var_type_string, $key, 'callable', - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); @@ -2269,7 +2287,8 @@ function (Type\Union $t): bool { $old_var_type_string, $key, $assertion, - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); diff --git a/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php b/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php index 78bb4c3a927..90eb36aa236 100644 --- a/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php +++ b/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php @@ -289,7 +289,8 @@ private static function reconcileBool( $old_var_type_string, $key, '!bool', - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); @@ -370,7 +371,8 @@ private static function reconcileNonEmptyCountable( $old_var_type_string, $key, '!non-empty-countable', - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); @@ -426,7 +428,8 @@ private static function reconcileNull( $old_var_type_string, $key, '!null', - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); @@ -491,7 +494,8 @@ private static function reconcileFalse( $old_var_type_string, $key, '!false', - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); @@ -768,7 +772,8 @@ private static function reconcileFalsyOrEmpty( $old_var_type_string, $key, '!' . $assertion, - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); @@ -849,7 +854,8 @@ private static function reconcileScalar( $old_var_type_string, $key, '!scalar', - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); @@ -941,7 +947,8 @@ private static function reconcileObject( $old_var_type_string, $key, '!object', - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); @@ -1030,7 +1037,8 @@ private static function reconcileNumeric( $old_var_type_string, $key, '!numeric', - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); @@ -1125,7 +1133,8 @@ private static function reconcileInt( $old_var_type_string, $key, '!int', - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); @@ -1215,7 +1224,8 @@ private static function reconcileFloat( $old_var_type_string, $key, '!float', - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); @@ -1318,7 +1328,8 @@ private static function reconcileString( $old_var_type_string, $key, '!string', - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); @@ -1418,7 +1429,8 @@ private static function reconcileArray( $old_var_type_string, $key, '!array', - $negated xor !$did_remove_type, + !$did_remove_type, + $negated, $code_location, $suppressed_issues ); diff --git a/src/Psalm/Type/Reconciler.php b/src/Psalm/Type/Reconciler.php index 0b9951958c4..45fb56a96ce 100644 --- a/src/Psalm/Type/Reconciler.php +++ b/src/Psalm/Type/Reconciler.php @@ -801,15 +801,27 @@ protected static function triggerIssueForImpossible( string $key, string $assertion, bool $redundant, + bool $negated, CodeLocation $code_location, array $suppressed_issues ): void { - $never = $assertion[0] === '!'; + $not = $assertion[0] === '!'; - if ($never) { + $safe_assertion = $assertion; + + if ($not) { $assertion = substr($assertion, 1); } + if ($negated) { + $redundant = !$redundant; + $not = !$not; + } + + if ($not) { + $assertion = '!' . $assertion; + } + $existing_var_atomic_types = $existing_var_type->getAtomicTypes(); $from_docblock = $existing_var_type->from_docblock @@ -822,9 +834,9 @@ protected static function triggerIssueForImpossible( new RedundantConditionGivenDocblockType( 'Docblock-defined type ' . $old_var_type_string . ' for ' . $key - . ' is ' . ($never ? 'never ' : 'always ') . $assertion, + . ' is always ' . $assertion, $code_location, - $old_var_type_string . ' ' . $assertion + $old_var_type_string . ' ' . $safe_assertion ), $suppressed_issues )) { @@ -835,9 +847,9 @@ protected static function triggerIssueForImpossible( new RedundantCondition( 'Type ' . $old_var_type_string . ' for ' . $key - . ' is ' . ($never ? 'never ' : 'always ') . $assertion, + . ' is always ' . $assertion, $code_location, - $old_var_type_string . ' ' . $assertion + $old_var_type_string . ' ' . $safe_assertion ), $suppressed_issues )) { @@ -850,9 +862,9 @@ protected static function triggerIssueForImpossible( new DocblockTypeContradiction( 'Docblock-defined type ' . $old_var_type_string . ' for ' . $key - . ' is ' . ($never ? 'always ' : 'never ') . $assertion, + . ' is never ' . $assertion, $code_location, - $old_var_type_string . ' ' . $assertion + $old_var_type_string . ' ' . $safe_assertion ), $suppressed_issues )) { @@ -863,17 +875,17 @@ protected static function triggerIssueForImpossible( $issue = new TypeDoesNotContainNull( 'Type ' . $old_var_type_string . ' for ' . $key - . ' is ' . ($never ? 'always ' : 'never ') . $assertion, + . ' is never ' . $assertion, $code_location, - $old_var_type_string . ' ' . $assertion + $old_var_type_string . ' ' . $safe_assertion ); } else { $issue = new TypeDoesNotContainType( 'Type ' . $old_var_type_string . ' for ' . $key - . ' is ' . ($never ? 'always ' : 'never ') . $assertion, + . ' is never ' . $assertion, $code_location, - $old_var_type_string . ' ' . $assertion + $old_var_type_string . ' ' . $safe_assertion ); } diff --git a/tests/TypeReconciliation/ConditionalTest.php b/tests/TypeReconciliation/ConditionalTest.php index 8f8b088121d..532ad40a9cc 100644 --- a/tests/TypeReconciliation/ConditionalTest.php +++ b/tests/TypeReconciliation/ConditionalTest.php @@ -258,7 +258,7 @@ function foo($a) { 'assertions' => [ '$b' => 'null', ], - 'error_levels' => ['TypeDoesNotContainNull', 'RedundantCondition'], + 'error_levels' => ['TypeDoesNotContainType', 'RedundantCondition'], ], 'ignoreNullCheckAndMaintainNullableValue' => [ '