1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-30 04:39:00 +01:00

Fix interface_exists after class_exists check

This commit is contained in:
Matthew Brown 2019-05-20 20:57:59 -04:00
parent 8c653b0312
commit ea930b8875
4 changed files with 42 additions and 8 deletions

View File

@ -1664,10 +1664,14 @@ class AssertionFinder
if ($first_var_name) {
$if_types[$first_var_name] = [[$prefix . 'iterable']];
}
} elseif (self::hasClassOrInterfaceExistsCheck($expr)) {
} elseif (self::hasClassExistsCheck($expr)) {
if ($first_var_name) {
$if_types[$first_var_name] = [[$prefix . 'class-string']];
}
} elseif (self::hasInterfaceExistsCheck($expr)) {
if ($first_var_name) {
$if_types[$first_var_name] = [[$prefix . 'interface-string']];
}
} elseif (self::hasFunctionExistsCheck($expr)) {
if ($first_var_name) {
$if_types[$first_var_name] = [[$prefix . 'callable-string']];
@ -2285,11 +2289,26 @@ class AssertionFinder
*
* @return bool
*/
protected static function hasClassOrInterfaceExistsCheck(PhpParser\Node\Expr\FuncCall $stmt)
protected static function hasClassExistsCheck(PhpParser\Node\Expr\FuncCall $stmt)
{
if ($stmt->name instanceof PhpParser\Node\Name
&& (($function_name = strtolower($stmt->name->parts[0])) === 'class_exists'
|| $function_name === 'interface_exists')
&& strtolower($stmt->name->parts[0]) === 'class_exists'
) {
return true;
}
return false;
}
/**
* @param PhpParser\Node\Expr\FuncCall $stmt
*
* @return bool
*/
protected static function hasInterfaceExistsCheck(PhpParser\Node\Expr\FuncCall $stmt)
{
if ($stmt->name instanceof PhpParser\Node\Name
&& strtolower($stmt->name->parts[0]) === 'interface_exists'
) {
return true;
}

View File

@ -163,6 +163,7 @@ abstract class Atomic
return $php_version !== null ? new TNamedObject($value) : new TMixed();
case 'class-string':
case 'interface-string':
return new TClassString();
case 'callable-string':

View File

@ -2165,9 +2165,13 @@ class Reconciler
}
}
}
} elseif ($scalar_type === 'string' || $scalar_type === 'class-string' || $scalar_type === 'callable-string') {
} elseif ($scalar_type === 'string'
|| $scalar_type === 'class-string'
|| $scalar_type === 'interface-string'
|| $scalar_type === 'callable-string'
) {
if ($existing_var_type->hasMixed() || $existing_var_type->hasScalar()) {
if ($scalar_type === 'class-string') {
if ($scalar_type === 'class-string' || $scalar_type === 'interface-string') {
return new Type\Union([new Type\Atomic\TLiteralClassString($value)]);
}
@ -2205,7 +2209,7 @@ class Reconciler
);
}
} else {
if ($scalar_type === 'class-string') {
if ($scalar_type === 'class-string' || $scalar_type === 'interface-string') {
$existing_var_type = new Type\Union([new Type\Atomic\TLiteralClassString($value)]);
} else {
$existing_var_type = new Type\Union([new Type\Atomic\TLiteralString($value)]);
@ -2349,7 +2353,11 @@ class Reconciler
$did_remove_type = true;
}
}
} elseif ($scalar_type === 'string' || $scalar_type === 'class-string' || $scalar_type === 'callable-string') {
} elseif ($scalar_type === 'string'
|| $scalar_type === 'class-string'
|| $scalar_type === 'interface-string'
|| $scalar_type === 'callable-string'
) {
if ($existing_var_type->hasString() && $existing_string_types = $existing_var_type->getLiteralStrings()) {
$did_match_literal_type = true;

View File

@ -385,6 +385,12 @@ class ClassTest extends TestCase
return null;
}',
],
'allowClassExistsAndInterfaceExists' => [
'<?php
function foo(string $s) : void {
if (class_exists($s) || interface_exists($s)) {}
}'
],
];
}