Fix a parser bug where tokens are misidentified as commas.

In the old and new readers, when parsing an object, a comment
followed by any non-`}` token is treated as a comma.

The new unit test required changing the runjsontests.py
flag regime so that failure tests could be run with default settings.
This commit is contained in:
Greg Falcon 2023-07-19 12:19:12 -04:00
parent 69098a18b9
commit eb21dc0697
4 changed files with 24 additions and 17 deletions

View File

@ -240,11 +240,14 @@ static int parseCommandLine(int argc, const char* argv[], Options* opts) {
return printUsage(argv); return printUsage(argv);
} }
int index = 1; int index = 1;
if (Json::String(argv[index]) == "--json-checker") { if (Json::String(argv[index]) == "--parse-only") {
opts->features = Json::Features::strictMode();
opts->parseOnly = true; opts->parseOnly = true;
++index; ++index;
} }
if (Json::String(argv[index]) == "--strict") {
opts->features = Json::Features::strictMode();
++index;
}
if (Json::String(argv[index]) == "--json-config") { if (Json::String(argv[index]) == "--json-config") {
printConfig(); printConfig();
return 3; return 3;

View File

@ -480,15 +480,15 @@ bool Reader::readObject(Token& token) {
return recoverFromError(tokenObjectEnd); return recoverFromError(tokenObjectEnd);
Token comma; Token comma;
if (!readToken(comma) || bool finalizeTokenOk = readToken(comma);
(comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && while (comma.type_ == tokenComment && finalizeTokenOk) {
comma.type_ != tokenComment)) { finalizeTokenOk = readToken(comma);
}
if (!finalizeTokenOk ||
(comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator)) {
return addErrorAndRecover("Missing ',' or '}' in object declaration", return addErrorAndRecover("Missing ',' or '}' in object declaration",
comma, tokenObjectEnd); comma, tokenObjectEnd);
} }
bool finalizeTokenOk = true;
while (comma.type_ == tokenComment && finalizeTokenOk)
finalizeTokenOk = readToken(comma);
if (comma.type_ == tokenObjectEnd) if (comma.type_ == tokenObjectEnd)
return true; return true;
} }
@ -1491,15 +1491,15 @@ bool OurReader::readObject(Token& token) {
return recoverFromError(tokenObjectEnd); return recoverFromError(tokenObjectEnd);
Token comma; Token comma;
if (!readToken(comma) || bool finalizeTokenOk = readToken(comma);
(comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && while (comma.type_ == tokenComment && finalizeTokenOk) {
comma.type_ != tokenComment)) { finalizeTokenOk = readToken(comma);
}
if (!finalizeTokenOk ||
(comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator)) {
return addErrorAndRecover("Missing ',' or '}' in object declaration", return addErrorAndRecover("Missing ',' or '}' in object declaration",
comma, tokenObjectEnd); comma, tokenObjectEnd);
} }
bool finalizeTokenOk = true;
while (comma.type_ == tokenComment && finalizeTokenOk)
finalizeTokenOk = readToken(comma);
if (comma.type_ == tokenObjectEnd) if (comma.type_ == tokenObjectEnd)
return true; return true;
} }

View File

@ -0,0 +1 @@
{"one": 1 /* } */ { "two" : 2 }

View File

@ -97,14 +97,17 @@ def runAllTests(jsontest_executable_path, input_dir = None,
valgrind_path = use_valgrind and VALGRIND_CMD or '' valgrind_path = use_valgrind and VALGRIND_CMD or ''
for input_path in tests + test_jsonchecker: for input_path in tests + test_jsonchecker:
expect_failure = os.path.basename(input_path).startswith('fail') expect_failure = os.path.basename(input_path).startswith('fail')
is_json_checker_test = (input_path in test_jsonchecker) or expect_failure is_json_checker_test = input_path in test_jsonchecker
is_parse_only = is_json_checker_test or expect_failure
is_strict_test = ('_strict_' in os.path.basename(input_path)) or is_json_checker_test
print('TESTING:', input_path, end=' ') print('TESTING:', input_path, end=' ')
options = is_json_checker_test and '--json-checker' or '' options = is_parse_only and '--parse-only' or ''
options += is_strict_test and ' --strict' or ''
options += ' --json-writer %s'%writerClass options += ' --json-writer %s'%writerClass
cmd = '%s%s %s "%s"' % ( valgrind_path, jsontest_executable_path, options, cmd = '%s%s %s "%s"' % ( valgrind_path, jsontest_executable_path, options,
input_path) input_path)
status, process_output = getStatusOutput(cmd) status, process_output = getStatusOutput(cmd)
if is_json_checker_test: if is_parse_only:
if expect_failure: if expect_failure:
if not status: if not status:
print('FAILED') print('FAILED')