Skip to content

Commit 5df4617

Browse files
authored
Fix matches[0] type for regexes containing \K
1 parent 99299a2 commit 5df4617

File tree

3 files changed

+86
-2
lines changed

3 files changed

+86
-2
lines changed

Diff for: src/Type/Regex/RegexGroupParser.php

+16-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ public function parseGroups(string $regex): ?RegexAstWalkResult
110110
RegexGroupWalkResult::createEmpty(),
111111
);
112112

113-
if (!$subjectAsGroupResult->mightContainEmptyStringLiteral()) {
113+
if (!$subjectAsGroupResult->mightContainEmptyStringLiteral() && !$this->containsEscapeK($ast)) {
114114
// we could handle numeric-string, in case we know the regex is delimited by ^ and $
115115
if ($subjectAsGroupResult->isNonFalsy()->yes()) {
116116
$astWalkResult = $astWalkResult->withSubjectBaseType(
@@ -171,6 +171,21 @@ private function updateCapturingAstAddEmptyToken(TreeNode $ast): void
171171
$ast->setChildren([$emptyAlternationAst]);
172172
}
173173

174+
private function containsEscapeK(TreeNode $ast): bool
175+
{
176+
if ($ast->getId() === 'token' && $ast->getValueToken() === 'match_point_reset') {
177+
return true;
178+
}
179+
180+
foreach ($ast->getChildren() as $child) {
181+
if ($this->containsEscapeK($child)) {
182+
return true;
183+
}
184+
}
185+
186+
return false;
187+
}
188+
174189
private function walkRegexAst(
175190
TreeNode $ast,
176191
?RegexAlternation $alternation,

Diff for: tests/PHPStan/Analyser/nsrt/preg_match_shapes.php

+59-1
Original file line numberDiff line numberDiff line change
@@ -1011,7 +1011,65 @@ function bug12749f(string $str): void
10111011
}
10121012
}
10131013

1014-
function bug12397(string $string) : array {
1014+
function bug12397(string $string): void {
10151015
$m = preg_match('#\b([A-Z]{2,})-(\d+)#', $string, $match);
10161016
assertType('list{0?: string, 1?: non-falsy-string, 2?: numeric-string}', $match);
10171017
}
1018+
1019+
function bug12792(string $string): void {
1020+
if (preg_match('~a\Kb~', $string, $match) === 1) {
1021+
assertType('array{string}', $match); // could be array{'b'}
1022+
}
1023+
1024+
if (preg_match('~a\K~', $string, $match) === 1) {
1025+
assertType('array{string}', $match); // could be array{''}
1026+
}
1027+
1028+
if (preg_match('~a\K.+~', $string, $match) === 1) {
1029+
assertType('array{string}', $match); // could be array{non-empty-string}
1030+
}
1031+
1032+
if (preg_match('~a\K.*~', $string, $match) === 1) {
1033+
assertType('array{string}', $match);
1034+
}
1035+
1036+
if (preg_match('~a\K(.+)~', $string, $match) === 1) {
1037+
assertType('array{string, non-empty-string}', $match); // could be array{non-empty-string, non-empty-string}
1038+
}
1039+
1040+
if (preg_match('~a\K(.*)~', $string, $match) === 1) {
1041+
assertType('array{string, string}', $match);
1042+
}
1043+
1044+
if (preg_match('~a\K(.+?)~', $string, $match) === 1) {
1045+
assertType('array{string, non-empty-string}', $match); // could be array{non-empty-string, non-empty-string}
1046+
}
1047+
1048+
if (preg_match('~a\K(.*?)~', $string, $match) === 1) {
1049+
assertType('array{string, string}', $match);
1050+
}
1051+
1052+
if (preg_match('~a\K(?=.+)~', $string, $match) === 1) {
1053+
assertType('array{string}', $match); // could be array{''}
1054+
}
1055+
1056+
if (preg_match('~a\K(?=.*)~', $string, $match) === 1) {
1057+
assertType('array{string}', $match); // could be array{''}
1058+
}
1059+
1060+
if (preg_match('~a(?:x\Kb|c)~', $string, $match) === 1) {
1061+
assertType('array{string}', $match); // could be array{'ac'|'b'}
1062+
}
1063+
1064+
if (preg_match('~a(?:c|x\Kb)~', $string, $match) === 1) {
1065+
assertType('array{string}', $match); // could be array{'ac'|'b'}
1066+
}
1067+
1068+
if (preg_match('~a(y|(?:x\Kb|c))d~', $string, $match) === 1) {
1069+
assertType('array{string, non-empty-string}', $match); // could be array{'acd'|'ayd'|'bd', 'c'|'xb'|'y'}
1070+
}
1071+
1072+
if (preg_match('~a((?:c|x\Kb)|y)d~', $string, $match) === 1) {
1073+
assertType('array{string, non-empty-string}', $match); // could be array{'acd'|'ayd'|'bd', 'c'|'xb'|'y'}
1074+
}
1075+
}

Diff for: tests/PHPStan/Analyser/nsrt/preg_replace_callback_shapes.php

+11
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,14 @@ function ($matches) {
4545
PREG_OFFSET_CAPTURE|PREG_UNMATCHED_AS_NULL
4646
);
4747
};
48+
49+
function bug12792(string $string) : void {
50+
preg_replace_callback(
51+
'~\'(?:[^\']+|\'\')*+\'\K|\[(\w*)\]~',
52+
function ($matches) {
53+
assertType("array{0: string, 1?: string}", $matches);
54+
return '';
55+
},
56+
$string
57+
);
58+
}

0 commit comments

Comments
 (0)