Skip to content

Commit f367882

Browse files
committed
Calculate absolute position for action offending token
Signed-off-by: Wojtek K <5840392+wojciszek@users.noreply.github.com>
1 parent 45ee4ec commit f367882

File tree

4 files changed

+188
-24
lines changed

4 files changed

+188
-24
lines changed

tool-testsuite/test/org/antlr/v4/test/tool/TestAttributeChecks.java

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,15 @@
66

77
package org.antlr.v4.test.tool;
88

9+
import org.antlr.runtime.CommonToken;
910
import org.antlr.runtime.RecognitionException;
11+
import org.antlr.v4.test.runtime.ErrorQueue;
12+
import org.antlr.v4.tool.ANTLRMessage;
1013
import org.antlr.v4.tool.ErrorType;
14+
import org.junit.jupiter.api.Assertions;
1115
import org.junit.jupiter.api.Test;
16+
import org.junit.jupiter.api.condition.EnabledOnOs;
17+
import org.junit.jupiter.api.condition.OS;
1218
import org.junit.jupiter.params.ParameterizedTest;
1319
import org.junit.jupiter.params.provider.Arguments;
1420
import org.junit.jupiter.params.provider.MethodSource;
@@ -18,6 +24,7 @@
1824

1925
import java.util.ArrayList;
2026
import java.util.List;
27+
import java.util.function.Consumer;
2128
import java.util.stream.Stream;
2229

2330
import static org.antlr.v4.test.tool.ToolTestUtils.testErrors;
@@ -235,6 +242,32 @@ public void testDynamicInlineActions(String input, String expected) {
235242
testAction("inline", attributeTemplate, input, expected);
236243
}
237244

245+
@Test
246+
@EnabledOnOs({OS.WINDOWS})
247+
public void shouldDetectOffendingTokenAbsolutePositionInWindows(){
248+
String expectedError = "error(" + ErrorType.ISOLATED_RULE_REF.code + "): A.g4:7:4: missing attribute access on rule reference lab in $lab\n";
249+
testAction("inline", attributeTemplate, "$lab", expectedError, q -> {
250+
Assertions.assertEquals(1, q.errors.size());
251+
ANTLRMessage antlrMessage = q.errors.get(0);
252+
CommonToken token = (CommonToken) antlrMessage.offendingToken;
253+
Assertions.assertEquals(131, token.getStartIndex(), "Offending Token start index assertion");
254+
Assertions.assertEquals(133, token.getStopIndex(), "Offending Token stop index assertion");
255+
});
256+
}
257+
258+
@Test
259+
@EnabledOnOs({OS.LINUX, OS.MAC})
260+
public void shouldDetectOffendingTokenAbsolutePositionInLinux(){
261+
String expectedError = "error(" + ErrorType.ISOLATED_RULE_REF.code + "): A.g4:7:4: missing attribute access on rule reference lab in $lab\n";
262+
testAction("inline", attributeTemplate, "$lab", expectedError, q -> {
263+
Assertions.assertEquals(1, q.errors.size());
264+
ANTLRMessage antlrMessage = q.errors.get(0);
265+
CommonToken token = (CommonToken) antlrMessage.offendingToken;
266+
Assertions.assertEquals(131, token.getStartIndex(), "Offending Token start index assertion");
267+
Assertions.assertEquals(133, token.getStopIndex(), "Offending Token stop index assertion");
268+
});
269+
}
270+
238271
@Test public void testBadInlineActions() throws RecognitionException {
239272
testActions("inline", bad_inlineChecks, attributeTemplate);
240273
}
@@ -266,11 +299,16 @@ private static void testActions(String location, String[] pairs, String template
266299
}
267300

268301
private static void testAction(String location, String template, String action, String expected) {
302+
testAction(location, template, action, expected, $ -> {});
303+
}
304+
305+
private static void testAction(String location, String template, String action, String expected, Consumer<ErrorQueue> errorQueueConsumer) {
269306
STGroup g = new STGroup('<', '>');
270-
g.setListener(new ErrorBuffer()); // hush warnings
307+
ErrorBuffer listener = new ErrorBuffer();
308+
g.setListener(listener); // hush warnings
271309
ST st = new ST(g, template);
272310
st.add(location, action);
273311
String grammar = st.render();
274-
testErrors(new String[]{grammar, expected}, false);
312+
testErrors(new String[]{grammar, expected}, errorQueueConsumer);
275313
}
276314
}

