Skip to content

Commit c3f580e

Browse files
authored
RegexArrayShapeMatcher - Fix matching literal "-" in character classes
1 parent 14fd384 commit c3f580e

File tree

2 files changed

+33
-14
lines changed

2 files changed

+33
-14
lines changed

src/Type/Regex/RegexGroupParser.php

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -431,8 +431,6 @@ private function walkGroupAst(
431431
if (!$inOptionalQuantification) {
432432
$isNonEmpty = TrinaryLogic::createYes();
433433
}
434-
} elseif (!in_array($ast->getValueToken(), ['capturing_name'], true)) {
435-
$onlyLiterals = null;
436434
}
437435
} elseif (!in_array($ast->getId(), ['#capturing', '#namedcapturing'], true)) {
438436
$onlyLiterals = null;
@@ -508,16 +506,26 @@ private function getLiteralValue(TreeNode $node, ?array &$onlyLiterals, bool $ap
508506
$token = $node->getValueToken();
509507
$value = $node->getValueValue();
510508

511-
if (in_array($token, ['literal', 'escaped_end_class'], true)) {
509+
if (
510+
in_array($token, [
511+
'literal', 'escaped_end_class',
512+
// literal "-" in front/back of a character class like '[-a-z]' or '[abc-]', not forming a range
513+
'range',
514+
// literal "[" or "]" inside character classes '[[]' or '[]]'
515+
'class_', '_class_literal',
516+
], true)
517+
) {
512518
if (str_contains($patternModifiers, 'x') && trim($value) === '') {
513519
return null;
514520
}
515521

516522
if (strlen($value) > 1 && $value[0] === '\\') {
517-
return substr($value, 1);
518-
} elseif (
523+
$value = substr($value, 1) ?: '';
524+
}
525+
526+
if (
519527
$appendLiterals
520-
&& $token === 'literal'
528+
&& in_array($token, ['literal', 'range', 'class_', '_class_literal'], true)
521529
&& $onlyLiterals !== null
522530
&& !in_array($value, ['.'], true)
523531
) {
@@ -533,14 +541,8 @@ private function getLiteralValue(TreeNode $node, ?array &$onlyLiterals, bool $ap
533541
return $value;
534542
}
535543

536-
// literal "-" in front/back of a character class like '[-a-z]' or '[abc-]', not forming a range
537-
if ($token === 'range') {
538-
return $value;
539-
}
540-
541-
// literal "[" or "]" inside character classes '[[]' or '[]]'
542-
if (in_array($token, ['class_', '_class_literal'], true)) {
543-
return $value;
544+
if (!in_array($token, ['capturing_name'], true)) {
545+
$onlyLiterals = null;
544546
}
545547

546548
// character escape sequences, just return a fixed string

tests/PHPStan/Analyser/nsrt/preg_match_shapes.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -699,3 +699,20 @@ function (string $s): void {
699699
preg_match('~a|(\d)|(\s)~', $s, $matches);
700700
assertType("array{0?: string, 1?: '', 2?: non-empty-string}|array{0?: string, 1?: numeric-string}", $matches);
701701
};
702+
703+
function bug11490 (string $expression): void {
704+
$matches = [];
705+
706+
if (preg_match('/([-+])?([\d]+)%/', $expression, $matches) === 1) {
707+
assertType("array{string, ''|'+'|'-', numeric-string}", $matches);
708+
}
709+
}
710+
711+
function bug11490b (string $expression): void {
712+
$matches = [];
713+
714+
if (preg_match('/([\\[+])?([\d]+)%/', $expression, $matches) === 1) {
715+
assertType("array{string, ''|'+'|'[', numeric-string}", $matches);
716+
}
717+
}
718+

0 commit comments

Comments
 (0)