Skip to content
8 changes: 4 additions & 4 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ parameters:
path: src/Analyser/AnalyserResultFinalizer.php

-
rawMessage: PHPDoc tag @var with type int|string is not subtype of type string.
identifier: varTag.type
rawMessage: PHPDoc tag @var with type int|string is not subtype of native type string.
identifier: varTag.nativeType
count: 1
path: src/Analyser/ArgumentsNormalizer.php

Expand Down Expand Up @@ -1054,8 +1054,8 @@ parameters:
path: src/Type/Constant/ConstantStringType.php

-
rawMessage: PHPDoc tag @var with type int|string is not subtype of type string.
identifier: varTag.type
rawMessage: PHPDoc tag @var with type int|string is not subtype of native type string.
identifier: varTag.nativeType
count: 1
path: src/Type/Constant/ConstantStringType.php

Expand Down
7 changes: 4 additions & 3 deletions src/Analyser/ExprHandler/FuncCallHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -750,9 +750,6 @@ public function resolveType(MutatingScope $scope, Expr $expr): Type
}

$functionReflection = $this->reflectionProvider->getFunction($expr->name, $scope);
if ($scope->nativeTypesPromoted) {
return ParametersAcceptorSelector::combineAcceptors($functionReflection->getVariants())->getNativeReturnType();
}

if ($functionReflection->getName() === 'call_user_func') {
$result = ArgumentsNormalizer::reorderCallUserFuncArguments($expr, $scope);
Expand Down Expand Up @@ -810,6 +807,10 @@ public function resolveType(MutatingScope $scope, Expr $expr): Type
}
}

if ($scope->nativeTypesPromoted) {
return ParametersAcceptorSelector::combineAcceptors($functionReflection->getVariants())->getNativeReturnType();
}

return VoidToNullTypeTransformer::transform($parametersAcceptor->getReturnType(), $expr);
}

Expand Down
36 changes: 36 additions & 0 deletions tests/PHPStan/Analyser/Bug14522Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php declare(strict_types = 1);

namespace PHPStan\Analyser;

use PHPStan\Testing\TypeInferenceTestCase;
use PHPUnit\Framework\Attributes\DataProvider;

class Bug14522Test extends TypeInferenceTestCase
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might make sense to rename this test into TreatPhpDocTypesAsCertainInferenceTestCase so we can re-use it in the future for other inference bugs which only depend on treatPhpDocTypesAsCertain: false

{

public static function dataFileAsserts(): iterable
{
yield from self::gatherAssertTypes(__DIR__ . '/nsrt/bug-14522.php');
}

/**
* @param mixed ...$args
*/
#[DataProvider('dataFileAsserts')]
public function testFileAsserts(
string $assertType,
string $file,
...$args,
): void
{
$this->assertFileAsserts($assertType, $file, ...$args);
}

public static function getAdditionalConfigFiles(): array
{
return [
__DIR__ . '/bug-14522.neon',
];
}

}
2 changes: 2 additions & 0 deletions tests/PHPStan/Analyser/bug-14522.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
parameters:
treatPhpDocTypesAsCertain: false
2 changes: 1 addition & 1 deletion tests/PHPStan/Analyser/data/bug-9307.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public function test(): void
}
}

assertType('array<*ERROR*>', $objects); // could be array<int, Bug9307\Item>
assertType('array<int, Bug9307\Item>', $objects);

$this->acceptObjects($objects);
}
Expand Down
19 changes: 19 additions & 0 deletions tests/PHPStan/Analyser/nsrt/bug-13273.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php declare(strict_types = 1);

namespace Bug13273;

use function PHPStan\Testing\assertType;
use function PHPStan\Testing\assertNativeType;

function test(int $param): void
{
$local = 'foo';

$vars = get_defined_vars();
assertType('true', array_key_exists('param', $vars));
assertType('true', array_key_exists('local', $vars));
assertType('false', array_key_exists('nonexistent', $vars));
assertNativeType('true', array_key_exists('param', $vars));
assertNativeType('true', array_key_exists('local', $vars));
assertNativeType('false', array_key_exists('nonexistent', $vars));
}
32 changes: 32 additions & 0 deletions tests/PHPStan/Analyser/nsrt/bug-14522.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php declare(strict_types = 1);

namespace Bug14522;

use function PHPStan\Testing\assertType;
use function PHPStan\Testing\assertNativeType;

