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 26352d31727..0a3e40b1985 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 @@ -639,7 +639,7 @@ public static class ExtraneousInput extends BaseParserTestDescriptor { @Override public boolean ignore(String targetName) { - return !"Java".equals(targetName); + return !"Java".equals(targetName) && !"Swift".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 218bdb789fc..ed4f9a384ec 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 @@ -291,7 +291,7 @@ public static class PredFromAltTestedInLoopBack_1 extends PredFromAltTestedInLoo @Override public boolean ignore(String targetName) { - return !"Java".equals(targetName); + return !"Java".equals(targetName) && !"Swift".equals(targetName); } } diff --git a/runtime/Swift/Sources/Antlr4/DefaultErrorStrategy.swift b/runtime/Swift/Sources/Antlr4/DefaultErrorStrategy.swift index ab13f181006..fbc82ed0294 100644 --- a/runtime/Swift/Sources/Antlr4/DefaultErrorStrategy.swift +++ b/runtime/Swift/Sources/Antlr4/DefaultErrorStrategy.swift @@ -32,6 +32,22 @@ open class DefaultErrorStrategy: ANTLRErrorStrategy { open var lastErrorStates: IntervalSet? + /** + * 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. + */ + open var nextTokensContext: ParserRuleContext? + + /** + * @see #nextTokensContext + */ + open var nextTokensState = ATNState.INVALID_STATE_NUMBER + + public init() { } @@ -207,7 +223,20 @@ open class DefaultErrorStrategy: ANTLRErrorStrategy { // try cheaper subset first; might get lucky. seems to shave a wee bit off let nextToks = recognizer.getATN().nextTokens(s) - if nextToks.contains(CommonToken.EPSILON) || nextToks.contains(la) { + if nextToks.contains(la) { + // We are sure the token matches + nextTokensContext = nil + nextTokensState = ATNState.INVALID_STATE_NUMBER + return + } + + if nextToks.contains(CommonToken.EPSILON) { + if nextTokensContext == nil { + // It's possible the next token won't match; information tracked + // by sync is restricted for performance. + nextTokensContext = recognizer.getContext() + nextTokensState = recognizer.getState() + } return } @@ -423,7 +452,8 @@ open class DefaultErrorStrategy: ANTLRErrorStrategy { return try getMissingSymbol(recognizer) } // even that didn't work; must throw the exception - throw ANTLRException.recognition(e: InputMismatchException(recognizer)) + let exn = InputMismatchException(recognizer, state: nextTokensState, ctx: nextTokensContext) + throw ANTLRException.recognition(e: exn) } /// diff --git a/runtime/Swift/Sources/Antlr4/InputMismatchException.swift b/runtime/Swift/Sources/Antlr4/InputMismatchException.swift index 8af781702bf..f297de1adfe 100644 --- a/runtime/Swift/Sources/Antlr4/InputMismatchException.swift +++ b/runtime/Swift/Sources/Antlr4/InputMismatchException.swift @@ -11,10 +11,14 @@ /// public class InputMismatchException: RecognitionException { - public init(_ recognizer: Parser) { - super.init(recognizer, recognizer.getInputStream()!, recognizer._ctx) + public init(_ recognizer: Parser, state: Int = ATNState.INVALID_STATE_NUMBER, ctx: ParserRuleContext? = nil) { + let bestCtx = ctx ?? recognizer._ctx + + super.init(recognizer, recognizer.getInputStream()!, bestCtx) + if let token = try? recognizer.getCurrentToken() { setOffendingToken(token) } + setOffendingState(state) } }