1#! /usr/bin/lua 2-- $NetBSD: check-msgs.lua,v 1.17 2022/07/05 22:50:41 rillig Exp $ 3 4--[[ 5 6usage: lua ./check-msgs.lua *.c *.y 7 8Check that the message text in the comments of the C source code matches the 9actual user-visible message text in err.c. 10 11]] 12 13 14local function load_messages() 15 local msgs = {} ---@type table<string>string 16 17 local f = assert(io.open("err.c")) 18 for line in f:lines() do 19 local msg, id = line:match("%s*\"(.+)\",%s*/%*%s*(Q?%d+)%s*%*/$") 20 if msg ~= nil then 21 msgs[id] = msg 22 end 23 end 24 25 f:close() 26 27 return msgs 28end 29 30 31local had_errors = false 32---@param fmt string 33function print_error(fmt, ...) 34 print(fmt:format(...)) 35 had_errors = true 36end 37 38 39local function check_message(fname, lineno, id, comment, msgs) 40 local msg = msgs[id] 41 42 if msg == nil then 43 print_error("%s:%d: id=%s not found", fname, lineno, id) 44 return 45 end 46 47 msg = msg:gsub("/%*", "**") 48 msg = msg:gsub("%*/", "**") 49 msg = msg:gsub("\\(.)", "%1") 50 51 if comment == msg then 52 return 53 end 54 55 local prefix = comment:match("^(.-)%s*%.%.%.$") 56 if prefix ~= nil and msg:find(prefix, 1, 1) == 1 then 57 return 58 end 59 60 print_error("%s:%d: id=%-3s msg=%-40s comment=%s", 61 fname, lineno, id, msg, comment) 62end 63 64local message_prefix = { 65 error = "", 66 error_at = "", 67 warning = "", 68 warning_at = "", 69 query_message = "Q", 70 c99ism = "", 71 c11ism = "", 72 gnuism = "", 73} 74 75local function check_file(fname, msgs) 76 local f = assert(io.open(fname, "r")) 77 local lineno = 0 78 local prev = "" 79 for line in f:lines() do 80 lineno = lineno + 1 81 82 local func, id = line:match("^%s+([%w_]+)%((%d+)[),]") 83 local prefix = message_prefix[func] 84 if prefix then 85 id = prefix .. id 86 local comment = prev:match("^%s+/%* (.+) %*/$") 87 if comment ~= nil then 88 check_message(fname, lineno, id, comment, msgs) 89 else 90 print_error("%s:%d: missing comment for %s: /* %s */", 91 fname, lineno, id, msgs[id]) 92 end 93 end 94 95 prev = line 96 end 97 98 f:close() 99end 100 101 102local function file_contains(filename, text) 103 local f = assert(io.open(filename, "r")) 104 local found = f:read("a"):find(text, 1, true) 105 f:close() 106 return found 107end 108 109 110-- Ensure that each test file for a particular message mentions the full text 111-- of that message and the message ID. 112local function check_test_files(msgs) 113 local testdir = "../../../tests/usr.bin/xlint/lint1" 114 local cmd = ("cd '%s' && printf '%%s\\n' msg_[0-9][0-9][0-9]*.c"):format(testdir) 115 local filenames = assert(io.popen(cmd)) 116 for filename in filenames:lines() do 117 local msgid = filename:match("^msg_(%d%d%d)") 118 if msgs[msgid] then 119 local unescaped_msg = msgs[msgid]:gsub("\\(.)", "%1") 120 local expected_text = ("%s [%s]"):format(unescaped_msg, msgid) 121 local fullname = ("%s/%s"):format(testdir, filename) 122 if not file_contains(fullname, expected_text) then 123 print_error("%s must contain: %s", fullname, expected_text) 124 end 125 end 126 end 127 filenames:close() 128end 129 130local function main(arg) 131 local msgs = load_messages() 132 for _, fname in ipairs(arg) do 133 check_file(fname, msgs) 134 end 135 check_test_files(msgs) 136end 137 138main(arg) 139os.exit(not had_errors) 140