diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOpAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOpAnalyzer.php index 79fda5cbdec..615a2f7c307 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOpAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOpAnalyzer.php @@ -777,7 +777,7 @@ function (\Psalm\Internal\Clause $c) use ($mixed_var_ids) { if ($stmt_left_type = $statements_analyzer->node_data->getType($stmt->left)) { $if_return_type_reconciled = AssertionReconciler::reconcile( '!null', - $stmt_left_type, + clone $stmt_left_type, '', $statements_analyzer, $context->inside_loop, @@ -786,7 +786,7 @@ function (\Psalm\Internal\Clause $c) use ($mixed_var_ids) { $statements_analyzer->getSuppressedIssues() ); - $lhs_type = $if_return_type_reconciled; + $lhs_type = clone $if_return_type_reconciled; } $stmt_right_type = null; diff --git a/src/Psalm/Internal/Type/NegatedAssertionReconciler.php b/src/Psalm/Internal/Type/NegatedAssertionReconciler.php index 29d695b48a3..df36f72cbea 100644 --- a/src/Psalm/Internal/Type/NegatedAssertionReconciler.php +++ b/src/Psalm/Internal/Type/NegatedAssertionReconciler.php @@ -9,6 +9,7 @@ use Psalm\Internal\Analyzer\StatementsAnalyzer; use Psalm\Internal\Analyzer\TraitAnalyzer; use Psalm\Internal\Analyzer\TypeAnalyzer; +use Psalm\Issue\DocblockTypeContradiction; use Psalm\Issue\ParadoxicalCondition; use Psalm\Issue\RedundantCondition; use Psalm\Issue\TypeDoesNotContainType; @@ -104,14 +105,28 @@ public static function reconcile( $failed_reconciliation = 2; if ($code_location) { - if (IssueBuffer::accepts( - new TypeDoesNotContainType( - 'Cannot resolve types for ' . $key . ' and !isset assertion', - $code_location - ), - $suppressed_issues - )) { - // fall through + if ($existing_var_type->from_docblock) { + if (IssueBuffer::accepts( + new DocblockTypeContradiction( + 'Cannot resolve types for ' . $key . ' with docblock-defined type ' + . $existing_var_type . ' and !isset assertion', + $code_location + ), + $suppressed_issues + )) { + // fall through + } + } else { + if (IssueBuffer::accepts( + new TypeDoesNotContainType( + 'Cannot resolve types for ' . $key . ' with type ' + . $existing_var_type . ' and !isset assertion', + $code_location + ), + $suppressed_issues + )) { + + } } } diff --git a/tests/TypeReconciliation/IssetTest.php b/tests/TypeReconciliation/IssetTest.php index e1d26ce8a8f..3e920132bad 100644 --- a/tests/TypeReconciliation/IssetTest.php +++ b/tests/TypeReconciliation/IssetTest.php @@ -742,6 +742,30 @@ function foo(?A $a) : string { return "bar"; }', ], + 'issetOnMethodCallInsideFunctionCall' => [ + 'foo() ?? ""); + }' + ], + 'issetOnMethodCallInsideMethodCall' => [ + 'foo() ?? ""); + }' + ], ]; }