[docs] Add LICENSE.txt to the root of the mono-repo
[llvm-project.git] / clang-tools-extra / docs / clang-tidy / checks / gen-static-analyzer-docs.py
blob51956d15f15e91d764e93f7dbc0d591dba601a71
1 """
2 Generates documentation based off the available static analyzers checks
3 References Checkers.td to determine what checks exist
4 """
6 import argparse
7 import subprocess
8 import json
9 import os
10 import re
12 """Get path of script so files are always in correct directory"""
13 __location__ = os.path.realpath(
14 os.path.join(os.getcwd(), os.path.dirname(__file__)))
16 """Get dict of checker related info and parse for full check names
18 Returns:
19 checkers: dict of checker info
20 """
21 def get_checkers(checkers_td_directory):
22 p = subprocess.Popen(["llvm-tblgen", "--dump-json", "-I",
23 checkers_td_directory, checkers_td_directory+"Checkers.td"],
24 stdout=subprocess.PIPE)
25 table_entries = json.loads(p.communicate()[0])
26 documentable_checkers = []
27 checkers = table_entries["!instanceof"]["Checker"]
28 packages = table_entries["!instanceof"]["Package"]
30 for checker_ in checkers:
31 checker = table_entries[checker_]
32 checker_name = checker["CheckerName"]
33 package_ = checker["ParentPackage"]["def"]
34 package = table_entries[package_]
35 package_name = package["PackageName"]
36 checker_package_prefix = package_name
37 parent_package_ = package["ParentPackage"]
38 hidden = (checker["Hidden"] != 0) or (package["Hidden"] != 0)
40 while(parent_package_ != None):
41 parent_package = table_entries[parent_package_["def"]]
42 checker_package_prefix = parent_package["PackageName"] + "." + checker_package_prefix
43 hidden = hidden or parent_package["Hidden"] != 0
44 parent_package_ = parent_package["ParentPackage"]
46 full_package_name = "clang-analyzer-" + checker_package_prefix + "." + checker_name
47 anchor_url = re.sub("\.", "-", checker_package_prefix + "." + checker_name).lower()
49 if(not hidden and "alpha" not in full_package_name.lower()):
50 checker["FullPackageName"] = full_package_name
51 checker["AnchorUrl"] = anchor_url
52 documentable_checkers.append(checker)
54 documentable_checkers.sort(key=lambda x: x["FullPackageName"])
55 return documentable_checkers
57 """Generate documentation for checker
59 Args:
60 checker: Checker for which to generate documentation.
61 only_help_text: Generate documentation based off the checker description.
62 Used when there is no other documentation to link to.
63 """
64 def generate_documentation(checker, only_help_text=False):
65 with open(os.path.join(__location__, checker["FullPackageName"]+".rst"),"w") as f:
66 f.write(".. title:: clang-tidy - %s\n" % checker["FullPackageName"])
67 if(not only_help_text):
68 f.write(".. meta::\n")
69 f.write(" :http-equiv=refresh: 5;URL=https://clang.llvm.org/docs/analyzer/checkers.html#%s\n" % checker["AnchorUrl"])
70 f.write("\n")
71 f.write("%s\n" % checker["FullPackageName"])
72 f.write("=" * len(checker["FullPackageName"]) + "\n")
73 f.write("\n")
74 if(only_help_text):
75 f.write("%s\n" % checker["HelpText"])
76 else:
77 f.write("The %s check is an alias, please see\n" % checker["FullPackageName"])
78 f.write("`Clang Static Analyzer Available Checkers <https://clang.llvm.org/docs/analyzer/checkers.html#%s>`_\n" % checker["AnchorUrl"])
79 f.write("for more information.\n")
80 f.close()
82 """Update list.rst to include the new checks
84 Args:
85 checkers: dict acquired from get_checkers()
86 """
87 def update_documentation_list(checkers):
88 with open(os.path.join(__location__, "list.rst"), "r+") as f:
89 f_text = f.read()
90 header, check_text= f_text.split(".. toctree::\n")
91 checks = check_text.split("\n")
92 for checker in checkers:
93 if((" %s" % checker["FullPackageName"]) not in checks):
94 checks.append(" %s" % checker["FullPackageName"])
95 checks.sort()
97 #Overwrite file with new data
98 f.seek(0)
99 f.write(header)
100 f.write(".. toctree::")
101 for check in checks:
102 f.write("%s\n" % check)
103 f.close()
105 default_path_monorepo = '../../../../clang/include/clang/StaticAnalyzer/Checkers/'
106 default_path_in_tree = '../../../../../include/clang/StaticAnalyzer/Checkers/'
108 def parse_arguments():
109 """Set up and parse command-line arguments
110 Returns:
111 file_path: Path to Checkers.td"""
112 usage = """Parse Checkers.td to generate documentation for static analyzer checks"""
113 parse = argparse.ArgumentParser(description=usage)
115 file_path_help = ("""Path to Checkers directory
116 defaults to ../../../../clang/include/clang/StaticAnalyzer/Checkers/ if it exists
117 then to ../../../../../include/clang/StaticAnalyzer/Checkers/""")
119 default_path=None
120 if(os.path.exists(default_path_monorepo)):
121 default_path = default_path_monorepo
122 elif(os.path.exists(default_path_in_tree)):
123 default_path = default_path_in_tree
125 parse.add_argument("file", type=str, help=file_path_help, nargs='?', default=default_path)
126 args = parse.parse_args()
128 if(args.file is None):
129 print("Could not find Checkers directory. Please see -h")
130 exit(1)
132 return args.file
135 def main():
136 file_path = parse_arguments()
137 checkers = get_checkers(file_path)
138 for checker in checkers:
139 #No documentation nor alpha documentation
140 if(checker["Documentation"][1] == 0 and checker["Documentation"][0] == 0):
141 generate_documentation(checker, True)
142 else:
143 generate_documentation(checker)
144 print("Generated documentation for: %s" % checker["FullPackageName"])
145 update_documentation_list(checkers)
147 if __name__ == '__main__':
148 main()