File indexing completed on 2024-05-19 04:19:13
0001 #!/usr/bin/python 0002 0003 # SPDX-FileCopyrightText: 2019 by Austin Hale, <ah at unc dot edu> 0004 # SPDX-FileCopyrightText: 2020-2024 by Gilles Caulier, <caulier dot gilles at gmail dot com> 0005 # 0006 # Export Clazy static analyzer output to HTML reports. 0007 # 0008 # SPDX-License-Identifier: BSD-3-Clause 0009 # 0010 0011 import sys 0012 import argparse 0013 import urllib.request 0014 import re 0015 from bs4 import BeautifulSoup 0016 from datetime import datetime 0017 0018 checks_list = ["[-Wclazy-assert-with-side-effects]", 0019 "[-Wclazy-container-inside-loop]", 0020 "[-Wclazy-detaching-member]", 0021 "[-Wclazy-heap-allocated-small-trivial-type]", 0022 "[-Wclazy-ifndef-define-typo]", 0023 "[-Wclazy-inefficient-qlist]", 0024 "[-Wclazy-isempty-vs-count]", 0025 "[-Wclazy-jni-signatures]", 0026 "[-Wclazy-qhash-with-char-pointer-key]", 0027 "[-Wclazy-qproperty-type-mismatch]", 0028 "[-Wclazy-qrequiredresult-candidates]", 0029 "[-Wclazy-qstring-varargs]", 0030 "[-Wclazy-qt-keywords]", 0031 "[-Wclazy-qt4-qstring-from-array]", 0032 "[-Wclazy-qt6-qhash-signature]", 0033 "[-Wclazy-qt6-qlatin1char-to-u]", 0034 "[-Wclazy-qt6-qlatin1string-to-u]", 0035 "[-Wclazy-qt6-qlatin1stringchar-to-u]", 0036 "[-Wclazy-qt6-qdir-fixes]", 0037 "[-Wclazy-qvariant-template-instantiation]", 0038 "[-Wclazy-raw-environment-function]", 0039 "[-Wclazy-reserve-candidates]", 0040 "[-Wclazy-signal-with-return-value]", 0041 "[-Wclazy-thread-with-slots]", 0042 "[-Wclazy-tr-non-literal]", 0043 "[-Wclazy-unneeded-cast]", 0044 "[-Wclazy-use-chrono-in-qtimer]", 0045 "[-Wclazy-connect-by-name]", 0046 "[-Wclazy-connect-non-signal]", 0047 "[-Wclazy-connect-not-normalized]", 0048 "[-Wclazy-container-anti-pattern]", 0049 "[-Wclazy-empty-qstringliteral]", 0050 "[-Wclazy-fully-qualified-moc-types]", 0051 "[-Wclazy-lambda-in-connect]", 0052 "[-Wclazy-lambda-unique-connection]", 0053 "[-Wclazy-lowercase-qml-type-name]", 0054 "[-Wclazy-mutable-container-key]", 0055 "[-Wclazy-overloaded-signal]", 0056 "[-Wclazy-qcolor-from-literal]", 0057 "[-Wclazy-qdatetime-utc]", 0058 "[-Wclazy-qenums]", 0059 "[-Wclazy-qfileinfo-exists]", 0060 "[-Wclazy-qgetenv]", 0061 "[-Wclazy-qmap-with-pointer-key]", 0062 "[-Wclazy-qstring-arg]", 0063 "[-Wclazy-qstring-comparison-to-implicit-char]", 0064 "[-Wclazy-qstring-insensitive-allocation]", 0065 "[-Wclazy-qstring-ref]", 0066 "[-Wclazy-qt-macros]", 0067 "[-Wclazy-strict-iterators]", 0068 "[-Wclazy-temporary-iterator]", 0069 "[-Wclazy-unused-non-trivial-variable]", 0070 "[-Wclazy-writing-to-temporary]", 0071 "[-Wclazy-wrong-qevent-cast]", 0072 "[-Wclazy-wrong-qglobalstatic]", 0073 "[-Wclazy-auto-unexpected-qstringbuilder]", 0074 "[-Wclazy-child-event-qobject-cast]", 0075 "[-Wclazy-connect-3arg-lambda]", 0076 "[-Wclazy-const-signal-or-slot]", 0077 "[-Wclazy-detaching-temporary]", 0078 "[-Wclazy-foreach]", 0079 "[-Wclazy-incorrect-emit]", 0080 "[-Wclazy-inefficient-qlist-soft]", 0081 "[-Wclazy-install-event-filter]", 0082 "[-Wclazy-non-pod-global-static]", 0083 "[-Wclazy-overridden-signal]", 0084 "[-Wclazy-post-event]", 0085 "[-Wclazy-qdeleteall]", 0086 "[-Wclazy-qhash-namespace]", 0087 "[-Wclazy-qlatin1string-non-ascii]", 0088 "[-Wclazy-qproperty-without-notify]", 0089 "[-Wclazy-qstring-left]", 0090 "[-Wclazy-range-loop]", 0091 "[-Wclazy-returning-data-from-temporary]", 0092 "[-Wclazy-rule-of-two-soft]", 0093 "[-Wclazy-skipped-base-method]", 0094 "[-Wclazy-virtual-signal]", 0095 "[-Wclazy-base-class-event]", 0096 "[-Wclazy-copyable-polymorphic]", 0097 "[-Wclazy-ctor-missing-parent-argument]", 0098 "[-Wclazy-function-args-by-ref]", 0099 "[-Wclazy-function-args-by-value]", 0100 "[-Wclazy-global-const-char-pointer]", 0101 "[-Wclazy-implicit-casts]", 0102 "[-Wclazy-missing-qobject-macro]", 0103 "[-Wclazy-missing-typeinfo]", 0104 "[-Wclazy-old-style-connec]", 0105 "[-Wclazy-qstring-allocations]", 0106 "[-Wclazy-returning-void-expression]", 0107 "[-Wclazy-rule-of-three]", 0108 "[-Wclazy-static-pmf]", 0109 "[-Wclazy-virtual-call-ctor]", 0110 ] 0111 0112 # Each check will have its own node of information. 0113 class checks: 0114 def __init__(self, dataval=None): 0115 self.name = '' 0116 self.count = 0 0117 self.data = '' 0118 0119 # Begin here. 0120 def main(): 0121 checks_list.sort() 0122 0123 # Process command line arguments. 0124 args = parse_command_line_options() 0125 external_link = '' 0126 external_name = '' 0127 0128 contents = args.file.readlines() 0129 0130 checks_used = [0] * len(checks_list) 0131 0132 # Increments each occurrence of a check. 0133 for line, content in enumerate(contents): 0134 content = content.replace('<', '<') 0135 content = content.replace('>', '>') 0136 for check_name in checks_list: 0137 if content.find(check_name) != -1: 0138 checks_used[checks_list.index(check_name)] += 1 0139 0140 # Counts the max number of used checks in the log file. 0141 num_used_checks = 0 0142 for line, check in enumerate(checks_list): 0143 if checks_used[line] != 0: 0144 num_used_checks += 1 0145 0146 names_of_used = [None] * num_used_checks 0147 names_of_usedL = [None] * num_used_checks 0148 0149 # Creates new check classes for each used check. 0150 used_line = 0 0151 total_num_checks = 0 0152 for line, check in enumerate(checks_list): 0153 if checks_used[line] != 0: 0154 new_node = checks(check) 0155 new_node.name = check 0156 new_node.count = checks_used[line] 0157 total_num_checks += checks_used[line] 0158 names_of_used[used_line] = new_node 0159 0160 names_of_usedL[used_line] = checks_list[line] 0161 used_line += 1 0162 0163 # Adds details for each organized check. 0164 for line, content in enumerate(contents): 0165 # Goes through each used check. 0166 for initial_check in names_of_usedL: 0167 # Adds the lines that detail the warning message. 0168 if content.find(initial_check) != -1: 0169 content = content.replace('<', '<') 0170 content = content.replace('>', '>') 0171 names_of_used[names_of_usedL.index(initial_check)].data += content 0172 details = line + 1 0173 finished = False 0174 while not finished: 0175 # Ensure there is no overflow. 0176 if details >= len(contents): 0177 break 0178 # If the line includes a used Clang-Tidy check name, 0179 # continue to find the next. 0180 for end_check in names_of_usedL: 0181 if contents[details].find(end_check) != -1: 0182 finished = True 0183 break 0184 # Otherwise, add the data to the specific used check 0185 # name for the organization of checks in the HTML file. 0186 if not finished: 0187 names_of_used[names_of_usedL.index(initial_check)].data += contents[details] 0188 details += 1 0189 0190 args.file.close() 0191 f = open("clazy.html", "w") 0192 0193 # Functions for writing to the clazy.html file. 0194 writeHeader(f) 0195 writeList(f, num_used_checks, names_of_used, args, 0196 external_link, external_name, total_num_checks) 0197 sortLogs(f, contents, num_used_checks, names_of_used, 0198 args, external_link, external_name) 0199 writeScript(f, num_used_checks) 0200 0201 # Close the file. 0202 f.close() 0203 sys.exit() 0204 0205 # Parses through the given command line options (-b, --button) 0206 # and returns the given file's contents if read successfully. 0207 def parse_command_line_options(): 0208 parser = argparse.ArgumentParser() 0209 parser.add_argument('file', type=argparse.FileType('r')) 0210 0211 try: 0212 args = parser.parse_args() 0213 except: 0214 parser.print_help() 0215 usage() 0216 sys.exit() 0217 0218 return args 0219 0220 # Prints usage information for the script. 0221 def usage(): 0222 print("**--------------------------- Clazy Visualizer --------------------------**\n\n \ 0223 Generates an html file as a visual for clazy checks.\n\n \ 0224 Arguments: python clazy_visualizer.py [logfile.log]\n\n \ 0225 \t\t-ex: python clazy_visualizer [logfile.log] \ 0226 \n\n**------------------------------------------------------------------------**") 0227 0228 # Header of the clazy.html file. 0229 def writeHeader(f): 0230 f.write("<!DOCTYPE html>\n") 0231 f.write("<html>\n") 0232 f.write("<head>\n") 0233 f.write("\t<title>Clazy Visualizer</title>\n\t<meta charset=\"UTF-8\">\n") 0234 f.write("\t<meta name=\"description\" content=\"Documentation tool for visualizing Clazy checks.\">\n") 0235 f.write("\t<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n") 0236 f.write("\t<link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css\">\n") 0237 f.write("\t<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>\n") 0238 f.write("\t<script src=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js\"></script>\n") 0239 f.write("</head>\n") 0240 0241 # List the used checks found in the source code. 0242 def writeList(f, num_used_checks, names_of_used, args, external_link, external_name, total_num_checks): 0243 now = datetime.now() 0244 f.write("<body style=\"background: rgb(220, 227, 230); width: 100%; height: 100%;\">\n") 0245 f.write("<div id=\"container\" style=\"margin-left: 2%; margin-right: 2%;\">\n") 0246 f.write("\t<div id=\"header\" style=\"height: 55px; display: flex; justify-content: left; position: relative;\">\n") 0247 f.write("\t\t<h3 style=\"text-align: center; color: #111; font-family: 'Helvetica Neue', sans-serif; font-weight: bold; \ 0248 letter-spacing: 0.5px; line-height: 1;\">digiKam Clazy Checks - %s</h3>\n" % (now.strftime("%m/%d/%Y %H:%M:%S"))) 0249 f.write("\t\t<div class=\"btn-group\" role=\"group\" style=\"position: absolute; right: 0;\">\n") 0250 f.write("\t\t</div>\n\t</div><br>\n") 0251 f.write("\t<ul id=\"list\" class=\"list-group\" align=\"left\" style=\"display: block; width: 25%; height: 0; margin-bottom: 0;\">\n") 0252 0253 # Iterates through each used check's details and organizes them into the given <pre> sections. 0254 f.write("\t\t<a id=\"log\" href=\"#\" class=\"list-group-item list-group-item-success\" style=\"color: black; font-weight: bold; letter-spacing:0.4px;\" \ 0255 onclick=\"toggleLog()\">%d Original Log</a>\n" % (total_num_checks)) 0256 0257 for line in range(0, num_used_checks): 0258 f.write("\t\t<a id=\"check%d\" style=\"color: black\" href=\"#\" class=\"list-group-item list-group-item-action\" \ 0259 onclick=\"toggleInfo(%d)\">%d %s</a>\n" % (line, line, names_of_used[line].count, names_of_used[line].name)) 0260 0261 f.write("\t</ul>\n\n") 0262 f.write("\t<div id=\"showLog\" style=\"display: none; width: 75%; float: right;\">\n") 0263 f.write("\t\t<div style=\"display: flex; justify-content: left; position: relative;\">\n") 0264 f.write("\t\t\t<button id=\"collapse-btn0\" type=\"button\" class=\"btn nohover\" onclick=\"collapseSidebar()\" style=\"outline: none; \ 0265 background-color: lightgray\" title=\"Collapse sidebar\">\n") 0266 f.write("\t\t\t<span id=\"collapse-img0\" class=\"glyphicon glyphicon-menu-left\"></button></span>\n") 0267 f.write("\t\t\t<h4 style=\"margin-top: 0; color: #111; position: absolute; left: 50%; transform: translateX(-50%); margin-bottom: 10;\">Original Log</h4>\n") 0268 0269 f.write("\t\t</div>\n\t\t<pre>\n") 0270 0271 # Sort through the used check logs for outputting the html. 0272 def sortLogs(f, contents, num_used_checks, names_of_used, args, external_link, external_name): 0273 for line in contents: 0274 line = line.replace('<', '<') 0275 line = line.replace('>', '>') 0276 f.write("%s" % (line)) 0277 0278 f.write("\n\t\t</pre>\n\t</div>\n") 0279 0280 for check_idx in range(0, num_used_checks): 0281 collapse_idx = check_idx+1 0282 f.write("\t<div id=\"show%d\"" % (check_idx)) 0283 f.write("style=\"display: none; width: 75%; float: right\">\n") 0284 f.write("\t\t<div style=\"display: flex; justify-content: left; position: relative;\">\n") 0285 f.write("\t\t\t<button id=\"collapse-btn%d\" type=\"button\" class=\"btn nohover\" onclick=\"collapseSidebar()\" \ 0286 style=\"outline: none; background-color: lightgray\" title=\"Collapse sidebar\">\n" % (collapse_idx)) 0287 f.write("\t\t\t<span id=\"collapse-img%d\" class=\"glyphicon glyphicon-menu-left\"></button></span>\n" % (collapse_idx)) 0288 f.write("\t\t\t<h4 style=\"margin-top: 0; color: #111; position: absolute; left: 50%; transform: translateX(-50%); margin-bottom: 10\">") 0289 f.write("<a href=\"https://github.com/KDE/clazy/blob/master/docs/checks/README-%s.md\">%s</a></h4>\n" % (names_of_used[check_idx].name[9:-1], names_of_used[check_idx].name[9:-1])) 0290 f.write("\t\t</div>\n\t\t<pre>\n") 0291 names_of_used[check_idx].data = names_of_used[check_idx].data.replace('<', '<') 0292 names_of_used[check_idx].data = names_of_used[check_idx].data.replace('>', '>') 0293 f.write("%s\t\t</pre>\n\t</div>\n" % (names_of_used[check_idx].data)) 0294 0295 f.write("</div>\n</body>\n") 0296 0297 # Writes Javascript and JQuery code to the html file for button and grouping functionalities. 0298 def writeScript(f, num_used_checks): 0299 f.write("<script>\nvar selected_idx;\nvar checks_arr = [];\nvar highlights = 'highlights';\n") 0300 f.write("// Retrieves local storage data on document load for highlighted checks.\n") 0301 f.write("$(document).ready(function() {\n\tfor (var all_checks=0; all_checks<%d; all_checks++) {\n" % (num_used_checks)) 0302 f.write("\t\tvar check_hl = document.getElementById(\"check\"+all_checks);\n") 0303 f.write("\t\tswitch (JSON.parse(localStorage.getItem(highlights))[all_checks]) {\n") 0304 f.write("\t\t\tcase \"warning\":\n\t\t\tcheck_hl.classList.add('list-group-item-warning');\n") 0305 f.write("\t\t\tchecks_arr[all_checks] = \"warning\"; break;\n\t\t\tcase \"danger\":\n") 0306 f.write("\t\t\tcheck_hl.classList.add('list-group-item-danger');\n\t\t\tchecks_arr[all_checks] = \"danger\"; break;\n") 0307 f.write("\t\t\tdefault:\n\t\t\tchecks_arr[all_checks] = \"action\";\n\t\t\tif (check_hl !== null) {\n") 0308 f.write("\t\t\t\tcheck_hl.classList.add('list-group-item-action');\n\t\t\t} break;\n\t\t}\n\t}\n") 0309 f.write("localStorage.setItem(highlights, JSON.stringify(checks_arr));\n});\n\n") 0310 0311 f.write("function toggleLog() {\n\tvar log = document.getElementById(\"showLog\");\n\tclearContent();\n") 0312 f.write("\tif (log.style.display === \"none\") {\n\t\tlog.style.display = \"block\";\n\t} else {\n") 0313 f.write("\t\tlog.style.display = \"none\";\n\t}\n}\n\n") 0314 0315 f.write("function toggleInfo(check_position) {\n\tselected_idx = check_position;\n\tclearContent();\n") 0316 f.write("\t// Displays the chosen clang-tidy category.\n\tvar category = document.getElementById(\"show\"+check_position);\n") 0317 f.write("\tif (category.style.display === \"none\") {\n\t\tcategory.style.display = \"block\";\n\t} else {\n") 0318 f.write("\t\tcategory.style.display = \"none\";\n\t}\n}\n\n") 0319 0320 f.write("// Clears document when choosing another selection.\nfunction clearContent() {\n") 0321 f.write("\tfor (var all_checks=0; all_checks<%d; all_checks++) {\n\t\tvar clear = document.getElementById(\"show\"+all_checks);\n" % (num_used_checks)) 0322 f.write("\t\tif (clear.style.display === \"block\") {\n\t\tclear.style.display = \"none\";\n\t\t}\n\t}\n") 0323 f.write("\tvar clearLog = document.getElementById(\"showLog\");\n\tif (clearLog.style.display === \"block\") {\n") 0324 f.write("\t\tclearLog.style.display = \"none\";\n\t}\n}\n\n") 0325 0326 f.write("// Type 1 used for highlighting danger checks and 0 for warnings.\nfunction highlightChecks(type) {\n") 0327 f.write("\tvar check_hl = document.getElementById(\"check\"+selected_idx);\n\tif (check_hl !== null) {\n") 0328 f.write("\t\tif (check_hl.classList.contains('list-group-item-action')) {\n\t\t\tcheck_hl.classList.remove('list-group-item-action');\n") 0329 f.write("\t\t\ttype == 1 ? check_hl.classList.add('list-group-item-danger') : check_hl.classList.add('list-group-item-warning');\n") 0330 f.write("\t\t\ttype == 1 ? checks_arr[selected_idx] = \"danger\" : checks_arr[selected_idx] = \"warning\";\n") 0331 f.write("\t\t} else if (check_hl.classList.contains('list-group-item-warning')) {\n\t\t\tcheck_hl.classList.remove('list-group-item-warning');\n") 0332 f.write("\t\t\ttype == 1 ? check_hl.classList.add('list-group-item-danger') : check_hl.classList.add('list-group-item-action');\n") 0333 f.write("\t\t\ttype == 1 ? checks_arr[selected_idx] = \"danger\" : checks_arr[selected_idx] = \"action\";\n\t\t} else {\n") 0334 f.write("\t\t\tcheck_hl.classList.remove('list-group-item-danger');\n") 0335 f.write("\t\t\ttype == 1 ? check_hl.classList.add('list-group-item-action') : check_hl.classList.add('list-group-item-warning');\n") 0336 f.write("\t\t\ttype == 1 ? checks_arr[selected_idx] = \"action\" : checks_arr[selected_idx] = \"warning\";\n\t\t}\n\t}\n") 0337 f.write("\t// Sets local storage for each occurrence of a highlighted check.\n\tlocalStorage.setItem(highlights, JSON.stringify(checks_arr));\n}\n\n") 0338 0339 f.write("function clearChecks(type) {\n\tfor (var all_checks=0; all_checks<%d; all_checks++) {\n" % (num_used_checks)) 0340 f.write("\t\tvar clear = (document.getElementById(\"check\"+all_checks));\n\t\tchecks_arr[all_checks] = \"action\";\n") 0341 f.write("\t\tif (clear !== null) {\n") 0342 f.write("\t\t\tif (clear.classList.contains('list-group-item-warning')) {\n\t\t\t\tclear.classList.remove('list-group-item-warning');\n") 0343 f.write("\t\t\t} else if (clear.classList.contains('list-group-item-danger')) {\n\t\t\t\tclear.classList.remove('list-group-item-danger');\n\t\t\t}\n") 0344 f.write("\t\t\tclear.classList.add('list-group-item-action');\n\t\t}\n\t}\n\t// Restores all checks to unhighlighted state on local storage.\n") 0345 f.write("\tlocalStorage.removeItem(highlights);\n}\n\n") 0346 0347 f.write("function collapseSidebar() {\n\tvar list = document.getElementById(\"list\"); var hasExpanded;\n") 0348 f.write("\tvar log_details = document.getElementById(\"showLog\");\n\tlist.style.display === \"block\" ? hasSidebar = true : hasSidebar = false;\n") 0349 f.write("\thasSidebar ? list.style.display = \"none\" : list.style.display = \"block\";\n") 0350 f.write("\tfor (var all_checks=0; all_checks<=%d; all_checks++) {\n\t\tvar collapse_img = document.getElementById(\"collapse-img\"+all_checks);\n" % (num_used_checks)) 0351 f.write("\t\tvar collapse_btn = document.getElementById(\"collapse-btn\"+all_checks);\n\t\tvar check_details = document.getElementById(\"show\"+all_checks);\n") 0352 f.write("\t\tif (collapse_img !== null) {\n\t\t\thasSidebar ? collapse_img.classList.remove('glyphicon-menu-left') : collapse_img.classList.remove('glyphicon-menu-right');\n") 0353 f.write("\t\t\thasSidebar ? collapse_img.classList.add('glyphicon-menu-right') : collapse_img.classList.add('glyphicon-menu-left');\n") 0354 f.write("\t\t\thasSidebar ? collapse_btn.title = \"Expand sidebar\" : collapse_btn.title = \"Collapse sidebar\";\n\t\t}\n") 0355 f.write("\t\tif (check_details !== null) {hasSidebar ? check_details.style.width = \"100%\" : check_details.style.width = \"75%\";}\n\t}\n") 0356 f.write("\thasSidebar ? log_details.style.width = \"100%\" : log_details.style.width = \"75%\";\n}\n") 0357 0358 # Begins writing style elements. 0359 f.write("</script>\n<style>\n\tpre {\n\t\twhite-space: pre-wrap;\n") 0360 f.write("\t\tword-break: keep-all;\n\t}\n\t#header {\n") 0361 f.write("\t\tborder-bottom: 2px solid darkgray\n\t}\n") 0362 f.write("</style>\n</html>") 0363 0364 0365 # Calls main function. 0366 if __name__ == "__main__": 0367 main()