tool-testsuite/test/org/antlr/v4/test/tool/ToolTestUtils.java

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.nio.file.Path;
3030
import java.nio.file.Paths;
3131
import java.util.List;
32+
import java.util.function.Consumer;
3233

3334
import static org.antlr.v4.test.runtime.FileUtils.deleteDirectory;
3435
import static org.antlr.v4.test.runtime.Generator.antlrOnString;
@@ -91,33 +92,40 @@ public static RunOptions createOptionsForJavaToolTests(
9192
}
9293

9394
public static void testErrors(String[] pairs, boolean printTree) {
95+
testErrors(pairs, $ -> {});
96+
}
97+
98+
public static void testErrors(String[] pairs, Consumer<ErrorQueue> errorQueueAssertionsConsumer) {
9499
for (int i = 0; i < pairs.length; i += 2) {
95100
String grammarStr = pairs[i];
96101
String expect = pairs[i + 1];
102+
testErrors(grammarStr, expect, errorQueueAssertionsConsumer);
103+
}
104+
}
97105

98-
String[] lines = grammarStr.split("\n");
99-
String fileName = getFilenameFromFirstLineOfGrammar(lines[0]);
100-
101-
String tempDirName = "AntlrTestErrors-" + Thread.currentThread().getName() + "-" + System.currentTimeMillis();
102-
String tempTestDir = Paths.get(TempDirectory, tempDirName).toString();
106+
private static void testErrors(String grammarStr, String expect, Consumer<ErrorQueue> additionalAssertions) {
107+
String[] lines = grammarStr.split("\n");
108+
String fileName = getFilenameFromFirstLineOfGrammar(lines[0]);
103109

104-
try {
105-
ErrorQueue equeue = antlrOnString(tempTestDir, null, fileName, grammarStr, false);
110+
String tempDirName = "AntlrTestErrors-" + Thread.currentThread().getName() + "-" + System.currentTimeMillis();
111+
String tempTestDir = Paths.get(TempDirectory, tempDirName).toString();
106112

107-
String actual = equeue.toString(true);
108-
actual = actual.replace(tempTestDir + File.separator, "");
109-
String msg = grammarStr;
110-
msg = msg.replace("\n", "\\n");
111-
msg = msg.replace("\r", "\\r");
112-
msg = msg.replace("\t", "\\t");
113+
try {
114+
ErrorQueue equeue = antlrOnString(tempTestDir, null, fileName, grammarStr, false);
115+
String actual = equeue.toString(true);
116+
actual = actual.replace(tempTestDir + File.separator, "");
117+
String msg = grammarStr;
118+
msg = msg.replace("\n", "\\n");
119+
msg = msg.replace("\r", "\\r");
120+
msg = msg.replace("\t", "\\t");
113121

114-
assertEquals(expect, actual, "error in: " + msg);
115-
}
116-
finally {
117-
try {
118-
deleteDirectory(new File(tempTestDir));
119-
} catch (IOException ignored) {
120-
}
122+
assertEquals(expect, actual, "error in: " + msg);
123+
additionalAssertions.accept(equeue);
124+
}
125+
finally {
126+
try {
127+
deleteDirectory(new File(tempTestDir));
128+
} catch (IOException ignored) {
121129
}
122130
}
123131
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package org.antlr.v4.semantics;
2+
3+
import org.antlr.runtime.CommonToken;
4+
import org.antlr.runtime.RecognitionException;
5+
import org.antlr.runtime.Token;
6+
import org.antlr.v4.tool.*;
7+
import org.stringtemplate.v4.ST;
8+
9+
import java.util.Collection;
10+
import java.util.Objects;
11+
12+
public class ActionErrorManager extends ErrorManager {
13+
private final Token actionToken;
14+
private final ErrorManager delegate;
15+
16+
public ActionErrorManager(Token actionToken, ErrorManager delegate) {
17+
super(delegate.tool);
18+
this.delegate = Objects.requireNonNull(delegate, "ErrorManager delegate cannot be null");
19+
this.actionToken = Objects.requireNonNull(actionToken, "Token actionToken cannot be null");
20+
}
21+
22+
@Override
23+
public void grammarError(ErrorType etype, String fileName, Token token, Object... args) {
24+
delegate.grammarError(etype, fileName, resolveTokenWithGrammarAbsolutePosition(token), args);
25+
}
26+
27+
private Token resolveTokenWithGrammarAbsolutePosition(Token token) {
28+
if (token instanceof CommonToken && actionToken instanceof CommonToken) {
29+
CommonToken absolutePositionToken = new CommonToken(token);
30+
CommonToken ruleToken = (CommonToken) actionToken;
31+
absolutePositionToken.setStartIndex(ruleToken.getStartIndex() + absolutePositionToken.getStartIndex());
32+
absolutePositionToken.setStopIndex(ruleToken.getStartIndex() + absolutePositionToken.getStopIndex());
33+
return absolutePositionToken;
34+
}
35+
return token;
36+
}
37+
38+
@Override
39+
public void resetErrorState() {
40+
delegate.resetErrorState();
41+
}
42+
43+
@Override
44+
public ST getMessageTemplate(ANTLRMessage msg) {
45+
return delegate.getMessageTemplate(msg);
46+
}
47+
48+
@Override
49+
public ST getLocationFormat() {
50+
return delegate.getLocationFormat();
51+
}
52+
53+
@Override
54+
public ST getReportFormat(ErrorSeverity severity) {
55+
return delegate.getReportFormat(severity);
56+
}
57+
58+
@Override
59+
public ST getMessageFormat() {
60+
return delegate.getMessageFormat();
61+
}
62+
63+
@Override
64+
public boolean formatWantsSingleLineMessage() {
65+
return delegate.formatWantsSingleLineMessage();
66+
}
67+
68+
@Override
69+
public void info(String msg) {
70+
delegate.info(msg);
71+
}
72+
73+
@Override
74+
public void syntaxError(ErrorType etype, String fileName, Token token, RecognitionException antlrException, Object... args) {
75+
delegate.syntaxError(etype, fileName, token, antlrException, args);
76+
}
77+
78+
@Override
79+
public void toolError(ErrorType errorType, Object... args) {
80+
delegate.toolError(errorType, args);
81+
}
82+
83+
@Override
84+
public void toolError(ErrorType errorType, Throwable e, Object... args) {
85+
delegate.toolError(errorType, e, args);
86+
}
87+
88+
@Override
89+
public void leftRecursionCycles(String fileName, Collection<? extends Collection<Rule>> cycles) {
90+
delegate.leftRecursionCycles(fileName, cycles);
91+
}
92+
93+
@Override
94+
public int getNumErrors() {
95+
return delegate.getNumErrors();
96+
}
97+
98+
@Override
99+
public void emit(ErrorType etype, ANTLRMessage msg) {
100+
delegate.emit(etype, msg);
101+
}
102+
103+
@Override
104+
public void setFormat(String formatName) {
105+
delegate.setFormat(formatName);
106+
}
107+
108+
@Override
109+
protected boolean verifyFormat() {
110+
return super.verifyFormat();
111+
}
112+
113+
@Override
114+
public void panic(ErrorType errorType, Object... args) {
115+
delegate.panic(errorType, args);
116+
}
117+
}

tool/src/org/antlr/v4/semantics/AttributeChecks.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
package org.antlr.v4.semantics;
88

99
import org.antlr.runtime.ANTLRStringStream;
10+
import org.antlr.runtime.CommonToken;
1011
import org.antlr.runtime.Token;
1112
import org.antlr.v4.parse.ActionSplitter;
1213
import org.antlr.v4.parse.ActionSplitterListener;
@@ -39,8 +40,8 @@ public AttributeChecks(Grammar g, Rule r, Alternative alt, ActionAST node, Token
3940
this.alt = alt;
4041
this.node = node;
4142
this.actionToken = actionToken;
42-
this.errMgr = g.tool.errMgr;
43-
}
43+
this.errMgr = new ActionErrorManager(actionToken, g.tool.errMgr);
44+
}
4445

4546
public static void checkAllAttributeExpressions(Grammar g) {
4647
for (ActionAST act : g.namedActions.values()) {

0 commit comments

Comments
 (0)