Skip to content

Commit dc813fe

Browse files
committed
Merge branch 2.1.x into 2.2.x
2 parents 6f6897d + 4c6ef6e commit dc813fe

File tree

7 files changed

+161
-7
lines changed

7 files changed

+161
-7
lines changed

src/Rules/FunctionCallParametersCheck.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,10 @@ public function check(
391391
!$parameter->passedByReference()->createsNewVariable()
392392
|| (!$isBuiltin && !$argumentValueType instanceof ErrorType)
393393
) {
394-
$accepts = $this->ruleLevelHelper->accepts($parameterType, $argumentValueType, $scope->isDeclareStrictTypes());
394+
// @see https://github.com/php/php-src/issues/21568#issuecomment-4148832540
395+
$isStrictTypes = $scope->isDeclareStrictTypes()
396+
&& (!$isBuiltin || !$parameterType->isCallable()->yes());
397+
$accepts = $this->ruleLevelHelper->accepts($parameterType, $argumentValueType, $isStrictTypes);
395398

396399
if (!$accepts->result) {
397400
$verbosityLevel = VerbosityLevel::getRecommendedLevelByType($parameterType, $argumentValueType);

src/Type/CallableType.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ public function accepts(Type $type, bool $strictTypes): AcceptsResult
139139
return $type->isAcceptedBy($this, $strictTypes);
140140
}
141141

142-
return $this->isSuperTypeOfInternal($type, true)->toAcceptsResult();
142+
return $this->isSuperTypeOfInternal($type, true, $strictTypes)->toAcceptsResult();
143143
}
144144

145145
public function isSuperTypeOf(Type $type): IsSuperTypeOfResult
@@ -151,7 +151,7 @@ public function isSuperTypeOf(Type $type): IsSuperTypeOfResult
151151
return $this->isSuperTypeOfInternal($type, false);
152152
}
153153

