From 6ca49327a343f1dcc8ee180b2e0983be9477fb57 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Wed, 11 Dec 2024 10:47:06 +0100 Subject: [PATCH 1/2] RegexArrayShapeMatcher: fix regex alternatives in capture group are concatenated --- src/Type/Regex/RegexGroupParser.php | 25 ++++++++++++++++++++- tests/PHPStan/Analyser/nsrt/bug-12210.php | 27 +++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 tests/PHPStan/Analyser/nsrt/bug-12210.php diff --git a/src/Type/Regex/RegexGroupParser.php b/src/Type/Regex/RegexGroupParser.php index 946e492aa7..75db1668c7 100644 --- a/src/Type/Regex/RegexGroupParser.php +++ b/src/Type/Regex/RegexGroupParser.php @@ -446,7 +446,30 @@ private function walkGroupAst( } if ($ast->getId() === '#alternation') { - $inAlternation = true; + $newLiterals = []; + foreach ($children as $child) { + $walkResult = $this->walkGroupAst( + $child, + true, + $inClass, + $patternModifiers, + $walkResult->onlyLiterals([]), + ); + + if ($newLiterals === null) { + continue; + } + + if (count($walkResult->getOnlyLiterals() ?? []) > 0) { + foreach ($walkResult->getOnlyLiterals() as $alternationLiterals) { + $newLiterals[] = $alternationLiterals; + } + } else { + $newLiterals = null; + } + } + + return $walkResult->onlyLiterals($newLiterals); } // [^0-9] should not parse as numeric-string, and [^list-everything-but-numbers] is technically diff --git a/tests/PHPStan/Analyser/nsrt/bug-12210.php b/tests/PHPStan/Analyser/nsrt/bug-12210.php new file mode 100644 index 0000000000..165b61b63e --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-12210.php @@ -0,0 +1,27 @@ += 7.4 + +declare(strict_types = 1); + +namespace Bug12210; + +use function PHPStan\Testing\assertType; + +function bug12210a(string $text): void { + assert(preg_match('(((sum|min|max)))', $text, $match) === 1); + assertType("array{string, 'max'|'min'|'sum', 'max'|'min'|'sum'}", $match); +} + +function bug12210b(string $text): void { + assert(preg_match('(((sum|min|ma.)))', $text, $match) === 1); + assertType("array{string, non-empty-string, non-falsy-string}", $match); +} + +function bug12210c(string $text): void { + assert(preg_match('(((su.|min|max)))', $text, $match) === 1); + assertType("array{string, non-empty-string, non-falsy-string}", $match); +} + +function bug12210d(string $text): void { + assert(preg_match('(((sum|mi.|max)))', $text, $match) === 1); + assertType("array{string, non-empty-string, non-falsy-string}", $match); +} From 375ed5ea6a8f419885270d7ffe3b1917ed8c120c Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Wed, 11 Dec 2024 11:01:03 +0100 Subject: [PATCH 2/2] Added regression test --- tests/PHPStan/Analyser/nsrt/bug-12173.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tests/PHPStan/Analyser/nsrt/bug-12173.php diff --git a/tests/PHPStan/Analyser/nsrt/bug-12173.php b/tests/PHPStan/Analyser/nsrt/bug-12173.php new file mode 100644 index 0000000000..e92ce7da4e --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-12173.php @@ -0,0 +1,19 @@ += 7.4 + +namespace Bug12173; + +use function PHPStan\Testing\assertType; + +class HelloWorld +{ + public function parse(string $string): void + { + $regex = '#.*(?(apple|orange)).*#'; + + if (preg_match($regex, $string, $matches) !== 1) { + throw new \Exception('Invalid input'); + } + + assertType("'apple'|'orange'", $matches['fruit']);; + } +}