From ca8675d3298b9305d143ce0b46ba20afaa3dc613 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 3 Sep 2024 22:16:17 +0200 Subject: [PATCH 1/3] ArrayShapeMatcher - Don't optimize alternations with optional-groups for tagged-unions --- src/Type/Php/RegexArrayShapeMatcher.php | 4 ++++ src/Type/Regex/RegexCapturingGroup.php | 5 +++++ tests/PHPStan/Analyser/nsrt/preg_match_shapes.php | 8 ++++++++ 3 files changed, 17 insertions(+) diff --git a/src/Type/Php/RegexArrayShapeMatcher.php b/src/Type/Php/RegexArrayShapeMatcher.php index 628bcb0d3c..fb94b88ab0 100644 --- a/src/Type/Php/RegexArrayShapeMatcher.php +++ b/src/Type/Php/RegexArrayShapeMatcher.php @@ -280,6 +280,10 @@ private function getOnlyTopLevelAlternation(array $captureGroups): ?RegexAlterna return null; } + if ($captureGroup->inOptionalQuantification()) { + return null; + } + if ($alternation === null) { $alternation = $captureGroup->getAlternation(); } elseif ($alternation->getId() !== $captureGroup->getAlternation()->getId()) { diff --git a/src/Type/Regex/RegexCapturingGroup.php b/src/Type/Regex/RegexCapturingGroup.php index 62708a2de3..51a1fc9d85 100644 --- a/src/Type/Regex/RegexCapturingGroup.php +++ b/src/Type/Regex/RegexCapturingGroup.php @@ -82,6 +82,11 @@ public function isOptional(): bool || $this->parent !== null && $this->parent->isOptional(); } + public function inOptionalQuantification(): bool + { + return $this->inOptionalQuantification; + } + public function inOptionalAlternation(): bool { if (!$this->inAlternation()) { diff --git a/tests/PHPStan/Analyser/nsrt/preg_match_shapes.php b/tests/PHPStan/Analyser/nsrt/preg_match_shapes.php index 694a7cc886..4b3d4ff17d 100644 --- a/tests/PHPStan/Analyser/nsrt/preg_match_shapes.php +++ b/tests/PHPStan/Analyser/nsrt/preg_match_shapes.php @@ -753,3 +753,11 @@ function bug11622 (string $expression): void { assertType("array{string, string}", $matches); } } + +function bug11604 (string $string): void { + if (! preg_match('/(XX)|(YY)?ZZ/', $string, $matches)) { + return; + } + + assertType("array{0: string, 1?: ''|'XX', 2?: 'YY'}", $matches); +} From aa007c0f8c323f02a4b76cf0115f6c935578ffd6 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 3 Sep 2024 22:23:22 +0200 Subject: [PATCH 2/3] Update preg_match_shapes.php --- tests/PHPStan/Analyser/nsrt/preg_match_shapes.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/PHPStan/Analyser/nsrt/preg_match_shapes.php b/tests/PHPStan/Analyser/nsrt/preg_match_shapes.php index 4b3d4ff17d..ce976cf924 100644 --- a/tests/PHPStan/Analyser/nsrt/preg_match_shapes.php +++ b/tests/PHPStan/Analyser/nsrt/preg_match_shapes.php @@ -760,4 +760,5 @@ function bug11604 (string $string): void { } assertType("array{0: string, 1?: ''|'XX', 2?: 'YY'}", $matches); + // could be array{string, '', 'YY'}|array{string, 'XX'}|array{string} } From 5d3895de24db5102173f73fdf7b069a44bd1c1af Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Wed, 4 Sep 2024 08:31:25 +0200 Subject: [PATCH 3/3] more tests --- tests/PHPStan/Analyser/nsrt/preg_match_shapes.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/PHPStan/Analyser/nsrt/preg_match_shapes.php b/tests/PHPStan/Analyser/nsrt/preg_match_shapes.php index ce976cf924..4b17f15ed4 100644 --- a/tests/PHPStan/Analyser/nsrt/preg_match_shapes.php +++ b/tests/PHPStan/Analyser/nsrt/preg_match_shapes.php @@ -762,3 +762,9 @@ function bug11604 (string $string): void { assertType("array{0: string, 1?: ''|'XX', 2?: 'YY'}", $matches); // could be array{string, '', 'YY'}|array{string, 'XX'}|array{string} } + +function bug11604b (string $string): void { + if (preg_match('/(XX)|(YY)?(ZZ)/', $string, $matches)) { + assertType("array{0: string, 1?: ''|'XX', 2?: ''|'YY', 3?: 'ZZ'}", $matches); + } +}