Skip to content

Commit 6ba9ef2

Browse files
committed
Fixed checkExplicitMixed with TemplateMixedType
1 parent b4f81db commit 6ba9ef2

File tree

7 files changed

+162
-3
lines changed

7 files changed

+162
-3
lines changed

src/Rules/RuleLevelHelper.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,10 @@ public function accepts(Type $acceptingType, Type $acceptedType, bool $strictTyp
6262
if (
6363
$this->checkExplicitMixed
6464
) {
65-
$acceptedType = TypeTraverser::map($acceptedType, static function (Type $type, callable $traverse): Type {
65+
$traverse = static function (Type $type, callable $traverse): Type {
66+
if ($type instanceof TemplateMixedType) {
67+
return $type->toStrictMixedType();
68+
}
6669
if (
6770
$type instanceof MixedType
6871
&& $type->isExplicitMixed()
@@ -71,7 +74,9 @@ public function accepts(Type $acceptingType, Type $acceptedType, bool $strictTyp
7174
}
7275

7376
return $traverse($type);
74-
});
77+
};
78+
$acceptingType = TypeTraverser::map($acceptingType, $traverse);
79+
$acceptedType = TypeTraverser::map($acceptedType, $traverse);
7580
}
7681

7782
if (

src/Type/Generic/TemplateMixedType.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use PHPStan\TrinaryLogic;
66
use PHPStan\Type\MixedType;
7+
use PHPStan\Type\StrictMixedType;
78
use PHPStan\Type\Type;
89

910
/** @api */
@@ -60,4 +61,15 @@ public function traverse(callable $cb): Type
6061
return $this;
6162
}
6263

64+
public function toStrictMixedType(): TemplateStrictMixedType
65+
{
66+
return new TemplateStrictMixedType(
67+
$this->scope,
68+
$this->strategy,
69+
$this->variance,
70+
$this->name,
71+
new StrictMixedType()
72+
);
73+
}
74+
6375
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Generic;
4+
5+
use PHPStan\TrinaryLogic;
6+
use PHPStan\Type\MixedType;
7+
use PHPStan\Type\StrictMixedType;
8+
use PHPStan\Type\Type;
9+
10+
/** @api */
11+
final class TemplateStrictMixedType extends StrictMixedType implements TemplateType
12+
{
13+
14+
/** @use TemplateTypeTrait<StrictMixedType> */
15+
use TemplateTypeTrait;
16+
17+
public function __construct(
18+
TemplateTypeScope $scope,
19+
TemplateTypeStrategy $templateTypeStrategy,
20+
TemplateTypeVariance $templateTypeVariance,
21+
string $name,
22+
StrictMixedType $bound
23+
)
24+
{
25+
$this->scope = $scope;
26+
$this->strategy = $templateTypeStrategy;
27+
$this->variance = $templateTypeVariance;
28+
$this->name = $name;
29+
$this->bound = $bound;
30+
}
31+
32+
public function isSuperTypeOfMixed(MixedType $type): TrinaryLogic
33+
{
34+
return $this->isSuperTypeOf($type);
35+
}
36+
37+
public function isAcceptedBy(Type $acceptingType, bool $strictTypes): TrinaryLogic
38+
{
39+
return $this->isSubTypeOf($acceptingType);
40+
}
41+
42+
public function traverse(callable $cb): Type
43+
{
44+
$newBound = $cb($this->getBound());
45+
if ($this->getBound() !== $newBound && $newBound instanceof MixedType) {
46+
return new self(
47+
$this->scope,
48+
$this->strategy,
49+
$this->variance,
50+
$this->name,
51+
$newBound
52+
);
53+
}
54+
55+
return $this;
56+
}
57+
58+
}

src/Type/StrictMixedType.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ public function getReferencedClasses(): array
2626

2727
public function accepts(Type $type, bool $strictTypes): TrinaryLogic
2828
{
29+
if ($type instanceof static) {
30+
return TrinaryLogic::createYes();
31+
}
32+
33+
if ($type instanceof CompoundType) {
34+
return CompoundTypeHelper::accepts($type, $this, $strictTypes);
35+
}
36+
2937
return TrinaryLogic::createNo();
3038
}
3139

tests/PHPStan/Rules/Functions/CallCallablesRuleTest.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@
1414
class CallCallablesRuleTest extends \PHPStan\Testing\RuleTestCase
1515
{
1616

17+
/** @var bool */
18+
private $checkExplicitMixed = false;
19+
1720
protected function getRule(): \PHPStan\Rules\Rule
1821
{
19-
$ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false);
22+
$ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, true, $this->checkExplicitMixed);
2023
return new CallCallablesRule(
2124
new FunctionCallParametersCheck(
2225
$ruleLevelHelper,
@@ -166,4 +169,15 @@ public function testNamedArguments(): void
166169
]);
167170
}
168171

172+
public function testBug3566(): void
173+
{
174+
$this->checkExplicitMixed = true;
175+
$this->analyse([__DIR__ . '/data/bug-3566.php'], [
176+
[
177+
'Parameter #1 $ of closure expects int, TMemberType given.',
178+
29,
179+
],
180+
]);
181+
}
182+
169183
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
namespace Bug3566;
4+
5+
class HelloWorld
6+
{
7+
8+
/**
9+
* @phpstan-template TMemberType
10+
* @phpstan-param array<mixed, TMemberType> $array
11+
* @phpstan-param \Closure(TMemberType) : void $validator
12+
*/
13+
public static function validateArrayValueType(array $array, \Closure $validator) : void{
14+
foreach($array as $k => $v){
15+
try{
16+
$validator($v);
17+
}catch(\TypeError $e){
18+
throw new \TypeError("Incorrect type of element at \"$k\": " . $e->getMessage(), 0, $e);
19+
}
20+
}
21+
}
22+
23+
/**
24+
* @phpstan-template TMemberType
25+
* @phpstan-param TMemberType $t
26+
* @phpstan-param \Closure(int) : void $validator
27+
*/
28+
public static function doFoo($t, \Closure $validator) : void{
29+
$validator($t);
30+
}
31+
32+
/**
33+
* @phpstan-template TMemberType
34+
* @phpstan-param TMemberType $t
35+
* @phpstan-param \Closure(mixed) : void $validator
36+
*/
37+
public static function doFoo2($t, \Closure $validator) : void{
38+
$validator($t);
39+
}
40+
}

tests/PHPStan/Rules/Methods/data/check-explicit-mixed.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,25 @@ public function doLorem($t): void
6767
}
6868

6969
}
70+
71+
class TemplateMixed
72+
{
73+
74+
/**
75+
* @template T
76+
* @param T $t
77+
*/
78+
public function doFoo($t): void
79+
{
80+
$this->doBar($t);
81+
}
82+
83+
/**
84+
* @param mixed $mixed
85+
*/
86+
public function doBar($mixed): void
87+
{
88+
$this->doFoo($mixed);
89+
}
90+
91+
}

0 commit comments

Comments
 (0)