public boolean checkForRuleIssues(final Grammar g) {
		// check for redefined rules
		GrammarAST RULES = (GrammarAST)g.ast.getFirstChildWithType(ANTLRParser.RULES);
		List<GrammarAST> rules = new ArrayList<GrammarAST>(RULES.getAllChildrenWithType(ANTLRParser.RULE));
		for (GrammarAST mode : g.ast.getAllChildrenWithType(ANTLRParser.MODE)) {
			rules.addAll(mode.getAllChildrenWithType(ANTLRParser.RULE));
		}

		boolean redefinition = false;
		final Map<String, RuleAST> ruleToAST = new HashMap<String, RuleAST>();
		for (GrammarAST r : rules) {
			RuleAST ruleAST = (RuleAST)r;
			GrammarAST ID = (GrammarAST)ruleAST.getChild(0);
			String ruleName = ID.getText();
			RuleAST prev = ruleToAST.get(ruleName);
			if ( prev !=null ) {
				GrammarAST prevChild = (GrammarAST)prev.getChild(0);
				g.tool.errMgr.grammarError(ErrorType.RULE_REDEFINITION,
										   g.fileName,
										   ID.getToken(),
										   ruleName,
										   prevChild.getToken().getLine());
				redefinition = true;
				continue;
			}
			ruleToAST.put(ruleName, ruleAST);
		}

		// check for undefined rules
		class UndefChecker extends GrammarTreeVisitor {
			public boolean badref = false;
			@Override
			public void tokenRef(TerminalAST ref) {
				if ("EOF".equals(ref.getText())) {
					// this is a special predefined reference
					return;
				}

				if ( g.isLexer() ) ruleRef(ref, null);
			}

			@Override
			public void ruleRef(GrammarAST ref, ActionAST arg) {
				RuleAST ruleAST = ruleToAST.get(ref.getText());
				String fileName = ref.getToken().getInputStream().getSourceName();
				if (Character.isUpperCase(currentRuleName.charAt(0)) &&
					Character.isLowerCase(ref.getText().charAt(0)))
				{
					badref = true;
					errMgr.grammarError(ErrorType.PARSER_RULE_REF_IN_LEXER_RULE,
										fileName, ref.getToken(), ref.getText(), currentRuleName);
				}
				else if ( ruleAST==null ) {
					badref = true;
					errMgr.grammarError(ErrorType.UNDEFINED_RULE_REF,
										fileName, ref.token, ref.getText());
				}
			}
			@Override
			public ErrorManager getErrorManager() { return errMgr; }
		}

		UndefChecker chk = new UndefChecker();
		chk.visitGrammar(g.ast);

		return redefinition || chk.badref;
	}
Important enough to avoid multiple definitions that we do very early,
right after AST construction. Also check for undefined rules in
parser/lexer to avoid exceptions later. Return true if we find multiple
definitions of the same rule or a reference to an undefined rule or
parser rule ref in lexer rule.
tool/src/org/antlr/v4/Tool.java