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:
parent
79acbadfad
commit
46d163996e
@ -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;
|
||||
|
@ -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'
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user