Skip to content

Commit 0ccbd91

Browse files
committed
fix: Parsing negative variable-length failed with joni SyntaxException
org.eclipse.tm4e.core.TMException: Parsing regex pattern "(?<=\s*\.)\w+" failed with org.joni.exception.SyntaxException: invalid pattern in look-behind
1 parent 787b99d commit 0ccbd91

2 files changed

Lines changed: 60 additions & 17 deletions

File tree

org.eclipse.tm4e.core/src/main/java/org/eclipse/tm4e/core/internal/oniguruma/OnigRegExp.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,17 @@ private String rewritePatternIfRequired(final String pattern) {
106106
return "(?<!\\.)\\s*" + pattern.substring(lookbehind1.length());
107107
}
108108

109-
// e.g. used in markdown.math.inline.tmLanguage.json
109+
// e.g. used in markdown.math.block.tmLanguage.json and tex.tmLanguage.json
110110
final var lookbehind2 = "(?<=^\\s*)";
111111
if (pattern.startsWith(lookbehind2)) {
112112
return "(?<=^)\\s*" + pattern.substring(lookbehind2.length());
113113
}
114+
115+
// e.g. used in carbon.tmLanguage.json
116+
final var lookbehind3 = "(?<=\\s*\\.)";
117+
if (pattern.startsWith(lookbehind3)) {
118+
return "\\s*\\." + pattern.substring(lookbehind3.length());
119+
}
114120
return pattern;
115121
}
116122

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright (c) 2022 Sebastian Thomschke and others.
2+
* Copyright (c) 2022,2024 Sebastian Thomschke and others.
33
*
44
* This program and the accompanying materials are made
55
* available under the terms of the Eclipse Public License 2.0
@@ -11,23 +11,46 @@
1111

1212
import static org.junit.jupiter.api.Assertions.*;
1313

14+
import org.eclipse.jdt.annotation.Nullable;
1415
import org.junit.jupiter.api.Test;
1516

1617
class OnigRegExpTest {
1718

18-
@Test
19-
void testOnigRegExp() throws Exception {
20-
final var regexp = new OnigRegExp(
21-
"\\G(MAKEFILES|VPATH|SHELL|MAKESHELL|MAKE|MAKELEVEL|MAKEFLAGS|MAKECMDGOALS|CURDIR|SUFFIXES|\\.LIBPATTERNS)(?=\\s*\\))");
19+
private void assertOnigRegExpSearch(final String input, final @Nullable OnigResult result, final int startPosition,
20+
final boolean shouldMatch, final String... expectedGroups) {
21+
if (shouldMatch) {
22+
assertNotNull(result, "Expected a match in input: \"" + input + "\" starting at position " + startPosition);
23+
assertEquals(expectedGroups.length, result.count(),
24+
"Expected " + expectedGroups.length + " groups, but found " + result.count() + " in input: \"" + input + "\"");
25+
for (int i = 0; i < expectedGroups.length; i++) {
26+
final String expectedGroup = expectedGroups[i];
27+
final int start = result.locationAt(i);
28+
final int end = start + result.lengthAt(i);
29+
final String actualGroup = input.substring(start, end);
30+
assertEquals(expectedGroup, actualGroup, "Expected group " + i + " to be \"" + expectedGroup + "\" but found \""
31+
+ actualGroup + "\" in input: \"" + input + "\"");
32+
}
33+
} else {
34+
assertNull(result, "Did not expect a match in input: \"" + input + "\" starting at position " + startPosition);
35+
}
36+
}
2237

23-
final var line = "ifeq (version,$(firstword $(MAKECMDGOALS))\n";
24-
final var onigLine = OnigString.of(line);
38+
private void assertOnigRegExpSearch(final String pattern, final String input, final int startPosition, final boolean shouldMatch,
39+
final String... expectedGroups) {
40+
final OnigRegExp regexp = new OnigRegExp(pattern);
41+
final OnigResult result = regexp.search(OnigString.of(input), startPosition);
42+
assertOnigRegExpSearch(input, result, startPosition, shouldMatch, expectedGroups);
43+
}
2544

26-
final var result = regexp.search(onigLine, 28);
27-
assertNotNull(result);
28-
assertEquals(2, result.count());
29-
assertEquals("MAKECMDGOALS", line.substring(result.locationAt(0), result.locationAt(0) + result.lengthAt(0)));
30-
assertEquals("MAKECMDGOALS", line.substring(result.locationAt(1), result.locationAt(1) + result.lengthAt(1)));
45+
@Test
46+
void testOnigRegExp() throws Exception {
47+
assertOnigRegExpSearch(
48+
"\\G(MAKEFILES|VPATH|SHELL|MAKESHELL|MAKE|MAKELEVEL|MAKEFLAGS|MAKECMDGOALS|CURDIR|SUFFIXES|\\.LIBPATTERNS)(?=\\s*\\))",
49+
"ifeq (version,$(firstword $(MAKECMDGOALS))\n",
50+
28,
51+
true,
52+
"MAKECMDGOALS",
53+
"MAKECMDGOALS");
3154
}
3255

3356
/**
@@ -45,9 +68,23 @@ void testOnigRegExpCaching() {
4568
assertNull(result);
4669

4770
result = regexp.search(onigLine, 28);
48-
assertNotNull(result);
49-
assertEquals(2, result.count());
50-
assertEquals("MAKECMDGOALS", line.substring(result.locationAt(0), result.locationAt(0) + result.lengthAt(0)));
51-
assertEquals("MAKECMDGOALS", line.substring(result.locationAt(1), result.locationAt(1) + result.lengthAt(1)));
71+
assertOnigRegExpSearch(line, result, 28, true, "MAKECMDGOALS", "MAKECMDGOALS");
72+
}
73+
74+
@Test
75+
void testNegativeLookBehinds() {
76+
// test of OnigRegExp.rewritePatternIfRequired (lookbehind1)
77+
assertOnigRegExpSearch("(?<!\\.\\s*)\\b(await)\\b", "await", 0, true, "await", "await");
78+
assertOnigRegExpSearch("(?<!\\.\\s*)\\b(await)\\b", " await", 0, true, " await", "await");
79+
assertOnigRegExpSearch("(?<!\\.\\s*)\\b(await)\\b", ".await", 0, false);
80+
assertOnigRegExpSearch("(?<!\\.\\s*)\\b(await)\\b", " .await", 0, false);
81+
82+
// test of OnigRegExp.rewritePatternIfRequired (lookbehind2)
83+
assertOnigRegExpSearch("(?<=^\\s*)\\\\fi", "\\fi", 0, true, "\\fi");
84+
assertOnigRegExpSearch("(?<=^\\s*)\\\\fi", " \\fi", 0, true, " \\fi");
85+
86+
// test of OnigRegExp.rewritePatternIfRequired (lookbehind3)
87+
assertOnigRegExpSearch("(?<=\\s*\\.)\\w+", ".foo", 0, true, ".foo");
88+
assertOnigRegExpSearch("(?<=\\s*\\.)\\w+", " .foo", 0, true, " .foo");
5289
}
5390
}

0 commit comments

Comments
 (0)