aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAmaan Qureshi <amaanq12@gmail.com>2023-07-19 05:02:49 -0400
committerLewis Russell <me@lewisr.dev>2023-08-31 13:33:40 +0100
commit845d5b8b64190e0e09a6a6dd97bdbc0e6f96eb02 (patch)
tree8c1116e6abe75c456f3a816cb633f76f7676016a
parent4afd33e69f4c4a061bcb51a5ed437163262f943c (diff)
downloadrneovim-845d5b8b64190e0e09a6a6dd97bdbc0e6f96eb02.tar.gz
rneovim-845d5b8b64190e0e09a6a6dd97bdbc0e6f96eb02.tar.bz2
rneovim-845d5b8b64190e0e09a6a6dd97bdbc0e6f96eb02.zip
feat(treesitter): improve query error message
-rw-r--r--runtime/doc/news.txt1
-rw-r--r--src/nvim/lua/treesitter.c80
-rw-r--r--test/functional/lua/overrides_spec.lua6
-rw-r--r--test/functional/treesitter/parser_spec.lua40
-rw-r--r--test/helpers.lua8
5 files changed, 122 insertions, 13 deletions
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index a233b66d1f..7607d218c3 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -135,6 +135,7 @@ The following new APIs and features were added.
or the parent LanguageTree's language, respectively.
• Added `vim.treesitter.preview_query()`, for live editing of treesitter
queries.
+ • Improved error messages for query parsing.
• |vim.ui.open()| opens URIs using the system default handler (macOS `open`,
Windows `explorer`, Linux `xdg-open`, etc.)
diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c
index 49e920f748..48057b0c65 100644
--- a/src/nvim/lua/treesitter.c
+++ b/src/nvim/lua/treesitter.c
@@ -1543,8 +1543,9 @@ int tslua_parse_query(lua_State *L)
TSQuery *query = ts_query_new(lang, src, (uint32_t)len, &error_offset, &error_type);
if (!query) {
- return luaL_error(L, "query: %s at position %d for language %s",
- query_err_string(error_type), (int)error_offset, lang_name);
+ char err_msg[IOSIZE];
+ query_err_string(src, (int)error_offset, error_type, err_msg, sizeof(err_msg));
+ return luaL_error(L, "%s", err_msg);
}
TSQuery **ud = lua_newuserdata(L, sizeof(TSQuery *)); // [udata]
@@ -1554,24 +1555,85 @@ int tslua_parse_query(lua_State *L)
return 1;
}
-static const char *query_err_string(TSQueryError err)
+static const char *query_err_to_string(TSQueryError error_type)
{
- switch (err) {
+ switch (error_type) {
case TSQueryErrorSyntax:
- return "invalid syntax";
+ return "Invalid syntax:\n";
case TSQueryErrorNodeType:
- return "invalid node type";
+ return "Invalid node type ";
case TSQueryErrorField:
- return "invalid field";
+ return "Invalid field name ";
case TSQueryErrorCapture:
- return "invalid capture";
+ return "Invalid capture name ";
case TSQueryErrorStructure:
- return "invalid structure";
+ return "Impossible pattern:\n";
default:
return "error";
}
}
+static void query_err_string(const char *src, int error_offset, TSQueryError error_type, char *err,
+ size_t errlen)
+{
+ int line_start = 0;
+ int row = 0;
+ const char *error_line = NULL;
+ int error_line_len = 0;
+
+ const char *end_str;
+ const char *src_tmp = src;
+ while ((end_str = strchr(src_tmp, '\n')) != NULL) {
+ int line_length = (int)(end_str - src_tmp) + 1;
+ int line_end = line_start + line_length;
+ if (line_end > error_offset) {
+ error_line = src_tmp;
+ error_line_len = line_length;
+ break;
+ }
+ line_start = line_end;
+ row++;
+ src_tmp += line_length;
+ }
+
+ // Additional check for the last line
+ if (line_start <= error_offset) {
+ error_line = src_tmp;
+ error_line_len = (int)strlen(src_tmp);
+ }
+
+ int column = error_offset - line_start;
+
+ const char *type_msg = query_err_to_string(error_type);
+ snprintf(err, errlen, "Query error at %d:%d. %s", row + 1, column + 1, type_msg);
+ size_t offset = strlen(err);
+ errlen = errlen - offset;
+ err = err + offset;
+
+ // Error types that report names
+ if (error_type == TSQueryErrorNodeType
+ || error_type == TSQueryErrorField
+ || error_type == TSQueryErrorCapture) {
+ const char *suffix = src + error_offset;
+ int suffix_len = 0;
+ char c = suffix[suffix_len];
+ while (isalnum(c) || c == '_' || c == '-' || c == '.') {
+ c = suffix[++suffix_len];
+ }
+ snprintf(err, errlen, "\"%.*s\":\n", suffix_len, suffix);
+ offset = strlen(err);
+ errlen = errlen - offset;
+ err = err + offset;
+ }
+
+ if (!error_line) {
+ snprintf(err, errlen, "Unexpected EOF\n");
+ return;
+ }
+
+ snprintf(err, errlen, "%.*s\n%*s^\n", error_line_len, error_line, column, "");
+}
+
static TSQuery *query_check(lua_State *L, int index)
{
TSQuery **ud = luaL_checkudata(L, index, TS_META_QUERY);
diff --git a/test/functional/lua/overrides_spec.lua b/test/functional/lua/overrides_spec.lua
index 1777dd078d..c08f3d06a9 100644
--- a/test/functional/lua/overrides_spec.lua
+++ b/test/functional/lua/overrides_spec.lua
@@ -54,7 +54,7 @@ describe('print', function()
-- TODO(bfredl): these look weird, print() should not use "E5114:" style errors..
eq('Vim(lua):E5108: Error executing lua E5114: Error while converting print argument #2: [NULL]',
pcall_err(command, 'lua print("foo", v_nilerr, "bar")'))
- eq('Vim(lua):E5108: Error executing lua E5114: Error while converting print argument #2: Xtest-functional-lua-overrides-luafile:0: abc',
+ eq('Vim(lua):E5108: Error executing lua E5114: Error while converting print argument #2: Xtest-functional-lua-overrides-luafile:2: abc',
pcall_err(command, 'lua print("foo", v_abcerr, "bar")'))
eq('Vim(lua):E5108: Error executing lua E5114: Error while converting print argument #2: <Unknown error: lua_tolstring returned NULL for tostring result>',
pcall_err(command, 'lua print("foo", v_tblout, "bar")'))
@@ -84,9 +84,9 @@ describe('print', function()
end
]])
eq('', exec_capture('luafile ' .. fname))
- eq('Vim(lua):E5108: Error executing lua Xtest-functional-lua-overrides-luafile:0: my mistake',
+ eq('Vim(lua):E5108: Error executing lua Xtest-functional-lua-overrides-luafile:1: my mistake',
pcall_err(command, 'lua string_error()'))
- eq('Vim(lua):E5108: Error executing lua Xtest-functional-lua-overrides-luafile:0: 1234',
+ eq('Vim(lua):E5108: Error executing lua Xtest-functional-lua-overrides-luafile:2: 1234',
pcall_err(command, 'lua number_error()'))
eq('Vim(lua):E5108: Error executing lua [NULL]',
pcall_err(command, 'lua nil_error()'))
diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua
index 56af0c8738..37dde37a64 100644
--- a/test/functional/treesitter/parser_spec.lua
+++ b/test/functional/treesitter/parser_spec.lua
@@ -1055,4 +1055,44 @@ int x = INT_MAX;
]])
end)
+ it('fails to load queries', function()
+ local function test(exp, cquery)
+ eq(exp, pcall_err(exec_lua, "vim.treesitter.query.parse('c', ...)", cquery))
+ end
+
+ -- Invalid node type
+ test(
+ '.../query.lua:0: Query error at 1:2. Invalid node type "dentifier":\n'..
+ '(dentifier) @variable\n'..
+ ' ^',
+ '(dentifier) @variable')
+
+ -- Impossible pattern
+ test(
+ '.../query.lua:0: Query error at 1:13. Impossible pattern:\n'..
+ '(identifier (identifier) @variable)\n'..
+ ' ^',
+ '(identifier (identifier) @variable)')
+
+ -- Invalid syntax
+ test(
+ '.../query.lua:0: Query error at 1:13. Invalid syntax:\n'..
+ '(identifier @variable\n'..
+ ' ^',
+ '(identifier @variable')
+
+ -- Invalid field name
+ test(
+ '.../query.lua:0: Query error at 1:15. Invalid field name "invalid_field":\n'..
+ '((identifier) invalid_field: (identifier))\n'..
+ ' ^',
+ '((identifier) invalid_field: (identifier))')
+
+ -- Invalid capture name
+ test(
+ '.../query.lua:0: Query error at 1:30. Invalid capture name "ok.capture":\n'..
+ '((identifier) @id (#eq? @id @ok.capture))\n'..
+ ' ^',
+ '((identifier) @id (#eq? @id @ok.capture))')
+ end)
end)
diff --git a/test/helpers.lua b/test/helpers.lua
index 51114611ab..02192e4924 100644
--- a/test/helpers.lua
+++ b/test/helpers.lua
@@ -188,10 +188,16 @@ function module.pcall(fn, ...)
local errmsg = tostring(rv):gsub('([%s<])vim[/\\]([^%s:/\\]+):%d+', '%1\xffvim\xff%2:0')
:gsub('[^%s<]-[/\\]([^%s:/\\]+):%d+', '.../%1:0')
:gsub('\xffvim\xff', 'vim/')
+
-- Scrub numbers in paths/stacktraces:
-- shared.lua:0: in function 'gsplit'
-- shared.lua:0: in function <shared.lua:0>'
- errmsg = errmsg:gsub('([^%s]):%d+', '%1:0')
+ errmsg = errmsg:gsub('([^%s].lua):%d+', '%1:0')
+ -- [string "<nvim>"]:0:
+ -- [string ":lua"]:0:
+ -- [string ":luado"]:0:
+ errmsg = errmsg:gsub('(%[string "[^"]+"%]):%d+', '%1:0')
+
-- Scrub tab chars:
errmsg = errmsg:gsub('\t', ' ')
-- In Lua 5.1, we sometimes get a "(tail call): ?" on the last line.