From 167330d624e203b35d4df35a33b935f04f810bea Mon Sep 17 00:00:00 2001 From: vaibhav0806 Date: Thu, 1 Oct 2026 12:38:43 +0530 Subject: [PATCH] fix: parse empty commented arrays in legacy Reader --- src/lib_json/json_reader.cpp | 12 +++++ src/test_lib_json/main.cpp | 91 ++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/src/lib_json/json_reader.cpp b/src/lib_json/json_reader.cpp index 2f901f510..598af4f83 100644 --- a/src/lib_json/json_reader.cpp +++ b/src/lib_json/json_reader.cpp @@ -482,6 +482,18 @@ bool Reader::readArray(Token& token) { currentValue().swapPayload(init); currentValue().setOffsetStart(token.start_ - begin_); skipSpaces(); + if (features_.allowComments_) { + while (current_ != end_ && *current_ == '/' && (current_ + 1) != end_ && + (current_[1] == '/' || current_[1] == '*')) { + Token comment; + if (!readToken(comment)) { + return addErrorAndRecover( + "Syntax error: value, object or array expected.", comment, + tokenArrayEnd); + } + skipSpaces(); + } + } if (current_ != end_ && *current_ == ']') // empty array { Token endArray; diff --git a/src/test_lib_json/main.cpp b/src/test_lib_json/main.cpp index e09b87b84..e868c3020 100644 --- a/src/test_lib_json/main.cpp +++ b/src/test_lib_json/main.cpp @@ -3007,6 +3007,11 @@ struct ReaderTest : JsonTest::TestCase { JSONTEST_ASSERT(reader->parse(input, root)); } + template + void checkParse(Input&& input, bool collectComments) { + JSONTEST_ASSERT(reader->parse(input, root, collectComments)); + } + template void checkParse(Input&& input, @@ -3023,6 +3028,11 @@ struct ReaderTest : JsonTest::TestCase { JSONTEST_ASSERT_EQUAL(formatted, reader->getFormattedErrorMessages()); } + template + void checkParseFailure(Input&& input, bool collectComments) { + JSONTEST_ASSERT(!reader->parse(input, root, collectComments)); + } + std::unique_ptr reader{new Json::Reader()}; Json::Value root; }; @@ -3095,6 +3105,87 @@ JSONTEST_FIXTURE_LOCAL(ReaderTest, parseComment) { checkParse(" true //comment1\n//comment2\r//comment3\r\n"); } +JSONTEST_FIXTURE_LOCAL(ReaderTest, parseEmptyArrayWithComments) { + for (bool collectComments : {false, true}) { + for (const auto& doc : + {std::string("[ // line\n]"), std::string("[ /* block */ ]"), + std::string("[ // one\n/* two */ ]"), + std::string("{\"list\":[ // line\n]}"), + std::string("{\"list\":[ /* block */ ]}"), + std::string("{\"list\":[ // one\n/* two */ ]}")}) { + root = Json::Value(); + checkParse(doc, collectComments); + + const Json::Value& arrayValue = + doc[0] == '[' ? root : root[Json::StaticString("list")]; + const std::size_t arrayStart = doc.find('['); + const std::size_t arrayLimit = doc.rfind(']') + 1; + JSONTEST_ASSERT(arrayValue.isArray()); + JSONTEST_ASSERT_EQUAL(0U, arrayValue.size()); + JSONTEST_ASSERT(!arrayValue.isValidIndex(0)); + JSONTEST_ASSERT_EQUAL(static_cast(arrayStart), + arrayValue.getOffsetStart()); + JSONTEST_ASSERT_EQUAL(static_cast(arrayLimit), + arrayValue.getOffsetLimit()); + } + } +} + +JSONTEST_FIXTURE_LOCAL(ReaderTest, parseArrayCommentBehaviorUnchanged) { + { + std::string doc = "[ /* before one */ 1 ]"; + const std::size_t valueStart = doc.find('1'); + const std::size_t valueLimit = valueStart + 1; + for (bool collectComments : {false, true}) { + root = Json::Value(); + checkParse(doc, collectComments); + JSONTEST_ASSERT_EQUAL(1U, root.size()); + JSONTEST_ASSERT_EQUAL(1, root[0].asInt()); + JSONTEST_ASSERT_EQUAL(static_cast(valueStart), + root[0].getOffsetStart()); + JSONTEST_ASSERT_EQUAL(static_cast(valueLimit), + root[0].getOffsetLimit()); + JSONTEST_ASSERT_EQUAL(collectComments, + root[0].hasComment(Json::commentBefore)); + if (collectComments) { + JSONTEST_ASSERT_STRING_EQUAL("/* before one */", + root[0].getComment(Json::commentBefore)); + } + } + } + { + std::string doc = "// before\n[]// after\n"; + root = Json::Value(); + checkParse(doc, true); + JSONTEST_ASSERT(root.hasComment(Json::commentBefore)); + JSONTEST_ASSERT_STRING_EQUAL("// before", + root.getComment(Json::commentBefore)); + JSONTEST_ASSERT(root.hasComment(Json::commentAfterOnSameLine)); + JSONTEST_ASSERT_STRING_EQUAL("// after", + root.getComment(Json::commentAfterOnSameLine)); + } +} + +JSONTEST_FIXTURE_LOCAL(ReaderTest, parseEmptyArrayCommentsDisallowed) { + Json::Features features = Json::Features::all(); + features.allowComments_ = false; + setFeatures(features); + + root = Json::Value(); + checkParseFailure("[ // comment\n]", true); + + root = Json::Value(); + checkParseFailure("[ /* comment */ ]", false); +} + +JSONTEST_FIXTURE_LOCAL(ReaderTest, parseEmptyArrayWithUnterminatedComment) { + root = Json::Value(); + checkParseFailure("[ /*", true); + JSONTEST_ASSERT(root.isArray()); + JSONTEST_ASSERT_EQUAL(0U, root.size()); + JSONTEST_ASSERT(!root.isValidIndex(0)); +} + JSONTEST_FIXTURE_LOCAL(ReaderTest, streamParseWithNoErrors) { std::string styled = R"({ "property" : "value" })"; std::istringstream iss(styled);