3Filters and processes warnings generated by Doxygen, which are
4annoyingly inconsistent and verbose, for greater readability.
6(Neo)vim commands to go to the file and linenumber listed on a
7line, in the reports this program generates:
8 :exe "let linenumber =" split(getline(
"."))[1]
9 :exe
"edit" fnameescape(split(getline(
"."))[0])
"|" linenumber
11It
's easy to put a workflow together to clear up redundant doc
12comments (which generate "multiple @param docs" warnings), using
13simple vim commands to move the cursor
and close buffers, Neovim
's
14support for the Language Server Protocol
or related tooling,
and
15the command shown above.
17A useful sequence,
for rapidly deleting a doc comment
from its last
18line,
is,
in normal mode, `$v%ddd`.
20For setting up LSP integration
in Neovim, refer to the lsp_config
23You may additionally need to generate compile_commands.json
in the
24repository root, to allow the language server to find everything.
25This can be done using Bear (found at https://github.com/rizsotto/Bear).
27@author: willow <willow
@howhill.com>
33# Regular expression construction
35def sep_re(field, separator):
36 "Constructs regex for a list"
37 return rf
"{field}(?:{separator}{field})*"
41filename = rf
"{fileclass}+"
43filepath = rf
"{sep_re(filename, '/')}\.(?:\w+)"
44main_match = rf
"(?P<path>/{filepath}|\[generated\]):(?P<linenumber>\d+): warning:"
47type_name = rf
"(?:const )?(?:unsigned (?:long )?|struct |enum )?(?:\w+)(?: \*?const)? \*{{0,3}}"
48var_def = rf
"{type_name}\w+(?:\[(?:\(\d+/\d+\))?\])?"
49func_params = rf
"\({sep_re(var_def, ', ')}(?:,\.\.\.)?\)"
51func_name = simple_name
52verbose_name = rf
"{sep_re(simple_name, ' ')}"
53command_re =
"(?:</[^>]+>|\\\w+)"
54macro_params = rf
"\({sep_re(simple_name, ', ')}(?:,\.\.\.)?\)"
58 "not an input @file": re.compile(rf
"{main_match} the name '(?P<name>{filepath}|{simple_name})' supplied as the argument in the \\file statement is not an input file"),
59 "multiple @param docs": re.compile(rf
"{main_match} argument '(?P<arg_name>\w+)' from the argument list of ({func_name}) has multiple @param documentation sections"),
60 "undocumented param": re.compile(rf
"{main_match} The following parameters? of ({func_name})(?:{func_params}|{macro_params}) (?:is|are) not documented:"),
61 "undocumented param (name)": re.compile(
r" parameter '([\w.]+)'"),
62 "explicit link not resolved": re.compile(rf
"{main_match} explicit link request to '(\w+(?:\(\))?)' could not be resolved"),
63 "unknown command": re.compile(rf
"{main_match} Found unknown command '(\\\w+)'"),
64 "missing argument": re.compile(rf
"{main_match} argument '(\w+)' of command @param is not found in the argument list of ({func_name})(?:{func_params}|{macro_params})"),
65 "eof inside group": re.compile(rf
"{main_match} end of file while inside a group"),
66 "eof inside comment": re.compile(rf
"{main_match} Reached end of file while still inside a \(nested\) comment. Nesting level \d+ \(probable line reference: (\d+)\)"),
67 "eof inside code block": re.compile(rf
"{main_match} reached end of file while inside a 'code' block!"),
68 "eof inside code block (line 2)": re.compile(rf
"The command that should end the block seems to be missing!"),
69 "title mismatch": re.compile(rf
"{main_match} group (?P<group_id>\w+): ignoring title \"(?P<new_title>{verbose_name})\" that does not match old title \"(?P<old_title>{verbose_name})\""),
70 "end of comment expecting command": re.compile(rf
"{main_match} end of comment block while expecting command ({command_re})"),
71 "no matching tag": re.compile(rf
"{main_match} found </(?P<tag>[^>]+)> tag without matching <(?P=tag)>"),
72 "documented empty return type": re.compile(rf
"{main_match} documented empty return type of {func_name}"),
73 "unsupported tag": re.compile(rf
"{main_match} Unsupported xml/html tag <(?P<tag>[^>]+)> found"),
74 "expected whitespace after command": re.compile(rf
"{main_match} expected whitespace after \\(?P<command>\w+) command"),
75 "illegal command": re.compile(rf
"{main_match} Illegal command (?P<illegal_cmd>(?:@|\\)\w+) as part of a \\(?P<command>\w+) command"),
76 "undeclared symbol": re.compile(rf
"{main_match} documented symbol '(\w+)' was not declared or defined\."),
77 "nameless member": re.compile(rf
"{main_match} member with no name found."),
78 "end of empty list": re.compile(rf
"{main_match} End of list marker found without any preceding list items"),
79 "blank": re.compile(rf
"^\s*$"),
83parser_choices = set(matches.keys()) - {
"blank",
84 "eof inside code block (line 2)",
85 "undocumented param (name)"}
87parser = ap.ArgumentParser()
88parser.add_argument(
"filename")
89parser.add_argument(
"--summary",
"-s", action=
"store_true")
90parser.add_argument(
"--key",
"-k", choices=parser_choices, action=
"append", dest=
"keys")
91args = parser.parse_args()
93sorted_lines = {k:[]
for k
in matches.keys()}
96with open(args.filename,
"r")
as file:
97 for line
in file.readlines():
98 for key, value
in matches.items():
99 if match := value.match(line):
100 sorted_lines[key].append(match)
103 unsorted_lines.append(line.strip(
"\n"))
106processed_lines = {k: [
" ".join(g
for g
in match.groups())
107 for match
in matches]
108 for k, matches
in sorted_lines.items()}
111processed_lines[
"undocumented param"] = [
112 l1+
" "+l2
for l1, l2
in zip(processed_lines[
"undocumented param"],
113 processed_lines[
"undocumented param (name)"])
117del processed_lines[
"blank"]
118del processed_lines[
"eof inside code block (line 2)"]
119del processed_lines[
"undocumented param (name)"]
122counts = {k: len(v)
for k, v
in processed_lines.items()}
124 for k, v
in counts.items():
128if args.keys
is not None:
129 for key
in args.keys:
130 print(f
"{key}: {counts[key]}")
131 for line
in processed_lines[key]: