brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.4 KiB · fba1592 Raw
173 lines · python
1"""2Generates documentation based off the available static analyzers checks3References Checkers.td to determine what checks exist4"""5 6import subprocess7import json8import os9import re10 11"""Get path of script so files are always in correct directory"""12__location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))13 14default_checkers_td_location = "../../../../clang/include/clang/StaticAnalyzer/Checkers/Checkers.td"15default_checkers_rst_location = "../../../../clang/docs/analyzer/checkers.rst"16 17"""Get dict of checker related info and parse for full check names18 19Returns:20  checkers: dict of checker info21"""22def get_checkers(checkers_td, checkers_rst):23    p = subprocess.Popen(24        [25            "llvm-tblgen",26            "--dump-json",27            "-I",28            os.path.dirname(checkers_td),29            checkers_td,30        ],31        stdout=subprocess.PIPE,32    )33    table_entries = json.loads(p.communicate()[0])34    documentable_checkers = []35    checkers = table_entries["!instanceof"]["Checker"]36 37    with open(checkers_rst, "r") as f:38        checker_rst_text = f.read()39 40    for checker_ in checkers:41        checker = table_entries[checker_]42        checker_name = checker["CheckerName"]43        package_ = checker["ParentPackage"]["def"]44        package = table_entries[package_]45        package_name = package["PackageName"]46        checker_package_prefix = package_name47        parent_package_ = package["ParentPackage"]48        hidden = (checker["Hidden"] != 0) or (package["Hidden"] != 0)49 50        while parent_package_ is not None:51            parent_package = table_entries[parent_package_["def"]]52            checker_package_prefix = (53                parent_package["PackageName"] + "." + checker_package_prefix54            )55            hidden = hidden or parent_package["Hidden"] != 056            parent_package_ = parent_package["ParentPackage"]57 58        full_package_name = (59            "clang-analyzer-" + checker_package_prefix + "." + checker_name60        )61        anchor_url = re.sub(62            r"\.", "-", checker_package_prefix + "." + checker_name63        ).lower()64 65        if not hidden and "alpha" not in full_package_name.lower():66            checker["FullPackageName"] = full_package_name67            checker["ShortName"] = checker_package_prefix + "." + checker_name68            checker["AnchorUrl"] = anchor_url69            checker["Documentation"] = ".. _%s:" % (checker["ShortName"].replace(".","-")) in checker_rst_text70            documentable_checkers.append(checker)71 72    documentable_checkers.sort(key=lambda x: x["FullPackageName"])73    return documentable_checkers74 75 76"""Generate documentation for checker77 78Args:79  checker: Checker for which to generate documentation.80  has_documentation: Specify that there is other documentation to link to.81"""82def generate_documentation(checker, has_documentation):83 84    with open(85        os.path.join(__location__, "clang-analyzer", checker["ShortName"] + ".rst"), "w"86    ) as f:87        f.write(".. title:: clang-tidy - %s\n" % checker["FullPackageName"])88        if has_documentation:89            f.write(".. meta::\n")90            f.write(91                "   :http-equiv=refresh: 5;URL=https://clang.llvm.org/docs/analyzer/checkers.html#%s\n"92                % checker["AnchorUrl"]93            )94        f.write("\n")95        f.write("%s\n" % checker["FullPackageName"])96        f.write("=" * len(checker["FullPackageName"]) + "\n")97        help_text = checker["HelpText"].strip()98        if not help_text.endswith("."):99            help_text += "."100        characters = 80101        for word in help_text.split(" "):102            if characters+len(word)+1 > 80:103                characters = len(word)104                f.write("\n")105                f.write(word)106            else:107                f.write(" ")108                f.write(word)109                characters += len(word) + 1110        f.write("\n\n")111        if has_documentation:112            f.write(113                "The `%s` check is an alias, please see\n" % checker["FullPackageName"]114            )115            f.write(116                "`Clang Static Analyzer Available Checkers\n<https://clang.llvm.org/docs/analyzer/checkers.html#%s>`_\n"117                % checker["AnchorUrl"]118            )119            f.write("for more information.\n")120        else:121            f.write("The %s check is an alias of\nClang Static Analyzer %s.\n" % (checker["FullPackageName"], checker["ShortName"]));122        f.close()123 124 125"""Update list.rst to include the new checks126 127Args:128  checkers: dict acquired from get_checkers()129"""130def update_documentation_list(checkers):131    with open(os.path.join(__location__, "list.rst"), "r+") as f:132        f_text = f.read()133        check_text = f_text.split(':header: "Name", "Redirect", "Offers fixes"\n')[1]134        checks = [x for x in check_text.split("\n") if ":header:" not in x and x]135        old_check_text = "\n".join(checks)136        checks = [x for x in checks if "clang-analyzer-" not in x]137        for checker in checkers:138            if checker["Documentation"]:139                checks.append("   :doc:`%s <clang-analyzer/%s>`, `Clang Static Analyzer %s <https://clang.llvm.org/docs/analyzer/checkers.html#%s>`_," % (checker["FullPackageName"],140                                                        checker["ShortName"],  checker["ShortName"], checker["AnchorUrl"]))141            else:142                checks.append("   :doc:`%s <clang-analyzer/%s>`, Clang Static Analyzer %s," % (checker["FullPackageName"], checker["ShortName"],  checker["ShortName"]))143 144        checks.sort()145 146        # Overwrite file with new data147        f.seek(0)148        f_text = f_text.replace(old_check_text, "\n".join(checks))149        f.write(f_text)150        f.close()151 152 153def main():154    CheckersPath = os.path.join(__location__, default_checkers_td_location)155    if not os.path.exists(CheckersPath):156        print("Could not find Checkers.td under %s." % (os.path.abspath(CheckersPath)))157        exit(1)158 159    CheckersDoc = os.path.join(__location__, default_checkers_rst_location)160    if not os.path.exists(CheckersDoc):161        print("Could not find checkers.rst under %s." % (os.path.abspath(CheckersDoc)))162        exit(1)163 164    checkers = get_checkers(CheckersPath, CheckersDoc)165    for checker in checkers:166        generate_documentation(checker, checker["Documentation"])167        print("Generated documentation for: %s" % (checker["FullPackageName"]))168    update_documentation_list(checkers)169 170 171if __name__ == "__main__":172    main()173