/**
* @return int<1, max>
*/
function getBackoffTime(int $retryCount, int $maxBackoff): int
{
$retryCount = max(0, $retryCount);
assertNativeType('int<0, max>', $retryCount);
$maxBackoff = max(1, $maxBackoff);
assertNativeType('int<1, max>', $maxBackoff);

$total = 0;
for ($i = 0; $i <= $retryCount; ++$i) {
$total += min(2 ** $i, $maxBackoff);
}
assertType('int<1, max>', $total);
return $total;
}

/** @param int<-2, 2> $retryCount */
function maxWithBoundedRange(int $retryCount): void
{
$result = max(0, $retryCount);
assertType('int<0, 2>', $result);
assertNativeType('int<0, max>', $result);
}
6 changes: 3 additions & 3 deletions tests/PHPStan/Analyser/nsrt/bug-8956.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,18 @@ public function doFoo(): void
{
foreach (array_chunk(range(0, 10), 60) as $chunk) {
assertType('array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}', $chunk);
assertNativeType('array', $chunk);
assertNativeType('array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}', $chunk);
foreach ($chunk as $val) {
assertType('0|1|2|3|4|5|6|7|8|9|10', $val);
assertNativeType('mixed', $val);
assertNativeType('0|1|2|3|4|5|6|7|8|9|10', $val);
}
}
}

