diff --git a/runtime/Java/src/org/antlr/v4/runtime/tree/pattern/ParseTreePatternMatcher.java b/runtime/Java/src/org/antlr/v4/runtime/tree/pattern/ParseTreePatternMatcher.java index 5fc2a4c85..c23787d82 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/tree/pattern/ParseTreePatternMatcher.java +++ b/runtime/Java/src/org/antlr/v4/runtime/tree/pattern/ParseTreePatternMatcher.java @@ -37,6 +37,7 @@ import org.antlr.v4.runtime.ListTokenSource; import org.antlr.v4.runtime.Parser; import org.antlr.v4.runtime.ParserInterpreter; import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.RecognitionException; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.atn.ATN; import org.antlr.v4.runtime.misc.MultiMap; @@ -114,6 +115,11 @@ public class ParseTreePatternMatcher { } } + // Fixes https://github.com/antlr/antlr4/issues/413 + // "Tree pattern compilation doesn't check for a complete parse" + public static class StartRuleDoesNotConsumeFullPattern extends RuntimeException { + } + /** * This is the backing field for {@link #getLexer()}. */ @@ -222,10 +228,18 @@ public class ParseTreePatternMatcher { tree = parserInterp.parse(patternRuleIndex); // System.out.println("pattern tree = "+tree.toStringTree(parserInterp)); } + catch (RecognitionException re) { + throw re; + } catch (Exception e) { throw new CannotInvokeStartRule(e); } + // Make sure tree pattern compilation checks for a complete parse + if ( tokens.LA(1)!=Token.EOF ) { + throw new StartRuleDoesNotConsumeFullPattern(); + } + return new ParseTreePattern(this, pattern, patternRuleIndex, tree); } diff --git a/tool/test/org/antlr/v4/test/TestParseTreeMatcher.java b/tool/test/org/antlr/v4/test/TestParseTreeMatcher.java index 62d20fb29..8109191a1 100644 --- a/tool/test/org/antlr/v4/test/TestParseTreeMatcher.java +++ b/tool/test/org/antlr/v4/test/TestParseTreeMatcher.java @@ -2,7 +2,9 @@ package org.antlr.v4.test; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.InputMismatchException; import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.NoViableAltException; import org.antlr.v4.runtime.Parser; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.TokenStream; @@ -119,6 +121,81 @@ public class TestParseTreeMatcher extends BaseTest { assertEquals(expected, results); } + @Test + public void testCompilingPatternConsumesAllTokens() throws Exception { + String grammar = + "grammar X2;\n" + + "s : ID '=' expr ';' ;\n" + + "expr : ID | INT ;\n" + + "ID : [a-z]+ ;\n" + + "INT : [0-9]+ ;\n" + + "WS : [ \\r\\n\\t]+ -> skip ;\n"; + boolean ok = + rawGenerateAndBuildRecognizer("X2.g4", grammar, "X2Parser", "X2Lexer", false); + assertTrue(ok); + + ParseTreePatternMatcher m = getPatternMatcher("X2"); + + boolean failed = false; + try { + m.compile(" = ; extra", m.getParser().getRuleIndex("s")); + } + catch (ParseTreePatternMatcher.StartRuleDoesNotConsumeFullPattern e) { + failed = true; + } + assertTrue(failed); + } + + @Test + public void testPatternMatchesStartRule() throws Exception { + String grammar = + "grammar X2;\n" + + "s : ID '=' expr ';' ;\n" + + "expr : ID | INT ;\n" + + "ID : [a-z]+ ;\n" + + "INT : [0-9]+ ;\n" + + "WS : [ \\r\\n\\t]+ -> skip ;\n"; + boolean ok = + rawGenerateAndBuildRecognizer("X2.g4", grammar, "X2Parser", "X2Lexer", false); + assertTrue(ok); + + ParseTreePatternMatcher m = getPatternMatcher("X2"); + + boolean failed = false; + try { + m.compile(" ;", m.getParser().getRuleIndex("s")); + } + catch (InputMismatchException e) { + failed = true; + } + assertTrue(failed); + } + + @Test + public void testPatternMatchesStartRule2() throws Exception { + String grammar = + "grammar X2;\n" + + "s : ID '=' expr ';' | expr ';' ;\n" + + "expr : ID | INT ;\n" + + "ID : [a-z]+ ;\n" + + "INT : [0-9]+ ;\n" + + "WS : [ \\r\\n\\t]+ -> skip ;\n"; + boolean ok = + rawGenerateAndBuildRecognizer("X2.g4", grammar, "X2Parser", "X2Lexer", false); + assertTrue(ok); + + ParseTreePatternMatcher m = getPatternMatcher("X2"); + + boolean failed = false; + try { + m.compile(" ;", m.getParser().getRuleIndex("s")); + } + catch (NoViableAltException e) { + failed = true; + } + assertTrue(failed); + } + @Test public void testHiddenTokensNotSeenByTreePatternParser() throws Exception { String grammar =