1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-22 05:41:20 +01:00

Fix #2409 - use more robust assertion parsing

This commit is contained in:
Matthew Brown 2019-12-31 09:10:14 -05:00
parent 5bd9b988fb
commit 16b8edd583
7 changed files with 107 additions and 27 deletions

View File

@ -2659,25 +2659,19 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
return null;
}
$assertion_type_parts = explode('|', $assertion_type);
$class_template_types = !$stmt instanceof PhpParser\Node\Stmt\ClassMethod || !$stmt->isStatic()
? $this->class_template_types
: [];
foreach ($assertion_type_parts as $i => $assertion_type_part) {
if ($assertion_type_part !== 'falsy'
&& $assertion_type_part !== 'array'
&& $assertion_type_part !== 'list'
&& $assertion_type_part !== 'iterable'
) {
$namespaced_type = Type::parseTokens(
Type::fixUpLocalType(
$assertion_type_part,
$assertion_type,
$this->aliases,
$this->function_template_types + $class_template_types,
$this->type_aliases,
null
null,
null,
true
)
);
@ -2687,9 +2681,20 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
$this->function_template_types + $class_template_types
);
$assertion_type_parts[$i] = $prefix . $namespaced_type->getId();
foreach ($namespaced_type->getTypes() as $namespaced_type_part) {
if ($namespaced_type_part instanceof Type\Atomic\TAssertionFalsy
|| ($namespaced_type_part instanceof Type\Atomic\TList
&& $namespaced_type_part->type_param->isMixed())
|| ($namespaced_type_part instanceof Type\Atomic\TArray
&& $namespaced_type_part->type_params[0]->isArrayKey()
&& $namespaced_type_part->type_params[1]->isMixed())
|| ($namespaced_type_part instanceof Type\Atomic\TIterable
&& $namespaced_type_part->type_params[0]->isMixed()
&& $namespaced_type_part->type_params[1]->isMixed())
) {
$assertion_type_parts[] = $prefix . $namespaced_type_part->getAssertionString();
} else {
$assertion_type_parts[$i] = $prefix . $assertion_type_part;
$assertion_type_parts[] = $prefix . $namespaced_type_part->getId();
}
}

View File

@ -1030,7 +1030,8 @@ abstract class Type
array $template_type_map = null,
array $type_aliases = null,
?string $self_fqcln = null,
?string $parent_fqcln = null
?string $parent_fqcln = null,
bool $allow_assertions = false
) {
$type_tokens = self::tokenize($string_type);
@ -1122,6 +1123,11 @@ abstract class Type
continue;
}
if ($allow_assertions && $string_type_token[0] === 'falsy') {
$type_tokens[$i][0] = 'false-y';
continue;
}
if (isset($type_aliases[$string_type_token[0]])) {
$replacement_tokens = $type_aliases[$string_type_token[0]];

View File

@ -26,6 +26,7 @@ use Psalm\Type;
use Psalm\Type\Atomic\ObjectLike;
use Psalm\Type\Atomic\TArray;
use Psalm\Type\Atomic\TArrayKey;
use Psalm\Type\Atomic\TAssertionFalsy;
use Psalm\Type\Atomic\TBool;
use Psalm\Type\Atomic\TCallable;
use Psalm\Type\Atomic\TCallableArray;
@ -217,6 +218,9 @@ abstract class Atomic
case 'html-escaped-string':
return new THtmlEscapedString();
case 'false-y':
return new TAssertionFalsy();
case '$this':
return new TNamedObject('static');
}

View File

@ -0,0 +1,50 @@
<?php
namespace Psalm\Type\Atomic;
class TAssertionFalsy extends \Psalm\Type\Atomic
{
public function __toString()
{
return 'falsy';
}
/**
* @return string
*/
public function getKey()
{
return 'falsy';
}
/**
* @return string
*/
public function getAssertionString()
{
return 'falsy';
}
/**
* @param string|null $namespace
* @param array<string> $aliased_classes
* @param string|null $this_class
* @param int $php_major_version
* @param int $php_minor_version
*
* @return null|string
*/
public function toPhpString(
$namespace,
array $aliased_classes,
$this_class,
$php_major_version,
$php_minor_version
) {
return null;
}
public function canBeFullyExpressedInPhp()
{
return false;
}
}

View File

@ -44,6 +44,14 @@ class TIterable extends Atomic
return 'iterable';
}
/**
* @return string
*/
public function getAssertionString()
{
return 'iterable';
}
public function getId()
{
$s = '';

View File

@ -209,7 +209,7 @@ class TList extends \Psalm\Type\Atomic
*/
public function getAssertionString()
{
return $this->getKey();
return 'list';
}
/**

View File

@ -822,6 +822,13 @@ class AssertAnnotationTest extends TestCase
echo count($a->getArray());
}'
],
'preventErrorWhenAssertingOnArrayUnion' => [
'<?php
/**
* @psalm-assert array<string,string|object> $data
*/
function validate(array $data): void {}'
],
];
}