@samitouri / QOSamiQemu / commits / b6368e7c77

json-parser: add location to JSON parsing errors

Now that all calls to parse_error have a token, add the line and column to the message. As far as I can see the two important TODOs (better errors and better EOI handling) are done, and the others (token range information and "parsed size"?) do not really matter or are handled better by json-streamer.c. So remove the list, which had sat unchanged since 2009. This needs some adjustments to provide a good x and y for error messages. First of all, they switch from zero-based to one-based, which is safe because they were both sitting unused. Second, right now the x and y are those of the *last* character in the token. Modify json-lexer.c to freeze tok->x and tok->y at the first character added to the GString. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Message-ID: <20260626101727.1727389-7-pbonzini@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>

Paolo Bonzini committed Jun 26, 2026 at 12:17 UTC b6368e7c77aad190f44570f589ccaa4b5cb4818d
3 files changed +10 -14
include/qobject/json-parser.h
+1
@@ -17,6 +17,7 @@
17 typedef struct JSONLexer {
18 int start_state, state;
19 GString *token;
20 + int cur_x, cur_y;
21 int x, y;
22 } JSONLexer;
23
qobject/json-lexer.c
+7 -4
@@ -277,7 +277,8 @@ void json_lexer_init(JSONLexer *lexer, bool enable_interpolation)
277 lexer->start_state = lexer->state = enable_interpolation
278 ? IN_START_INTERP : IN_START;
279 lexer->token = g_string_sized_new(3);
280 - lexer->x = lexer->y = 0;
280 + lexer->cur_x = lexer->cur_y = 1;
281 + lexer->x = lexer->y = 1;
282 }
283
284 static void json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush)
@@ -285,10 +286,10 @@ static void json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush)
286 int new_state;
287 bool char_consumed = false;
288
288 - lexer->x++;
289 + lexer->cur_x++;
290 if (ch == '\n') {
290 - lexer->x = 0;
291 - lexer->y++;
291 + lexer->cur_x = 1;
292 + lexer->cur_y++;
293 }
294
295 while (flush ? lexer->state != lexer->start_state : !char_consumed) {
@@ -316,6 +317,8 @@ static void json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush)
317 case IN_START:
318 g_string_truncate(lexer->token, 0);
319 new_state = lexer->start_state;
320 + lexer->x = lexer->cur_x;
321 + lexer->y = lexer->cur_y;
322 break;
323 case JSON_ERROR:
324 json_message_process_token(lexer, lexer->token, JSON_ERROR,
qobject/json-parser.c
+2 -10
@@ -133,15 +133,6 @@ typedef struct JSONParserStackEntry {
133
134 #define BUG_ON(cond) assert(!(cond))
135
136 -/**
137 - * TODO
138 - *
139 - * 0) make errors meaningful again
140 - * 1) add geometry information to tokens
141 - * 3) should we return a parsed size?
142 - * 4) deal with premature EOI
143 - */
144 -
136 static inline JSONParserStackEntry *current_entry(JSONParserContext *ctxt)
137 {
138 return g_queue_peek_tail(ctxt->stack);
@@ -180,7 +171,8 @@ static void G_GNUC_PRINTF(3, 4) parse_error(JSONParserContext *ctxt,
171 va_start(ap, msg);
172 vsnprintf(message, sizeof(message), msg, ap);
173 va_end(ap);
183 - error_setg(&ctxt->err, "JSON parse error, %s", message);
174 + error_setg(&ctxt->err, "%d:%d: JSON parse error, %s",
175 + token->y, token->x, message);
176 }
177
178 static int cvt4hex(const char *s)