154-
private function isSuperTypeOfInternal(Type $type, bool $treatMixedAsAny): IsSuperTypeOfResult
154+
private function isSuperTypeOfInternal(Type $type, bool $treatMixedAsAny, bool $strictTypes = true): IsSuperTypeOfResult
155155
{
156156
$isCallable = new IsSuperTypeOfResult($type->isCallable(), []);
157157
if ($isCallable->no()) {
@@ -184,7 +184,7 @@ private function isSuperTypeOfInternal(Type $type, bool $treatMixedAsAny): IsSup
184184
if (!$variant instanceof CallableParametersAcceptor) {
185185
return IsSuperTypeOfResult::createNo([]);
186186
}
187-
$isSuperType = CallableTypeHelper::isParametersAcceptorSuperTypeOf($this, $variant, $treatMixedAsAny);
187+
$isSuperType = CallableTypeHelper::isParametersAcceptorSuperTypeOf($this, $variant, $treatMixedAsAny, $strictTypes);
188188
if ($variantsResult === null) {
189189
$variantsResult = $isSuperType;
190190
} else {

src/Type/CallableTypeHelper.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public static function isParametersAcceptorSuperTypeOf(
1616
CallableParametersAcceptor $ours,
1717
CallableParametersAcceptor $theirs,
1818
bool $treatMixedAsAny,
19+
bool $strictTypes = true,
1920
): IsSuperTypeOfResult
2021
{
2122
$theirParameters = $theirs->getParameters();
@@ -72,7 +73,7 @@ public static function isParametersAcceptorSuperTypeOf(
7273
}
7374

7475
if ($treatMixedAsAny) {
75-
$isSuperType = $theirParameter->getType()->accepts($ourParameterType, true);
76+
$isSuperType = $theirParameter->getType()->accepts($ourParameterType, $strictTypes);
7677
$isSuperType = new IsSuperTypeOfResult($isSuperType->result, $isSuperType->reasons);
7778
} else {
7879
$isSuperType = $theirParameter->getType()->isSuperTypeOf($ourParameterType);

src/Type/ClosureType.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ public function accepts(Type $type, bool $strictTypes): AcceptsResult
225225
return $this->objectType->accepts($type, $strictTypes);
226226
}
227227

228-
return $this->isSuperTypeOfInternal($type, true)->toAcceptsResult();
228+
return $this->isSuperTypeOfInternal($type, true, $strictTypes)->toAcceptsResult();
229229
}
230230

231231
public function isSuperTypeOf(Type $type): IsSuperTypeOfResult
@@ -237,7 +237,7 @@ public function isSuperTypeOf(Type $type): IsSuperTypeOfResult
237237
return $this->isSuperTypeOfInternal($type, false);
238238
}
239239

240-
private function isSuperTypeOfInternal(Type $type, bool $treatMixedAsAny): IsSuperTypeOfResult
240+
private function isSuperTypeOfInternal(Type $type, bool $treatMixedAsAny, bool $strictTypes = true): IsSuperTypeOfResult
241241
{
242242
if ($type instanceof self) {
243243
$parameterTypes = array_map(static fn ($parameter) => $parameter->getType(), $this->getParameters());
@@ -249,6 +249,7 @@ private function isSuperTypeOfInternal(Type $type, bool $treatMixedAsAny): IsSup
249249
$this,
250250
$variant,
251251
$treatMixedAsAny,
252+
$strictTypes,
252253
);
253254
}
254255

tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2789,6 +2789,31 @@ public function testBug12363(): void
27892789
$this->analyse([__DIR__ . '/data/bug-12363.php'], []);
27902790
}
27912791

2792+
#[RequiresPhp('>= 8.1')]
2793+
public function testBug11619(): void
2794+
{
2795+
$this->analyse([__DIR__ . '/data/bug-11619.php'], []);
2796+
}
2797+
2798+
#[RequiresPhp('>= 8.1')]
2799+
public function testBug11619Strict(): void
2800+
{
2801+
$this->analyse([__DIR__ . '/data/bug-11619-strict.php'], [
2802+
[
2803+
'Parameter #1 $string1 of function strnatcasecmp expects string, Bug11619Strict\Foo given.',
2804+
31,
2805+
],
2806+
[
2807+
'Parameter #2 $string2 of function strnatcasecmp expects string, Bug11619Strict\Foo given.',
2808+
31,
2809+
],
2810+
[
2811+
'Parameter #2 $f of function Bug11619Strict\customUsort expects callable(Stringable, Stringable): int, \'strnatcasecmp\' given.',
2812+
58,
2813+
],
2814+
]);
2815+
}
2816+
27922817
public function testBug13247(): void
27932818
{
27942819
$this->analyse([__DIR__ . '/data/bug-13247.php'], []);
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php declare(strict_types = 1); // lint >= 8.1
2+
3+
namespace Bug11619Strict;
4+
5+
final class Foo implements \Stringable {
6+
7+
private function __construct(public readonly string $value) {
8+
}
9+
10+
public static function fromString(string $string): self {
11+
return new self($string);
12+
}
13+
14+
public function __toString(): string {
15+
return $this->value;
16+
}
17+
18+
}
19+
20+
function test(): void
21+
{
22+
$options = [
23+
Foo::fromString('c'),
24+
Foo::fromString('b'),
25+
Foo::fromString('a'),
26+
];
27+
28+
uasort($options, 'strnatcasecmp');
29+
usort($options, 'strnatcasecmp');
30+
31+
uasort($options, fn($a, $b) => strnatcasecmp($a, $b));
32+
uasort($options, fn(string $a, string $b) => strnatcasecmp($a, $b));
33+
}
34+
35+
/**
36+
* @param array<\Stringable> $a
37+
* @param callable(\Stringable, \Stringable): int $f
38+
*/
39+
function customUsort(array &$a, callable $f): void
40+
{
41+
for ($i = 1; $i < count($a); $i++)
42+
for ($j = $i; $j > 0 && $f($a[$j-1], $a[$j]) > 0; $j--)
43+
[$a[$j-1], $a[$j]] = [$a[$j], $a[$j-1]];
44+
}
45+
46+
function userlandComparator(string $a, string $b): int {
47+
return strnatcasecmp($a, $b);
48+
}
49+
50+
function test2(): void
51+
{
52+
$options = [
53+
Foo::fromString('c'),
54+
Foo::fromString('b'),
55+
Foo::fromString('a'),
56+
];
57+
58+
customUsort($options, 'strnatcasecmp');
59+
60+
uasort($options, 'Bug11619Strict\userlandComparator');
61+
usort($options, 'Bug11619Strict\userlandComparator');
62+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php // lint >= 8.1
2+
3+
namespace Bug11619;
4+
5+
final class Foo implements \Stringable {
6+
7+
private function __construct(public readonly string $value) {
8+
}
9+
10+
public static function fromString(string $string): self {
11+
return new self($string);
12+
}
13+
14+
public function __toString(): string {
15+
return $this->value;
16+
}
17+
18+
}
19+
20+
function test(): void
21+
{
22+
$options = [
23+
Foo::fromString('c'),
24+
Foo::fromString('b'),
25+
Foo::fromString('a'),
26+
];
27+
28+
uasort($options, 'strnatcasecmp');
29+
usort($options, 'strnatcasecmp');
30+
31+
uasort($options, fn($a, $b) => strnatcasecmp($a, $b));
32+
uasort($options, fn(string $a, string $b) => strnatcasecmp($a, $b));
33+
}
34+
35+
/**
36+
* @param array<\Stringable> $a
37+
* @param callable(\Stringable, \Stringable): int $f
38+
*/
39+
function customUsort(array &$a, callable $f): void
40+
{
41+
for ($i = 1; $i < count($a); $i++)
42+
for ($j = $i; $j > 0 && $f($a[$j-1], $a[$j]) > 0; $j--)
43+
[$a[$j-1], $a[$j]] = [$a[$j], $a[$j-1]];
44+
}
45+
46+
function userlandComparator(string $a, string $b): int {
47+
return strnatcasecmp($a, $b);
48+
}
49+
50+
function test2(): void
51+
{
52+
$options = [
53+
Foo::fromString('c'),
54+
Foo::fromString('b'),
55+
Foo::fromString('a'),
56+
];
57+
58+
customUsort($options, 'strnatcasecmp');
59+
60+
uasort($options, 'Bug11619\userlandComparator');
61+
usort($options, 'Bug11619\userlandComparator');
62+
}

0 commit comments

Comments
 (0)