Skip to content

Commit 2f97147

Browse files
committed
Improve error messages to show problematic position in the filter
This commit improves the parser and compiler error messages, making it more easier to understand where in the filter the problem is. Like modern compilers, jq will show the line in the filter where the error occurred, will display squiggly line under the invalid token.
1 parent eff9caf commit 2f97147

File tree

6 files changed

+548
-488
lines changed

6 files changed

+548
-488
lines changed

src/jq_test.c

Lines changed: 40 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,6 @@ static void test_err_cb(void *data, jv e) {
7070
e = jv_dump_string(e, JV_PRINT_INVALID);
7171
if (!strncmp(jv_string_value(e), "jq: error", sizeof("jq: error") - 1))
7272
snprintf(err_data->buf, sizeof(err_data->buf), "%s", jv_string_value(e));
73-
if (strchr(err_data->buf, '\n'))
74-
*(strchr(err_data->buf, '\n')) = '\0';
7573
jv_free(e);
7674
}
7775

@@ -108,18 +106,7 @@ static void run_jq_tests(jv lib_dirs, int verbose, FILE *testdata, int skip, int
108106

109107
if (skip > 0) {
110108
skip--;
111-
112-
// skip past test data
113-
while (fgets(buf, sizeof(buf), testdata)) {
114-
lineno++;
115-
if (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n'))
116-
break;
117-
}
118-
119-
must_fail = 0;
120-
check_msg = 0;
121-
122-
continue;
109+
goto next;
123110
} else if (skip == 0) {
124111
printf("Skipped %d tests\n", tests_to_skip);
125112
skip = -1;
@@ -139,37 +126,40 @@ static void run_jq_tests(jv lib_dirs, int verbose, FILE *testdata, int skip, int
139126

140127
if (must_fail) {
141128
jq_set_error_cb(jq, NULL, NULL);
142-
if (!fgets(buf, sizeof(buf), testdata)) { invalid++; break; }
143-
lineno++;
144-
if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = 0;
145129
if (compiled) {
146-
printf("*** Test program compiled that should not have at line %u: %s\n", lineno, prog);
147-
must_fail = 0;
148-
check_msg = 0;
149-
invalid++;
150-
continue;
130+
printf("*** Test program compiled successfully, but should fail at line number %u: %s\n", lineno, prog);
131+
goto fail;
132+
}
133+
char *err_buf = err_msg.buf;
134+
while (fgets(buf, sizeof(buf), testdata)) {
135+
lineno++;
136+
if (skipline(buf)) break;
137+
if (check_msg) {
138+
if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = '\0';
139+
if (strncmp(buf, err_buf, strlen(buf)) != 0) {
140+
if (strchr(err_buf, '\n')) *strchr(err_buf, '\n') = '\0';
141+
printf("*** Erroneous program failed with '%s', but expected '%s' at line number %u: %s\n", err_buf, buf, lineno, prog);
142+
goto fail;
143+
}
144+
err_buf += strlen(buf);
145+
if (*err_buf == '\n') err_buf++;
146+
}
151147
}
152-
if (check_msg && strcmp(buf, err_msg.buf) != 0) {
153-
printf("*** Erroneous test program failed with wrong message (%s) at line %u: %s\n", err_msg.buf, lineno, prog);
148+
if (check_msg && *err_buf != '\0') {
149+
if (strchr(err_buf, '\n')) *strchr(err_buf, '\n') = '\0';
150+
printf("*** Erroneous program failed with extra message '%s' at line %u: %s\n", err_buf, lineno, prog);
154151
invalid++;
155-
} else {
156-
passed++;
152+
pass = 0;
157153
}
158154
must_fail = 0;
159155
check_msg = 0;
156+
passed += pass;
160157
continue;
161158
}
162159

163160
if (!compiled) {
164161
printf("*** Test program failed to compile at line %u: %s\n", lineno, prog);
165-
invalid++;
166-
// skip past test data
167-
while (fgets(buf, sizeof(buf), testdata)) {
168-
lineno++;
169-
if (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n'))
170-
break;
171-
}
172-
continue;
162+
goto fail;
173163
}
174164
if (verbose) {
175165
printf("Disassembly:\n");
@@ -181,8 +171,7 @@ static void run_jq_tests(jv lib_dirs, int verbose, FILE *testdata, int skip, int
181171
jv input = jv_parse(buf);
182172
if (!jv_is_valid(input)) {
183173
printf("*** Input is invalid on line %u: %s\n", lineno, buf);
184-
invalid++;
185-
continue;
174+
goto fail;
186175
}
187176
jq_start(jq, input, verbose ? JQ_DEBUG_TRACE : 0);
188177

@@ -192,8 +181,7 @@ static void run_jq_tests(jv lib_dirs, int verbose, FILE *testdata, int skip, int
192181
jv expected = jv_parse(buf);
193182
if (!jv_is_valid(expected)) {
194183
printf("*** Expected result is invalid on line %u: %s\n", lineno, buf);
195-
invalid++;
196-
continue;
184+
goto fail;
197185
}
198186
jv actual = jq_next(jq);
199187
if (!jv_is_valid(actual)) {
@@ -226,12 +214,25 @@ static void run_jq_tests(jv lib_dirs, int verbose, FILE *testdata, int skip, int
226214
printf("*** Superfluous result: ");
227215
jv_dump(extra, 0);
228216
printf(" for test at line number %u, %s\n", lineno, prog);
217+
invalid++;
229218
pass = 0;
230219
} else {
231220
jv_free(extra);
232221
}
233222
}
234-
passed+=pass;
223+
passed += pass;
224+
continue;
225+
226+
fail:
227+
invalid++;
228+
229+
next:
230+
while (fgets(buf, sizeof(buf), testdata)) {
231+
lineno++;
232+
if (skipline(buf)) break;
233+
}
234+
must_fail = 0;
235+
check_msg = 0;
235236
}
236237
jq_teardown(&jq);
237238

src/locfile.c

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
#include "jq.h"
88
#include "jv_alloc.h"
99
#include "locfile.h"
10-
10+
#include "util.h"
1111

1212
struct locfile* locfile_init(jq_state *jq, const char *fname, const char* data, int length) {
1313
struct locfile* l = jv_mem_alloc(sizeof(struct locfile));
@@ -63,13 +63,6 @@ static int locfile_line_length(struct locfile* l, int line) {
6363
void locfile_locate(struct locfile* l, location loc, const char* fmt, ...) {
6464
va_list fmtargs;
6565
va_start(fmtargs, fmt);
66-
int startline;
67-
int offset;
68-
69-
if (loc.start != -1) {
70-
startline = locfile_get_line(l, loc.start);
71-
offset = l->linemap[startline];
72-
}
7366

7467
jv m1 = jv_string_vfmt(fmt, fmtargs);
7568
va_end(fmtargs);
@@ -78,16 +71,23 @@ void locfile_locate(struct locfile* l, location loc, const char* fmt, ...) {
7871
return;
7972
}
8073
if (loc.start == -1) {
81-
jq_report_error(l->jq, jv_string_fmt("jq: error: %s\n<unknown location>", jv_string_value(m1)));
74+
jq_report_error(l->jq, jv_string_fmt("jq: error: %s", jv_string_value(m1)));
8275
jv_free(m1);
8376
return;
8477
}
85-
jv m2 = jv_string_fmt("%s at %s, line %d, column %d:\n%.*s%*s",
78+
79+
int startline = locfile_get_line(l, loc.start);
80+
int offset = l->linemap[startline];
81+
int end = MIN(loc.end, l->linemap[startline+1] - 1);
82+
assert(end > loc.start);
83+
jv underline = jv_string_repeat(jv_string("^"), end - loc.start);
84+
jv m2 = jv_string_fmt("%s at %s, line %d, column %d:\n %.*s\n %*s",
8685
jv_string_value(m1), jv_string_value(l->fname),
8786
startline + 1, loc.start - offset + 1,
8887
locfile_line_length(l, startline), l->data + offset,
89-
loc.start - offset, "");
88+
end - offset, jv_string_value(underline));
9089
jv_free(m1);
90+
jv_free(underline);
9191
jq_report_error(l->jq, m2);
9292
return;
9393
}

0 commit comments

Comments
 (0)