1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-21 21:31:13 +01:00

Improve behaviour of templated template assertions

Fixes #1956
This commit is contained in:
Matthew Brown 2019-07-21 01:40:19 -04:00
parent 3516b48173
commit 76508e6d64
3 changed files with 99 additions and 31 deletions

View File

@ -2972,39 +2972,43 @@ class CallAnalyzer
$rule = substr($rule, 1);
}
if (isset($template_type_map[$rule][''])) {
if ($template_type_map[$rule][''][0]->hasMixed()) {
continue;
}
$replacement_atomic_types = $template_type_map[$rule][''][0]->getTypes();
if (count($replacement_atomic_types) > 1) {
continue;
}
$ored_type_assertions = [];
foreach ($replacement_atomic_types as $replacement_atomic_type) {
if ($replacement_atomic_type instanceof Type\Atomic\TMixed) {
if (isset($template_type_map[$rule])) {
foreach ($template_type_map[$rule] as $template_map) {
if ($template_map[0]->hasMixed()) {
continue 2;
}
if ($replacement_atomic_type instanceof Type\Atomic\TArray
|| $replacement_atomic_type instanceof Type\Atomic\ObjectLike
) {
$ored_type_assertions[] = $prefix . 'array';
} elseif ($replacement_atomic_type instanceof Type\Atomic\TNamedObject) {
$ored_type_assertions[] = $prefix . $replacement_atomic_type->value;
} elseif ($replacement_atomic_type instanceof Type\Atomic\Scalar) {
$ored_type_assertions[] = $prefix . $replacement_atomic_type->getId();
} elseif ($replacement_atomic_type instanceof Type\Atomic\TNull) {
$ored_type_assertions[] = $prefix . 'null';
}
}
$replacement_atomic_types = $template_map[0]->getTypes();
if ($ored_type_assertions) {
$type_assertions[$assertion_var_id] = [$ored_type_assertions];
if (count($replacement_atomic_types) > 1) {
continue 2;
}
$ored_type_assertions = [];
foreach ($replacement_atomic_types as $replacement_atomic_type) {
if ($replacement_atomic_type instanceof Type\Atomic\TMixed) {
continue 3;
}
if ($replacement_atomic_type instanceof Type\Atomic\TArray
|| $replacement_atomic_type instanceof Type\Atomic\ObjectLike
) {
$ored_type_assertions[] = $prefix . 'array';
} elseif ($replacement_atomic_type instanceof Type\Atomic\TNamedObject) {
$ored_type_assertions[] = $prefix . $replacement_atomic_type->value;
} elseif ($replacement_atomic_type instanceof Type\Atomic\Scalar) {
$ored_type_assertions[] = $prefix . $replacement_atomic_type->getId();
} elseif ($replacement_atomic_type instanceof Type\Atomic\TNull) {
$ored_type_assertions[] = $prefix . 'null';
} elseif ($replacement_atomic_type instanceof Type\Atomic\TTemplateParam) {
$ored_type_assertions[] = $prefix . $replacement_atomic_type->param_name;
}
}
if ($ored_type_assertions) {
$type_assertions[$assertion_var_id] = [$ored_type_assertions];
}
}
} else {
$type_assertions[$assertion_var_id] = $assertion->rule;

View File

@ -50,7 +50,8 @@ class Assertion
if (isset($template_type_map[$rule_token[0]])) {
foreach ($template_type_map[$rule_token[0]] as list($type)) {
$substitute = true;
$rule_token[0] = $type->getId();
$rule_token[0] = $type->getKey();
}
}
}

View File

@ -173,7 +173,7 @@ class FunctionTemplateAssertTest extends TestCase
'$f' => 'Foo',
],
],
'suppressRedundantCondition' => [
'suppressRedundantCondition' => [
'<?php
namespace Bar;
@ -449,6 +449,69 @@ class FunctionTemplateAssertTest extends TestCase
return $value;
}'
],
'assertTemplatedTemplateSimple' => [
'<?php
/**
* @template T1
*/
class Clazz
{
/**
* @param mixed $x
*
* @psalm-assert T1 $x
*/
public function is($x) : void {}
}
/**
* @template T2
*
* @param Clazz<T2> $c
*
* @return T2
* @psalm-suppress MixedAssignment
*/
function example(Clazz $c) {
/** @var mixed */
$x = 0;
$c->is($x);
return $x;
}'
],
'assertTemplatedTemplateIfTrue' => [
'<?php
/**
* @template T1
*/
class Clazz
{
/**
* @param mixed $x
*
* @return bool
*
* @psalm-assert-if-true T1 $x
*/
public function is($x) : bool {
return true;
}
}
/**
* @template T2
*
* @param Clazz<T2> $c
*
* @return T2|false
* @psalm-suppress MixedAssignment
*/
function example(Clazz $c) {
/** @var mixed */
$x = 0;
return $c->is($x) ? $x : false;
}'
],
];
}