Skip to content

Commit

Permalink
Fix is_numeric() filtering in truthy condition
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Aug 24, 2020
1 parent 967b251 commit 7f04f75
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 6 deletions.
6 changes: 6 additions & 0 deletions src/Rules/Comparison/ImpossibleCheckTypeHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ public function findSpecifiedType(
) {
if ($node->name instanceof \PhpParser\Node\Name) {
$functionName = strtolower((string) $node->name);
if ($functionName === 'assert') {
return $this->findSpecifiedType($scope, $node->args[0]->value);
}
if (in_array($functionName, [
'class_exists',
'interface_exists',
Expand All @@ -77,6 +80,9 @@ public function findSpecifiedType(
if (count(TypeUtils::getConstantScalars($argType)) > 0) {
return !$argType->toNumber() instanceof ErrorType;
}
if (TypeUtils::containsGeneralString($argType)) {
return null;
}
} elseif ($functionName === 'defined') {
return null;
} elseif (
Expand Down
6 changes: 0 additions & 6 deletions src/Type/Php/IsNumericFunctionTypeSpecifyingExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
use PHPStan\Type\FloatType;
use PHPStan\Type\FunctionTypeSpecifyingExtension;
use PHPStan\Type\IntegerType;
use PHPStan\Type\MixedType;
use PHPStan\Type\StringType;
use PHPStan\Type\UnionType;

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

$argType = $scope->getType($node->args[0]->value);
if ($context->truthy() && !(new StringType())->isSuperTypeOf($argType)->no() && !$argType instanceof MixedType) {
return new SpecifiedTypes([], []);
}

$numericTypes = [
new IntegerType(),
new FloatType(),
Expand Down
17 changes: 17 additions & 0 deletions src/Type/TypeUtils.php
Original file line number Diff line number Diff line change
Expand Up @@ -339,4 +339,21 @@ public static function containsCallable(Type $type): bool
return false;
}

public static function containsGeneralString(Type $type): bool
{
if ($type instanceof StringType && !$type instanceof ConstantType) {
return true;
}

if ($type instanceof UnionType) {
foreach ($type->getTypes() as $innerType) {
if ($innerType instanceof StringType && !$innerType instanceof ConstantType) {
return true;
}
}
}

return false;
}

}
6 changes: 6 additions & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10033,6 +10033,11 @@ public function dataNonEmptyArrayKeyType(): array
return $this->gatherAssertTypes(__DIR__ . '/data/non-empty-array-key-type.php');
}

public function dataBug3133(): array
{
return $this->gatherAssertTypes(__DIR__ . '/data/bug-3133.php');
}

/**
* @dataProvider dataBug2574
* @dataProvider dataBug2577
Expand Down Expand Up @@ -10098,6 +10103,7 @@ public function dataNonEmptyArrayKeyType(): array
* @dataProvider dataNativeStaticReturnType
* @dataProvider dataClassPhpDocs
* @dataProvider dataNonEmptyArrayKeyType
* @dataProvider dataBug3133
* @param string $assertType
* @param string $file
* @param mixed ...$args
Expand Down
33 changes: 33 additions & 0 deletions tests/PHPStan/Analyser/data/bug-3133.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace Bug3133;

use function PHPStan\Analyser\assertType;

class Foo
{

/**
* @param string[]|string $arg
*/
public function doFoo($arg): void
{
if (!is_numeric($arg)) {
assertType('array<string>|string', $arg);
return;
}

assertType('string', $arg);
}

/**
* @param string|bool|float|int|mixed[]|null $arg
*/
public function doBar($arg): void
{
if (\is_numeric($arg)) {
assertType('float|int|string', $arg);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -206,10 +206,18 @@ public function testImpossibleCheckTypeFunctionCall(): void
677,
'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 assert() with true will always evaluate to true.',
692,
],
[
'Call to function is_numeric() with \'123\' will always evaluate to true.',
692,
],
[
'Call to function assert() with false will always evaluate to false.',
693,
],
[
'Call to function is_numeric() with \'blabla\' will always evaluate to false.',
693,
Expand Down Expand Up @@ -326,6 +334,10 @@ public function testImpossibleCheckTypeFunctionCallWithoutAlwaysTrue(): void
'Call to function method_exists() with \'CheckTypeFunctionCall\\\\MethodExistsWithTrait\' and \'unknown\' will always evaluate to false.',
648,
],
[
'Call to function assert() with false will always evaluate to false.',
693,
],
[
'Call to function is_numeric() with \'blabla\' will always evaluate to false.',
693,
Expand Down

0 comments on commit 7f04f75

Please sign in to comment.