3 Filters and processes warnings generated by Doxygen, which are
4 annoyingly inconsistent and verbose, for greater readability.
6 (Neo)vim commands to go to the file and linenumber listed on a
7 line, in the reports this program generates:
8 :exe "let linenumber =" split(getline("."))[1]
9 :exe "edit" fnameescape(split(getline("."))[0]) "|" linenumber
11 It's easy to put a workflow together to clear up redundant doc
12 comments (which generate "multiple @param docs" warnings), using
13 simple vim commands to move the cursor and close buffers, Neovim's
14 support for the Language Server Protocol or related tooling, and
15 the command shown above.
17 A useful sequence, for rapidly deleting a doc comment from its last
18 line, is, in normal mode, `$v%ddd`.
20 For setting up LSP integration in Neovim, refer to the lsp_config
23 You may additionally need to generate compile_commands.json in the
24 repository root, to allow the language server to find everything.
25 This can be done using Bear (found at https://github.com/rizsotto/Bear).
27 @author: willow <willow@howhill.com>
36 "Constructs regex for a list"
37 return rf
"{field}(?:{separator}{field})*"
41 filename = rf
"{fileclass}+"
43 filepath = rf
"{sep_re(filename, '/')}\.(?:\w+)"
44 main_match = rf
"(?P<path>/{filepath}|\[generated\]):(?P<linenumber>\d+): warning:"
47 type_name = rf
"(?:const )?(?:unsigned (?:long )?|struct |enum )?(?:\w+)(?: \*?const)? \*{{0,3}}"
48 var_def = rf
"{type_name}\w+(?:\[(?:\(\d+/\d+\))?\])?"
49 func_params = rf
"\({sep_re(var_def, ', ')}(?:,\.\.\.)?\)"
51 func_name = simple_name
52 verbose_name = rf
"{sep_re(simple_name, ' ')}"
53 command_re =
"(?:</[^>]+>|\\\w+)"
54 macro_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*$"),
83 parser_choices = set(matches.keys()) - {
"blank",
84 "eof inside code block (line 2)",
85 "undocumented param (name)"}
87 parser = ap.ArgumentParser()
88 parser.add_argument(
"filename")
89 parser.add_argument(
"--summary",
"-s", action=
"store_true")
90 parser.add_argument(
"--key",
"-k", choices=parser_choices, action=
"append", dest=
"keys")
91 args = parser.parse_args()
93 sorted_lines = {k:[]
for k
in matches.keys()}
96 with 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"))
106 processed_lines = {k: [
" ".join(g
for g
in match.groups())
107 for match
in matches]
108 for k, matches
in sorted_lines.items()}
111 processed_lines[
"undocumented param"] = [
112 l1+
" "+l2
for l1, l2
in zip(processed_lines[
"undocumented param"],
113 processed_lines[
"undocumented param (name)"])
117 del processed_lines[
"blank"]
118 del processed_lines[
"eof inside code block (line 2)"]
119 del processed_lines[
"undocumented param (name)"]
122 counts = {k:
len(v)
for k, v
in processed_lines.items()}
124 for k, v
in counts.items():
128 if args.keys
is not None:
129 for key
in args.keys:
130 print(f
"{key}: {counts[key]}")
131 for line
in processed_lines[key]:
uint16_t len
length of data (which is always a uint32_t, but presumably this can be used to specify that fewer byt...
def sep_re(field, separator)