From 5419aff0caac59115cc9ab16faf7d726717c9146 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Thu, 11 Jul 2024 17:29:26 +0200 Subject: [PATCH] Fix preg_match named capturing groups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Michael Voříšek <2228672+mvorisek@users.noreply.github.com> --- patches/Grammar.patch | 21 ++++++++++++---- .../Analyser/nsrt/preg_match_shapes.php | 25 ++++++++++++++++--- .../Analyser/nsrt/preg_match_shapes_php82.php | 4 +-- 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/patches/Grammar.patch b/patches/Grammar.patch index f35bbf3b9f..e3fba1b624 100644 --- a/patches/Grammar.patch +++ b/patches/Grammar.patch @@ -1,5 +1,16 @@ ---- Grammar.pp 2024-05-18 12:15:53 -+++ Grammar.pp.fix 2024-05-18 12:15:05 +diff --git a/Grammar.pp b/Grammar.pp +index 5157084..8384417 100644 +--- a/Grammar.pp ++++ b/Grammar.pp +@@ -73,7 +73,7 @@ + %token co:comment .*?(?=(? nc ++%token named_capturing_ \(\?P?< -> nc + %token nc:_named_capturing > -> default + %token nc:capturing_name .+?(?=(?) + %token non_capturing_ \(\?: @@ -109,7 +109,7 @@ // Please, see PCRESYNTAX(3), General Category properties, PCRE special category // properties and script names for \p{} and \P{}. @@ -9,7 +20,7 @@ %token match_point_reset \\K %token literal \\.|. -@@ -168,7 +168,7 @@ +@@ -168,7 +168,7 @@ quantifier: ::negative_class_:: #negativeclass | ::class_:: ) @@ -18,7 +29,7 @@ ::_class:: #range: -@@ -178,7 +178,7 @@ +@@ -178,7 +178,7 @@ simple: capturing() | literal() @@ -27,7 +38,7 @@ ::comment_:: ? ::_comment:: #comment | ( ::named_capturing_:: ::_named_capturing:: #namedcapturing -@@ -191,6 +191,7 @@ +@@ -191,6 +191,7 @@ capturing: literal: diff --git a/tests/PHPStan/Analyser/nsrt/preg_match_shapes.php b/tests/PHPStan/Analyser/nsrt/preg_match_shapes.php index 6878a77011..768787194d 100644 --- a/tests/PHPStan/Analyser/nsrt/preg_match_shapes.php +++ b/tests/PHPStan/Analyser/nsrt/preg_match_shapes.php @@ -93,10 +93,9 @@ function doNonCapturingGroup(string $s): void { function doNamedSubpattern(string $s): void { if (preg_match('/\w-(?P\d+)-(\w)/', $s, $matches)) { - // could be assertType('array{0: string, num: string, 1: string, 2: string, 3: string}', $matches); - assertType('array', $matches); + assertType('array{0: string, num: string, 1: string, 2: string}', $matches); } - assertType('array', $matches); + assertType('array{}|array{0: string, num: string, 1: string, 2: string}', $matches); if (preg_match('/^(?\S+::\S+)/', $s, $matches)) { assertType('array{0: string, name: string, 1: string}', $matches); @@ -364,3 +363,23 @@ function bug11291(string $s): void { } assertType('array{}|array{0: string, 1: string, 2?: string, 3?: string}', $matches); } + +function bug11323a(string $s): void +{ + if (preg_match('/Price: (?P£|€)\d+/', $s, $matches)) { + assertType('array{0: string, currency: string, 1: string}', $matches); + } else { + assertType('array{}', $matches); + } + assertType('array{}|array{0: string, currency: string, 1: string}', $matches); +} + +function bug11323b(string $s): void +{ + if (preg_match('/Price: (?£|€)\d+/', $s, $matches)) { + assertType('array{0: string, currency: string, 1: string}', $matches); + } else { + assertType('array{}', $matches); + } + assertType('array{}|array{0: string, currency: string, 1: string}', $matches); +} diff --git a/tests/PHPStan/Analyser/nsrt/preg_match_shapes_php82.php b/tests/PHPStan/Analyser/nsrt/preg_match_shapes_php82.php index ee28d12ac1..737f7a8c8d 100644 --- a/tests/PHPStan/Analyser/nsrt/preg_match_shapes_php82.php +++ b/tests/PHPStan/Analyser/nsrt/preg_match_shapes_php82.php @@ -13,7 +13,7 @@ function doNonAutoCapturingFlag(string $s): void { assertType('array{}|array{string, string}', $matches); if (preg_match('/(\d+)(?P\d+)/n', $s, $matches)) { - assertType('array', $matches); // could be 'array{0: string, num: string, 1: string}' + assertType('array{0: string, 1: string, num: string, 2: string}', $matches); } - assertType('array', $matches); + assertType('array{}|array{0: string, 1: string, num: string, 2: string}', $matches); }