public function doBar(): void
{
assertType('array{array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}', array_chunk(range(0, 10), 60));
assertNativeType('list<array>', array_chunk(range(0, 10), 60));
assertNativeType('array{array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}', array_chunk(range(0, 10), 60));
}

}
2 changes: 1 addition & 1 deletion tests/PHPStan/Analyser/nsrt/native-types.php
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ public function doFoo(): void
{
$a = array_replace([1, 2, 3], [4, 5, 6]);
assertType('array{4, 5, 6}', $a);
assertNativeType('array', $a);
assertNativeType('array{4, 5, 6}', $a);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,17 +198,14 @@ public function testImpossibleCheckTypeFunctionCall(): void
[
'Call to function method_exists() with \'CheckTypeFunctionCall\\\\MethodExistsWithTrait\' and \'method\' will always evaluate to true.',
659,
'Because the type is coming from a PHPDoc, you can turn off this check by setting <fg=cyan>treatPhpDocTypesAsCertain: false</> in your <fg=cyan>%configurationFile%</>.',
],
[
'Call to function method_exists() with \'CheckTypeFunctionCall\\\\MethodExistsWithTrait\' and \'someAnother\' will always evaluate to true.',
662,
'Because the type is coming from a PHPDoc, you can turn off this check by setting <fg=cyan>treatPhpDocTypesAsCertain: false</> in your <fg=cyan>%configurationFile%</>.',
],
[
'Call to function method_exists() with \'CheckTypeFunctionCall\\\\MethodExistsWithTrait\' and \'unknown\' will always evaluate to false.',
665,
'Because the type is coming from a PHPDoc, you can turn off this check by setting <fg=cyan>treatPhpDocTypesAsCertain: false</> in your <fg=cyan>%configurationFile%</>.',
],
[
'Call to function method_exists() with \'CheckTypeFunctionCall\\\\MethodExistsWithTrait\' and \'method\' will always evaluate to true.',
Expand Down Expand Up @@ -958,8 +955,6 @@ public function testBug4890b(): void

public function testBug10502(): void
{
$tipText = 'Because the type is coming from a PHPDoc, you can turn off this check by setting <fg=cyan>treatPhpDocTypesAsCertain: false</> in your <fg=cyan>%configurationFile%</>.';

$this->treatPhpDocTypesAsCertain = true;
$this->analyse([__DIR__ . '/data/bug-10502.php'], [
[
Expand All @@ -969,7 +964,6 @@ public function testBug10502(): void
[
"Call to function is_callable() with array{1: 'count', 0: ArrayObject<int, int>} will always evaluate to true.",
24,
$tipText,
],
]);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,6 @@ public function testStrictComparison(): void
[
'Strict comparison using === between int<0, 1> and 100 will always evaluate to false.',
622,
$tipText,
],
[
'Strict comparison using === between 100 and \'foo\' will always evaluate to false.',
Expand Down Expand Up @@ -444,7 +443,6 @@ public function testBug7555(): void
[
'Strict comparison using === between 2 and 2 will always evaluate to true.',
11,
'Because the type is coming from a PHPDoc, you can turn off this check by setting <fg=cyan>treatPhpDocTypesAsCertain: false</> in your <fg=cyan>%configurationFile%</>.',
],
]);
}
Expand Down Expand Up @@ -493,13 +491,10 @@ public function testBug6181(): void

public function testBug2851b(): void
{
$tipText = 'Because the type is coming from a PHPDoc, you can turn off this check by setting <fg=cyan>treatPhpDocTypesAsCertain: false</> in your <fg=cyan>%configurationFile%</>.';

$this->analyse([__DIR__ . '/data/bug-2851b.php'], [
[
'Strict comparison using === between 0 and 0 will always evaluate to true.',
21,
$tipText,
],
]);
}
Expand Down Expand Up @@ -570,17 +565,14 @@ public function testBug4242(): void

public function testBug3633(): void
{
$tipText = 'Because the type is coming from a PHPDoc, you can turn off this check by setting <fg=cyan>treatPhpDocTypesAsCertain: false</> in your <fg=cyan>%configurationFile%</>.';
$this->analyse([__DIR__ . '/data/bug-3633.php'], [
[
'Strict comparison using === between class-string<$this(Bug3633\HelloWorld)> and \'Bug3633\\\OtherClass\' will always evaluate to false.',
37,
$tipText,
],
[
'Strict comparison using === between \'Bug3633\\\HelloWorld\' and \'Bug3633\\\HelloWorld\' will always evaluate to true.',
41,
$tipText,
],
[
'Strict comparison using === between \'Bug3633\\\HelloWorld\' and \'Bug3633\\\OtherClass\' will always evaluate to false.',
Expand All @@ -589,47 +581,38 @@ public function testBug3633(): void
[
'Strict comparison using === between class-string<$this(Bug3633\OtherClass)> and \'Bug3633\\\HelloWorld\' will always evaluate to false.',
64,
$tipText,
],
[
'Strict comparison using === between \'Bug3633\\\OtherClass\' and \'Bug3633\\\HelloWorld\' will always evaluate to false.',
71,
$tipText,
],
[
'Strict comparison using === between \'Bug3633\\\OtherClass\' and \'Bug3633\\\OtherClass\' will always evaluate to true.',
74,
$tipText,
],
[
'Strict comparison using === between class-string<$this(Bug3633\FinalClass)> and \'Bug3633\\\HelloWorld\' will always evaluate to false.',
93,
$tipText,
],
[
'Strict comparison using === between class-string<$this(Bug3633\FinalClass)> and \'Bug3633\\\OtherClass\' will always evaluate to false.',
96,
$tipText,
],
[
'Strict comparison using === between \'Bug3633\\\FinalClass\' and \'Bug3633\\\FinalClass\' will always evaluate to true.',
102,
$tipText,
],
[
'Strict comparison using === between \'Bug3633\\\FinalClass\' and \'Bug3633\\\HelloWorld\' will always evaluate to false.',
106,
$tipText,
],
[
'Strict comparison using === between \'Bug3633\\\FinalClass\' and \'Bug3633\\\OtherClass\' will always evaluate to false.',
109,
$tipText,
],
[
'Strict comparison using !== between \'Bug3633\\\FinalClass\' and \'Bug3633\\\FinalClass\' will always evaluate to false.',
112,
$tipText,
],
[
'Strict comparison using === between \'Bug3633\\\FinalClass\' and \'Bug3633\\\FinalClass\' will always evaluate to true.',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,16 @@ public function testReportPhpDoc(): void
public function testBug7580(): void
{
$this->treatPhpDocTypesAsCertain = false;
$this->analyse([__DIR__ . '/data/bug-7580.php'], []);
$this->analyse([__DIR__ . '/data/bug-7580.php'], [
[
'Ternary operator condition is always false.',
6,
],
[
'Ternary operator condition is always true.',
9,
],
]);
}

public function testBug3370(): void
Expand Down
2 changes: 1 addition & 1 deletion tests/PHPStan/Rules/Debug/DumpNativeTypeRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public function testBug14508(): void
{
$this->analyse([__DIR__ . '/data/bug-14508-native.php'], [
[
'Dumped type #1: int',
'Dumped type #1: int<0, 100>',
10,
],
[
Expand Down
Loading