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