Improve error message truncation with closing delimiters (#3478)
Truncated strings now show closing delimiters: `..."` for strings, `...]` for arrays, and `...}` for objects, making error messages clearer.
This commit is contained in:
1 parent
a2741ee0a8
commit
22361cfb87
9 files changed
+88
-72
No files matched your search
+10
-11
@@ -50,24 +50,23 @@ BINOPS
|
||||
|
||||
|
||||
static jv type_error(jv bad, const char* msg) {
|
||||
char errbuf[15];
|
||||
char errbuf[30];
|
||||
const char *badkind = jv_kind_name(jv_get_kind(bad));
|
||||
jv err = jv_invalid_with_msg(jv_string_fmt("%s (%s) %s", badkind,
|
||||
jv_dump_string_trunc(bad, errbuf, sizeof(errbuf)),
|
||||
msg));
|
||||
jv err = jv_invalid_with_msg(jv_string_fmt(
|
||||
"%s (%s) %s", badkind,
|
||||
jv_dump_string_trunc(bad, errbuf, sizeof(errbuf)), msg));
|
||||
return err;
|
||||
}
|
||||
|
||||
static jv type_error2(jv bad1, jv bad2, const char* msg) {
|
||||
char errbuf1[15],errbuf2[15];
|
||||
char errbuf1[30], errbuf2[30];
|
||||
const char *badkind1 = jv_kind_name(jv_get_kind(bad1));
|
||||
const char *badkind2 = jv_kind_name(jv_get_kind(bad2));
|
||||
jv err = jv_invalid_with_msg(jv_string_fmt("%s (%s) and %s (%s) %s",
|
||||
badkind1,
|
||||
jv_dump_string_trunc(bad1, errbuf1, sizeof(errbuf1)),
|
||||
badkind2,
|
||||
jv_dump_string_trunc(bad2, errbuf2, sizeof(errbuf2)),
|
||||
msg));
|
||||
jv err = jv_invalid_with_msg(jv_string_fmt(
|
||||
"%s (%s) and %s (%s) %s", badkind1,
|
||||
jv_dump_string_trunc(bad1, errbuf1, sizeof(errbuf1)),
|
||||
badkind2,
|
||||
jv_dump_string_trunc(bad2, errbuf2, sizeof(errbuf2)), msg));
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
+11
-13
@@ -10,13 +10,10 @@
|
||||
#include "bytecode.h"
|
||||
|
||||
#include "jv_alloc.h"
|
||||
#include "jq_parser.h"
|
||||
#include "locfile.h"
|
||||
#include "jv.h"
|
||||
#include "jq.h"
|
||||
#include "parser.h"
|
||||
#include "builtin.h"
|
||||
#include "util.h"
|
||||
#include "linker.h"
|
||||
|
||||
struct jq_state {
|
||||
@@ -499,10 +496,11 @@ jv jq_next(jq_state *jq) {
|
||||
stack_push(jq, jv_object_set(objv, k, v));
|
||||
stack_push(jq, stktop);
|
||||
} else {
|
||||
char errbuf[15];
|
||||
set_error(jq, jv_invalid_with_msg(jv_string_fmt("Cannot use %s (%s) as object key",
|
||||
jv_kind_name(jv_get_kind(k)),
|
||||
jv_dump_string_trunc(jv_copy(k), errbuf, sizeof(errbuf)))));
|
||||
char errbuf[30];
|
||||
set_error(jq, jv_invalid_with_msg(jv_string_fmt(
|
||||
"Cannot use %s (%s) as object key",
|
||||
jv_kind_name(jv_get_kind(k)),
|
||||
jv_dump_string_trunc(jv_copy(k), errbuf, sizeof(errbuf)))));
|
||||
jv_free(stktop);
|
||||
jv_free(v);
|
||||
jv_free(k);
|
||||
@@ -685,7 +683,7 @@ jv jq_next(jq_state *jq) {
|
||||
jv k = stack_pop(jq);
|
||||
// detect invalid path expression like path(reverse | .a)
|
||||
if (!path_intact(jq, jv_copy(t))) {
|
||||
char keybuf[15];
|
||||
char keybuf[30];
|
||||
char objbuf[30];
|
||||
jv msg = jv_string_fmt(
|
||||
"Invalid path expression near attempt to access element %s of %s",
|
||||
@@ -771,11 +769,11 @@ jv jq_next(jq_state *jq) {
|
||||
} else {
|
||||
assert(opcode == EACH || opcode == EACH_OPT);
|
||||
if (opcode == EACH) {
|
||||
char errbuf[15];
|
||||
set_error(jq,
|
||||
jv_invalid_with_msg(jv_string_fmt("Cannot iterate over %s (%s)",
|
||||
jv_kind_name(jv_get_kind(container)),
|
||||
jv_dump_string_trunc(jv_copy(container), errbuf, sizeof(errbuf)))));
|
||||
char errbuf[30];
|
||||
set_error(jq, jv_invalid_with_msg(jv_string_fmt(
|
||||
"Cannot iterate over %s (%s)",
|
||||
jv_kind_name(jv_get_kind(container)),
|
||||
jv_dump_string_trunc(jv_copy(container), errbuf, sizeof(errbuf)))));
|
||||
}
|
||||
keep_going = 0;
|
||||
}
|
||||
|
||||
+6
-14
@@ -135,20 +135,12 @@ jv jv_get(jv t, jv k) {
|
||||
jv_free(k);
|
||||
v = jv_null();
|
||||
} else {
|
||||
/*
|
||||
* If k is a short string it's probably from a jq .foo expression or
|
||||
* similar, in which case putting it in the invalid msg may help the
|
||||
* user. The length 30 is arbitrary.
|
||||
*/
|
||||
if (jv_get_kind(k) == JV_KIND_STRING && jv_string_length_bytes(jv_copy(k)) < 30) {
|
||||
v = jv_invalid_with_msg(jv_string_fmt("Cannot index %s with string \"%s\"",
|
||||
jv_kind_name(jv_get_kind(t)),
|
||||
jv_string_value(k)));
|
||||
} else {
|
||||
v = jv_invalid_with_msg(jv_string_fmt("Cannot index %s with %s",
|
||||
jv_kind_name(jv_get_kind(t)),
|
||||
jv_kind_name(jv_get_kind(k))));
|
||||
}
|
||||
char errbuf[30];
|
||||
v = jv_invalid_with_msg(jv_string_fmt(
|
||||
"Cannot index %s with %s (%s)",
|
||||
jv_kind_name(jv_get_kind(t)),
|
||||
jv_kind_name(jv_get_kind(k)),
|
||||
jv_dump_string_trunc(jv_copy(k), errbuf, sizeof(errbuf))));
|
||||
jv_free(t);
|
||||
jv_free(k);
|
||||
}
|
||||
|
||||
+21
-7
@@ -15,6 +15,7 @@
|
||||
#include "jv_unicode.h"
|
||||
#include "jv_alloc.h"
|
||||
#include "jv_private.h"
|
||||
#include "util.h"
|
||||
|
||||
#ifndef MAX_PRINT_DEPTH
|
||||
#define MAX_PRINT_DEPTH (10000)
|
||||
@@ -408,17 +409,30 @@ jv jv_dump_string(jv x, int flags) {
|
||||
}
|
||||
|
||||
char *jv_dump_string_trunc(jv x, char *outbuf, size_t bufsize) {
|
||||
assert(bufsize > 0);
|
||||
x = jv_dump_string(x, 0);
|
||||
const char *str = jv_string_value(x);
|
||||
const size_t len = strlen(str);
|
||||
strncpy(outbuf, str, bufsize);
|
||||
if (len > bufsize - 1 && bufsize >= 4) {
|
||||
// Indicate truncation with '...' without breaking UTF-8.
|
||||
const char *s = jvp_utf8_backtrack(outbuf + bufsize - 4, outbuf, NULL);
|
||||
if (s) bufsize = s + 4 - outbuf;
|
||||
strcpy(outbuf + bufsize - 4, "...");
|
||||
if (len > bufsize - 1 && bufsize >= 8) {
|
||||
char delim = 0;
|
||||
switch (str[0]) {
|
||||
case '"': delim = '"'; break;
|
||||
case '[': delim = ']'; break;
|
||||
case '{': delim = '}'; break;
|
||||
}
|
||||
size_t l = bufsize - (delim ? 5 : 4); // "...", delim (if any), '\0'
|
||||
const char *s = jvp_utf8_backtrack(str + l, str, NULL);
|
||||
if (s) l = s - str;
|
||||
memcpy(outbuf, str, l);
|
||||
outbuf[l++] = '.';
|
||||
outbuf[l++] = '.';
|
||||
outbuf[l++] = '.';
|
||||
if (delim) outbuf[l++] = delim;
|
||||
outbuf[l] = '\0';
|
||||
} else {
|
||||
outbuf[bufsize - 1] = '\0';
|
||||
size_t l = MIN(len, bufsize - 1);
|
||||
memcpy(outbuf, str, l);
|
||||
outbuf[l] = '\0';
|
||||
}
|
||||
jv_free(x);
|
||||
return outbuf;
|
||||
|
||||
Generated
+1
-1
@@ -431,7 +431,7 @@ int yylex(YYSTYPE* yylval, YYLTYPE* yylloc, block* answer, int* errors,
|
||||
* object key. */
|
||||
static jv check_object_key(block k) {
|
||||
if (block_is_const(k) && block_const_kind(k) != JV_KIND_STRING) {
|
||||
char errbuf[15];
|
||||
char errbuf[30];
|
||||
return jv_string_fmt("Cannot use %s (%s) as object key",
|
||||
jv_kind_name(block_const_kind(k)),
|
||||
jv_dump_string_trunc(block_const(k), errbuf, sizeof(errbuf)));
|
||||
|
||||
+1
-1
@@ -164,7 +164,7 @@ int yylex(YYSTYPE* yylval, YYLTYPE* yylloc, block* answer, int* errors,
|
||||
* object key. */
|
||||
static jv check_object_key(block k) {
|
||||
if (block_is_const(k) && block_const_kind(k) != JV_KIND_STRING) {
|
||||
char errbuf[15];
|
||||
char errbuf[30];
|
||||
return jv_string_fmt("Cannot use %s (%s) as object key",
|
||||
jv_kind_name(block_const_kind(k)),
|
||||
jv_dump_string_trunc(block_const(k), errbuf, sizeof(errbuf)));
|
||||
|
||||
Reference in new issue
Block a user