From 0ed651cbc6d2350286fc9ef8335d6c84e891086b Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Sun, 23 Mar 2014 17:45:58 -0500 Subject: [PATCH] Add compiler warning 158: FRAGMENT_ACTION_IGNORED (fixes #472) --- .../v4/semantics/BasicSemanticChecks.java | 32 +++++++++++++++++-- tool/src/org/antlr/v4/tool/ErrorType.java | 31 ++++++++++++++++++ .../antlr/v4/test/TestToolSyntaxErrors.java | 29 +++++++++++++++++ 3 files changed, 89 insertions(+), 3 deletions(-) diff --git a/tool/src/org/antlr/v4/semantics/BasicSemanticChecks.java b/tool/src/org/antlr/v4/semantics/BasicSemanticChecks.java index 93eb4fd8c..71e064bb8 100644 --- a/tool/src/org/antlr/v4/semantics/BasicSemanticChecks.java +++ b/tool/src/org/antlr/v4/semantics/BasicSemanticChecks.java @@ -116,6 +116,13 @@ public class BasicSemanticChecks extends GrammarTreeVisitor { */ protected int nonFragmentRuleCount; + /** + * This is {@code true} from the time {@link #discoverLexerRule} is called + * for a lexer rule with the {@code fragment} modifier until + * {@link #exitLexerRule} is called. + */ + private boolean inFragmentRule; + public BasicSemanticChecks(Grammar g, RuleCollector ruleCollector) { this.g = g; this.ruleCollector = ruleCollector; @@ -200,20 +207,24 @@ public class BasicSemanticChecks extends GrammarTreeVisitor { { checkInvalidRuleDef(ID.token); - boolean fragmentRule = false; if (modifiers != null) { for (GrammarAST tree : modifiers) { if (tree.getType() == ANTLRParser.FRAGMENT) { - fragmentRule = true; + inFragmentRule = true; } } } - if (!fragmentRule) { + if (!inFragmentRule) { nonFragmentRuleCount++; } } + @Override + protected void exitLexerRule(GrammarAST tree) { + inFragmentRule = false; + } + @Override public void ruleRef(GrammarAST ref, ActionAST arg) { checkInvalidRuleRef(ref.token); @@ -381,6 +392,21 @@ public class BasicSemanticChecks extends GrammarTreeVisitor { @Override protected void enterLexerCommand(GrammarAST tree) { checkElementIsOuterMostInSingleAlt(tree); + + if (inFragmentRule) { + String fileName = tree.token.getInputStream().getSourceName(); + String ruleName = currentRuleName; + g.tool.errMgr.grammarError(ErrorType.FRAGMENT_ACTION_IGNORED, fileName, tree.token, ruleName); + } + } + + @Override + public void actionInAlt(ActionAST action) { + if (inFragmentRule) { + String fileName = action.token.getInputStream().getSourceName(); + String ruleName = currentRuleName; + g.tool.errMgr.grammarError(ErrorType.FRAGMENT_ACTION_IGNORED, fileName, action.token, ruleName); + } } /** diff --git a/tool/src/org/antlr/v4/tool/ErrorType.java b/tool/src/org/antlr/v4/tool/ErrorType.java index 69f55c51c..94ed4a5ab 100644 --- a/tool/src/org/antlr/v4/tool/ErrorType.java +++ b/tool/src/org/antlr/v4/tool/ErrorType.java @@ -852,6 +852,37 @@ public enum ErrorType { * @since 4.2.1 */ UNRECOGNIZED_ASSOC_OPTION(157, "rule '' contains an 'assoc' terminal option in an unrecognized location", ErrorSeverity.WARNING), + /** + * Compiler Warning 158. + * + *

fragment rule 'rule' contains an action or command which can + * never be executed

+ * + *

A lexer rule which is marked with the {@code fragment} modifier + * contains an embedded action or lexer command. ANTLR lexers only execute + * commands and embedded actions located in the top-level matched rule. + * Since fragment rules can never be the top-level rule matched by a lexer, + * actions or commands placed in these rules can never be executed during + * the lexing process.

+ * + *

The following rule produces this warning.

+ * + *
+	 * X1 : 'x' -> more    // ok
+	 *    ;
+	 * Y1 : 'x' {more();}  // ok
+	 *    ;
+	 * fragment
+	 * X2 : 'x' -> more    // warning 158
+	 *    ;
+	 * fragment
+	 * Y2 : 'x' {more();}  // warning 158
+	 *    ;
+	 * 
+ * + * @since 4.2.1 + */ + FRAGMENT_ACTION_IGNORED(158, "fragment rule '' contains an action or command which can never be executed", ErrorSeverity.WARNING), /* * Backward incompatibility errors diff --git a/tool/test/org/antlr/v4/test/TestToolSyntaxErrors.java b/tool/test/org/antlr/v4/test/TestToolSyntaxErrors.java index 6ab452621..30f5a1d0f 100644 --- a/tool/test/org/antlr/v4/test/TestToolSyntaxErrors.java +++ b/tool/test/org/antlr/v4/test/TestToolSyntaxErrors.java @@ -473,4 +473,33 @@ public class TestToolSyntaxErrors extends BaseTest { super.testErrors(pair, true); } + + /** + * This test ensures the {@link ErrorType#FRAGMENT_ACTION_IGNORED} warning + * is produced as described in the documentation. + */ + @Test public void testFragmentActionIgnored() { + String grammar = + "lexer grammar A;\n" + + "X1 : 'x' -> more // ok\n" + + " ;\n" + + "Y1 : 'x' {more();} // ok\n" + + " ;\n" + + "fragment\n" + + "X2 : 'x' -> more // warning 158\n" + + " ;\n" + + "fragment\n" + + "Y2 : 'x' {more();} // warning 158\n" + + " ;\n"; + String expected = + "warning(" + ErrorType.FRAGMENT_ACTION_IGNORED.code + "): A.g4:7:12: fragment rule 'X2' contains an action or command which can never be executed\n" + + "warning(" + ErrorType.FRAGMENT_ACTION_IGNORED.code + "): A.g4:10:9: fragment rule 'Y2' contains an action or command which can never be executed\n"; + + String[] pair = new String[] { + grammar, + expected + }; + + super.testErrors(pair, true); + } }