diff --git a/src/Type/Regex/RegexGroupParser.php b/src/Type/Regex/RegexGroupParser.php index 0e61d50e9f..dba7fcfe4e 100644 --- a/src/Type/Regex/RegexGroupParser.php +++ b/src/Type/Regex/RegexGroupParser.php @@ -367,6 +367,7 @@ private function walkGroupAst( if ( $ast->getId() === '#concatenation' && count($children) > 0 + && !$walkResult->isInOptionalQuantification() ) { $meaningfulTokens = 0; foreach ($children as $child) { @@ -400,13 +401,14 @@ private function walkGroupAst( if ($min === 0) { $walkResult = $walkResult->inOptionalQuantification(true); } - if ($min >= 1) { - $walkResult = $walkResult - ->nonEmpty(TrinaryLogic::createYes()) - ->inOptionalQuantification(false); - } - if ($min >= 2 && !$inAlternation) { - $walkResult = $walkResult->nonFalsy(TrinaryLogic::createYes()); + + if (!$walkResult->isInOptionalQuantification()) { + if ($min >= 1) { + $walkResult = $walkResult->nonEmpty(TrinaryLogic::createYes()); + } + if ($min >= 2 && !$inAlternation) { + $walkResult = $walkResult->nonFalsy(TrinaryLogic::createYes()); + } } $walkResult = $walkResult->onlyLiterals(null); diff --git a/tests/PHPStan/Analyser/nsrt/preg_match_shapes.php b/tests/PHPStan/Analyser/nsrt/preg_match_shapes.php index 0a6b883e4c..701dc1f049 100644 --- a/tests/PHPStan/Analyser/nsrt/preg_match_shapes.php +++ b/tests/PHPStan/Analyser/nsrt/preg_match_shapes.php @@ -894,8 +894,27 @@ function testEscapedDelimiter (string $string): void { } } -function bugUnescapedDashAfterRange (string $string): void { +function bugUnescapedDashAfterRange (string $string): void +{ if (preg_match('/([0-1-y])/', $string, $matches)) { assertType("array{string, non-empty-string}", $matches); } } + +function bug11744(string $string): void +{ + if (!preg_match('~^((/[a-z]+)?)~', $string, $matches)) { + return; + } + assertType('array{0: string, 1: string, 2?: non-falsy-string}', $matches); + + if (!preg_match('~^((/[a-z]+)?.*)~', $string, $matches)) { + return; + } + assertType('array{0: string, 1: string, 2?: non-falsy-string}', $matches); + + if (!preg_match('~^((/[a-z]+)?.+)~', $string, $matches)) { + return; + } + assertType('array{0: string, 1: non-empty-string, 2?: non-falsy-string}', $matches); +}