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

Fix #1044 - allow templated params in assert

This commit is contained in:
Matthew Brown 2018-10-30 09:20:34 -04:00
parent 2eb0122549
commit d1baff4b92
5 changed files with 56 additions and 2 deletions

View File

@ -481,6 +481,7 @@ class FunctionCallChecker extends \Psalm\Checker\Statements\Expression\CallCheck
self::applyAssertionsToContext(
$function_storage->assertions,
$stmt->args,
$function_storage->template_typeof_params ?: [],
$context,
$statements_checker
);

View File

@ -694,6 +694,7 @@ class MethodCallChecker extends \Psalm\Checker\Statements\Expression\CallChecker
self::applyAssertionsToContext(
$method_storage->assertions,
$args,
$method_storage->template_typeof_params ?: [],
$context,
$statements_checker
);

View File

@ -512,6 +512,7 @@ class StaticCallChecker extends \Psalm\Checker\Statements\Expression\CallChecker
self::applyAssertionsToContext(
$method_storage->assertions,
$stmt->args,
$method_storage->template_typeof_params ?: [],
$context,
$statements_checker
);

View File

@ -2092,6 +2092,7 @@ class CallChecker
* @param \Psalm\Storage\Assertion[] $assertions
* @param array<int, PhpParser\Node\Arg> $args
* @param Context $context
* @param array<int, string> $template_typeof_params
* @param StatementsChecker $statements_checker
*
* @return void
@ -2099,12 +2100,15 @@ class CallChecker
protected static function applyAssertionsToContext(
array $assertions,
array $args,
array $template_typeof_params,
Context $context,
StatementsChecker $statements_checker
) {
$type_assertions = [];
foreach ($assertions as $assertion) {
$assertion_var_id = null;
if (is_int($assertion->var_id)) {
if (!isset($args[$assertion->var_id])) {
continue;
@ -2115,10 +2119,26 @@ class CallChecker
$arg_var_id = ExpressionChecker::getArrayVarId($arg_value, null, $statements_checker);
if ($arg_var_id) {
$type_assertions[$arg_var_id] = $assertion->rule;
$assertion_var_id = $arg_var_id;
}
} else {
$type_assertions[$assertion->var_id] = $assertion->rule;
$assertion_var_id = $assertion->var_id;
}
if ($assertion_var_id) {
$offset = array_search($assertion->rule[0][0], $template_typeof_params, true);
if ($offset !== false) {
if (isset($args[$offset]->value->inferredType)) {
$templated_type = $args[$offset]->value->inferredType;
if ($templated_type->isSingleStringLiteral()) {
$type_assertions[$assertion_var_id] = [[$templated_type->getSingleStringLiteral()->value]];
}
}
} else {
$type_assertions[$assertion_var_id] = $assertion->rule;
}
}
}

View File

@ -204,6 +204,37 @@ class AssertTest extends TestCase
$i = substr($_SERVER["abc"], 1, 2);
}',
],
'assertTemplatedType' => [
'<?php
interface Foo {}
class Bar implements Foo {
public function sayHello(): void {
echo "Hello";
}
}
/**
* @param mixed $value
* @param class-string $type
* @template T
* @template-typeof T $type
* @psalm-assert T $value
*/
function assertInstanceOf($value, string $type): void {
// some code
}
// Returns concreate implmenetation of Foo, which in this case is Bar
function getImplementationOfFoo(): Foo {
return new Bar();
}
$bar = getImplementationOfFoo();
assertInstanceOf($bar, Bar::class);
$bar->sayHello();'
],
];
}