From ab250d21bc41b3a145d82330c92d30632980fb2f Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Tue, 9 Feb 2016 17:33:44 +0800 Subject: [PATCH] Rectify parsing error offsets --- include/rapidjson/reader.h | 142 ++++++++++++++++++----------------- test/unittest/readertest.cpp | 106 ++++++++++++++------------ 2 files changed, 130 insertions(+), 118 deletions(-) diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index f2670b6..5100703 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -474,28 +474,21 @@ private: SkipWhitespace(is); if (parseFlags & kParseCommentsFlag) { - while (RAPIDJSON_UNLIKELY(is.Peek() == '/')) { - is.Take(); - - if (is.Peek() == '*') { - is.Take(); + while (RAPIDJSON_UNLIKELY(Consume(is, '/'))) { + if (Consume(is, '*')) { while (true) { if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); - - if (is.Take() == '*') { - if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) - RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); - - if (is.Take() == '/') + else if (Consume(is, '*')) { + if (Consume(is, '/')) break; } + else + is.Take(); } } - else if (RAPIDJSON_LIKELY(is.Peek() == '/')) { - is.Take(); - while (is.Peek() != '\0' && is.Take() != '\n') { } - } + else if (RAPIDJSON_LIKELY(Consume(is, '/'))) + while (is.Peek() != '\0' && is.Take() != '\n'); else RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); @@ -516,8 +509,7 @@ private: SkipWhitespaceAndComments(is); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - if (is.Peek() == '}') { - is.Take(); + if (Consume(is, '}')) { if (RAPIDJSON_UNLIKELY(!handler.EndObject(0))) // empty object RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); return; @@ -533,7 +525,7 @@ private: SkipWhitespaceAndComments(is); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - if (RAPIDJSON_UNLIKELY(is.Take() != ':')) + if (RAPIDJSON_UNLIKELY(!Consume(is, ':'))) RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); SkipWhitespaceAndComments(is); @@ -547,12 +539,14 @@ private: ++memberCount; - switch (is.Take()) { + switch (is.Peek()) { case ',': + is.Take(); SkipWhitespaceAndComments(is); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; break; case '}': + is.Take(); if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); return; @@ -575,8 +569,7 @@ private: SkipWhitespaceAndComments(is); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - if (is.Peek() == ']') { - is.Take(); + if (Consume(is, ']')) { if (RAPIDJSON_UNLIKELY(!handler.EndArray(0))) // empty array RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); return; @@ -590,19 +583,17 @@ private: SkipWhitespaceAndComments(is); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - switch (is.Take()) { - case ',': - SkipWhitespaceAndComments(is); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - break; - case ']': - if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); - return; - default: - RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); - break; + if (Consume(is, ',')) { + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; } + else if (Consume(is, ']')) { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); } } @@ -611,12 +602,12 @@ private: RAPIDJSON_ASSERT(is.Peek() == 'n'); is.Take(); - if (RAPIDJSON_LIKELY(is.Take() == 'u' && is.Take() == 'l' && is.Take() == 'l')) { + if (RAPIDJSON_LIKELY(Consume(is, 'u') && Consume(is, 'l') && Consume(is, 'l'))) { if (RAPIDJSON_UNLIKELY(!handler.Null())) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); } else - RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); } template @@ -624,12 +615,12 @@ private: RAPIDJSON_ASSERT(is.Peek() == 't'); is.Take(); - if (RAPIDJSON_LIKELY(is.Take() == 'r' && is.Take() == 'u' && is.Take() == 'e')) { + if (RAPIDJSON_LIKELY(Consume(is, 'r') && Consume(is, 'u') && Consume(is, 'e'))) { if (RAPIDJSON_UNLIKELY(!handler.Bool(true))) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); } else - RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); } template @@ -637,20 +628,30 @@ private: RAPIDJSON_ASSERT(is.Peek() == 'f'); is.Take(); - if (RAPIDJSON_LIKELY(is.Take() == 'a' && is.Take() == 'l' && is.Take() == 's' && is.Take() == 'e')) { + if (RAPIDJSON_LIKELY(Consume(is, 'a') && Consume(is, 'l') && Consume(is, 's') && Consume(is, 'e'))) { if (RAPIDJSON_UNLIKELY(!handler.Bool(false))) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); } else - RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + RAPIDJSON_FORCEINLINE static bool Consume(InputStream& is, typename InputStream::Ch expect) { + if (RAPIDJSON_LIKELY(is.Peek() == expect)) { + is.Take(); + return true; + } + else + return false; } // Helper function to parse four hexidecimal digits in \uXXXX in ParseString(). template - unsigned ParseHex4(InputStream& is) { + unsigned ParseHex4(InputStream& is, size_t escapeOffset) { unsigned codepoint = 0; for (int i = 0; i < 4; i++) { - Ch c = is.Take(); + Ch c = is.Peek(); codepoint <<= 4; codepoint += static_cast(c); if (c >= '0' && c <= '9') @@ -660,9 +661,10 @@ private: else if (c >= 'a' && c <= 'f') codepoint -= 'a' - 10; else { - RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, is.Tell() - 1); + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, escapeOffset); RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0); } + is.Take(); } return codepoint; } @@ -751,27 +753,31 @@ private: Ch c = is.Peek(); if (RAPIDJSON_UNLIKELY(c == '\\')) { // Escape + size_t escapeOffset = is.Tell(); // For invalid escaping, report the inital '\\' as error offset is.Take(); - Ch e = is.Take(); - if ((sizeof(Ch) == 1 || unsigned(e) < 256) && RAPIDJSON_LIKELY(escape[static_cast(e)])) + Ch e = is.Peek(); + if ((sizeof(Ch) == 1 || unsigned(e) < 256) && RAPIDJSON_LIKELY(escape[static_cast(e)])) { + is.Take(); os.Put(static_cast(escape[static_cast(e)])); + } else if (RAPIDJSON_LIKELY(e == 'u')) { // Unicode - unsigned codepoint = ParseHex4(is); + is.Take(); + unsigned codepoint = ParseHex4(is, escapeOffset); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; if (RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDBFF)) { // Handle UTF-16 surrogate pair - if (RAPIDJSON_UNLIKELY(is.Take() != '\\' || is.Take() != 'u')) - RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, is.Tell() - 2); - unsigned codepoint2 = ParseHex4(is); + if (RAPIDJSON_UNLIKELY(!Consume(is, '\\') || !Consume(is, 'u'))) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + unsigned codepoint2 = ParseHex4(is, escapeOffset); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; if (RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF)) - RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, is.Tell() - 2); + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; } TEncoding::Encode(os, codepoint); } else - RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell() - 1); + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, escapeOffset); } else if (RAPIDJSON_UNLIKELY(c == '"')) { // Closing double quote is.Take(); @@ -780,15 +786,16 @@ private: } else if (RAPIDJSON_UNLIKELY(static_cast(c) < 0x20)) { // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF if (c == '\0') - RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell() - 1); + RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell()); else - RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell() - 1); + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell()); } else { + size_t offset = is.Tell(); if (RAPIDJSON_UNLIKELY((parseFlags & kParseValidateEncodingFlag ? !Transcoder::Validate(is, os) : !Transcoder::Transcode(is, os)))) - RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, is.Tell()); + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, offset); } } } @@ -961,6 +968,8 @@ private: template class NumberStream { public: + typedef typename InputStream::Ch Ch; + NumberStream(GenericReader& reader, InputStream& s) : is(s) { (void)reader; } ~NumberStream() {} @@ -1004,13 +1013,10 @@ private: void ParseNumber(InputStream& is, Handler& handler) { internal::StreamLocalCopy copy(is); NumberStream s(*this, copy.s); + size_t startOffset = s.Tell(); // Parse minus - bool minus = false; - if (s.Peek() == '-') { - minus = true; - s.Take(); - } + bool minus = Consume(s, '-'); // Parse int: zero / ( digit1-9 *DIGIT ) unsigned i = 0; @@ -1084,7 +1090,7 @@ private: if (useDouble) { while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { if (RAPIDJSON_UNLIKELY(d >= 1.7976931348623157e307)) // DBL_MAX / 10.0 - RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, s.Tell()); + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); d = d * 10 + (s.TakePush() - '0'); } } @@ -1092,8 +1098,7 @@ private: // Parse frac = decimal-point 1*DIGIT int expFrac = 0; size_t decimalPosition; - if (s.Peek() == '.') { - s.Take(); + if (Consume(s, '.')) { decimalPosition = s.Length(); if (RAPIDJSON_UNLIKELY(!(s.Peek() >= '0' && s.Peek() <= '9'))) @@ -1140,20 +1145,17 @@ private: // Parse exp = e [ minus / plus ] 1*DIGIT int exp = 0; - if (s.Peek() == 'e' || s.Peek() == 'E') { + if (Consume(s, 'e') || Consume(s, 'E')) { if (!useDouble) { d = static_cast(use64bit ? i64 : i); useDouble = true; } - s.Take(); bool expMinus = false; - if (s.Peek() == '+') - s.Take(); - else if (s.Peek() == '-') { - s.Take(); + if (Consume(s, '+')) + ; + else if (Consume(s, '-')) expMinus = true; - } if (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { exp = static_cast(s.Take() - '0'); @@ -1171,7 +1173,7 @@ private: while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { exp = exp * 10 + static_cast(s.Take() - '0'); if (RAPIDJSON_UNLIKELY(exp > maxExp)) - RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, s.Tell()); + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); } } } @@ -1211,7 +1213,7 @@ private: } } if (RAPIDJSON_UNLIKELY(!cont)) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, startOffset); } // Parse any JSON value diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index 61fd95c..04e0883 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -418,7 +418,7 @@ TEST(Reader, ParseNumber_NormalPrecisionError) { } TEST(Reader, ParseNumber_Error) { -#define TEST_NUMBER_ERROR(errorCode, str) \ +#define TEST_NUMBER_ERROR(errorCode, str, errorOffset) \ { \ char buffer[1001]; \ sprintf(buffer, "%s", str); \ @@ -427,6 +427,7 @@ TEST(Reader, ParseNumber_Error) { Reader reader; \ EXPECT_FALSE(reader.Parse(s, h)); \ EXPECT_EQ(errorCode, reader.GetParseErrorCode());\ + EXPECT_EQ(errorOffset, reader.GetErrorOffset());\ } // Number too big to be stored in double. @@ -436,17 +437,17 @@ TEST(Reader, ParseNumber_Error) { for (int i = 1; i < 310; i++) n1e309[i] = '0'; n1e309[310] = '\0'; - TEST_NUMBER_ERROR(kParseErrorNumberTooBig, n1e309); + TEST_NUMBER_ERROR(kParseErrorNumberTooBig, n1e309, 0); } - TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1e309"); + TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1e309", 0); // Miss fraction part in number. - TEST_NUMBER_ERROR(kParseErrorNumberMissFraction, "1."); - TEST_NUMBER_ERROR(kParseErrorNumberMissFraction, "1.a"); + TEST_NUMBER_ERROR(kParseErrorNumberMissFraction, "1.", 2); + TEST_NUMBER_ERROR(kParseErrorNumberMissFraction, "1.a", 2); // Miss exponent in number. - TEST_NUMBER_ERROR(kParseErrorNumberMissExponent, "1e"); - TEST_NUMBER_ERROR(kParseErrorNumberMissExponent, "1e_"); + TEST_NUMBER_ERROR(kParseErrorNumberMissExponent, "1e", 2); + TEST_NUMBER_ERROR(kParseErrorNumberMissExponent, "1e_", 2); #undef TEST_NUMBER_ERROR } @@ -604,8 +605,15 @@ ParseErrorCode TestString(const typename Encoding::Ch* str) { } TEST(Reader, ParseString_Error) { -#define TEST_STRING_ERROR(errorCode, str)\ - EXPECT_EQ(errorCode, TestString >(str)) +#define TEST_STRING_ERROR(errorCode, str, errorOffset)\ +{\ + GenericStringStream > s(str);\ + BaseReaderHandler > h;\ + GenericReader , UTF8<> > reader;\ + reader.template Parse(s, h);\ + EXPECT_EQ(errorCode, reader.GetParseErrorCode());\ + EXPECT_EQ(errorOffset, reader.GetErrorOffset());\ +} #define ARRAY(...) { __VA_ARGS__ } #define TEST_STRINGENCODING_ERROR(Encoding, TargetEncoding, utype, array) \ @@ -622,21 +630,21 @@ TEST(Reader, ParseString_Error) { } // Invalid escape character in string. - TEST_STRING_ERROR(kParseErrorStringEscapeInvalid, "[\"\\a\"]"); + TEST_STRING_ERROR(kParseErrorStringEscapeInvalid, "[\"\\a\"]", 2); // Incorrect hex digit after \\u escape in string. - TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uABCG\"]"); + TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uABCG\"]", 2); // Quotation in \\u escape in string (Issue #288) - TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uaaa\"]"); - TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uD800\\uFFF\"]"); + TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uaaa\"]", 2); + TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uD800\\uFFF\"]", 2); // The surrogate pair in string is invalid. - TEST_STRING_ERROR(kParseErrorStringUnicodeSurrogateInvalid, "[\"\\uD800X\"]"); - TEST_STRING_ERROR(kParseErrorStringUnicodeSurrogateInvalid, "[\"\\uD800\\uFFFF\"]"); + TEST_STRING_ERROR(kParseErrorStringUnicodeSurrogateInvalid, "[\"\\uD800X\"]", 2); + TEST_STRING_ERROR(kParseErrorStringUnicodeSurrogateInvalid, "[\"\\uD800\\uFFFF\"]", 2); // Missing a closing quotation mark in string. - TEST_STRING_ERROR(kParseErrorStringMissQuotationMark, "[\"Test]"); + TEST_STRING_ERROR(kParseErrorStringMissQuotationMark, "[\"Test]", 7); // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt @@ -659,7 +667,7 @@ TEST(Reader, ParseString_Error) { char e[] = { '[', '\"', 0, ' ', '\"', ']', '\0' }; for (unsigned c = 0xC0u; c <= 0xFFu; c++) { e[2] = static_cast(c); - TEST_STRING_ERROR(kParseErrorStringInvalidEncoding, e); + TEST_STRING_ERROR(kParseErrorStringInvalidEncoding, e, 2); } } @@ -738,7 +746,7 @@ TEST(Reader, ParseArray) { } TEST(Reader, ParseArray_Error) { -#define TEST_ARRAY_ERROR(errorCode, str) \ +#define TEST_ARRAY_ERROR(errorCode, str, errorOffset) \ { \ char buffer[1001]; \ strncpy(buffer, str, 1000); \ @@ -747,12 +755,13 @@ TEST(Reader, ParseArray_Error) { GenericReader, UTF8<>, CrtAllocator> reader; \ EXPECT_FALSE(reader.Parse(s, h)); \ EXPECT_EQ(errorCode, reader.GetParseErrorCode());\ + EXPECT_EQ(errorOffset, reader.GetErrorOffset());\ } // Missing a comma or ']' after an array element. - TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1"); - TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1}"); - TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1 2]"); + TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1", 2); + TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1}", 2); + TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1 2]", 3); #undef TEST_ARRAY_ERROR } @@ -899,7 +908,7 @@ TEST(Reader, ParseInsituIterative_MultipleRoot) { TestInsituMultipleRoot(); } -#define TEST_ERROR(errorCode, str) \ +#define TEST_ERROR(errorCode, str, errorOffset) \ { \ char buffer[1001]; \ strncpy(buffer, str, 1000); \ @@ -908,48 +917,49 @@ TEST(Reader, ParseInsituIterative_MultipleRoot) { Reader reader; \ EXPECT_FALSE(reader.Parse(s, h)); \ EXPECT_EQ(errorCode, reader.GetParseErrorCode());\ + EXPECT_EQ(errorOffset, reader.GetErrorOffset());\ } TEST(Reader, ParseDocument_Error) { // The document is empty. - TEST_ERROR(kParseErrorDocumentEmpty, ""); - TEST_ERROR(kParseErrorDocumentEmpty, " "); - TEST_ERROR(kParseErrorDocumentEmpty, " \n"); + TEST_ERROR(kParseErrorDocumentEmpty, "", 0); + TEST_ERROR(kParseErrorDocumentEmpty, " ", 1); + TEST_ERROR(kParseErrorDocumentEmpty, " \n", 2); // The document root must not follow by other values. - TEST_ERROR(kParseErrorDocumentRootNotSingular, "[] 0"); - TEST_ERROR(kParseErrorDocumentRootNotSingular, "{} 0"); - TEST_ERROR(kParseErrorDocumentRootNotSingular, "null []"); - TEST_ERROR(kParseErrorDocumentRootNotSingular, "0 {}"); + TEST_ERROR(kParseErrorDocumentRootNotSingular, "[] 0", 3); + TEST_ERROR(kParseErrorDocumentRootNotSingular, "{} 0", 3); + TEST_ERROR(kParseErrorDocumentRootNotSingular, "null []", 5); + TEST_ERROR(kParseErrorDocumentRootNotSingular, "0 {}", 2); } TEST(Reader, ParseValue_Error) { // Invalid value. - TEST_ERROR(kParseErrorValueInvalid, "nulL"); - TEST_ERROR(kParseErrorValueInvalid, "truE"); - TEST_ERROR(kParseErrorValueInvalid, "falsE"); - TEST_ERROR(kParseErrorValueInvalid, "a]"); - TEST_ERROR(kParseErrorValueInvalid, ".1"); + TEST_ERROR(kParseErrorValueInvalid, "nulL", 3); + TEST_ERROR(kParseErrorValueInvalid, "truE", 3); + TEST_ERROR(kParseErrorValueInvalid, "falsE", 4); + TEST_ERROR(kParseErrorValueInvalid, "a]", 0); + TEST_ERROR(kParseErrorValueInvalid, ".1", 0); } TEST(Reader, ParseObject_Error) { // Missing a name for object member. - TEST_ERROR(kParseErrorObjectMissName, "{1}"); - TEST_ERROR(kParseErrorObjectMissName, "{:1}"); - TEST_ERROR(kParseErrorObjectMissName, "{null:1}"); - TEST_ERROR(kParseErrorObjectMissName, "{true:1}"); - TEST_ERROR(kParseErrorObjectMissName, "{false:1}"); - TEST_ERROR(kParseErrorObjectMissName, "{1:1}"); - TEST_ERROR(kParseErrorObjectMissName, "{[]:1}"); - TEST_ERROR(kParseErrorObjectMissName, "{{}:1}"); - TEST_ERROR(kParseErrorObjectMissName, "{xyz:1}"); + TEST_ERROR(kParseErrorObjectMissName, "{1}", 1); + TEST_ERROR(kParseErrorObjectMissName, "{:1}", 1); + TEST_ERROR(kParseErrorObjectMissName, "{null:1}", 1); + TEST_ERROR(kParseErrorObjectMissName, "{true:1}", 1); + TEST_ERROR(kParseErrorObjectMissName, "{false:1}", 1); + TEST_ERROR(kParseErrorObjectMissName, "{1:1}", 1); + TEST_ERROR(kParseErrorObjectMissName, "{[]:1}", 1); + TEST_ERROR(kParseErrorObjectMissName, "{{}:1}", 1); + TEST_ERROR(kParseErrorObjectMissName, "{xyz:1}", 1); // Missing a colon after a name of object member. - TEST_ERROR(kParseErrorObjectMissColon, "{\"a\" 1}"); - TEST_ERROR(kParseErrorObjectMissColon, "{\"a\",1}"); + TEST_ERROR(kParseErrorObjectMissColon, "{\"a\" 1}", 5); + TEST_ERROR(kParseErrorObjectMissColon, "{\"a\",1}", 4); // Must be a comma or '}' after an object member - TEST_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, "{\"a\":1]"); + TEST_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, "{\"a\":1]", 6); // This tests that MemoryStream is checking the length in Peek(). { @@ -1089,10 +1099,10 @@ TEST(Reader, IterativeParsing_ErrorHandling) { TESTERRORHANDLING("{\"a\"}", kParseErrorObjectMissColon, 4u); TESTERRORHANDLING("{\"a\": 1", kParseErrorObjectMissCommaOrCurlyBracket, 7u); TESTERRORHANDLING("[1 2 3]", kParseErrorArrayMissCommaOrSquareBracket, 3u); - TESTERRORHANDLING("{\"a: 1", kParseErrorStringMissQuotationMark, 5u); + TESTERRORHANDLING("{\"a: 1", kParseErrorStringMissQuotationMark, 6u); // Any JSON value can be a valid root element in RFC7159. - TESTERRORHANDLING("\"ab", kParseErrorStringMissQuotationMark, 2u); + TESTERRORHANDLING("\"ab", kParseErrorStringMissQuotationMark, 3u); TESTERRORHANDLING("truE", kParseErrorValueInvalid, 3u); TESTERRORHANDLING("False", kParseErrorValueInvalid, 0u); TESTERRORHANDLING("true, false", kParseErrorDocumentRootNotSingular, 4u);