mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Fix #1825 - improve behaviour of callable reconciliation
This commit is contained in:
parent
db38270a6a
commit
ac06ea659e
@ -865,6 +865,12 @@ class TypeAnalyzer
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($input_type_part instanceof Type\Atomic\TCallableObject &&
|
||||
$container_type_part instanceof TObject
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($container_type_part instanceof TNumeric &&
|
||||
($input_type_part->isNumericType() || $input_type_part instanceof TString)
|
||||
) {
|
||||
|
@ -911,6 +911,7 @@ class Reconciler
|
||||
foreach ($existing_var_atomic_types as $type) {
|
||||
if ($type instanceof TBool) {
|
||||
$bool_types[] = $type;
|
||||
$type->from_docblock = false;
|
||||
} elseif ($type instanceof TScalar) {
|
||||
$bool_types[] = new TBool;
|
||||
$did_remove_type = true;
|
||||
@ -942,6 +943,54 @@ class Reconciler
|
||||
return Type::getMixed();
|
||||
}
|
||||
|
||||
if ($new_var_type === 'string' && !$existing_var_type->hasMixed()) {
|
||||
$string_types = [];
|
||||
$did_remove_type = false;
|
||||
|
||||
foreach ($existing_var_atomic_types as $type) {
|
||||
if ($type instanceof TString) {
|
||||
$string_types[] = $type;
|
||||
|
||||
if (get_class($type) === TString::class) {
|
||||
$type->from_docblock = false;
|
||||
}
|
||||
} elseif ($type instanceof TCallable) {
|
||||
$string_types[] = new Type\Atomic\TCallableString;
|
||||
$did_remove_type = true;
|
||||
} elseif ($type instanceof TNumeric) {
|
||||
$string_types[] = new TNumericString;
|
||||
$did_remove_type = true;
|
||||
} elseif ($type instanceof TScalar || $type instanceof TArrayKey) {
|
||||
$string_types[] = new TString;
|
||||
$did_remove_type = true;
|
||||
} else {
|
||||
$did_remove_type = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((!$did_remove_type || !$string_types) && !$is_equality) {
|
||||
if ($key && $code_location) {
|
||||
self::triggerIssueForImpossible(
|
||||
$existing_var_type,
|
||||
$old_var_type_string,
|
||||
$key,
|
||||
$new_var_type,
|
||||
!$did_remove_type,
|
||||
$code_location,
|
||||
$suppressed_issues
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($string_types) {
|
||||
return new Type\Union($string_types);
|
||||
}
|
||||
|
||||
$failed_reconciliation = 2;
|
||||
|
||||
return Type::getMixed();
|
||||
}
|
||||
|
||||
$is_maybe_callable_array = false;
|
||||
|
||||
if ($new_var_type === 'array' && isset($existing_var_atomic_types['callable'])) {
|
||||
@ -950,14 +999,14 @@ class Reconciler
|
||||
if (!isset($existing_var_atomic_types['array'])) {
|
||||
$existing_var_type->addType(
|
||||
new TCallableObjectLikeArray([
|
||||
Type::getMixed(),
|
||||
new Type\Union([new TObject, new TString]),
|
||||
Type::getString()
|
||||
])
|
||||
);
|
||||
} else {
|
||||
$array_combination = \Psalm\Internal\Type\TypeCombination::combineTypes([
|
||||
new TCallableObjectLikeArray([
|
||||
Type::getMixed(),
|
||||
new Type\Union([new TObject, new TString]),
|
||||
Type::getString()
|
||||
]),
|
||||
$existing_var_atomic_types['array']
|
||||
|
@ -874,8 +874,8 @@ class CallableTest extends TestCase
|
||||
* @psalm-suppress MixedArgument
|
||||
*/
|
||||
function foo($c) : void {
|
||||
if (is_array($c)) {
|
||||
echo $c[2];
|
||||
if (is_array($c) && is_string($c[1])) {
|
||||
echo $c[1];
|
||||
}
|
||||
}',
|
||||
],
|
||||
|
@ -272,7 +272,20 @@ class RedundantConditionTest extends TestCase
|
||||
'assertions' => [],
|
||||
'error_levels' => ['MixedAssignment', 'MixedArrayAccess'],
|
||||
],
|
||||
'hardPhpTypeAssertionsOnDocblockType' => [
|
||||
'hardPhpTypeAssertionsOnDocblockBoolType' => [
|
||||
'<?php
|
||||
/** @param bool|null $bar */
|
||||
function foo($bar): void {
|
||||
if (!is_null($bar) && !is_bool($bar)) {
|
||||
throw new \Exception("bad");
|
||||
}
|
||||
|
||||
if ($bar !== null) {}
|
||||
}',
|
||||
'assertions' => [],
|
||||
'error_levels' => ['DocblockTypeContradiction'],
|
||||
],
|
||||
'hardPhpTypeAssertionsOnDocblockStringType' => [
|
||||
'<?php
|
||||
/** @param string|null $bar */
|
||||
function foo($bar): void {
|
||||
|
@ -1363,6 +1363,20 @@ class TypeReconciliationTest extends TestCase
|
||||
}
|
||||
}',
|
||||
],
|
||||
'reconcileCallable' => [
|
||||
'<?php
|
||||
function reflectCallable(callable $callable): ReflectionFunctionAbstract {
|
||||
if (\is_array($callable)) {
|
||||
return new \ReflectionMethod($callable[0], $callable[1]);
|
||||
} elseif ($callable instanceof \Closure || \is_string($callable)) {
|
||||
return new \ReflectionFunction($callable);
|
||||
} elseif (\is_object($callable)) {
|
||||
return new \ReflectionMethod($callable, "__invoke");
|
||||
} else {
|
||||
throw new \InvalidArgumentException("Bad");
|
||||
}
|
||||
}'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user