diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/descriptors/ParserErrorsDescriptors.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/descriptors/ParserErrorsDescriptors.java index 0b53e994e..26352d317 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/descriptors/ParserErrorsDescriptors.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/descriptors/ParserErrorsDescriptors.java @@ -618,4 +618,28 @@ public class ParserErrorsDescriptors { public String grammar; } + + public static class ExtraneousInput extends BaseParserTestDescriptor { + public String input = "baa"; + public String output = null; + public String errors = "line 1:0 mismatched input 'b' expecting {, 'a'}\n"; + public String startRule = "file"; + public String grammarName = "T"; + + /** + grammar T; + + member : 'a'; + body : member*; + file : body EOF; + B : 'b'; + */ + @CommentHasStringValue + public String grammar; + + @Override + public boolean ignore(String targetName) { + return !"Java".equals(targetName); + } + } } diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/descriptors/SemPredEvalParserDescriptors.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/descriptors/SemPredEvalParserDescriptors.java index fbf6cfbfc..218bdb789 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/descriptors/SemPredEvalParserDescriptors.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/descriptors/SemPredEvalParserDescriptors.java @@ -283,11 +283,16 @@ public class SemPredEvalParserDescriptors { public String input = "s\n\n\nx\n"; public String output = "(file_ (para (paraContent s) \\n \\n) (para (paraContent \\n x \\n)) )\n"; /** - line 5:0 mismatched input '' expecting ' - ' + line 5:0 mismatched input '' expecting {'s', ' + ', 'x'} */ @CommentHasStringValue public String errors; + + @Override + public boolean ignore(String targetName) { + return !"Java".equals(targetName); + } } public static class PredFromAltTestedInLoopBack_2 extends PredFromAltTestedInLoopBack { diff --git a/runtime/Java/src/org/antlr/v4/runtime/DefaultErrorStrategy.java b/runtime/Java/src/org/antlr/v4/runtime/DefaultErrorStrategy.java index 819538539..02b5ee510 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/DefaultErrorStrategy.java +++ b/runtime/Java/src/org/antlr/v4/runtime/DefaultErrorStrategy.java @@ -36,6 +36,21 @@ public class DefaultErrorStrategy implements ANTLRErrorStrategy { protected IntervalSet lastErrorStates; + /** + * This field is used to propagate information about the lookahead following + * the previous match. Since prediction prefers completing the current rule + * to error recovery efforts, error reporting may occur later than the + * original point where it was discoverable. The original context is used to + * compute the true expected sets as though the reporting occurred as early + * as possible. + */ + protected ParserRuleContext nextTokensContext; + + /** + * @see #nextTokensContext + */ + protected int nextTokensState; + /** * {@inheritDoc} * @@ -225,7 +240,20 @@ public class DefaultErrorStrategy implements ANTLRErrorStrategy { // try cheaper subset first; might get lucky. seems to shave a wee bit off IntervalSet nextTokens = recognizer.getATN().nextTokens(s); - if (nextTokens.contains(Token.EPSILON) || nextTokens.contains(la)) { + if (nextTokens.contains(la)) { + // We are sure the token matches + nextTokensContext = null; + nextTokensState = ATNState.INVALID_STATE_NUMBER; + return; + } + + if (nextTokens.contains(Token.EPSILON)) { + if (nextTokensContext == null) { + // It's possible the next token won't match; information tracked + // by sync is restricted for performance. + nextTokensContext = recognizer.getContext(); + nextTokensState = recognizer.getState(); + } return; } @@ -450,7 +478,14 @@ public class DefaultErrorStrategy implements ANTLRErrorStrategy { } // even that didn't work; must throw the exception - throw new InputMismatchException(recognizer); + InputMismatchException e; + if (nextTokensContext == null) { + e = new InputMismatchException(recognizer); + } else { + e = new InputMismatchException(recognizer, nextTokensState, nextTokensContext); + } + + throw e; } /** diff --git a/runtime/Java/src/org/antlr/v4/runtime/InputMismatchException.java b/runtime/Java/src/org/antlr/v4/runtime/InputMismatchException.java index fc4261558..08ef67c58 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/InputMismatchException.java +++ b/runtime/Java/src/org/antlr/v4/runtime/InputMismatchException.java @@ -13,4 +13,10 @@ public class InputMismatchException extends RecognitionException { super(recognizer, recognizer.getInputStream(), recognizer._ctx); this.setOffendingToken(recognizer.getCurrentToken()); } + + public InputMismatchException(Parser recognizer, int state, ParserRuleContext ctx) { + super(recognizer, recognizer.getInputStream(), ctx); + this.setOffendingState(state); + this.setOffendingToken(recognizer.getCurrentToken()); + } }