Skip to content

Commit 0c715d1

Browse files
authored
Fixes #1173: column/offset locations wrong for many error conditions (#1176)
1 parent 170b33a commit 0c715d1

File tree

7 files changed

+79
-2
lines changed

7 files changed

+79
-2
lines changed

release-notes/CREDITS-2.x

+5
Original file line numberDiff line numberDiff line change
@@ -405,3 +405,8 @@ David Schlosnagle (@schlosna)
405405
Mario Fusco (@mariofusco)
406406
* Contributed #1064: Add full set of `BufferRecyclerPool` implementations
407407
(2.16.0)
408+
409+
Paul Bunyan (@hal7df)
410+
* Reported #1173: `JsonLocation` consistently off by one character for many invalid
411+
JSON parsing cases
412+
(2.16.2)

release-notes/VERSION-2.x

+6
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ a pure JSON library.
1414
=== Releases ===
1515
------------------------------------------------------------------------
1616

17+
2.16.2 (not yet released)
18+
19+
#1173: `JsonLocation` consistently off by one character for many invalid JSON
20+
parsing cases
21+
(reported by Paul B)
22+
1723
2.16.1 (24-Dec-2023)
1824

1925
#1141: NPE in `Version.equals()` if snapshot-info `null`

src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java

+17
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,7 @@ public final JsonToken nextToken() throws IOException
769769
case '}':
770770
// Error: } is not valid at this point; valid closers have
771771
// been handled earlier
772+
--_inputPtr; // for correct error reporting
772773
_reportUnexpectedChar(i, "expected a value");
773774
case 't':
774775
_matchTrue();
@@ -1455,6 +1456,7 @@ private final JsonToken _parseFloat(int ch, int startPtr, int ptr, boolean neg,
14551456
// must be followed by sequence of ints, one minimum
14561457
if (fractLen == 0) {
14571458
if (!isEnabled(JsonReadFeature.ALLOW_TRAILING_DECIMAL_POINT_FOR_NUMBERS.mappedFeature())) {
1459+
--_inputPtr; // for correct error reporting
14581460
_reportUnexpectedNumberChar(ch, "Decimal point not followed by a digit");
14591461
}
14601462
}
@@ -1484,6 +1486,7 @@ private final JsonToken _parseFloat(int ch, int startPtr, int ptr, boolean neg,
14841486
}
14851487
// must be followed by sequence of ints, one minimum
14861488
if (expLen == 0) {
1489+
--_inputPtr; // for correct error reporting
14871490
_reportUnexpectedNumberChar(ch, "Exponent indicator not followed by a digit");
14881491
}
14891492
}
@@ -1644,6 +1647,7 @@ private final JsonToken _parseNumber2(boolean neg, int startPtr) throws IOExcept
16441647
// must be followed by sequence of ints, one minimum
16451648
if (fractLen == 0) {
16461649
if (!isEnabled(JsonReadFeature.ALLOW_TRAILING_DECIMAL_POINT_FOR_NUMBERS.mappedFeature())) {
1650+
--_inputPtr; // for correct error reporting
16471651
_reportUnexpectedNumberChar(c, "Decimal point not followed by a digit");
16481652
}
16491653
}
@@ -1688,6 +1692,7 @@ private final JsonToken _parseNumber2(boolean neg, int startPtr) throws IOExcept
16881692
}
16891693
// must be followed by sequence of ints, one minimum
16901694
if (expLen == 0) {
1695+
--_inputPtr; // for correct error reporting
16911696
_reportUnexpectedNumberChar(c, "Exponent indicator not followed by a digit");
16921697
}
16931698
}
@@ -1788,11 +1793,13 @@ protected JsonToken _handleInvalidNumberStart(int ch, final boolean negative, fi
17881793
}
17891794
}
17901795
if (!isEnabled(JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS.mappedFeature()) && hasSign && !negative) {
1796+
--_inputPtr; // for correct error reporting
17911797
_reportUnexpectedNumberChar('+', "JSON spec does not allow numbers to have plus signs: enable `JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS` to allow");
17921798
}
17931799
final String message = negative ?
17941800
"expected digit (0-9) to follow minus sign, for valid numeric value" :
17951801
"expected digit (0-9) for valid numeric value";
1802+
--_inputPtr; // for correct error reporting
17961803
_reportUnexpectedNumberChar(ch, message);
17971804
return null;
17981805
}
@@ -1940,6 +1947,7 @@ protected String _handleOddName(int i) throws IOException
19401947
}
19411948
// [JACKSON-69]: allow unquoted names if feature enabled:
19421949
if ((_features & FEAT_MASK_ALLOW_UNQUOTED_NAMES) == 0) {
1950+
--_inputPtr; // for correct error reporting
19431951
_reportUnexpectedChar(i, "was expecting double-quote to start field name");
19441952
}
19451953
final int[] codes = CharTypes.getInputCodeLatin1JsNames();
@@ -1954,6 +1962,7 @@ protected String _handleOddName(int i) throws IOException
19541962
firstOk = Character.isJavaIdentifierPart((char) i);
19551963
}
19561964
if (!firstOk) {
1965+
--_inputPtr; // for correct error reporting
19571966
_reportUnexpectedChar(i, "was expecting either valid name character (for unquoted name) or double-quote (for quoted) to start field name");
19581967
}
19591968
int ptr = _inputPtr;
@@ -2085,6 +2094,7 @@ protected JsonToken _handleOddValue(int i) throws IOException
20852094
_reportInvalidToken(""+((char) i), _validJsonTokenList());
20862095
}
20872096
// but if it doesn't look like a token:
2097+
--_inputPtr; // for correct error reporting
20882098
_reportUnexpectedChar(i, "expected a valid value "+_validJsonValueList());
20892099
return null;
20902100
}
@@ -2387,6 +2397,7 @@ private final int _skipColon2(boolean gotColon) throws IOException
23872397
return i;
23882398
}
23892399
if (i != INT_COLON) {
2400+
--_inputPtr; // for correct error reporting
23902401
_reportUnexpectedChar(i, "was expecting a colon to separate field name and value");
23912402
}
23922403
gotColon = true;
@@ -2460,6 +2471,7 @@ private final int _skipColonFast(int ptr) throws IOException
24602471
private final int _skipComma(int i) throws IOException
24612472
{
24622473
if (i != INT_COMMA) {
2474+
--_inputPtr; // for correct error reporting
24632475
_reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries");
24642476
}
24652477
while (_inputPtr < _inputEnd) {
@@ -2602,6 +2614,7 @@ private int _skipWSOrEnd2() throws IOException
26022614
private void _skipComment() throws IOException
26032615
{
26042616
if ((_features & FEAT_MASK_ALLOW_JAVA_COMMENTS) == 0) {
2617+
--_inputPtr; // for correct error reporting
26052618
_reportUnexpectedChar('/', "maybe a (non-standard) comment? (not recognized as one since Feature 'ALLOW_COMMENTS' not enabled for parser)");
26062619
}
26072620
// First: check which comment (if either) it is:
@@ -2614,6 +2627,7 @@ private void _skipComment() throws IOException
26142627
} else if (c == '*') {
26152628
_skipCComment();
26162629
} else {
2630+
--_inputPtr; // for correct error reporting
26172631
_reportUnexpectedChar(c, "was expecting either '*' or '/' for a comment");
26182632
}
26192633
}
@@ -2725,6 +2739,7 @@ protected char _decodeEscaped() throws IOException
27252739
int ch = (int) _inputBuffer[_inputPtr++];
27262740
int digit = CharTypes.charToHex(ch);
27272741
if (digit < 0) {
2742+
--_inputPtr; // for correct error reporting
27282743
_reportUnexpectedChar(ch, "expected a hex-digit for character escape sequence");
27292744
}
27302745
value = (value << 4) | digit;
@@ -3052,6 +3067,7 @@ private void _closeScope(int i) throws JsonParseException {
30523067
if (i == INT_RBRACKET) {
30533068
_updateLocation();
30543069
if (!_parsingContext.inArray()) {
3070+
--_inputPtr; // for correct error reporting
30553071
_reportMismatchedEndMarker(i, '}');
30563072
}
30573073
_parsingContext = _parsingContext.clearAndGetParent();
@@ -3060,6 +3076,7 @@ private void _closeScope(int i) throws JsonParseException {
30603076
if (i == INT_RCURLY) {
30613077
_updateLocation();
30623078
if (!_parsingContext.inObject()) {
3079+
--_inputPtr; // for correct error reporting
30633080
_reportMismatchedEndMarker(i, ']');
30643081
}
30653082
_parsingContext = _parsingContext.clearAndGetParent();

src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java

+17
Original file line numberDiff line numberDiff line change
@@ -779,6 +779,7 @@ public JsonToken nextToken() throws IOException
779779
// Nope: do we then expect a comma?
780780
if (_parsingContext.expectComma()) {
781781
if (i != INT_COMMA) {
782+
--_inputPtr; // for correct error reporting
782783
_reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries");
783784
}
784785
i = _skipWS();
@@ -977,6 +978,7 @@ public boolean nextFieldName(SerializableString str) throws IOException
977978
// Nope: do we then expect a comma?
978979
if (_parsingContext.expectComma()) {
979980
if (i != INT_COMMA) {
981+
--_inputPtr; // for correct error reporting
980982
_reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries");
981983
}
982984
i = _skipWS();
@@ -1062,6 +1064,7 @@ public String nextFieldName() throws IOException
10621064
// Nope: do we then expect a comma?
10631065
if (_parsingContext.expectComma()) {
10641066
if (i != INT_COMMA) {
1067+
--_inputPtr; // for correct error reporting
10651068
_reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries");
10661069
}
10671070
i = _skipWS();
@@ -1685,6 +1688,7 @@ private final JsonToken _parseFloat(char[] outBuf, int outPtr, int c,
16851688
// must be followed by sequence of ints, one minimum
16861689
if (fractLen == 0) {
16871690
if (!isEnabled(JsonReadFeature.ALLOW_TRAILING_DECIMAL_POINT_FOR_NUMBERS.mappedFeature())) {
1691+
--_inputPtr; // for correct error reporting
16881692
_reportUnexpectedNumberChar(c, "Decimal point not followed by a digit");
16891693
}
16901694
}
@@ -1732,6 +1736,7 @@ private final JsonToken _parseFloat(char[] outBuf, int outPtr, int c,
17321736
}
17331737
// must be followed by sequence of ints, one minimum
17341738
if (expLen == 0) {
1739+
--_inputPtr; // for correct error reporting
17351740
_reportUnexpectedNumberChar(c, "Exponent indicator not followed by a digit");
17361741
}
17371742
}
@@ -2144,6 +2149,7 @@ protected String _handleOddName(int ch) throws IOException
21442149
// Allow unquoted names if feature enabled:
21452150
if ((_features & FEAT_MASK_ALLOW_UNQUOTED_NAMES) == 0) {
21462151
char c = (char) _decodeCharForError(ch);
2152+
--_inputPtr; // for correct error reporting
21472153
_reportUnexpectedChar(c, "was expecting double-quote to start field name");
21482154
}
21492155
/* Also: note that although we use a different table here,
@@ -2153,6 +2159,7 @@ protected String _handleOddName(int ch) throws IOException
21532159
final int[] codes = CharTypes.getInputCodeUtf8JsNames();
21542160
// Also: must start with a valid character...
21552161
if (codes[ch] != 0) {
2162+
--_inputPtr; // for correct error reporting
21562163
_reportUnexpectedChar(ch, "was expecting either valid name character (for unquoted name) or double-quote (for quoted) to start field name");
21572164
}
21582165

@@ -2758,6 +2765,7 @@ protected JsonToken _handleUnexpectedValue(int c) throws IOException
27582765
case '}':
27592766
// Error: neither is valid at this point; valid closers have
27602767
// been handled earlier
2768+
--_inputPtr; // for correct error reporting
27612769
_reportUnexpectedChar(c, "expected a value");
27622770
case '\'':
27632771
if ((_features & FEAT_MASK_ALLOW_SINGLE_QUOTES) != 0) {
@@ -2791,6 +2799,7 @@ protected JsonToken _handleUnexpectedValue(int c) throws IOException
27912799
_reportInvalidToken(""+((char) c), _validJsonTokenList());
27922800
}
27932801
// but if it doesn't look like a token:
2802+
--_inputPtr; // for correct error reporting
27942803
_reportUnexpectedChar(c, "expected a valid value "+_validJsonValueList());
27952804
return null;
27962805
}
@@ -2923,11 +2932,13 @@ protected JsonToken _handleInvalidNumberStart(int ch, final boolean neg, final b
29232932
match);
29242933
}
29252934
if (!isEnabled(JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS.mappedFeature()) && hasSign && !neg) {
2935+
--_inputPtr; // for correct error reporting
29262936
_reportUnexpectedNumberChar('+', "JSON spec does not allow numbers to have plus signs: enable `JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS` to allow");
29272937
}
29282938
final String message = neg ?
29292939
"expected digit (0-9) to follow minus sign, for valid numeric value" :
29302940
"expected digit (0-9) for valid numeric value";
2941+
--_inputPtr; // for correct error reporting
29312942
_reportUnexpectedNumberChar(ch, message);
29322943
return null;
29332944
}
@@ -3253,6 +3264,7 @@ private final int _skipColon2(boolean gotColon) throws IOException
32533264
return i;
32543265
}
32553266
if (i != INT_COLON) {
3267+
--_inputPtr; // for correct error reporting
32563268
_reportUnexpectedChar(i, "was expecting a colon to separate field name and value");
32573269
}
32583270
gotColon = true;
@@ -3275,6 +3287,7 @@ private final int _skipColon2(boolean gotColon) throws IOException
32753287
private final void _skipComment() throws IOException
32763288
{
32773289
if ((_features & FEAT_MASK_ALLOW_JAVA_COMMENTS) == 0) {
3290+
--_inputPtr; // for correct error reporting
32783291
_reportUnexpectedChar('/', "maybe a (non-standard) comment? (not recognized as one since Feature 'ALLOW_COMMENTS' not enabled for parser)");
32793292
}
32803293
// First: check which comment (if either) it is:
@@ -3287,6 +3300,7 @@ private final void _skipComment() throws IOException
32873300
} else if (c == INT_ASTERISK) {
32883301
_skipCComment();
32893302
} else {
3303+
--_inputPtr; // for correct error reporting
32903304
_reportUnexpectedChar(c, "was expecting either '*' or '/' for a comment");
32913305
}
32923306
}
@@ -3432,6 +3446,7 @@ protected char _decodeEscaped() throws IOException
34323446
int ch = _inputBuffer[_inputPtr++];
34333447
int digit = CharTypes.charToHex(ch);
34343448
if (digit < 0) {
3449+
--_inputPtr; // for correct error reporting
34353450
_reportUnexpectedChar(ch & 0xFF, "expected a hex-digit for character escape sequence");
34363451
}
34373452
value = (value << 4) | digit;
@@ -3923,6 +3938,7 @@ private final JsonToken _closeScope(int i) throws JsonParseException {
39233938
private final void _closeArrayScope() throws JsonParseException {
39243939
_updateLocation();
39253940
if (!_parsingContext.inArray()) {
3941+
--_inputPtr; // for correct error reporting
39263942
_reportMismatchedEndMarker(']', '}');
39273943
}
39283944
_parsingContext = _parsingContext.clearAndGetParent();
@@ -3931,6 +3947,7 @@ private final void _closeArrayScope() throws JsonParseException {
39313947
private final void _closeObjectScope() throws JsonParseException {
39323948
_updateLocation();
39333949
if (!_parsingContext.inObject()) {
3950+
--_inputPtr; // for correct error reporting
39343951
_reportMismatchedEndMarker('}', ']');
39353952
}
39363953
_parsingContext = _parsingContext.clearAndGetParent();

src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingJsonParserBase.java

+2
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,7 @@ protected final JsonToken _startObjectScope() throws IOException
597597
protected final JsonToken _closeArrayScope() throws IOException
598598
{
599599
if (!_parsingContext.inArray()) {
600+
--_inputPtr; // for correct error reporting
600601
_reportMismatchedEndMarker(']', '}');
601602
}
602603
JsonReadContext ctxt = _parsingContext.getParent();
@@ -617,6 +618,7 @@ protected final JsonToken _closeArrayScope() throws IOException
617618
protected final JsonToken _closeObjectScope() throws IOException
618619
{
619620
if (!_parsingContext.inObject()) {
621+
--_inputPtr; // for correct error reporting
620622
_reportMismatchedEndMarker('}', ']');
621623
}
622624
JsonReadContext ctxt = _parsingContext.getParent();

0 commit comments

Comments
 (0)