Skip to content

Commit 444acab

Browse files
phpstan-botstaabm
andauthored
Narrow regex subject to decimal-int-string when every alternation branch is a decimal integer (#5814)
Co-authored-by: Markus Staab <maggus.staab@googlemail.com>
1 parent 3cbc108 commit 444acab

2 files changed

Lines changed: 47 additions & 1 deletion

File tree

‎src/Type/Regex/RegexGroupParser.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,7 @@ private function walkGroupAst(
617617
->onlyLiterals($newLiterals)
618618
->nonEmpty($walkResult->isNonEmpty()->or($nonEmpty))
619619
->nonFalsy($walkResult->isNonFalsy()->or($nonFalsy))
620-
->decimalInteger($walkResult->isDecimalInteger()->and($decimalInteger));
620+
->decimalInteger(TrinaryLogic::maxMin($walkResult->isDecimalInteger(), $decimalInteger));
621621
}
622622

623623
// [^0-9] should not parse as decimal-int-string, and [^list-everything-but-numbers] is technically
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug14784;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
function doFoo(string $str): void
8+
{
9+
if (preg_match('/^(?:0|-?[1-9][0-9]*)$/', $str)) {
10+
assertType('decimal-int-string', $str);
11+
}
12+
13+
// capturing group variant
14+
if (preg_match('/^(0|-?[1-9][0-9]*)$/', $str, $matches)) {
15+
assertType('decimal-int-string', $str);
16+
assertType("array{decimal-int-string, '0'|(decimal-int-string&non-falsy-string)}", $matches);
17+
}
18+
19+
// alternation of two decimal literals
20+
if (preg_match('/^(?:0|123)$/', $str)) {
21+
assertType('decimal-int-string', $str);
22+
}
23+
24+
// alternation without a leading sign
25+
if (preg_match('/^(?:0|[1-9][0-9]*)$/', $str)) {
26+
assertType('decimal-int-string', $str);
27+
}
28+
29+
// alternation digits prefixed by required digits stays decimal
30+
if (preg_match('/^[0-9]+(?:0|5)$/', $str)) {
31+
assertType('decimal-int-string', $str);
32+
}
33+
34+
// an alternation branch that is not decimal breaks the narrowing
35+
if (preg_match('/^(?:0|abc)$/', $str)) {
36+
assertType('non-empty-string', $str);
37+
}
38+
if (preg_match('/^(?:2|abc)$/', $str)) {
39+
assertType('non-falsy-string', $str);
40+
}
41+
42+
// a capturing decimal alternation keeps the group decimal too
43+
if (preg_match('/^(0|[1-9][0-9]*)$/', $str, $matches)) {
44+
assertType("'0'|(decimal-int-string&non-falsy-string)", $matches[1]);
45+
}
46+
}

0 commit comments

Comments
 (0)