2 Generates documentation based off the available static analyzers checks
3 References Checkers.td to determine what checks exist
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
19 checkers: dict of checker info
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
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.
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"])
71 f
.write("%s\n" % checker
["FullPackageName"])
72 f
.write("=" * len(checker
["FullPackageName"]) + "\n")
75 f
.write("%s\n" % checker
["HelpText"])
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")
82 """Update list.rst to include the new checks
85 checkers: dict acquired from get_checkers()
87 def update_documentation_list(checkers
):
88 with
open(os
.path
.join(__location__
, "list.rst"), "r+") as f
:
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"])
97 #Overwrite file with new data
100 f
.write(".. toctree::")
102 f
.write("%s\n" % check
)
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
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/""")
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")
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)
143 generate_documentation(checker
)
144 print("Generated documentation for: %s" % checker
["FullPackageName"])
145 update_documentation_list(checkers
)
147 if __name__
== '__main__':