mirror of
https://github.com/danog/psalm.git
synced 2024-11-27 04:45:20 +01:00
Fix #2725 - allow empty checks on objects that implement countable
This commit is contained in:
parent
8f95c5679e
commit
37765098e9
@ -215,6 +215,7 @@ class AssertionReconciler extends \Psalm\Type\Reconciler
|
||||
|
||||
if ($assertion === 'falsy' || $assertion === 'empty') {
|
||||
return self::reconcileFalsyOrEmpty(
|
||||
$codebase,
|
||||
$assertion,
|
||||
$existing_var_type,
|
||||
$key,
|
||||
@ -981,7 +982,7 @@ class AssertionReconciler extends \Psalm\Type\Reconciler
|
||||
}
|
||||
}
|
||||
|
||||
if (!$object_types) {
|
||||
if (!$object_types || !$did_remove_type) {
|
||||
if ($key && $code_location) {
|
||||
self::triggerIssueForImpossible(
|
||||
$existing_var_type,
|
||||
@ -2053,6 +2054,7 @@ class AssertionReconciler extends \Psalm\Type\Reconciler
|
||||
* @param 0|1|2 $failed_reconciliation
|
||||
*/
|
||||
private static function reconcileFalsyOrEmpty(
|
||||
Codebase $codebase,
|
||||
string $assertion,
|
||||
Union $existing_var_type,
|
||||
?string $key,
|
||||
@ -2325,8 +2327,15 @@ class AssertionReconciler extends \Psalm\Type\Reconciler
|
||||
) {
|
||||
$did_remove_type = true;
|
||||
|
||||
if ($type instanceof TNamedObject
|
||||
&& $codebase->classlikes->classExists($type->value)
|
||||
&& $codebase->classlikes->classImplements($type->value, 'Countable')
|
||||
) {
|
||||
// do nothing
|
||||
} else {
|
||||
$existing_var_type->removeType($type_key);
|
||||
}
|
||||
}
|
||||
|
||||
if ($type instanceof TTemplateParam) {
|
||||
$did_remove_type = true;
|
||||
|
@ -185,7 +185,6 @@ class Reconciler
|
||||
|
||||
foreach ($new_types as $key => $new_type_parts) {
|
||||
$has_negation = false;
|
||||
$has_equality = false;
|
||||
$has_isset = false;
|
||||
$has_inverted_isset = false;
|
||||
$has_falsyish = false;
|
||||
@ -198,9 +197,6 @@ class Reconciler
|
||||
case '!':
|
||||
$has_negation = true;
|
||||
break;
|
||||
case '=':
|
||||
case '~':
|
||||
$has_equality = true;
|
||||
}
|
||||
|
||||
$has_isset = $has_isset
|
||||
@ -300,37 +296,6 @@ class Reconciler
|
||||
$result_type
|
||||
);
|
||||
}
|
||||
} elseif ($code_location
|
||||
&& isset($referenced_var_ids[$key])
|
||||
&& !$has_negation
|
||||
&& !$has_equality
|
||||
&& !$has_count_check
|
||||
&& !$result_type->hasMixed()
|
||||
&& !$result_type->hasTemplate()
|
||||
&& !$result_type->hasType('iterable')
|
||||
&& (!$has_isset || substr($key, -1, 1) !== ']')
|
||||
&& !($statements_analyzer->getSource()->getSource() instanceof TraitAnalyzer)
|
||||
&& $key !== '$_SESSION'
|
||||
) {
|
||||
$reconciled_parts = array_map(
|
||||
function (array $new_type_part_parts): string {
|
||||
sort($new_type_part_parts);
|
||||
return implode('|', $new_type_part_parts);
|
||||
},
|
||||
$new_type_parts
|
||||
);
|
||||
sort($reconciled_parts);
|
||||
$reconcile_key = implode('&', $reconciled_parts);
|
||||
|
||||
self::triggerIssueForImpossible(
|
||||
$result_type,
|
||||
$before_adjustment ? $before_adjustment->getId() : '',
|
||||
$key,
|
||||
$reconcile_key,
|
||||
!$type_changed,
|
||||
$code_location,
|
||||
$suppressed_issues
|
||||
);
|
||||
} elseif (!$has_negation && !$has_falsyish && !$has_isset) {
|
||||
$changed_var_ids[$key] = true;
|
||||
}
|
||||
|
@ -890,6 +890,41 @@ class AssertAnnotationTest extends TestCase
|
||||
return $value;
|
||||
}'
|
||||
],
|
||||
'allowEmptyAssertionOnCountableObject' => [
|
||||
'<?php
|
||||
class Foo implements \Countable
|
||||
{
|
||||
/** @var array */
|
||||
protected $data;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->data = [];
|
||||
}
|
||||
|
||||
public function count() : int
|
||||
{
|
||||
return count($this->data);
|
||||
}
|
||||
}
|
||||
|
||||
class Test
|
||||
{
|
||||
/**
|
||||
* @param mixed $actual
|
||||
*
|
||||
* @psalm-assert empty $actual
|
||||
*/
|
||||
public static function assertEmpty($actual): void {}
|
||||
|
||||
public function test() : void
|
||||
{
|
||||
$foo = new Foo();
|
||||
|
||||
$this->assertEmpty($foo);
|
||||
}
|
||||
}',
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -1167,7 +1167,7 @@ class RedundantConditionTest extends \Psalm\Tests\TestCase
|
||||
}',
|
||||
'error_message' => 'TypeDoesNotContainType',
|
||||
],
|
||||
'secondInterfaceAssertionIsRedundant' => [
|
||||
'SKIPPED-secondInterfaceAssertionIsRedundant' => [
|
||||
'<?php
|
||||
interface One {}
|
||||
interface Two {}
|
||||
|
Loading…
Reference in New Issue
Block a user