Skip to content

Commit 2e6f270

Browse files
committed
add empty subexpression grammar and tests
1 parent ecdd6c6 commit 2e6f270

File tree

2 files changed

+49
-5
lines changed

2 files changed

+49
-5
lines changed

resources/RegexGrammar.pp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@
135135
alternation()
136136

137137
alternation:
138-
concatenation() ( ::alternation:: concatenation() #alternation )*
138+
concatenation()? ( <alternation> concatenation()? #alternation )*
139139

140140
concatenation:
141141
( internal_options() | assertion() | quantification() | condition() )
@@ -154,8 +154,8 @@
154154
<index>
155155
| ::assertion_reference_:: alternation() #assertioncondition
156156
)
157-
::_capturing:: concatenation()?
158-
( ::alternation:: concatenation()? )?
157+
::_capturing::
158+
alternation()
159159
::_capturing::
160160

161161
assertion:
@@ -165,7 +165,8 @@
165165
| ::lookbehind_:: #lookbehind
166166
| ::negative_lookbehind_:: #negativelookbehind
167167
)
168-
alternation() ::_capturing::
168+
alternation()
169+
::_capturing::
169170

170171
quantification:
171172
( class() | simple() ) ( quantifier() #quantification )?
@@ -208,7 +209,8 @@
208209
| ::atomic_group_:: #atomicgroup
209210
| ::capturing_::
210211
)
211-
alternation() ::_capturing::
212+
alternation()
213+
::_capturing::
212214

213215
non_capturing_internal_options:
214216
<non_capturing_internal_option>

tests/PHPStan/Analyser/nsrt/preg_match_shapes.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -901,6 +901,48 @@ function bugUnescapedDashAfterRange (string $string): void
901901
}
902902
}
903903

904+
function bugEmptySubexpression (string $string): void {
905+
if (preg_match('//', $string, $matches)) {
906+
assertType("array{string}", $matches); // could be array{''}
907+
}
908+
909+
if (preg_match('/()/', $string, $matches)) {
910+
assertType("array{string, string}", $matches); // could be array{'', ''}
911+
}
912+
913+
if (preg_match('/|/', $string, $matches)) {
914+
assertType("array{string}", $matches); // could be array{''}
915+
}
916+
917+
if (preg_match('~|(a)~', $string, $matches)) {
918+
assertType("array{0: string, 1?: 'a'}", $matches);
919+
}
920+
921+
if (preg_match('~(a)|~', $string, $matches)) {
922+
assertType("array{0: string, 1?: 'a'}", $matches);
923+
}
924+
925+
if (preg_match('~(a)||(b)~', $string, $matches)) {
926+
assertType("array{0: string, 1?: 'a'}|array{string, '', 'b'}", $matches);
927+
}
928+
929+
if (preg_match('~(|(a))~', $string, $matches)) {
930+
assertType("array{0: string, 1: ''|'a', 2?: 'a'}", $matches);
931+
}
932+
933+
if (preg_match('~((a)|)~', $string, $matches)) {
934+
assertType("array{0: string, 1: ''|'a', 2?: 'a'}", $matches);
935+
}
936+
937+
if (preg_match('~((a)||(b))~', $string, $matches)) {
938+
assertType("array{0: string, 1: ''|'a'|'b', 2?: ''|'a', 3?: 'b'}", $matches);
939+
}
940+
941+
if (preg_match('~((a)|()|(b))~', $string, $matches)) {
942+
assertType("array{0: string, 1: string, 2?: ''|'a', 3?: string, 4?: 'b'}", $matches);
943+
}
944+
}
945+
904946
function bug11744(string $string): void
905947
{
906948
if (!preg_match('~^((/[a-z]+)?)~', $string, $matches)) {

0 commit comments

Comments
 (0)