diff options
author | Willow Liquorice <willow@howhill.com> | 2022-09-07 21:44:26 +0100 |
---|---|---|
committer | Willow Liquorice <willow@howhill.com> | 2022-10-03 12:52:00 +0100 |
commit | 4ef0c17165207fc21f7f1c3245de84facb6e3ef9 (patch) | |
tree | d8c319bde6c2af44ededafc78542592db9ccb231 /contrib/scripts | |
parent | a35b078783a7e263f1b6dffe0da916f7339eab97 (diff) | |
download | gnunet-4ef0c17165207fc21f7f1c3245de84facb6e3ef9.tar.gz gnunet-4ef0c17165207fc21f7f1c3245de84facb6e3ef9.zip |
-DOC: Moved warningfilter to scripts/doc
Diffstat (limited to 'contrib/scripts')
-rwxr-xr-x | contrib/scripts/doc/warningfilter.py | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/contrib/scripts/doc/warningfilter.py b/contrib/scripts/doc/warningfilter.py new file mode 100755 index 000000000..31ac5865c --- /dev/null +++ b/contrib/scripts/doc/warningfilter.py | |||
@@ -0,0 +1,134 @@ | |||
1 | #!/usr/bin/env python3 | ||
2 | """ | ||
3 | Filters and processes warnings generated by Doxygen, which are | ||
4 | annoyingly inconsistent and verbose, for greater readability. | ||
5 | |||
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 | ||
10 | |||
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. | ||
16 | |||
17 | A useful sequence, for rapidly deleting a doc comment from its last | ||
18 | line, is, in normal mode, `$v%ddd`. | ||
19 | |||
20 | For setting up LSP integration in Neovim, refer to the lsp_config | ||
21 | plugin. | ||
22 | |||
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). | ||
26 | |||
27 | @author: willow <willow@howhill.com> | ||
28 | """ | ||
29 | |||
30 | import argparse as ap | ||
31 | import re | ||
32 | |||
33 | # Regular expression construction | ||
34 | |||
35 | def sep_re(field, separator): | ||
36 | "Constructs regex for a list" | ||
37 | return rf"{field}(?:{separator}{field})*" | ||
38 | |||
39 | # File names and paths | ||
40 | fileclass = r"[\w-]" | ||
41 | filename = rf"{fileclass}+" | ||
42 | # filename = rf"(/{fileclass}+)+\.\w" | ||
43 | filepath = rf"{sep_re(filename, '/')}\.(?:\w+)" | ||
44 | main_match = rf"(?P<path>/{filepath}|\[generated\]):(?P<linenumber>\d+): warning:" | ||
45 | |||
46 | # Symbols | ||
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, ', ')}(?:,\.\.\.)?\)" | ||
50 | simple_name = r"\w+" | ||
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, ', ')}(?:,\.\.\.)?\)" | ||
55 | |||
56 | |||
57 | matches = { | ||
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*$"), | ||
80 | # "": re.compile(rf"{main_match} "), | ||
81 | } | ||
82 | |||
83 | parser_choices = set(matches.keys()) - {"blank", | ||
84 | "eof inside code block (line 2)", | ||
85 | "undocumented param (name)"} | ||
86 | |||
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() | ||
92 | |||
93 | sorted_lines = {k:[] for k in matches.keys()} | ||
94 | unsorted_lines = [] | ||
95 | |||
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) | ||
101 | break | ||
102 | else: | ||
103 | unsorted_lines.append(line.strip("\n")) | ||
104 | |||
105 | |||
106 | processed_lines = {k: [" ".join(g for g in match.groups()) | ||
107 | for match in matches] | ||
108 | for k, matches in sorted_lines.items()} | ||
109 | |||
110 | # Combining multiline warnings | ||
111 | processed_lines["undocumented param"] = [ | ||
112 | l1+" "+l2 for l1, l2 in zip(processed_lines["undocumented param"], | ||
113 | processed_lines["undocumented param (name)"]) | ||
114 | ] | ||
115 | |||
116 | # Removing chaff | ||
117 | del processed_lines["blank"] | ||
118 | del processed_lines["eof inside code block (line 2)"] | ||
119 | del processed_lines["undocumented param (name)"] | ||
120 | |||
121 | # Preparing count dictionary and summarising the results | ||
122 | counts = {k: len(v) for k, v in processed_lines.items()} | ||
123 | if args.summary: | ||
124 | for k, v in counts.items(): | ||
125 | print(k+":", v) | ||
126 | print("") | ||
127 | |||
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]: | ||
132 | print(line) | ||
133 | print("") | ||
134 | |||