Home | History | Annotate | Line # | Download | only in lint1
check-msgs.lua revision 1.16
      1   1.1  rillig #! /usr/bin/lua
      2  1.16  rillig -- $NetBSD: check-msgs.lua,v 1.16 2022/07/03 21:17:24 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.16  rillig local function load_messages()
     15  1.16  rillig   local msgs = {} ---@type table<string>string
     16   1.1  rillig 
     17  1.16  rillig   local f = assert(io.open("err.c"))
     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.16  rillig       msgs[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.16  rillig     print_error("%s:%d: id=%s 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.16  rillig   print_error("%s:%d:   id=%-3s   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.2  rillig       local comment = prev:match("^%s+/%* (.+) %*/$")
     84   1.1  rillig       if comment ~= nil then
     85  1.11  rillig         check_message(fname, lineno, id, comment, msgs)
     86   1.4  rillig       else
     87  1.16  rillig         print_error("%s:%d: missing comment for %s: /* %s */",
     88   1.4  rillig           fname, lineno, id, msgs[id])
     89   1.1  rillig       end
     90   1.1  rillig     end
     91   1.1  rillig 
     92   1.1  rillig     prev = line
     93   1.1  rillig   end
     94   1.1  rillig 
     95   1.1  rillig   f:close()
     96   1.1  rillig end
     97   1.1  rillig 
     98   1.1  rillig 
     99   1.8  rillig local function file_contains(filename, text)
    100   1.8  rillig   local f = assert(io.open(filename, "r"))
    101   1.9  rillig   local found = f:read("a"):find(text, 1, true)
    102   1.8  rillig   f:close()
    103   1.9  rillig   return found
    104   1.8  rillig end
    105   1.8  rillig 
    106  1.11  rillig 
    107  1.13  rillig -- Ensure that each test file for a particular message mentions the full text
    108  1.13  rillig -- of that message and the message ID.
    109   1.8  rillig local function check_test_files(msgs)
    110   1.8  rillig   local testdir = "../../../tests/usr.bin/xlint/lint1"
    111  1.13  rillig   local cmd = ("cd '%s' && printf '%%s\\n' msg_[0-9][0-9][0-9]*.c"):format(testdir)
    112  1.13  rillig   local filenames = assert(io.popen(cmd))
    113  1.13  rillig   for filename in filenames:lines() do
    114  1.16  rillig     local msgid = filename:match("^msg_(%d%d%d)")
    115  1.13  rillig     if msgs[msgid] then
    116  1.13  rillig       local unescaped_msg = msgs[msgid]:gsub("\\(.)", "%1")
    117  1.16  rillig       local expected_text = ("%s [%s]"):format(unescaped_msg, msgid)
    118  1.13  rillig       local fullname = ("%s/%s"):format(testdir, filename)
    119  1.13  rillig       if not file_contains(fullname, expected_text) then
    120  1.13  rillig         print_error("%s must contain: %s", fullname, expected_text)
    121  1.13  rillig       end
    122   1.8  rillig     end
    123   1.8  rillig   end
    124  1.13  rillig   filenames:close()
    125   1.8  rillig end
    126   1.1  rillig 
    127   1.1  rillig local function main(arg)
    128  1.16  rillig   local msgs = load_messages()
    129   1.1  rillig   for _, fname in ipairs(arg) do
    130  1.11  rillig     check_file(fname, msgs)
    131   1.1  rillig   end
    132  1.11  rillig   check_test_files(msgs)
    133   1.1  rillig end
    134   1.1  rillig 
    135  1.11  rillig main(arg)
    136  1.11  rillig os.exit(not had_errors)
    137