diff --git a/CHANGES.txt b/CHANGES.txt index 03db1a707..af0254c77 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -10,6 +10,7 @@ November 17, 2012 * wasn't checking soon enough for rule redef; now it sets a dead flag in AST so no more walking dup. error(51): T.g:7:0: rule s redefinition (ignoring); previous at line 3 +* fixed: undefined rule refs caused exception November 11, 2012 diff --git a/tool/playground/T.g b/tool/playground/T.g index 014d3068f..62ed58c66 100644 --- a/tool/playground/T.g +++ b/tool/playground/T.g @@ -1,7 +1,7 @@ grammar T; s : A # X - | B + | x ; -s : A ; +A : C ; diff --git a/tool/src/org/antlr/v4/Tool.java b/tool/src/org/antlr/v4/Tool.java index 87794d227..03e8ade46 100644 --- a/tool/src/org/antlr/v4/Tool.java +++ b/tool/src/org/antlr/v4/Tool.java @@ -44,6 +44,7 @@ import org.antlr.v4.misc.Graph; import org.antlr.v4.parse.ANTLRLexer; import org.antlr.v4.parse.ANTLRParser; import org.antlr.v4.parse.GrammarASTAdaptor; +import org.antlr.v4.parse.GrammarTreeVisitor; import org.antlr.v4.parse.ToolANTLRParser; import org.antlr.v4.runtime.misc.LogManager; import org.antlr.v4.runtime.misc.Nullable; @@ -59,10 +60,12 @@ import org.antlr.v4.tool.Grammar; import org.antlr.v4.tool.GrammarTransformPipeline; import org.antlr.v4.tool.LexerGrammar; import org.antlr.v4.tool.Rule; +import org.antlr.v4.tool.ast.ActionAST; import org.antlr.v4.tool.ast.GrammarAST; import org.antlr.v4.tool.ast.GrammarASTErrorNode; import org.antlr.v4.tool.ast.GrammarRootAST; import org.antlr.v4.tool.ast.RuleAST; +import org.antlr.v4.tool.ast.TerminalAST; import org.stringtemplate.v4.STGroup; import java.io.BufferedWriter; @@ -388,7 +391,8 @@ public class Tool { if ( g.ast.hasErrors ) return; - checkForRedefinedRules(g); + boolean ruleFail = checkForRuleIssues(g); + if ( ruleFail ) return; int prevErrors = errMgr.getNumErrors(); // MAKE SURE GRAMMAR IS SEMANTICALLY CORRECT (FILL IN GRAMMAR OBJECT) @@ -422,12 +426,14 @@ public class Tool { /** Important enough to avoid multiple defs that we do very early, * right after AST construction. Turn redef'd rule's AST RULE node dead - * field to true. + * field to true. Also check for undefined rules in parser/lexer to + * avoid exceptions later. Return true if we find an undefined rule. */ - public void checkForRedefinedRules(Grammar g) { + public boolean checkForRuleIssues(final Grammar g) { + // check for redefined rules GrammarAST RULES = (GrammarAST)g.ast.getFirstChildWithType(ANTLRParser.RULES); List rules = (List)RULES.getChildren(); - Map ruleToAST = new HashMap(); + final Map ruleToAST = new HashMap(); for (RuleAST r : rules) { GrammarAST ID = (GrammarAST)r.getChild(0); String ruleName = ID.getText(); @@ -444,6 +450,29 @@ public class Tool { } ruleToAST.put(ruleName, r); } + + // check for undefined rules + class UndefChecker extends GrammarTreeVisitor { + public boolean undefined = false; + @Override + public void tokenRef(TerminalAST ref) { + if ( g.isLexer() ) ruleRef(ref, null); + } + + @Override + public void ruleRef(GrammarAST ref, ActionAST arg) { + RuleAST ruleAST = ruleToAST.get(ref.getText()); + if ( ruleAST==null ) { + undefined = true; + errMgr.grammarError(ErrorType.UNDEFINED_RULE_REF, + g.fileName, ref.token, ref.getText()); + } + } + } + UndefChecker chk = new UndefChecker(); + chk.visitGrammar(g.ast); + + return chk.undefined; // no problem } public List sortGrammarByTokenVocab(List fileNames) { diff --git a/tool/src/org/antlr/v4/semantics/SymbolChecks.java b/tool/src/org/antlr/v4/semantics/SymbolChecks.java index 7352095b8..ddf829c55 100644 --- a/tool/src/org/antlr/v4/semantics/SymbolChecks.java +++ b/tool/src/org/antlr/v4/semantics/SymbolChecks.java @@ -29,7 +29,6 @@ package org.antlr.v4.semantics; -import org.antlr.v4.parse.ANTLRParser; import org.antlr.v4.tool.Alternative; import org.antlr.v4.tool.ErrorManager; import org.antlr.v4.tool.ErrorType; @@ -218,11 +217,6 @@ public class SymbolChecks { for (GrammarAST ref : rulerefs) { String ruleName = ref.getText(); Rule r = g.getRule(ruleName); - if ( r==null && !ref.hasAncestor(ANTLRParser.DOT)) { - // only give error for unqualified rule refs now - errMgr.grammarError(ErrorType.UNDEFINED_RULE_REF, - g.fileName, ref.token, ruleName); - } GrammarAST arg = (GrammarAST)ref.getChild(0); if ( arg!=null && (r==null || r.args==null) ) { errMgr.grammarError(ErrorType.RULE_HAS_NO_ARGS,