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