Skip to content

Commit ab3c439

Browse files
committed
Fix nested brackets
1 parent 797d6b7 commit ab3c439

File tree

2 files changed

+27
-15
lines changed

2 files changed

+27
-15
lines changed

src/Util/FileMatcher.php

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ private static function processTokens(array $tokens): array
137137
{
138138
$resolved = [];
139139
$escaped = false;
140+
$bracketOpen = false;
140141
$brackets = [];
141142

142143
for ($offset = 0; $offset < count($tokens); $offset++) {
@@ -205,14 +206,24 @@ private static function processTokens(array $tokens): array
205206
}
206207

207208
if ($type === self::T_BRACKET_OPEN && $tokens[$offset + 1][0] === self::T_BRACKET_CLOSE) {
208-
$resolved[] = [self::T_BRACKET_OPEN, $char];
209+
$bracketOpen = true;
210+
$resolved[] = [self::T_BRACKET_OPEN, '['];
209211
$brackets[] = array_key_last($resolved);
210-
$resolved[] = [self::T_CHAR, $char];
212+
$resolved[] = [self::T_CHAR, ']'];
213+
$offset += 1;
214+
215+
continue;
216+
}
211217

218+
if ($bracketOpen === true && $type === self::T_BRACKET_OPEN) {
219+
// if bracket is already open, interpret everything as a
220+
// literal char
221+
$resolved[] = [self::T_CHAR, $char];
212222
continue;
213223
}
214224

215-
if ($type === self::T_BRACKET_OPEN) {
225+
if ($bracketOpen === false && $type === self::T_BRACKET_OPEN) {
226+
$bracketOpen = true;
216227
$resolved[] = [$type, $char];
217228
$brackets[] = array_key_last($resolved);
218229

tests/unit/Util/FileMatcherTest.php

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -297,22 +297,24 @@ public static function provideCharacterGroup(): Generator
297297
yield 'unterminated char group followed by char group' => [
298298
new FileMatcherPattern('/[AB[a-z]'),
299299
[
300-
'/[' => false,
301-
'/[Ac' => false,
302-
'/[ABc' => true,
303-
'/[ABc/foo' => true,
300+
'/[' => true, // nested [ is literal
301+
'/f' => true, // within a-z
302+
'/A' => true,
303+
'/B' => true,
304+
305+
'/Z' => false,
306+
'/[c' => false,
304307
],
305308
];
306309

307310
yield 'multiple unterminated char groups followed by char group' => [
308311
new FileMatcherPattern('/[AB[CD[a-z]EF'),
309312
[
310-
'/[' => false,
311-
'/[Ac' => false,
312-
'/[AB[C' => false,
313-
'/[AB[CD' => false,
314-
'/[AB[CDz' => false,
315-
'/[AB[CDzEF' => true,
313+
'/[EF' => true,
314+
'/AEF' => true,
315+
'/[EF' => true,
316+
'/DEF' => true,
317+
'/EEF' => false,
316318
],
317319
];
318320

@@ -358,7 +360,7 @@ public static function provideCharacterGroup(): Generator
358360

359361
// https://man7.org/linux/man-pages/man7/glob.7.html
360362
yield 'square bracket in char group' => [
361-
new FileMatcherPattern('/[][!]'),
363+
new FileMatcherPattern('/[][!]*'),
362364
[
363365
'/[hello' => true,
364366
'/[' => true,
@@ -367,7 +369,6 @@ public static function provideCharacterGroup(): Generator
367369
'/a' => false,
368370
'/' => false,
369371
],
370-
'This test fails because `[` should be interpreted a literal',
371372
];
372373

373374
yield 'match ranges' => [

0 commit comments

Comments
 (0)