diff --git a/tool-testsuite/test/org/antlr/v4/test/tool/TestSymbolIssues.java b/tool-testsuite/test/org/antlr/v4/test/tool/TestSymbolIssues.java index 41aeee815..bc4e72244 100644 --- a/tool-testsuite/test/org/antlr/v4/test/tool/TestSymbolIssues.java +++ b/tool-testsuite/test/org/antlr/v4/test/tool/TestSymbolIssues.java @@ -254,18 +254,57 @@ public class TestSymbolIssues extends BaseJavaToolTest { "lexer grammar Lexer;\n" + "channels { CHANNEL1, CHANNEL2 }\n" + "tokens { TEST1, TEST2 }\n" + - "TOKEN: 'aaaa' -> mode(MODE1), mode(MODE2);\n" + + "TOKEN: 'a' -> mode(MODE1), mode(MODE2);\n" + + "TOKEN1: 'b' -> pushMode(MODE1), mode(MODE2);\n" + + "TOKEN2: 'c' -> pushMode(MODE1), pushMode(MODE2); // pushMode is not duplicate\n" + + "TOKEN3: 'd' -> popMode, popMode; // popMode is not duplicate\n" + "mode MODE1;\n" + - "MODE1_TOKEN: 'bbbb';\n" + + "MODE1_TOKEN: 'e';\n" + "mode MODE2;\n" + - "MODE2_TOKEN: 'bbbb';\n" + - "MODE2_TOKEN1: 'cccc' -> type(TEST1), type(TEST2);\n" + - "MODE2_TOKEN2: 'dddd' -> channel(CHANNEL1), channel(CHANNEL2), channel(DEFAULT_TOKEN_CHANNEL);", + "MODE2_TOKEN: 'f';\n" + + "MODE2_TOKEN1: 'g' -> type(TEST1), type(TEST2);\n" + + "MODE2_TOKEN2: 'h' -> channel(CHANNEL1), channel(CHANNEL2), channel(DEFAULT_TOKEN_CHANNEL);", - "warning(" + ErrorType.DUPLICATED_COMMAND.code + "): Lexer.g4:4:30: duplicated command mode\n" + - "warning(" + ErrorType.DUPLICATED_COMMAND.code + "): Lexer.g4:9:37: duplicated command type\n" + - "warning(" + ErrorType.DUPLICATED_COMMAND.code + "): Lexer.g4:10:43: duplicated command channel\n" + - "warning(" + ErrorType.DUPLICATED_COMMAND.code + "): Lexer.g4:10:62: duplicated command channel\n" + "warning(" + ErrorType.DUPLICATED_COMMAND.code + "): Lexer.g4:4:27: duplicated command mode\n" + + "warning(" + ErrorType.DUPLICATED_COMMAND.code + "): Lexer.g4:12:34: duplicated command type\n" + + "warning(" + ErrorType.DUPLICATED_COMMAND.code + "): Lexer.g4:13:40: duplicated command channel\n" + + "warning(" + ErrorType.DUPLICATED_COMMAND.code + "): Lexer.g4:13:59: duplicated command channel\n" + }; + + testErrors(test, false); + } + + // https://github.com/antlr/antlr4/issues/1388 + @Test public void testIncompatibleCommands() throws Exception { + String[] test = { + "lexer grammar L;\n" + + "channels { CHANNEL1 }\n" + + "tokens { TYPE1 }\n" + + "// Incompatible\n" + + "T00: 'a00' -> skip, more;\n" + + "T01: 'a01' -> skip, type(TYPE1);\n" + + "T02: 'a02' -> skip, channel(CHANNEL1);\n" + + "T03: 'a03' -> more, type(TYPE1);\n" + + "T04: 'a04' -> more, channel(CHANNEL1);\n" + + "T05: 'a05' -> more, skip;\n" + + "T06: 'a06' -> type(TYPE1), skip;\n" + + "T07: 'a07' -> type(TYPE1), more;\n" + + "T08: 'a08' -> channel(CHANNEL1), skip;\n" + + "T09: 'a09' -> channel(CHANNEL1), more;\n" + + "// Allowed\n" + + "T10: 'a10' -> type(TYPE1), channel(CHANNEL1);\n" + + "T11: 'a11' -> channel(CHANNEL1), type(TYPE1);", + + "warning(" + ErrorType.INCOMPATIBLE_COMMANDS.code + "): L.g4:5:20: incompatible commands skip and more\n" + + "warning(" + ErrorType.INCOMPATIBLE_COMMANDS.code + "): L.g4:6:20: incompatible commands skip and type\n" + + "warning(" + ErrorType.INCOMPATIBLE_COMMANDS.code + "): L.g4:7:20: incompatible commands skip and channel\n" + + "warning(" + ErrorType.INCOMPATIBLE_COMMANDS.code + "): L.g4:8:20: incompatible commands more and type\n" + + "warning(" + ErrorType.INCOMPATIBLE_COMMANDS.code + "): L.g4:9:20: incompatible commands more and channel\n" + + "warning(" + ErrorType.INCOMPATIBLE_COMMANDS.code + "): L.g4:10:20: incompatible commands more and skip\n" + + "warning(" + ErrorType.INCOMPATIBLE_COMMANDS.code + "): L.g4:11:27: incompatible commands type and skip\n" + + "warning(" + ErrorType.INCOMPATIBLE_COMMANDS.code + "): L.g4:12:27: incompatible commands type and more\n" + + "warning(" + ErrorType.INCOMPATIBLE_COMMANDS.code + "): L.g4:13:33: incompatible commands channel and skip\n" + + "warning(" + ErrorType.INCOMPATIBLE_COMMANDS.code + "): L.g4:14:33: incompatible commands channel and more\n" }; testErrors(test, false); diff --git a/tool/src/org/antlr/v4/automata/LexerATNFactory.java b/tool/src/org/antlr/v4/automata/LexerATNFactory.java index f2dfe4518..49b85b9ff 100644 --- a/tool/src/org/antlr/v4/automata/LexerATNFactory.java +++ b/tool/src/org/antlr/v4/automata/LexerATNFactory.java @@ -412,6 +412,23 @@ public class LexerATNFactory extends ParserATNFactory { if (ruleCommands.contains(command) && !command.equals("pushMode") && !command.equals("popMode")) { g.tool.errMgr.grammarError(ErrorType.DUPLICATED_COMMAND, g.fileName, ID.getToken(), command); } + String firstCommand = null; + if ((ruleCommands.contains("skip") && (command.equals("more") || command.equals("type") || command.equals("channel")))) { + firstCommand = "skip"; + } + else if ((ruleCommands.contains("more") && (command.equals("type") || command.equals("channel") || command.equals("skip")))) { + firstCommand = "more"; + } + else if ((ruleCommands.contains("type") && (command.equals("skip") || command.equals("more")))) { + firstCommand = "type"; + } + else if ((ruleCommands.contains("channel") && (command.equals("skip") || command.equals("more")))) { + firstCommand = "channel"; + } + if (firstCommand != null) { + g.tool.errMgr.grammarError(ErrorType.INCOMPATIBLE_COMMANDS, g.fileName, ID.getToken(), firstCommand, command); + } + ruleCommands.add(command); if ("skip".equals(command) && arg == null) { diff --git a/tool/src/org/antlr/v4/tool/ErrorType.java b/tool/src/org/antlr/v4/tool/ErrorType.java index 79b6d226c..a26adc5a0 100644 --- a/tool/src/org/antlr/v4/tool/ErrorType.java +++ b/tool/src/org/antlr/v4/tool/ErrorType.java @@ -983,6 +983,13 @@ public enum ErrorType { *
TOKEN: 'asdf' -> mode(MODE1), mode(MODE2);
* */ DUPLICATED_COMMAND(172, "duplicated commandincompatible commands command1 and command2
+ * + *T00: 'a00' -> skip, more;
+ */ + INCOMPATIBLE_COMMANDS(173, "incompatible commands