Skip to content

Commit

Permalink
ctype_digit - support type narrowing with cast parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
staabm authored Jan 17, 2023
1 parent 980551b commit 279c781
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 2 deletions.
15 changes: 13 additions & 2 deletions src/Type/Php/CtypeDigitFunctionTypeSpecifyingExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace PHPStan\Type\Php;

use PhpParser\Node\Expr\Cast;
use PhpParser\Node\Expr\FuncCall;
use PHPStan\Analyser\Scope;
use PHPStan\Analyser\SpecifiedTypes;
Expand Down Expand Up @@ -38,7 +39,8 @@ public function specifyTypes(FunctionReflection $functionReflection, FuncCall $n
throw new ShouldNotHappenException();
}

if ($context->true() && $scope->getType($node->getArgs()[0]->value)->isNumericString()->yes()) {
$exprArg = $node->getArgs()[0]->value;
if ($context->true() && $scope->getType($exprArg)->isNumericString()->yes()) {
return new SpecifiedTypes();
}

Expand All @@ -54,7 +56,16 @@ public function specifyTypes(FunctionReflection $functionReflection, FuncCall $n
]);
}

return $this->typeSpecifier->create($node->getArgs()[0]->value, TypeCombinator::union(...$types), $context, false, $scope);
$unionType = TypeCombinator::union(...$types);
$specifiedTypes = $this->typeSpecifier->create($exprArg, $unionType, $context, false, $scope);

if ($context->true() && $exprArg instanceof Cast) {
$specifiedTypes = $specifiedTypes->unionWith(
$this->typeSpecifier->create($exprArg->expr, $unionType, $context, false, $scope),
);
}

return $specifiedTypes;
}

public function setTypeSpecifier(TypeSpecifier $typeSpecifier): void
Expand Down
2 changes: 2 additions & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1175,6 +1175,8 @@ public function dataFileAsserts(): iterable
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-8621.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-8084.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-3019.php');

yield from $this->gatherAssertTypes(__DIR__ . '/data/callsite-cast-narrowing.php');
}

/**
Expand Down
54 changes: 54 additions & 0 deletions tests/PHPStan/Analyser/data/callsite-cast-narrowing.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

namespace CallsiteCastNarrowing;

use function PHPStan\Testing\assertType;

class HelloWorld
{
public function sayHello($mixed, int $int, string $string): void
{
if (ctype_digit((string) $mixed)) {
assertType('int<48, 57>|int<256, max>|numeric-string', $mixed);
} else {
assertType('mixed', $mixed);
}
assertType('mixed', $mixed);

if (ctype_digit((int) $mixed)) {
assertType('int<48, 57>|int<256, max>|numeric-string', $mixed);
} else {
assertType('mixed', $mixed);
}
assertType('mixed', $mixed);

if (ctype_digit((string) $int)) {
assertType('int', $int);
} else {
assertType('int', $int);
}
assertType('int', $int);

if (ctype_digit((int) $int)) {
assertType('int<48, 57>|int<256, max>', $int);
} else {
assertType('int', $int);
}
assertType('int', $int);

if (ctype_digit((string) $string)) {
assertType('numeric-string', $string);
} else {
assertType('string', $string);
}
assertType('string', $string);

if (ctype_digit((int) $string)) {
assertType('numeric-string', $string);
} else {
assertType('string', $string);
}
assertType('string', $string);
}

}

0 comments on commit 279c781

Please sign in to comment.