diff options
author | Richard Maw <richard.maw@codethink.co.uk> | 2015-11-27 16:57:09 +0000 |
---|---|---|
committer | Richard Maw <richard.maw@codethink.co.uk> | 2015-11-27 17:46:53 +0000 |
commit | b1a8d13ae5854b488ef4e7243fa7bf626eeb458e (patch) | |
tree | b947d85d7ee9cffe622f62294b83ddadee93bc47 | |
parent | 7a07b10c07aeb1138467ded79e708c60032eb46f (diff) | |
download | lace-b1a8d13ae5854b488ef4e7243fa7bf626eeb458e.tar.gz |
lace.error.render: Report all lines that caused error
This makes use of the line information added to the error traces,
to report exactly which definitions also caused the problem.
-rw-r--r-- | lib/lace/error.lua | 56 | ||||
-rw-r--r-- | test/test-lace.engine-chaindefine-error.rules | 2 | ||||
-rw-r--r-- | test/test-lace.engine.lua | 19 |
3 files changed, 58 insertions, 19 deletions
diff --git a/lib/lace/error.lua b/lib/lace/error.lua index cf21446..abbfa2f 100644 --- a/lib/lace/error.lua +++ b/lib/lace/error.lua @@ -103,28 +103,38 @@ local function _render(err) local ret = { err.msg } local wordset = {} - local function build_wordset(words, wordset) + local function build_wordset(words, wordset, parent_source, parent_linenr) + wordset.source = words.source or source + wordset.linenr = words.linenr or linenr for _, word in ipairs(words) do if type(word) ~= "table" then wordset[word] = true else local subwordset = {} - build_wordset(word.sub, subwordset) + build_wordset(word.sub, subwordset, wordset.source, wordset.linenr) wordset[word.nr] = subwordset end end end build_wordset(err.words, wordset) - -- The second is the source filename and line - ret[2] = err.words.source.source .. " :: " .. tostring(err.words.linenr) - -- The third line is the line of the input - local srcline = err.words.source.lines[err.words.linenr] or { - original = "???", content = { {spos = 1, epos = 3, str = "???"} } - } - ret[3] = srcline.original + local linelist = {} + local function build_linelist(wordset, parent_source, parent_linenr) + if parent_source ~= wordset.source or parent_linenr ~= wordset.linenr then + linelist[#linelist+1] = wordset + end + local srcline = wordset.source.lines[wordset.linenr] or { + original = "???", content = { {spos = 1, epos = 3, str = "???"} } + } + for w, info in ipairs(srcline.content) do + -- TODO: Sometimes wordset is table, but token has no subwords. + if type(wordset[w]) == "table" and info.sub then + build_linelist(wordset[w], wordset.source, wordset.linenr) + end + end + end + build_linelist(wordset) - -- The fourth line is the highlight for each word in question local function mark_my_words(line, wordset) local hlstr, cpos = "", 1 for w, info in ipairs(line) do @@ -133,12 +143,12 @@ local function _render(err) hlstr = hlstr .. " " cpos = cpos + 1 end - -- TODO: The subword can be defined in a different line entirely, - -- at which point it's not a subword of word in this line. - -- This is the norm for explicit definitions. - -- Eventually we should trace back to the define's - -- definition and highlight where in that line the problem is. - if type(wordset[w]) == "table" and info.sub then + -- The subword can be defined in a different line entirely, + -- at which point it's not a subword of word in this line. + -- This is the norm for explicit definitions. + if info.sub and type(wordset[w]) == "table" + and wordset[w].source == wordset.source + and wordset[w].linenr == wordset.linenr then -- space for [ hlstr, cpos = hlstr .. " ", cpos + 1 @@ -159,10 +169,18 @@ local function _render(err) end return hlstr, cpos end - local hlstr, _ = mark_my_words(srcline.content, wordset) - ret[4] = hlstr - -- The rendered error is those four strings joined by newlines + for _, wordset in ipairs(linelist) do + ret[#ret+1] = wordset.source.source .. " :: " .. tostring(wordset.linenr) + local srcline = wordset.source.lines[wordset.linenr] or { + original = "???", content = { {spos = 1, epos = 3, str = "???"} } + } + ret[#ret+1] = srcline.original + local hlstr, _ = mark_my_words(srcline.content, wordset) + ret[#ret+1] = hlstr + end + + -- The rendered error is those strings joined by newlines return table.concat(ret, "\n") end diff --git a/test/test-lace.engine-chaindefine-error.rules b/test/test-lace.engine-chaindefine-error.rules new file mode 100644 index 0000000..1620172 --- /dev/null +++ b/test/test-lace.engine-chaindefine-error.rules @@ -0,0 +1,2 @@ +define bogus error +allow "FAIL" [anyof [equal jeff banana] bogus] diff --git a/test/test-lace.engine.lua b/test/test-lace.engine.lua index 936a2ed..2e08bfc 100644 --- a/test/test-lace.engine.lua +++ b/test/test-lace.engine.lua @@ -289,6 +289,25 @@ function suite.subsubdefine_err_reported() assert(line4 == " ^^^^^ ", "The fourth line highlights relevant words") end +function suite.subdefine_chained_err_reported() + local ruleset, msg = lace.compiler.compile(comp_context, "chaindefine-error") + print"" + --require 'pl.pretty'.dump(ruleset) + assert(type(ruleset) == "table", "Ruleset did not compile") + local ectx = {error = true} + local result, msg = lace.engine.run(ruleset, ectx) + assert(result == false, "Did not error out") + local lines = {} + msg:gsub("([^\n]*)\n?", function(c) table.insert(lines, c) end) + assert(lines[1] == "woah", "The first line must mention the error") + assert(lines[2] == "real-chaindefine-error :: 2", "The second line is where the error happened") + assert(lines[3] == 'allow "FAIL" [anyof [equal jeff banana] bogus]', "The third line is the original line") + assert(lines[4] == " ^^^^^ ", "The fourth line highlights relevant words") + assert(lines[5] == "real-chaindefine-error :: 1", "The fifth line is where the definition that errored comes from") + assert(lines[6] == 'define bogus error', "The sixth line is the define") + assert(lines[7] == " ^^^^^", "The seventh line highlights relevant words") +end + local count_ok = 0 for _, testname in ipairs(testnames) do -- print("Run: " .. testname) |