Skip to content

Commit 7f04f75

Browse files
committed
Fix is_numeric() filtering in truthy condition
1 parent 967b251 commit 7f04f75

File tree

6 files changed

+74
-6
lines changed

6 files changed

+74
-6
lines changed

src/Rules/Comparison/ImpossibleCheckTypeHelper.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ public function findSpecifiedType(
6363
) {
6464
if ($node->name instanceof \PhpParser\Node\Name) {
6565
$functionName = strtolower((string) $node->name);
66+
if ($functionName === 'assert') {
67+
return $this->findSpecifiedType($scope, $node->args[0]->value);
68+
}
6669
if (in_array($functionName, [
6770
'class_exists',
6871
'interface_exists',
@@ -77,6 +80,9 @@ public function findSpecifiedType(
7780
if (count(TypeUtils::getConstantScalars($argType)) > 0) {
7881
return !$argType->toNumber() instanceof ErrorType;
7982
}
83+
if (TypeUtils::containsGeneralString($argType)) {
84+
return null;
85+
}
8086
} elseif ($functionName === 'defined') {
8187
return null;
8288
} elseif (

src/Type/Php/IsNumericFunctionTypeSpecifyingExtension.php

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
use PHPStan\Type\FloatType;
1313
use PHPStan\Type\FunctionTypeSpecifyingExtension;
1414
use PHPStan\Type\IntegerType;
15-
use PHPStan\Type\MixedType;
1615
use PHPStan\Type\StringType;
1716
use PHPStan\Type\UnionType;
1817

@@ -34,11 +33,6 @@ public function specifyTypes(FunctionReflection $functionReflection, FuncCall $n
3433
throw new \PHPStan\ShouldNotHappenException();
3534
}
3635

37-
$argType = $scope->getType($node->args[0]->value);
38-
if ($context->truthy() && !(new StringType())->isSuperTypeOf($argType)->no() && !$argType instanceof MixedType) {
39-
return new SpecifiedTypes([], []);
40-
}
41-
4236
$numericTypes = [
4337
new IntegerType(),
4438
new FloatType(),

src/Type/TypeUtils.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,4 +339,21 @@ public static function containsCallable(Type $type): bool
339339
return false;
340340
}
341341

342+
public static function containsGeneralString(Type $type): bool
343+
{
344+
if ($type instanceof StringType && !$type instanceof ConstantType) {
345+
return true;
346+
}
347+
348+
if ($type instanceof UnionType) {
349+
foreach ($type->getTypes() as $innerType) {
350+
if ($innerType instanceof StringType && !$innerType instanceof ConstantType) {
351+
return true;
352+
}
353+
}
354+
}
355+
356+
return false;
357+
}
358+
342359
}

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10033,6 +10033,11 @@ public function dataNonEmptyArrayKeyType(): array
1003310033
return $this->gatherAssertTypes(__DIR__ . '/data/non-empty-array-key-type.php');
1003410034
}
1003510035

10036+
public function dataBug3133(): array
10037+
{
10038+
return $this->gatherAssertTypes(__DIR__ . '/data/bug-3133.php');
10039+
}
10040+
1003610041
/**
1003710042
* @dataProvider dataBug2574
1003810043
* @dataProvider dataBug2577
@@ -10098,6 +10103,7 @@ public function dataNonEmptyArrayKeyType(): array
1009810103
* @dataProvider dataNativeStaticReturnType
1009910104
* @dataProvider dataClassPhpDocs
1010010105
* @dataProvider dataNonEmptyArrayKeyType
10106+
* @dataProvider dataBug3133
1010110107
* @param string $assertType
1010210108
* @param string $file
1010310109
* @param mixed ...$args
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace Bug3133;
4+
5+
use function PHPStan\Analyser\assertType;
6+
7+
class Foo
8+
{
9+
10+
/**
11+
* @param string[]|string $arg
12+
*/
13+
public function doFoo($arg): void
14+
{
15+
if (!is_numeric($arg)) {
16+
assertType('array<string>|string', $arg);
17+
return;
18+
}
19+
20+
assertType('string', $arg);
21+
}
22+
23+
/**
24+
* @param string|bool|float|int|mixed[]|null $arg
25+
*/
26+
public function doBar($arg): void
27+
{
28+
if (\is_numeric($arg)) {
29+
assertType('float|int|string', $arg);
30+
}
31+
}
32+
33+
}

tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,10 +206,18 @@ public function testImpossibleCheckTypeFunctionCall(): void
206206
677,
207207
'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%</>.',
208208
],
209+
[
210+
'Call to function assert() with true will always evaluate to true.',
211+
692,
212+
],
209213
[
210214
'Call to function is_numeric() with \'123\' will always evaluate to true.',
211215
692,
212216
],
217+
[
218+
'Call to function assert() with false will always evaluate to false.',
219+
693,
220+
],
213221
[
214222
'Call to function is_numeric() with \'blabla\' will always evaluate to false.',
215223
693,
@@ -326,6 +334,10 @@ public function testImpossibleCheckTypeFunctionCallWithoutAlwaysTrue(): void
326334
'Call to function method_exists() with \'CheckTypeFunctionCall\\\\MethodExistsWithTrait\' and \'unknown\' will always evaluate to false.',
327335
648,
328336
],
337+
[
338+
'Call to function assert() with false will always evaluate to false.',
339+
693,
340+
],
329341
[
330342
'Call to function is_numeric() with \'blabla\' will always evaluate to false.',
331343
693,

0 commit comments

Comments
 (0)