summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Maw <richard.maw@codethink.co.uk>2015-11-27 16:57:09 +0000
committerRichard Maw <richard.maw@codethink.co.uk>2015-11-27 17:46:53 +0000
commitb1a8d13ae5854b488ef4e7243fa7bf626eeb458e (patch)
treeb947d85d7ee9cffe622f62294b83ddadee93bc47
parent7a07b10c07aeb1138467ded79e708c60032eb46f (diff)
downloadlace-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.lua56
-rw-r--r--test/test-lace.engine-chaindefine-error.rules2
-rw-r--r--test/test-lace.engine.lua19
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)