From 0c715d13f1d1b30cfc3a05e405b2b1d6c655ab95 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Wed, 27 Dec 2023 18:22:08 -0800 Subject: [PATCH] Fixes #1173: column/offset locations wrong for many error conditions (#1176) --- release-notes/CREDITS-2.x | 5 ++++ release-notes/VERSION-2.x | 6 ++++ .../core/json/ReaderBasedJsonParser.java | 17 +++++++++++ .../core/json/UTF8StreamJsonParser.java | 17 +++++++++++ .../json/async/NonBlockingJsonParserBase.java | 2 ++ .../async/NonBlockingUtf8JsonParserBase.java | 30 +++++++++++++++++++ .../failing/read/LocationOfError1173Test.java | 4 +-- 7 files changed, 79 insertions(+), 2 deletions(-) diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index b19b7eab59..22e08b2d1c 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -405,3 +405,8 @@ David Schlosnagle (@schlosna) Mario Fusco (@mariofusco) * Contributed #1064: Add full set of `BufferRecyclerPool` implementations (2.16.0) + +Paul Bunyan (@hal7df) + * Reported #1173: `JsonLocation` consistently off by one character for many invalid + JSON parsing cases + (2.16.2) diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 10a340a7ba..9f7c44e607 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -14,6 +14,12 @@ a pure JSON library. === Releases === ------------------------------------------------------------------------ +2.16.2 (not yet released) + +#1173: `JsonLocation` consistently off by one character for many invalid JSON + parsing cases + (reported by Paul B) + 2.16.1 (24-Dec-2023) #1141: NPE in `Version.equals()` if snapshot-info `null` diff --git a/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java b/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java index 27b5cfec2a..15e4bf4854 100644 --- a/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java +++ b/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java @@ -769,6 +769,7 @@ public final JsonToken nextToken() throws IOException case '}': // Error: } is not valid at this point; valid closers have // been handled earlier + --_inputPtr; // for correct error reporting _reportUnexpectedChar(i, "expected a value"); case 't': _matchTrue(); @@ -1455,6 +1456,7 @@ private final JsonToken _parseFloat(int ch, int startPtr, int ptr, boolean neg, // must be followed by sequence of ints, one minimum if (fractLen == 0) { if (!isEnabled(JsonReadFeature.ALLOW_TRAILING_DECIMAL_POINT_FOR_NUMBERS.mappedFeature())) { + --_inputPtr; // for correct error reporting _reportUnexpectedNumberChar(ch, "Decimal point not followed by a digit"); } } @@ -1484,6 +1486,7 @@ private final JsonToken _parseFloat(int ch, int startPtr, int ptr, boolean neg, } // must be followed by sequence of ints, one minimum if (expLen == 0) { + --_inputPtr; // for correct error reporting _reportUnexpectedNumberChar(ch, "Exponent indicator not followed by a digit"); } } @@ -1644,6 +1647,7 @@ private final JsonToken _parseNumber2(boolean neg, int startPtr) throws IOExcept // must be followed by sequence of ints, one minimum if (fractLen == 0) { if (!isEnabled(JsonReadFeature.ALLOW_TRAILING_DECIMAL_POINT_FOR_NUMBERS.mappedFeature())) { + --_inputPtr; // for correct error reporting _reportUnexpectedNumberChar(c, "Decimal point not followed by a digit"); } } @@ -1688,6 +1692,7 @@ private final JsonToken _parseNumber2(boolean neg, int startPtr) throws IOExcept } // must be followed by sequence of ints, one minimum if (expLen == 0) { + --_inputPtr; // for correct error reporting _reportUnexpectedNumberChar(c, "Exponent indicator not followed by a digit"); } } @@ -1788,11 +1793,13 @@ protected JsonToken _handleInvalidNumberStart(int ch, final boolean negative, fi } } if (!isEnabled(JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS.mappedFeature()) && hasSign && !negative) { + --_inputPtr; // for correct error reporting _reportUnexpectedNumberChar('+', "JSON spec does not allow numbers to have plus signs: enable `JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS` to allow"); } final String message = negative ? "expected digit (0-9) to follow minus sign, for valid numeric value" : "expected digit (0-9) for valid numeric value"; + --_inputPtr; // for correct error reporting _reportUnexpectedNumberChar(ch, message); return null; } @@ -1940,6 +1947,7 @@ protected String _handleOddName(int i) throws IOException } // [JACKSON-69]: allow unquoted names if feature enabled: if ((_features & FEAT_MASK_ALLOW_UNQUOTED_NAMES) == 0) { + --_inputPtr; // for correct error reporting _reportUnexpectedChar(i, "was expecting double-quote to start field name"); } final int[] codes = CharTypes.getInputCodeLatin1JsNames(); @@ -1954,6 +1962,7 @@ protected String _handleOddName(int i) throws IOException firstOk = Character.isJavaIdentifierPart((char) i); } if (!firstOk) { + --_inputPtr; // for correct error reporting _reportUnexpectedChar(i, "was expecting either valid name character (for unquoted name) or double-quote (for quoted) to start field name"); } int ptr = _inputPtr; @@ -2085,6 +2094,7 @@ protected JsonToken _handleOddValue(int i) throws IOException _reportInvalidToken(""+((char) i), _validJsonTokenList()); } // but if it doesn't look like a token: + --_inputPtr; // for correct error reporting _reportUnexpectedChar(i, "expected a valid value "+_validJsonValueList()); return null; } @@ -2387,6 +2397,7 @@ private final int _skipColon2(boolean gotColon) throws IOException return i; } if (i != INT_COLON) { + --_inputPtr; // for correct error reporting _reportUnexpectedChar(i, "was expecting a colon to separate field name and value"); } gotColon = true; @@ -2460,6 +2471,7 @@ private final int _skipColonFast(int ptr) throws IOException private final int _skipComma(int i) throws IOException { if (i != INT_COMMA) { + --_inputPtr; // for correct error reporting _reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries"); } while (_inputPtr < _inputEnd) { @@ -2602,6 +2614,7 @@ private int _skipWSOrEnd2() throws IOException private void _skipComment() throws IOException { if ((_features & FEAT_MASK_ALLOW_JAVA_COMMENTS) == 0) { + --_inputPtr; // for correct error reporting _reportUnexpectedChar('/', "maybe a (non-standard) comment? (not recognized as one since Feature 'ALLOW_COMMENTS' not enabled for parser)"); } // First: check which comment (if either) it is: @@ -2614,6 +2627,7 @@ private void _skipComment() throws IOException } else if (c == '*') { _skipCComment(); } else { + --_inputPtr; // for correct error reporting _reportUnexpectedChar(c, "was expecting either '*' or '/' for a comment"); } } @@ -2725,6 +2739,7 @@ protected char _decodeEscaped() throws IOException int ch = (int) _inputBuffer[_inputPtr++]; int digit = CharTypes.charToHex(ch); if (digit < 0) { + --_inputPtr; // for correct error reporting _reportUnexpectedChar(ch, "expected a hex-digit for character escape sequence"); } value = (value << 4) | digit; @@ -3052,6 +3067,7 @@ private void _closeScope(int i) throws JsonParseException { if (i == INT_RBRACKET) { _updateLocation(); if (!_parsingContext.inArray()) { + --_inputPtr; // for correct error reporting _reportMismatchedEndMarker(i, '}'); } _parsingContext = _parsingContext.clearAndGetParent(); @@ -3060,6 +3076,7 @@ private void _closeScope(int i) throws JsonParseException { if (i == INT_RCURLY) { _updateLocation(); if (!_parsingContext.inObject()) { + --_inputPtr; // for correct error reporting _reportMismatchedEndMarker(i, ']'); } _parsingContext = _parsingContext.clearAndGetParent(); diff --git a/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java b/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java index 4f43c7eeb9..d28d632dc8 100644 --- a/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java +++ b/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java @@ -779,6 +779,7 @@ public JsonToken nextToken() throws IOException // Nope: do we then expect a comma? if (_parsingContext.expectComma()) { if (i != INT_COMMA) { + --_inputPtr; // for correct error reporting _reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries"); } i = _skipWS(); @@ -977,6 +978,7 @@ public boolean nextFieldName(SerializableString str) throws IOException // Nope: do we then expect a comma? if (_parsingContext.expectComma()) { if (i != INT_COMMA) { + --_inputPtr; // for correct error reporting _reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries"); } i = _skipWS(); @@ -1062,6 +1064,7 @@ public String nextFieldName() throws IOException // Nope: do we then expect a comma? if (_parsingContext.expectComma()) { if (i != INT_COMMA) { + --_inputPtr; // for correct error reporting _reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries"); } i = _skipWS(); @@ -1685,6 +1688,7 @@ private final JsonToken _parseFloat(char[] outBuf, int outPtr, int c, // must be followed by sequence of ints, one minimum if (fractLen == 0) { if (!isEnabled(JsonReadFeature.ALLOW_TRAILING_DECIMAL_POINT_FOR_NUMBERS.mappedFeature())) { + --_inputPtr; // for correct error reporting _reportUnexpectedNumberChar(c, "Decimal point not followed by a digit"); } } @@ -1732,6 +1736,7 @@ private final JsonToken _parseFloat(char[] outBuf, int outPtr, int c, } // must be followed by sequence of ints, one minimum if (expLen == 0) { + --_inputPtr; // for correct error reporting _reportUnexpectedNumberChar(c, "Exponent indicator not followed by a digit"); } } @@ -2144,6 +2149,7 @@ protected String _handleOddName(int ch) throws IOException // Allow unquoted names if feature enabled: if ((_features & FEAT_MASK_ALLOW_UNQUOTED_NAMES) == 0) { char c = (char) _decodeCharForError(ch); + --_inputPtr; // for correct error reporting _reportUnexpectedChar(c, "was expecting double-quote to start field name"); } /* Also: note that although we use a different table here, @@ -2153,6 +2159,7 @@ protected String _handleOddName(int ch) throws IOException final int[] codes = CharTypes.getInputCodeUtf8JsNames(); // Also: must start with a valid character... if (codes[ch] != 0) { + --_inputPtr; // for correct error reporting _reportUnexpectedChar(ch, "was expecting either valid name character (for unquoted name) or double-quote (for quoted) to start field name"); } @@ -2758,6 +2765,7 @@ protected JsonToken _handleUnexpectedValue(int c) throws IOException case '}': // Error: neither is valid at this point; valid closers have // been handled earlier + --_inputPtr; // for correct error reporting _reportUnexpectedChar(c, "expected a value"); case '\'': if ((_features & FEAT_MASK_ALLOW_SINGLE_QUOTES) != 0) { @@ -2791,6 +2799,7 @@ protected JsonToken _handleUnexpectedValue(int c) throws IOException _reportInvalidToken(""+((char) c), _validJsonTokenList()); } // but if it doesn't look like a token: + --_inputPtr; // for correct error reporting _reportUnexpectedChar(c, "expected a valid value "+_validJsonValueList()); return null; } @@ -2923,11 +2932,13 @@ protected JsonToken _handleInvalidNumberStart(int ch, final boolean neg, final b match); } if (!isEnabled(JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS.mappedFeature()) && hasSign && !neg) { + --_inputPtr; // for correct error reporting _reportUnexpectedNumberChar('+', "JSON spec does not allow numbers to have plus signs: enable `JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS` to allow"); } final String message = neg ? "expected digit (0-9) to follow minus sign, for valid numeric value" : "expected digit (0-9) for valid numeric value"; + --_inputPtr; // for correct error reporting _reportUnexpectedNumberChar(ch, message); return null; } @@ -3253,6 +3264,7 @@ private final int _skipColon2(boolean gotColon) throws IOException return i; } if (i != INT_COLON) { + --_inputPtr; // for correct error reporting _reportUnexpectedChar(i, "was expecting a colon to separate field name and value"); } gotColon = true; @@ -3275,6 +3287,7 @@ private final int _skipColon2(boolean gotColon) throws IOException private final void _skipComment() throws IOException { if ((_features & FEAT_MASK_ALLOW_JAVA_COMMENTS) == 0) { + --_inputPtr; // for correct error reporting _reportUnexpectedChar('/', "maybe a (non-standard) comment? (not recognized as one since Feature 'ALLOW_COMMENTS' not enabled for parser)"); } // First: check which comment (if either) it is: @@ -3287,6 +3300,7 @@ private final void _skipComment() throws IOException } else if (c == INT_ASTERISK) { _skipCComment(); } else { + --_inputPtr; // for correct error reporting _reportUnexpectedChar(c, "was expecting either '*' or '/' for a comment"); } } @@ -3432,6 +3446,7 @@ protected char _decodeEscaped() throws IOException int ch = _inputBuffer[_inputPtr++]; int digit = CharTypes.charToHex(ch); if (digit < 0) { + --_inputPtr; // for correct error reporting _reportUnexpectedChar(ch & 0xFF, "expected a hex-digit for character escape sequence"); } value = (value << 4) | digit; @@ -3923,6 +3938,7 @@ private final JsonToken _closeScope(int i) throws JsonParseException { private final void _closeArrayScope() throws JsonParseException { _updateLocation(); if (!_parsingContext.inArray()) { + --_inputPtr; // for correct error reporting _reportMismatchedEndMarker(']', '}'); } _parsingContext = _parsingContext.clearAndGetParent(); @@ -3931,6 +3947,7 @@ private final void _closeArrayScope() throws JsonParseException { private final void _closeObjectScope() throws JsonParseException { _updateLocation(); if (!_parsingContext.inObject()) { + --_inputPtr; // for correct error reporting _reportMismatchedEndMarker('}', ']'); } _parsingContext = _parsingContext.clearAndGetParent(); diff --git a/src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingJsonParserBase.java b/src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingJsonParserBase.java index 4eeb24ee95..70cf7c2ae7 100644 --- a/src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingJsonParserBase.java +++ b/src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingJsonParserBase.java @@ -597,6 +597,7 @@ protected final JsonToken _startObjectScope() throws IOException protected final JsonToken _closeArrayScope() throws IOException { if (!_parsingContext.inArray()) { + --_inputPtr; // for correct error reporting _reportMismatchedEndMarker(']', '}'); } JsonReadContext ctxt = _parsingContext.getParent(); @@ -617,6 +618,7 @@ protected final JsonToken _closeArrayScope() throws IOException protected final JsonToken _closeObjectScope() throws IOException { if (!_parsingContext.inObject()) { + --_inputPtr; // for correct error reporting _reportMismatchedEndMarker('}', ']'); } JsonReadContext ctxt = _parsingContext.getParent(); diff --git a/src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingUtf8JsonParserBase.java b/src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingUtf8JsonParserBase.java index de2f081380..55563b061c 100644 --- a/src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingUtf8JsonParserBase.java +++ b/src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingUtf8JsonParserBase.java @@ -527,6 +527,7 @@ private final JsonToken _startFieldNameAfterComma(int ch) throws IOException if (ch == INT_SLASH) { return _startSlashComment(MINOR_FIELD_LEADING_COMMA); } + --_inputPtr; // for correct error reporting _reportUnexpectedChar(ch, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries"); } int ptr = _inputPtr; @@ -666,6 +667,7 @@ private final JsonToken _startValueExpectComma(int ch) throws IOException if (ch == INT_HASH) { return _finishHashComment(MINOR_VALUE_EXPECTING_COMMA); } + --_inputPtr; // for correct error reporting _reportUnexpectedChar(ch, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries"); } @@ -760,6 +762,7 @@ private final JsonToken _startValueExpectColon(int ch) throws IOException return _finishHashComment(MINOR_VALUE_EXPECTING_COLON); } // can not omit colon here + --_inputPtr; // for correct error reporting _reportUnexpectedChar(ch, "was expecting a colon to separate field name and value"); } int ptr = _inputPtr; @@ -921,6 +924,7 @@ protected JsonToken _startUnexpectedValue(boolean leadingComma, int ch) throws I return _finishNonStdToken(NON_STD_TOKEN_INFINITY, 1); } // !!! TODO: maybe try to collect more information for better diagnostics + --_inputPtr; // for correct error reporting _reportUnexpectedChar(ch, "expected a valid value "+_validJsonValueList()); return null; } @@ -957,6 +961,7 @@ private final int _skipWS(int ch) throws IOException private final JsonToken _startSlashComment(int fromMinorState) throws IOException { if ((_features & FEAT_MASK_ALLOW_JAVA_COMMENTS) == 0) { + --_inputPtr; // for correct error reporting _reportUnexpectedChar('/', "maybe a (non-standard) comment? (not recognized as one since Feature 'ALLOW_COMMENTS' not enabled for parser)"); } @@ -973,6 +978,7 @@ private final JsonToken _startSlashComment(int fromMinorState) throws IOExceptio if (ch == INT_SLASH) { // c++-style return _finishCppComment(fromMinorState); } + --_inputPtr; // for correct error reporting _reportUnexpectedChar(ch & 0xFF, "was expecting either '*' or '/' for a comment"); return null; } @@ -981,6 +987,7 @@ private final JsonToken _finishHashComment(int fromMinorState) throws IOExceptio { // Could by-pass this check by refactoring, but for now simplest way... if ((_features & FEAT_MASK_ALLOW_YAML_COMMENTS) == 0) { + --_inputPtr; // for correct error reporting _reportUnexpectedChar('#', "maybe a (non-standard) comment? (not recognized as one since Feature 'ALLOW_YAML_COMMENTS' not enabled for parser)"); } while (true) { @@ -1344,11 +1351,13 @@ protected JsonToken _startNegativeNumber() throws IOException return _finishNumberLeadingNegZeroes(); } // One special case: if first char is 0, must not be followed by a digit + --_inputPtr; // for correct error reporting _reportUnexpectedNumberChar(ch, "expected digit (0-9) to follow minus sign, for valid numeric value"); } else if (ch > INT_9) { if (ch == 'I') { return _finishNonStdToken(NON_STD_TOKEN_MINUS_INFINITY, 2); } + --_inputPtr; // for correct error reporting _reportUnexpectedNumberChar(ch, "expected digit (0-9) to follow minus sign, for valid numeric value"); } char[] outBuf = _textBuffer.emptyAndGetCurrentSegment(); @@ -1408,19 +1417,23 @@ protected JsonToken _startPositiveNumber() throws IOException if (ch <= INT_0) { if (ch == INT_0) { if (!isEnabled(JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS.mappedFeature())) { + --_inputPtr; // for correct error reporting _reportUnexpectedNumberChar('+', "JSON spec does not allow numbers to have plus signs: enable `JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS` to allow"); } return _finishNumberLeadingPosZeroes(); } // One special case: if first char is 0, must not be followed by a digit + --_inputPtr; // for correct error reporting _reportUnexpectedNumberChar(ch, "expected digit (0-9) to follow plus sign, for valid numeric value"); } else if (ch > INT_9) { if (ch == 'I') { return _finishNonStdToken(NON_STD_TOKEN_PLUS_INFINITY, 2); } + --_inputPtr; // for correct error reporting _reportUnexpectedNumberChar(ch, "expected digit (0-9) to follow plus sign, for valid numeric value"); } if (!isEnabled(JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS.mappedFeature())) { + --_inputPtr; // for correct error reporting _reportUnexpectedNumberChar('+', "JSON spec does not allow numbers to have plus signs: enable `JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS` to allow"); } char[] outBuf = _textBuffer.emptyAndGetCurrentSegment(); @@ -1503,6 +1516,7 @@ protected JsonToken _startNumberLeadingZero() throws IOException // (colon not possible since this is within value, not after key) // if ((ch != INT_RBRACKET) && (ch != INT_RCURLY)) { + --_inputPtr; // for correct error reporting _reportUnexpectedNumberChar(ch, "expected digit (0-9), decimal point (.) or exponent indicator (e/E) to follow '0'"); } @@ -1532,6 +1546,7 @@ protected JsonToken _finishNumberPlusMinus(final int ch, final boolean negative) return _finishNumberLeadingNegZeroes(); } else { if (!isEnabled(JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS.mappedFeature())) { + --_inputPtr; // for correct error reporting _reportUnexpectedNumberChar('+', "JSON spec does not allow numbers to have plus signs: enable `JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS` to allow"); } return _finishNumberLeadingPosZeroes(); @@ -1542,6 +1557,7 @@ protected JsonToken _finishNumberPlusMinus(final int ch, final boolean negative) return _finishNumberLeadingNegZeroes(); } else { if (!isEnabled(JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS.mappedFeature())) { + --_inputPtr; // for correct error reporting _reportUnexpectedNumberChar('+', "JSON spec does not allow numbers to have plus signs: enable `JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS` to allow"); } _inputPtr--; @@ -1551,6 +1567,7 @@ protected JsonToken _finishNumberPlusMinus(final int ch, final boolean negative) final String message = negative ? "expected digit (0-9) to follow minus sign, for valid numeric value" : "expected digit (0-9) for valid numeric value"; + --_inputPtr; // for correct error reporting _reportUnexpectedNumberChar(ch, message); } else if (ch > INT_9) { if (ch == 'I') { @@ -1560,6 +1577,7 @@ protected JsonToken _finishNumberPlusMinus(final int ch, final boolean negative) final String message = negative ? "expected digit (0-9) to follow minus sign, for valid numeric value" : "expected digit (0-9) for valid numeric value"; + --_inputPtr; // for correct error reporting _reportUnexpectedNumberChar(ch, message); } if (!negative && !isEnabled(JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS.mappedFeature())) { @@ -1600,6 +1618,7 @@ protected JsonToken _finishNumberLeadingZeroes() throws IOException // (colon not possible since this is within value, not after key) // if ((ch != INT_RBRACKET) && (ch != INT_RCURLY)) { + --_inputPtr; // for correct error reporting _reportUnexpectedNumberChar(ch, "expected digit (0-9), decimal point (.) or exponent indicator (e/E) to follow '0'"); } @@ -1660,6 +1679,7 @@ protected JsonToken _finishNumberLeadingPosNegZeroes(final boolean negative) thr // (colon not possible since this is within value, not after key) // if ((ch != INT_RBRACKET) && (ch != INT_RCURLY)) { + --_inputPtr; // for correct error reporting _reportUnexpectedNumberChar(ch, "expected digit (0-9), decimal point (.) or exponent indicator (e/E) to follow '0'"); } @@ -1744,6 +1764,7 @@ protected JsonToken _startFloat(char[] outBuf, int outPtr, int ch) throws IOExce // must be followed by sequence of ints, one minimum if (fractLen == 0) { if (!isEnabled(JsonReadFeature.ALLOW_TRAILING_DECIMAL_POINT_FOR_NUMBERS.mappedFeature())) { + --_inputPtr; // for correct error reporting _reportUnexpectedNumberChar(ch, "Decimal point not followed by a digit"); } } @@ -1800,6 +1821,7 @@ protected JsonToken _startFloat(char[] outBuf, int outPtr, int ch) throws IOExce // must be followed by sequence of ints, one minimum ch &= 0xFF; if (expLen == 0) { + --_inputPtr; // for correct error reporting _reportUnexpectedNumberChar(ch, "Exponent indicator not followed by a digit"); } } @@ -1834,8 +1856,10 @@ protected JsonToken _finishFloatFraction() throws IOException } ch = getNextSignedByteFromBuffer(); } else if (ch == 'f' || ch == 'd' || ch == 'F' || ch == 'D') { + --_inputPtr; // for correct error reporting _reportUnexpectedNumberChar(ch, "JSON does not support parsing numbers that have 'f' or 'd' suffixes"); } else if (ch == INT_PERIOD) { + --_inputPtr; // for correct error reporting _reportUnexpectedNumberChar(ch, "Cannot parse number with more than one decimal point"); } else { loop = false; @@ -1846,6 +1870,7 @@ protected JsonToken _finishFloatFraction() throws IOException // must be followed by sequence of ints, one minimum if (fractLen == 0) { if (!isEnabled(JsonReadFeature.ALLOW_TRAILING_DECIMAL_POINT_FOR_NUMBERS.mappedFeature())) { + --_inputPtr; // for correct error reporting _reportUnexpectedNumberChar(ch, "Decimal point not followed by a digit"); } } @@ -1907,6 +1932,7 @@ protected JsonToken _finishFloatExponent(boolean checkSign, int ch) throws IOExc // must be followed by sequence of ints, one minimum ch &= 0xFF; if (expLen == 0) { + --_inputPtr; // for correct error reporting _reportUnexpectedNumberChar(ch, "Exponent indicator not followed by a digit"); } // push back the last char @@ -2214,6 +2240,7 @@ private JsonToken _handleOddName(int ch) throws IOException // !!! TODO: Decode UTF-8 characters properly... // char c = (char) _decodeCharForError(ch); char c = (char) ch; + --_inputPtr; // for correct error reporting _reportUnexpectedChar(c, "was expecting double-quote to start field name"); } // Also: note that although we use a different table here, it does NOT handle UTF-8 @@ -2221,6 +2248,7 @@ private JsonToken _handleOddName(int ch) throws IOException final int[] codes = CharTypes.getInputCodeUtf8JsNames(); // Also: must start with a valid character... if (codes[ch] != 0) { + --_inputPtr; // for correct error reporting _reportUnexpectedChar(ch, "was expecting either valid name character (for unquoted name) or double-quote (for quoted) to start field name"); } @@ -2480,6 +2508,7 @@ private int _decodeSplitEscaped(int value, int bytesRead) throws IOException while (true) { int digit = CharTypes.charToHex(c); if (digit < 0) { + --_inputPtr; // for correct error reporting _reportUnexpectedChar(c & 0xFF, "expected a hex-digit for character escape sequence"); } value = (value << 4) | digit; @@ -2939,6 +2968,7 @@ private final int _decodeFastCharEscape() throws IOException } } } + --_inputPtr; // for correct error reporting _reportUnexpectedChar(ch & 0xFF, "expected a hex-digit for character escape sequence"); return -1; } diff --git a/src/test/java/com/fasterxml/jackson/failing/read/LocationOfError1173Test.java b/src/test/java/com/fasterxml/jackson/failing/read/LocationOfError1173Test.java index 43ef25ec7e..aea27cc9a6 100644 --- a/src/test/java/com/fasterxml/jackson/failing/read/LocationOfError1173Test.java +++ b/src/test/java/com/fasterxml/jackson/failing/read/LocationOfError1173Test.java @@ -226,8 +226,8 @@ public JsonParser createParser(String input) throws Exception ), new InvalidJson( "Invalid JSON with raw unicode character", - // javac will parse the unicode control sequence, it will be passed to the parser as a raw unicode character - "{\"validJson\":\"\u274c\",\"right\", \"here\"}", + // javac will parse the 3-byte unicode control sequence, it will be passed to the parser as a raw unicode character + a2q("{'validJson':'\u274c','right', 'here'}"), 26, 24, 1,