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

Fix #2321 - prevent inferred template coercion

This commit is contained in:
Brown 2019-11-11 09:14:34 -05:00
parent 79acbadfad
commit 46d163996e
2 changed files with 74 additions and 33 deletions

View File

@ -1940,11 +1940,11 @@ class TypeAnalyzer
&& !$container_param->hasTemplate()
&& !$input_param->hasTemplate()
) {
if ($input_param->had_template
|| $input_param->hasEmptyArray()
if ($input_param->hasEmptyArray()
|| $input_param->hasLiteralValue()
) {
if (!$atomic_comparison_result->replacement_atomic_type) {
$atomic_comparison_result->replacement_atomic_type = clone $input_type_part;
}
@ -1966,7 +1966,7 @@ class TypeAnalyzer
$container_param->ignore_falsable_issues,
$param_comparison_result,
$allow_interface_equality
) || $atomic_comparison_result->type_coerced
) || $param_comparison_result->type_coerced
) {
if ($container_param->hasMixed() || $container_param->isArrayKey()) {
$atomic_comparison_result->type_coerced_from_mixed = true;

View File

@ -1682,7 +1682,7 @@ class ClassTemplateTest extends TestCase
class Child extends Base {}
/**
* @template T
* @template-covariant T
*/
class Foo
{
@ -1700,7 +1700,7 @@ class ClassTemplateTest extends TestCase
],
'allowMoreSpecificArray' => [
'<?php
/** @template T */
/** @template-covariant T */
class Foo {
/** @param \Closure():T $closure */
public function __construct($closure) {}
@ -1715,6 +1715,33 @@ class ClassTemplateTest extends TestCase
}
}'
],
'specializeTypeInPropertyAssignment' => [
'<?php
/** @template-covariant T */
class Foo {
/** @var \Closure():T $closure */
private $closure;
/** @param \Closure():T $closure */
public function __construct($closure)
{
$this->closure = $closure;
}
}
class Bar {
/** @var Foo<array> */
private $FooArray;
public function __construct() {
$this->FooArray = new Foo(function(): array { return ["foo" => "bar"]; });
expectsShape($this->FooArray);
}
}
/** @param Foo<array{foo: string}> $_ */
function expectsShape($_): void {}',
],
'reflectTemplatedClass' => [
'<?php
/** @template T1 of object */
@ -2446,34 +2473,6 @@ class ClassTemplateTest extends TestCase
function takesFooDerived($foo): void {}',
'error_message' => 'InvalidReturnStatement'
],
'specializeTypeInPropertyAssignment' => [
'<?php
/** @template T */
class Foo {
/** @var \Closure():T $closure */
private $closure;
/** @param \Closure():T $closure */
public function __construct($closure)
{
$this->closure = $closure;
}
}
class Bar {
/** @var Foo<array> */
private $FooArray;
public function __construct() {
$this->FooArray = new Foo(function(): array { return ["foo" => "bar"]; });
expectsShape($this->FooArray);
}
}
/** @param Foo<array{foo: string}> $_ */
function expectsShape($_): void {}',
'error_message' => 'MixedArgumentTypeCoercion'
],
'preventUseWithMoreSpecificParamInt' => [
'<?php
/** @template T */
@ -2508,6 +2507,48 @@ class ClassTemplateTest extends TestCase
}',
'error_message' => 'InvalidArgument'
],
'preventTemplatedCorrectionBeingWrittenTo' => [
'<?php
namespace NS;
/**
* @template TKey
* @template TValue
*/
class ArrayCollection {
/** @var array<TKey,TValue> */
private $data;
/** @param array<TKey,TValue> $data */
public function __construct(array $data) {
$this->data = $data;
}
/**
* @param TKey $key
* @param TValue $value
*/
public function addItem($key, $value) : void {
$this->data[$key] = $value;
}
}
class Item {}
class SubItem extends Item {}
class OtherSubItem extends Item {}
/**
* @param ArrayCollection<int,Item> $i
*/
function takesCollectionOfItems(ArrayCollection $i): void {
$i->addItem(10, new OtherSubItem);
}
$subitem_collection = new ArrayCollection([ new SubItem ]);
takesCollectionOfItems($subitem_collection);',
'error_message' => 'InvalidArgument'
],
];
}
}