diff --git a/tool/src/org/antlr/v4/analysis/LeftRecursiveRuleTransformer.java b/tool/src/org/antlr/v4/analysis/LeftRecursiveRuleTransformer.java index cb58c54ee..ea67493f9 100644 --- a/tool/src/org/antlr/v4/analysis/LeftRecursiveRuleTransformer.java +++ b/tool/src/org/antlr/v4/analysis/LeftRecursiveRuleTransformer.java @@ -93,7 +93,12 @@ public class LeftRecursiveRuleTransformer { if ( !Grammar.isTokenName(r.name) ) { if ( LeftRecursiveRuleAnalyzer.hasImmediateRecursiveRuleRefs(r.ast, r.name) ) { boolean fitsPattern = translateLeftRecursiveRule(ast, (LeftRecursiveRule)r, language); - if ( fitsPattern ) leftRecursiveRuleNames.add(r.name); + if ( fitsPattern ) { + leftRecursiveRuleNames.add(r.name); + } + else { // better given an error that non-conforming left-recursion exists + tool.errMgr.grammarError(ErrorType.NONCONFORMING_LR_RULE, g.fileName, ((GrammarAST)r.ast.getChild(0)).token, r.name); + } } } } diff --git a/tool/src/org/antlr/v4/parse/GrammarTreeVisitor.g b/tool/src/org/antlr/v4/parse/GrammarTreeVisitor.g index 72f6ffb85..9c8b1be43 100644 --- a/tool/src/org/antlr/v4/parse/GrammarTreeVisitor.g +++ b/tool/src/org/antlr/v4/parse/GrammarTreeVisitor.g @@ -79,6 +79,7 @@ package org.antlr.v4.parse; import org.antlr.v4.Tool; import org.antlr.v4.tool.*; import org.antlr.v4.tool.ast.*; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; } @@ -105,8 +106,12 @@ public void visit(GrammarAST t, String ruleName) { Method m = getClass().getMethod(ruleName); m.invoke(this); } - catch (Exception e) { + catch (Throwable e) { ErrorManager errMgr = getErrorManager(); + if ( e instanceof InvocationTargetException ) { + e = e.getCause(); + } + //e.printStackTrace(System.err); if ( errMgr==null ) { System.err.println("can't find rule "+ruleName+ " or tree structure error: "+t.toStringTree() diff --git a/tool/src/org/antlr/v4/semantics/SemanticPipeline.java b/tool/src/org/antlr/v4/semantics/SemanticPipeline.java index ba8dae740..3561f91a8 100644 --- a/tool/src/org/antlr/v4/semantics/SemanticPipeline.java +++ b/tool/src/org/antlr/v4/semantics/SemanticPipeline.java @@ -85,14 +85,15 @@ public class SemanticPipeline { BasicSemanticChecks basics = new BasicSemanticChecks(g, ruleCollector); basics.process(); - // don't continue if we get errors in this basic check - //if ( false ) return; - // TRANSFORM LEFT-RECURSIVE RULES + int prevErrors = g.tool.errMgr.getNumErrors(); LeftRecursiveRuleTransformer lrtrans = new LeftRecursiveRuleTransformer(g.ast, ruleCollector.rules.values(), g); lrtrans.translateLeftRecursiveRules(); + // don't continue if we got errors during left-recursion elimination + if ( g.tool.errMgr.getNumErrors()>prevErrors ) return; + // STORE RULES IN GRAMMAR for (Rule r : ruleCollector.rules.values()) { g.defineRule(r); diff --git a/tool/src/org/antlr/v4/tool/ErrorType.java b/tool/src/org/antlr/v4/tool/ErrorType.java index 8e70ff452..6b94f9d3d 100644 --- a/tool/src/org/antlr/v4/tool/ErrorType.java +++ b/tool/src/org/antlr/v4/tool/ErrorType.java @@ -956,6 +956,8 @@ public enum ErrorType { */ CHANNELS_BLOCK_IN_COMBINED_GRAMMAR(164, "custom channels are not supported in combined grammars", ErrorSeverity.ERROR), + NONCONFORMING_LR_RULE(165, "rule is left recursive but doesn't conform to a pattern ANTLR can handle", ErrorSeverity.ERROR), + /* * Backward incompatibility errors */ diff --git a/tool/test/org/antlr/v4/test/tool/TestLeftRecursion.java b/tool/test/org/antlr/v4/test/tool/TestLeftRecursionToolIssues.java similarity index 56% rename from tool/test/org/antlr/v4/test/tool/TestLeftRecursion.java rename to tool/test/org/antlr/v4/test/tool/TestLeftRecursionToolIssues.java index f13b99bea..973befa85 100644 --- a/tool/test/org/antlr/v4/test/tool/TestLeftRecursion.java +++ b/tool/test/org/antlr/v4/test/tool/TestLeftRecursionToolIssues.java @@ -34,7 +34,7 @@ import org.antlr.v4.tool.ErrorType; import org.junit.Test; /** */ -public class TestLeftRecursion extends BaseTest { +public class TestLeftRecursionToolIssues extends BaseTest { protected boolean debug = false; @Test public void testCheckForNonLeftRecursiveRule() throws Exception { @@ -64,4 +64,60 @@ public class TestLeftRecursion extends BaseTest { testErrors(new String[] { grammar, expected }, false); } + /** Reproduces https://github.com/antlr/antlr4/issues/855 */ + @Test public void testLeftRecursiveRuleRefWithArg() throws Exception { + String grammar = + "grammar T;\n" + + "statement\n" + + "locals[Scope scope]\n" + + " : expressionA[$scope] ';'\n" + + " ;\n" + + "expressionA[Scope scope]\n" + + " : atom[$scope]\n" + + " | expressionA[$scope] '[' expressionA[$scope] ']'\n" + + " ;\n" + + "atom[Scope scope]\n" + + " : 'dummy'\n" + + " ;\n"; + String expected = + "error(" + ErrorType.NONCONFORMING_LR_RULE.code + "): T.g4:6:0: rule expressionA is left recursive but doesn't conform to a pattern ANTLR can handle\n"; + testErrors(new String[]{grammar, expected}, false); + } + + /** Reproduces https://github.com/antlr/antlr4/issues/855 */ + @Test public void testLeftRecursiveRuleRefWithArg2() throws Exception { + String grammar = + "grammar T;\n" + + "a[int i] : 'x'\n" + + " | a[3] 'y'\n" + + " ;"; + String expected = + "error(" + ErrorType.NONCONFORMING_LR_RULE.code + "): T.g4:2:0: rule a is left recursive but doesn't conform to a pattern ANTLR can handle\n"; + testErrors(new String[]{grammar, expected}, false); + } + + + /** Reproduces https://github.com/antlr/antlr4/issues/855 */ + @Test public void testLeftRecursiveRuleRefWithArg3() throws Exception { + String grammar = + "grammar T;\n" + + "a : 'x'\n" + + " | a[3] 'y'\n" + + " ;"; + String expected = + "error(" + ErrorType.NONCONFORMING_LR_RULE.code + "): T.g4:2:0: rule a is left recursive but doesn't conform to a pattern ANTLR can handle\n"; + testErrors(new String[]{grammar, expected}, false); + } + + + /** Reproduces https://github.com/antlr/antlr4/issues/822 */ + @Test public void testIsolatedLeftRecursiveRuleRef() throws Exception { + String grammar = + "grammar T;\n" + + "a : a | b ;\n" + + "b : 'B' ;\n"; + String expected = + "error(" + ErrorType.NONCONFORMING_LR_RULE.code + "): T.g4:2:0: rule a is left recursive but doesn't conform to a pattern ANTLR can handle\n"; + testErrors(new String[]{grammar, expected}, false); + } }