diff --git a/quickjs.c b/quickjs.c index 109856661..064a9e29c 100644 --- a/quickjs.c +++ b/quickjs.c @@ -22452,8 +22452,18 @@ int JS_PRINTF_FORMAT_ATTR(2, 3) js_parse_error(JSParseState *s, JS_PRINTF_FORMAT backtrace_flags = 0; if (s->cur_func && s->cur_func->backtrace_barrier) backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL; + /* s->col_num is not advanced during token scanning, so derive the column + as the 1-based offset of the token from the start of its line. */ + int err_col_num = s->token.col_num; + if (s->token.ptr && s->token.ptr >= s->buf_start) { + const uint8_t *line_start = s->token.ptr; + while (line_start > s->buf_start && + line_start[-1] != '\n' && line_start[-1] != '\r') + line_start--; + err_col_num = (int)(s->token.ptr - line_start) + 1; + } build_backtrace(ctx, ctx->rt->current_exception, JS_UNDEFINED, s->filename, - s->line_num, s->col_num, backtrace_flags); + s->token.line_num, err_col_num, backtrace_flags); return -1; } diff --git a/tests/parse-error-column.js b/tests/parse-error-column.js new file mode 100644 index 000000000..07445aaab --- /dev/null +++ b/tests/parse-error-column.js @@ -0,0 +1,26 @@ +import { assert } from "./assert.js"; + +/* A SyntaxError should report the column of the offending token, not always + column 1. The location is exposed through the error's stack trace. */ +function errorLocation(src) { + try { + eval(src); + } catch (e) { + const m = /:(\d+):(\d+)/.exec(e.stack); + if (m) + return { line: +m[1], col: +m[2] }; + } + return { line: -1, col: -1 }; +} + +assert(errorLocation("let x = ;").col, 9); +assert(errorLocation("1 + + ;").col, 7); +assert(errorLocation("hocuspocus(").col, 12); +assert(errorLocation("a b").col, 3); + +/* line number stays correct and the column is relative to the line start */ +{ + const loc = errorLocation("let ok = 1\nlet z = @"); + assert(loc.line, 2); + assert(loc.col, 9); +}