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

Add more robust template constraint checks

This commit is contained in:
Matthew Brown 2019-01-19 18:35:53 -05:00
parent ed68cb973d
commit 1d2a0f8b39
2 changed files with 107 additions and 1 deletions

View File

@ -4,6 +4,7 @@ namespace Psalm\Type;
use Psalm\Codebase;
use Psalm\CodeLocation;
use Psalm\StatementsSource;
use Psalm\Internal\Analyzer\TypeAnalyzer;
use Psalm\Storage\FileStorage;
use Psalm\Type;
use Psalm\Type\Atomic\TFloat;
@ -823,7 +824,15 @@ class Union
];
}
} elseif ($add_upper_bound && $input_type) {
$template_types[$key][0] = clone $input_type;
if ($codebase
&& TypeAnalyzer::isContainedBy(
$codebase,
$input_type,
$template_types[$key][0]
)
) {
$template_types[$key][0] = clone $input_type;
}
}
}
} elseif ($atomic_type instanceof Type\Atomic\TGenericParamClass

View File

@ -1949,6 +1949,34 @@ class TemplateTest extends TestCase
'$b' => 'null|SpecificEntity',
]
],
'multipleArgConstraints' => [
'<?php
class A {}
class AChild extends A {}
/**
* @template T
* @param callable(T):void $c1
* @param callable(T):void $c2
* @param T $a
*/
function foo(callable $c1, callable $c2, $a): void {
$c1($a);
$c2($a);
}
foo(
function(A $_a) : void {},
function(A $_a) : void {},
new A()
);
foo(
function(A $_a) : void {},
function(A $_a) : void {},
new AChild()
);'
],
];
}
@ -2474,6 +2502,75 @@ class TemplateTest extends TestCase
takesReturnTCallable($b);',
'error_message' => 'InvalidScalarArgument',
],
'multipleArgConstraintWithMoreRestrictiveFirstArg' => [
'<?php
class A {}
class AChild extends A {}
/**
* @template T
* @param callable(T):void $c1
* @param callable(T):void $c2
* @param T $a
*/
function foo(callable $c1, callable $c2, $a): void {
$c1($a);
$c2($a);
}
foo(
function(AChild $_a) : void {},
function(A $_a) : void {},
new A()
);',
'error_message' => 'TypeCoercion',
],
'multipleArgConstraintWithMoreRestrictiveSecondArg' => [
'<?php
class A {}
class AChild extends A {}
/**
* @template T
* @param callable(T):void $c1
* @param callable(T):void $c2
* @param T $a
*/
function foo(callable $c1, callable $c2, $a): void {
$c1($a);
$c2($a);
}
foo(
function(A $_a) : void {},
function(AChild $_a) : void {},
new A()
);',
'error_message' => 'TypeCoercion',
],
'multipleArgConstraintWithLessRestrictiveThirdArg' => [
'<?php
class A {}
class AChild extends A {}
/**
* @template T
* @param callable(T):void $c1
* @param callable(T):void $c2
* @param T $a
*/
function foo(callable $c1, callable $c2, $a): void {
$c1($a);
$c2($a);
}
foo(
function(AChild $_a) : void {},
function(AChild $_a) : void {},
new A()
);',
'error_message' => 'TypeCoercion',
],
];
}
}