Home | History | Annotate | Line # | Download | only in lint1
check-msgs.lua revision 1.7
      1 #! /usr/bin/lua
      2 -- $NetBSD: check-msgs.lua,v 1.7 2021/02/19 23:22:19 rillig Exp $
      3 
      4 --[[
      5 
      6 usage: lua ./check-msgs.lua *.c
      7 
      8 Check that the message text in the comments of the C source code matches the
      9 actual user-visible message text in err.c.
     10 
     11 ]]
     12 
     13 
     14 local function load_messages(fname)
     15   local msgs = {}
     16 
     17   local f = assert(io.open(fname, "r"))
     18   for line in f:lines() do
     19     local msg, id = line:match("%s*\"(.+)\",%s*/%*%s*(%d+)%s*%*/$")
     20     if msg ~= nil then
     21       msgs[tonumber(id)] = msg
     22     end
     23   end
     24 
     25   f:close()
     26 
     27   return msgs
     28 end
     29 
     30 
     31 local function check_message(fname, lineno, id, comment, msgs, errors)
     32   local msg = msgs[id]
     33 
     34   if msg == nil then
     35     errors:add("%s:%d: id=%d not found", fname, lineno, id)
     36     return
     37   end
     38 
     39   msg = msg:gsub("/%*", "**")
     40   msg = msg:gsub("%*/", "**")
     41   msg = msg:gsub("\\(.)", "%1")
     42 
     43   comment = comment:gsub("arg%.", "argument")
     44   comment = comment:gsub("comb%.", "combination")
     45   comment = comment:gsub("conv%.", "conversion")
     46   comment = comment:gsub("decl%.", "declaration")
     47   comment = comment:gsub("defn%.", "definition")
     48   comment = comment:gsub("des%.s", "designators")
     49   comment = comment:gsub("expr%.", "expression")
     50   comment = comment:gsub("func%.", "function")
     51   comment = comment:gsub("incomp%.", "incompatible")
     52   comment = comment:gsub("init%.", "initialize")
     53   comment = comment:gsub("param%.", "parameter")
     54   comment = comment:gsub("req%.", "requires")
     55   comment = comment:gsub("poss%.", "possibly")
     56   comment = comment:gsub("trad%.", "traditional")
     57 
     58   if comment == msg then
     59     return
     60   end
     61 
     62   local prefix = comment:match("^(.-)%s*%.%.%.$")
     63   if prefix ~= nil and msg:find(prefix, 1, 1) == 1 then
     64     return
     65   end
     66 
     67   errors:add("%s:%d:   id=%-3d   msg=%-40s   comment=%s",
     68     fname, lineno, id, msg, comment)
     69 end
     70 
     71 
     72 local function collect_errors(fname, msgs)
     73   local errors = {}
     74   errors.add = function(self, fmt, ...)
     75     table.insert(self, fmt:format(...))
     76   end
     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     id = tonumber(id)
     85     if func == "error" or func == "warning" or func == "c99ism" or
     86        func == "gnuism" or func == "message" then
     87       local comment = prev:match("^%s+/%* (.+) %*/$")
     88       if comment ~= nil then
     89         check_message(fname, lineno, id, comment, msgs, errors)
     90       else
     91         errors:add("%s:%d: missing comment for %d: /* %s */",
     92           fname, lineno, id, msgs[id])
     93       end
     94     end
     95 
     96     prev = line
     97   end
     98 
     99   f:close()
    100 
    101   return errors
    102 end
    103 
    104 
    105 local function check_file(fname, msgs)
    106   local errors = collect_errors(fname, msgs)
    107   for _, err in ipairs(errors) do
    108     print(err)
    109   end
    110   return #errors == 0
    111 end
    112 
    113 
    114 local function main(arg)
    115   local msgs = load_messages("err.c")
    116   local ok = true
    117   for _, fname in ipairs(arg) do
    118     ok = check_file(fname, msgs) and ok
    119   end
    120   return ok
    121 end
    122 
    123 
    124 os.exit(main(arg))
    125