Module Name: src
Committed By: rillig
Date: Fri Jun 17 20:31:56 UTC 2022
Modified Files:
src/tests/usr.bin/xlint/lint1: Makefile
Added Files:
src/tests/usr.bin/xlint/lint1: check-expect.lua
Removed Files:
src/tests/usr.bin/xlint: check-expect.lua
Log Message:
tests/lint: move check-expect.lua to tests/lint1
It is only used for testing lint1, not for lint2 or xlint.
To generate a diff of this commit:
cvs rdiff -u -r1.23 -r0 src/tests/usr.bin/xlint/check-expect.lua
cvs rdiff -u -r1.127 -r1.128 src/tests/usr.bin/xlint/lint1/Makefile
cvs rdiff -u -r0 -r1.1 src/tests/usr.bin/xlint/lint1/check-expect.lua
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/tests/usr.bin/xlint/lint1/Makefile
diff -u src/tests/usr.bin/xlint/lint1/Makefile:1.127 src/tests/usr.bin/xlint/lint1/Makefile:1.128
--- src/tests/usr.bin/xlint/lint1/Makefile:1.127 Fri Jun 17 20:23:58 2022
+++ src/tests/usr.bin/xlint/lint1/Makefile Fri Jun 17 20:31:56 2022
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.127 2022/06/17 20:23:58 rillig Exp $
+# $NetBSD: Makefile,v 1.128 2022/06/17 20:31:56 rillig Exp $
NOMAN= # defined
MAX_MESSAGE= 349 # see lint1/err.c
@@ -171,7 +171,7 @@ FILES+= stmt_for.c
FILES+= stmt_goto.c
FILES+= stmt_if.c
-FILES+= ../check-expect.lua
+FILES+= check-expect.lua
MSG_IDS= ${:U0 ${:U:${:Urange=${MAX_MESSAGE}}}:C,^.$,0&,:C,^..$,0&,}
MSG_FILES= ${MSG_IDS:%=msg_%.c}
@@ -185,7 +185,7 @@ sync-mi: .PHONY
cvs update "$$mi"; \
fmt="./usr/tests/usr.bin/xlint/lint1/%s\ttests-usr.bin-tests\tcompattestfile,atf\n"; \
cat "$$mi" > "$$mi.tmp"; \
- printf "$$fmt" ${FILES:T} >> "$$mi.tmp"; \
+ printf "$$fmt" ${FILES} >> "$$mi.tmp"; \
distrib/sets/fmt-list "$$mi.tmp"; \
mv "$$mi.tmp" "$$mi"; \
cvs diff "$$mi" || true
Added files:
Index: src/tests/usr.bin/xlint/lint1/check-expect.lua
diff -u /dev/null src/tests/usr.bin/xlint/lint1/check-expect.lua:1.1
--- /dev/null Fri Jun 17 20:31:56 2022
+++ src/tests/usr.bin/xlint/lint1/check-expect.lua Fri Jun 17 20:31:56 2022
@@ -0,0 +1,226 @@
+#! /usr/bin/lua
+-- $NetBSD: check-expect.lua,v 1.1 2022/06/17 20:31:56 rillig Exp $
+
+--[[
+
+usage: lua ./check-expect.lua *.c
+
+Check that the /* expect+-n: ... */ comments in the .c source files match the
+actual messages found in the corresponding .exp files. The .exp files are
+expected in the current working directory.
+
+The .exp files are generated on the fly during the ATF tests, see
+t_integration.sh. During development, they can be generated using
+lint1/accept.sh.
+]]
+
+
+local function test(func)
+ func()
+end
+
+local function assert_equals(got, expected)
+ if got ~= expected then
+ assert(false, string.format("got %q, expected %q", got, expected))
+ end
+end
+
+
+local had_errors = false
+---@param fmt string
+function print_error(fmt, ...)
+ print(fmt:format(...))
+ had_errors = true
+end
+
+
+local function load_lines(fname)
+ local lines = {}
+
+ local f = io.open(fname, "r")
+ if f == nil then return nil end
+
+ for line in f:lines() do
+ table.insert(lines, line)
+ end
+ f:close()
+
+ return lines
+end
+
+
+-- Load the 'expect:' comments from a C source file.
+--
+-- example return values:
+-- {
+-- ["file.c(18)"] = {"invalid argument 'a'", "invalid argument 'b'"},
+-- ["file.c(23)"] = {"not a constant expression [123]"},
+-- },
+-- { "file.c(18)", "file.c(23)" }
+local function load_c(fname)
+
+ local lines = load_lines(fname)
+ if lines == nil then return nil, nil end
+
+ local pp_fname = fname
+ local pp_lineno = 0
+ local comment_locations = {}
+ local comments_by_location = {}
+
+ local function add_expectation(offset, message)
+ local location = ("%s(%d)"):format(pp_fname, pp_lineno + offset)
+ if comments_by_location[location] == nil then
+ table.insert(comment_locations, location)
+ comments_by_location[location] = {}
+ end
+ local trimmed_msg = message:match("^%s*(.-)%s*$")
+ table.insert(comments_by_location[location], trimmed_msg)
+ end
+
+ for phys_lineno, line in ipairs(lines) do
+
+ for offset, comment in line:gmatch("/%* expect([+%-]%d+): (.-) %*/") do
+ add_expectation(tonumber(offset), comment)
+ end
+
+ pp_lineno = pp_lineno + 1
+
+ local ppl_lineno, ppl_fname = line:match("^#%s*(%d+)%s+\"([^\"]+)\"")
+ if ppl_lineno ~= nil then
+ if ppl_fname == fname and tonumber(ppl_lineno) ~= phys_lineno + 1 then
+ print_error("error: %s:%d: preprocessor line number must be %d",
+ fname, phys_lineno, phys_lineno + 1)
+ end
+ pp_fname = ppl_fname
+ pp_lineno = ppl_lineno
+ end
+ end
+
+ return comment_locations, comments_by_location
+end
+
+
+-- Load the expected raw lint output from a .exp file.
+--
+-- example return value: {
+-- {
+-- exp_lineno = "18",
+-- location = "file.c(18)",
+-- message = "not a constant expression [123]",
+-- }
+-- }
+local function load_exp(exp_fname)
+
+ local lines = load_lines(exp_fname)
+ if lines == nil then return {} end
+
+ local messages = {}
+ for exp_lineno, line in ipairs(lines) do
+ for location, message in line:gmatch("(%S+%(%d+%)): (.+)$") do
+ table.insert(messages, {
+ exp_lineno = exp_lineno,
+ location = location,
+ message = message
+ })
+ end
+ end
+
+ return messages
+end
+
+
+---@param comment string
+---@param pattern string
+---@return boolean
+local function matches(comment, pattern)
+ if comment == "" then return false end
+
+ local any_prefix = pattern:sub(1, 3) == "..."
+ if any_prefix then pattern = pattern:sub(4) end
+ local any_suffix = pattern:sub(-3) == "..."
+ if any_suffix then pattern = pattern:sub(1, -4) end
+
+ if any_prefix and any_suffix then
+ return comment:find(pattern, 1, true) ~= nil
+ elseif any_prefix then
+ return pattern ~= "" and comment:sub(-#pattern) == pattern
+ elseif any_suffix then
+ return comment:sub(1, #pattern) == pattern
+ else
+ return comment == pattern
+ end
+end
+
+test(function()
+ assert_equals(matches("a", "a"), true)
+ assert_equals(matches("a", "b"), false)
+ assert_equals(matches("a", "aaa"), false)
+
+ assert_equals(matches("abc", "a..."), true)
+ assert_equals(matches("abc", "c..."), false)
+
+ assert_equals(matches("abc", "...c"), true)
+ assert_equals(matches("abc", "...a"), false)
+
+ assert_equals(matches("abc123xyz", "...a..."), true)
+ assert_equals(matches("abc123xyz", "...b..."), true)
+ assert_equals(matches("abc123xyz", "...c..."), true)
+ assert_equals(matches("abc123xyz", "...1..."), true)
+ assert_equals(matches("abc123xyz", "...2..."), true)
+ assert_equals(matches("abc123xyz", "...3..."), true)
+ assert_equals(matches("abc123xyz", "...x..."), true)
+ assert_equals(matches("abc123xyz", "...y..."), true)
+ assert_equals(matches("abc123xyz", "...z..."), true)
+ assert_equals(matches("pattern", "...pattern..."), true)
+end)
+
+
+local function check_test(c_fname)
+ local exp_fname = c_fname:gsub("%.c$", ".exp"):gsub(".+/", "")
+
+ local c_comment_locations, c_comments_by_location = load_c(c_fname)
+ if c_comment_locations == nil then return end
+
+ local exp_messages = load_exp(exp_fname) or {}
+
+ for _, exp_message in ipairs(exp_messages) do
+ local c_comments = c_comments_by_location[exp_message.location] or {}
+ local expected_message =
+ exp_message.message:gsub("/%*", "**"):gsub("%*/", "**")
+
+ local found = false
+ for i, c_comment in ipairs(c_comments) do
+ if c_comment ~= "" and matches(expected_message, c_comment) then
+ c_comments[i] = ""
+ found = true
+ break
+ end
+ end
+
+ if not found then
+ print_error("error: %s: missing /* expect+1: %s */",
+ exp_message.location, expected_message)
+ end
+ end
+
+ for _, c_comment_location in ipairs(c_comment_locations) do
+ for _, c_comment in ipairs(c_comments_by_location[c_comment_location]) do
+ if c_comment ~= "" then
+ print_error(
+ "error: %s: declared message \"%s\" is not in the actual output",
+ c_comment_location, c_comment)
+ end
+ end
+ end
+end
+
+
+local function main(args)
+ for _, name in ipairs(args) do
+ check_test(name)
+ end
+end
+
+
+main(arg)
+os.exit(not had_errors)