xref: /netbsd-src/usr.bin/xlint/lint1/check-msgs.lua (revision 782713e6c126f1866c6d9cfdee4ceb49483b5828)
1#! /usr/bin/lua
2-- $NetBSD: check-msgs.lua,v 1.17 2022/07/05 22:50:41 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  gnuism = "",
73}
74
75local function check_file(fname, msgs)
76  local f = assert(io.open(fname, "r"))
77  local lineno = 0
78  local prev = ""
79  for line in f:lines() do
80    lineno = lineno + 1
81
82    local func, id = line:match("^%s+([%w_]+)%((%d+)[),]")
83    local prefix = message_prefix[func]
84    if prefix then
85      id = prefix .. id
86      local comment = prev:match("^%s+/%* (.+) %*/$")
87      if comment ~= nil then
88        check_message(fname, lineno, id, comment, msgs)
89      else
90        print_error("%s:%d: missing comment for %s: /* %s */",
91          fname, lineno, id, msgs[id])
92      end
93    end
94
95    prev = line
96  end
97
98  f:close()
99end
100
101
102local function file_contains(filename, text)
103  local f = assert(io.open(filename, "r"))
104  local found = f:read("a"):find(text, 1, true)
105  f:close()
106  return found
107end
108
109
110-- Ensure that each test file for a particular message mentions the full text
111-- of that message and the message ID.
112local function check_test_files(msgs)
113  local testdir = "../../../tests/usr.bin/xlint/lint1"
114  local cmd = ("cd '%s' && printf '%%s\\n' msg_[0-9][0-9][0-9]*.c"):format(testdir)
115  local filenames = assert(io.popen(cmd))
116  for filename in filenames:lines() do
117    local msgid = filename:match("^msg_(%d%d%d)")
118    if msgs[msgid] then
119      local unescaped_msg = msgs[msgid]:gsub("\\(.)", "%1")
120      local expected_text = ("%s [%s]"):format(unescaped_msg, msgid)
121      local fullname = ("%s/%s"):format(testdir, filename)
122      if not file_contains(fullname, expected_text) then
123        print_error("%s must contain: %s", fullname, expected_text)
124      end
125    end
126  end
127  filenames:close()
128end
129
130local function main(arg)
131  local msgs = load_messages()
132  for _, fname in ipairs(arg) do
133    check_file(fname, msgs)
134  end
135  check_test_files(msgs)
136end
137
138main(arg)
139os.exit(not had_errors)
140