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('<', '&lt;')
0135         content = content.replace('>', '&gt;')
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('<', '&lt;')
0170                 content = content.replace('>', '&gt;')
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('<', '&lt;')
0275         line = line.replace('>', '&gt;')
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('<', '&lt;')
0292         names_of_used[check_idx].data = names_of_used[check_idx].data.replace('>', '&gt;')
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()