1#! /usr/bin/lua 2-- $NetBSD: check-msgs.lua,v 1.19 2023/07/10 11:46:14 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 c23ism = "", 73 gnuism = "", 74} 75 76local function check_file(fname, msgs) 77 local f = assert(io.open(fname, "r")) 78 local lineno = 0 79 local prev = "" 80 for line in f:lines() do 81 lineno = lineno + 1 82 83 local func, id = line:match("^%s+([%w_]+)%((%d+)[),]") 84 local prefix = message_prefix[func] 85 if prefix then 86 id = prefix .. id 87 local comment = prev:match("^%s+/%* (.+) %*/$") 88 if comment ~= nil then 89 check_message(fname, lineno, id, comment, msgs) 90 else 91 print_error("%s:%d: missing comment for %s: /* %s */", 92 fname, lineno, id, msgs[id]) 93 end 94 end 95 96 prev = line 97 end 98 99 f:close() 100end 101 102 103local function file_contains(filename, text) 104 local f = assert(io.open(filename, "r")) 105 local found = f:read("a"):find(text, 1, true) 106 f:close() 107 return found 108end 109 110 111-- Ensure that each test file for a particular message mentions the full text 112-- of that message and the message ID. 113local function check_test_files(msgs) 114 local testdir = "../../../tests/usr.bin/xlint/lint1" 115 local cmd = ("cd '%s' && printf '%%s\\n' msg_[0-9][0-9][0-9]*.c"):format(testdir) 116 local filenames = assert(io.popen(cmd)) 117 for filename in filenames:lines() do 118 local msgid = filename:match("^msg_(%d%d%d)") 119 if msgs[msgid] then 120 local unescaped_msg = msgs[msgid]:gsub("\\(.)", "%1") 121 local expected_text = ("%s [%s]"):format(unescaped_msg, msgid) 122 local fullname = ("%s/%s"):format(testdir, filename) 123 if not file_contains(fullname, expected_text) then 124 print_error("%s must contain: %s", fullname, expected_text) 125 end 126 end 127 end 128 filenames:close() 129end 130 131local function check_yacc_file(filename) 132 local decl = {} 133 local f = assert(io.open(filename, "r")) 134 local lineno = 0 135 for line in f:lines() do 136 lineno = lineno + 1 137 local type = line:match("^%%type%s+<[%w_]+>%s+(%S+)$") or 138 line:match("^/%* No type for ([%w_]+)%. %*/$") 139 if type then 140 decl[type] = lineno 141 end 142 local rule = line:match("^([%w_]+):") 143 if rule then 144 if decl[rule] then 145 decl[rule] = nil 146 else 147 print_error("%s:%d: missing type declaration for rule %q", 148 filename, lineno, rule) 149 end 150 end 151 end 152 for rule, decl_lineno in pairs(decl) do 153 print_error("%s:%d: missing rule %q", filename, decl_lineno, rule) 154 end 155 f:close() 156end 157 158local function main(arg) 159 local msgs = load_messages() 160 for _, fname in ipairs(arg) do 161 check_file(fname, msgs) 162 if fname:match("%.y$") then 163 check_yacc_file(fname) 164 end 165 end 166 check_test_files(msgs) 167end 168 169main(arg) 170os.exit(not had_errors) 171