1 //=- ClangSACheckersEmitter.cpp - Generate Clang SA checkers tables -*- C++ -*- 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This tablegen backend emits Clang Static Analyzer checkers tables. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "llvm/ADT/StringMap.h" 14 #include "llvm/TableGen/Error.h" 15 #include "llvm/TableGen/Record.h" 16 #include "llvm/TableGen/TableGenBackend.h" 17 #include <map> 18 #include <string> 19 20 using namespace llvm; 21 22 //===----------------------------------------------------------------------===// 23 // Static Analyzer Checkers Tables generation 24 //===----------------------------------------------------------------------===// 25 26 static std::string getPackageFullName(const Record *R); 27 28 static std::string getParentPackageFullName(const Record *R) { 29 std::string name; 30 if (DefInit *DI = dyn_cast<DefInit>(R->getValueInit("ParentPackage"))) 31 name = getPackageFullName(DI->getDef()); 32 return name; 33 } 34 35 static std::string getPackageFullName(const Record *R) { 36 std::string name = getParentPackageFullName(R); 37 if (!name.empty()) 38 name += "."; 39 assert(!R->getValueAsString("PackageName").empty()); 40 name += R->getValueAsString("PackageName"); 41 return name; 42 } 43 44 static std::string getCheckerFullName(const Record *R) { 45 std::string name = getParentPackageFullName(R); 46 if (!name.empty()) 47 name += "."; 48 assert(!R->getValueAsString("CheckerName").empty()); 49 name += R->getValueAsString("CheckerName"); 50 return name; 51 } 52 53 static std::string getStringValue(const Record &R, StringRef field) { 54 if (StringInit *SI = dyn_cast<StringInit>(R.getValueInit(field))) 55 return SI->getValue(); 56 return std::string(); 57 } 58 59 // Calculates the integer value representing the BitsInit object 60 static inline uint64_t getValueFromBitsInit(const BitsInit *B, const Record &R) { 61 assert(B->getNumBits() <= sizeof(uint64_t) * 8 && "BitInits' too long!"); 62 63 uint64_t Value = 0; 64 for (unsigned i = 0, e = B->getNumBits(); i != e; ++i) { 65 const auto *Bit = dyn_cast<BitInit>(B->getBit(i)); 66 if (Bit) 67 Value |= uint64_t(Bit->getValue()) << i; 68 else 69 PrintFatalError(R.getLoc(), 70 "missing Documentation for " + getCheckerFullName(&R)); 71 } 72 return Value; 73 } 74 75 static std::string getCheckerDocs(const Record &R) { 76 StringRef LandingPage; 77 if (BitsInit *BI = R.getValueAsBitsInit("Documentation")) { 78 uint64_t V = getValueFromBitsInit(BI, R); 79 if (V == 1) 80 LandingPage = "available_checks.html"; 81 else if (V == 2) 82 LandingPage = "alpha_checks.html"; 83 } 84 85 if (LandingPage.empty()) 86 return ""; 87 88 return (llvm::Twine("https://clang-analyzer.llvm.org/") + LandingPage + "#" + 89 getCheckerFullName(&R)) 90 .str(); 91 } 92 93 static void printChecker(llvm::raw_ostream &OS, const Record &R) { 94 OS << "CHECKER(" << "\""; 95 OS.write_escaped(getCheckerFullName(&R)) << "\", "; 96 OS << R.getName() << ", "; 97 OS << "\""; 98 OS.write_escaped(getStringValue(R, "HelpText")) << "\", "; 99 OS << "\""; 100 OS.write_escaped(getCheckerDocs(R)); 101 OS << "\""; 102 } 103 104 namespace clang { 105 void EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) { 106 std::vector<Record*> checkers = Records.getAllDerivedDefinitions("Checker"); 107 std::vector<Record*> packages = Records.getAllDerivedDefinitions("Package"); 108 109 using SortedRecords = llvm::StringMap<const Record *>; 110 111 OS << "// This file is automatically generated. Do not edit this file by " 112 "hand.\n"; 113 114 // Emit packages. 115 // 116 // PACKAGE(PACKAGENAME) 117 // - PACKAGENAME: The name of the package. 118 OS << "\n" 119 "#ifdef GET_PACKAGES\n"; 120 { 121 SortedRecords sortedPackages; 122 for (unsigned i = 0, e = packages.size(); i != e; ++i) 123 sortedPackages[getPackageFullName(packages[i])] = packages[i]; 124 125 for (SortedRecords::iterator 126 I = sortedPackages.begin(), E = sortedPackages.end(); I != E; ++I) { 127 const Record &R = *I->second; 128 129 OS << "PACKAGE(" << "\""; 130 OS.write_escaped(getPackageFullName(&R)) << '\"'; 131 OS << ")\n"; 132 } 133 } 134 OS << "#endif // GET_PACKAGES\n" 135 "\n"; 136 137 // Emit checkers. 138 // 139 // CHECKER(FULLNAME, CLASS, HELPTEXT) 140 // - FULLNAME: The full name of the checker, including packages, e.g.: 141 // alpha.cplusplus.UninitializedObject 142 // - CLASS: The name of the checker, with "Checker" appended, e.g.: 143 // UninitializedObjectChecker 144 // - HELPTEXT: The description of the checker. 145 OS << "\n" 146 "#ifdef GET_CHECKERS\n" 147 "\n"; 148 for (const Record *checker : checkers) { 149 printChecker(OS, *checker); 150 OS << ")\n"; 151 } 152 OS << "\n" 153 "#endif // GET_CHECKERS\n" 154 "\n"; 155 156 // Emit dependencies. 157 // 158 // CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY) 159 // - FULLNAME: The full name of the checker that depends on another checker. 160 // - DEPENDENCY: The full name of the checker FULLNAME depends on. 161 OS << "\n" 162 "#ifdef GET_CHECKER_DEPENDENCIES\n"; 163 for (const Record *checker : checkers) { 164 if (checker->isValueUnset("Dependencies")) 165 continue; 166 167 for (const Record *Dependency : 168 checker->getValueAsListOfDefs("Dependencies")) { 169 OS << "CHECKER_DEPENDENCY("; 170 OS << '\"'; 171 OS.write_escaped(getCheckerFullName(checker)) << "\", "; 172 OS << '\"'; 173 OS.write_escaped(getCheckerFullName(Dependency)) << '\"'; 174 OS << ")\n"; 175 } 176 } 177 OS << "\n" 178 "#endif // GET_CHECKER_DEPENDENCIES\n"; 179 } 180 } // end namespace clang 181