From 7b6a018dff623a4f13f6bcd52c7c56d9b4a4165f Mon Sep 17 00:00:00 2001 From: Assaf Gordon Date: Fri, 17 Apr 2015 18:18:33 -0400 Subject: [PATCH] Print offending object in runtime error messages When reporting an error to the user, add information about the offending object/value (possibly truncated). The goal is to give a user some context regarding which input object caused the runtime error. Examples: $ echo '"hello"' | ./jq '-.' jq: error: string ("hello") cannot be negated $ echo '"very-long-string"' | ./jq '-.' jq: error: string ("very-long-...) cannot be negated $ echo '["1",2]' | ./jq '.|join(",")' jq: error: string (",") and number (2) cannot be added $ echo '["1","2",{"a":{"b":{"c":33}}}]' | ./jq '.|join(",")' jq: error: string (",") and object ({"a":{"b":{...) cannot be added $ echo '{"a":{"b":{"c":33}}}' | ./jq '.a | @tsv' jq: error: object ({"b":{"c":33}}) cannot be tsv-formatted, only array (Fix #754) --- builtin.c | 9 +++++++-- execute.c | 12 ++++++++---- jv.h | 1 + jv_print.c | 16 ++++++++++++++++ tests/all.test | 12 ++++++++++++ 5 files changed, 44 insertions(+), 6 deletions(-) diff --git a/builtin.c b/builtin.c index 8ea8319..640c6d7 100644 --- a/builtin.c +++ b/builtin.c @@ -22,17 +22,22 @@ static jv type_error(jv bad, const char* msg) { - jv err = jv_invalid_with_msg(jv_string_fmt("%s %s", + char errbuf[15]; + jv err = jv_invalid_with_msg(jv_string_fmt("%s (%s) %s", jv_kind_name(jv_get_kind(bad)), + jv_dump_string_trunc(jv_copy(bad), errbuf, sizeof(errbuf)), msg)); jv_free(bad); return err; } static jv type_error2(jv bad1, jv bad2, const char* msg) { - jv err = jv_invalid_with_msg(jv_string_fmt("%s and %s %s", + char errbuf1[15],errbuf2[15]; + jv err = jv_invalid_with_msg(jv_string_fmt("%s (%s) and %s (%s) %s", jv_kind_name(jv_get_kind(bad1)), + jv_dump_string_trunc(jv_copy(bad1), errbuf1, sizeof(errbuf1)), jv_kind_name(jv_get_kind(bad2)), + jv_dump_string_trunc(jv_copy(bad2), errbuf2, sizeof(errbuf2)), msg)); jv_free(bad1); jv_free(bad2); diff --git a/execute.c b/execute.c index d5f243b..9437ac9 100644 --- a/execute.c +++ b/execute.c @@ -423,8 +423,10 @@ jv jq_next(jq_state *jq) { stack_push(jq, jv_object_set(objv, k, v)); stack_push(jq, stktop); } else { - set_error(jq, jv_invalid_with_msg(jv_string_fmt("Cannot use %s as object key", - jv_kind_name(jv_get_kind(k))))); + 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))))); jv_free(stktop); jv_free(v); jv_free(k); @@ -634,9 +636,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", - jv_kind_name(jv_get_kind(container))))); + 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; } diff --git a/jv.h b/jv.h index 5f2b318..aa374ad 100644 --- a/jv.h +++ b/jv.h @@ -171,6 +171,7 @@ void jv_dumpf(jv, FILE *f, int flags); void jv_dump(jv, int flags); void jv_show(jv, int flags); jv jv_dump_string(jv, int flags); +char *jv_dump_string_trunc(jv x, char *outbuf, size_t bufsize); enum { JV_PARSE_SEQ = 1, diff --git a/jv_print.c b/jv_print.c index fb21c63..4b6c4bb 100644 --- a/jv_print.c +++ b/jv_print.c @@ -313,3 +313,19 @@ jv jv_dump_string(jv x, int flags) { jvp_dtoa_context_free(&C); return s; } + +char *jv_dump_string_trunc(jv x, char *outbuf, size_t bufsize) { + x = jv_dump_string(x,0); + const char* p = jv_string_value(x); + const size_t len = strlen(p); + strncpy(outbuf, p, bufsize); + outbuf[bufsize - 1] = 0; + if (len > bufsize - 1 && bufsize >= 4) { + // Indicate truncation with '...' + outbuf[bufsize - 2]='.'; + outbuf[bufsize - 3]='.'; + outbuf[bufsize - 4]='.'; + } + jv_free(x); + return outbuf; +} diff --git a/tests/all.test b/tests/all.test index 106afaa..950801a 100644 --- a/tests/all.test +++ b/tests/all.test @@ -1173,3 +1173,15 @@ import "test_bind_order" as check; check::check null true +try -. catch . +"very-long-string" +"string (\"very-long-...) cannot be negated" + +try join(",") catch . +["1",2] +"string (\",\") and number (2) cannot be added" + +try join(",") catch . +["1","2",{"a":{"b":{"c":33}}}] +"string (\",\") and object ({\"a\":{\"b\":{...) cannot be added" + -- 2.40.0