1 1.1 joerg //=- ClangSACheckersEmitter.cpp - Generate Clang SA checkers tables -*- C++ -*- 2 1.1 joerg // 3 1.1 joerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 1.1 joerg // See https://llvm.org/LICENSE.txt for license information. 5 1.1 joerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 1.1 joerg // 7 1.1 joerg //===----------------------------------------------------------------------===// 8 1.1 joerg // 9 1.1 joerg // This tablegen backend emits Clang Static Analyzer checkers tables. 10 1.1 joerg // 11 1.1 joerg //===----------------------------------------------------------------------===// 12 1.1 joerg 13 1.1 joerg #include "TableGenBackends.h" 14 1.1 joerg #include "llvm/ADT/StringMap.h" 15 1.1 joerg #include "llvm/TableGen/Error.h" 16 1.1 joerg #include "llvm/TableGen/Record.h" 17 1.1 joerg #include "llvm/TableGen/TableGenBackend.h" 18 1.1 joerg #include <map> 19 1.1 joerg #include <string> 20 1.1 joerg 21 1.1 joerg using namespace llvm; 22 1.1 joerg 23 1.1 joerg //===----------------------------------------------------------------------===// 24 1.1 joerg // Static Analyzer Checkers Tables generation 25 1.1 joerg //===----------------------------------------------------------------------===// 26 1.1 joerg 27 1.1 joerg static std::string getPackageFullName(const Record *R); 28 1.1 joerg 29 1.1 joerg static std::string getParentPackageFullName(const Record *R) { 30 1.1 joerg std::string name; 31 1.1 joerg if (DefInit *DI = dyn_cast<DefInit>(R->getValueInit("ParentPackage"))) 32 1.1 joerg name = getPackageFullName(DI->getDef()); 33 1.1 joerg return name; 34 1.1 joerg } 35 1.1 joerg 36 1.1 joerg static std::string getPackageFullName(const Record *R) { 37 1.1 joerg std::string name = getParentPackageFullName(R); 38 1.1 joerg if (!name.empty()) 39 1.1 joerg name += "."; 40 1.1 joerg assert(!R->getValueAsString("PackageName").empty()); 41 1.1 joerg name += R->getValueAsString("PackageName"); 42 1.1 joerg return name; 43 1.1 joerg } 44 1.1 joerg 45 1.1 joerg static std::string getCheckerFullName(const Record *R) { 46 1.1 joerg std::string name = getParentPackageFullName(R); 47 1.1 joerg if (!name.empty()) 48 1.1 joerg name += "."; 49 1.1 joerg assert(!R->getValueAsString("CheckerName").empty()); 50 1.1 joerg name += R->getValueAsString("CheckerName"); 51 1.1 joerg return name; 52 1.1 joerg } 53 1.1 joerg 54 1.1 joerg static std::string getStringValue(const Record &R, StringRef field) { 55 1.1 joerg if (StringInit *SI = dyn_cast<StringInit>(R.getValueInit(field))) 56 1.1.1.2 joerg return std::string(SI->getValue()); 57 1.1 joerg return std::string(); 58 1.1 joerg } 59 1.1 joerg 60 1.1 joerg // Calculates the integer value representing the BitsInit object 61 1.1 joerg static inline uint64_t getValueFromBitsInit(const BitsInit *B, const Record &R) { 62 1.1 joerg assert(B->getNumBits() <= sizeof(uint64_t) * 8 && "BitInits' too long!"); 63 1.1 joerg 64 1.1 joerg uint64_t Value = 0; 65 1.1 joerg for (unsigned i = 0, e = B->getNumBits(); i != e; ++i) { 66 1.1 joerg const auto *Bit = dyn_cast<BitInit>(B->getBit(i)); 67 1.1 joerg if (Bit) 68 1.1 joerg Value |= uint64_t(Bit->getValue()) << i; 69 1.1 joerg else 70 1.1 joerg PrintFatalError(R.getLoc(), 71 1.1 joerg "missing Documentation for " + getCheckerFullName(&R)); 72 1.1 joerg } 73 1.1 joerg return Value; 74 1.1 joerg } 75 1.1 joerg 76 1.1 joerg static std::string getCheckerDocs(const Record &R) { 77 1.1 joerg StringRef LandingPage; 78 1.1 joerg if (BitsInit *BI = R.getValueAsBitsInit("Documentation")) { 79 1.1 joerg uint64_t V = getValueFromBitsInit(BI, R); 80 1.1 joerg if (V == 1) 81 1.1 joerg LandingPage = "available_checks.html"; 82 1.1 joerg else if (V == 2) 83 1.1 joerg LandingPage = "alpha_checks.html"; 84 1.1 joerg } 85 1.1 joerg 86 1.1 joerg if (LandingPage.empty()) 87 1.1 joerg return ""; 88 1.1 joerg 89 1.1 joerg return (llvm::Twine("https://clang-analyzer.llvm.org/") + LandingPage + "#" + 90 1.1 joerg getCheckerFullName(&R)) 91 1.1 joerg .str(); 92 1.1 joerg } 93 1.1 joerg 94 1.1 joerg /// Retrieves the type from a CmdOptionTypeEnum typed Record object. Note that 95 1.1 joerg /// the class itself has to be modified for adding a new option type in 96 1.1 joerg /// CheckerBase.td. 97 1.1 joerg static std::string getCheckerOptionType(const Record &R) { 98 1.1 joerg if (BitsInit *BI = R.getValueAsBitsInit("Type")) { 99 1.1 joerg switch(getValueFromBitsInit(BI, R)) { 100 1.1 joerg case 0: 101 1.1 joerg return "int"; 102 1.1 joerg case 1: 103 1.1 joerg return "string"; 104 1.1 joerg case 2: 105 1.1 joerg return "bool"; 106 1.1 joerg } 107 1.1 joerg } 108 1.1 joerg PrintFatalError(R.getLoc(), 109 1.1 joerg "unable to parse command line option type for " 110 1.1 joerg + getCheckerFullName(&R)); 111 1.1 joerg return ""; 112 1.1 joerg } 113 1.1 joerg 114 1.1 joerg static std::string getDevelopmentStage(const Record &R) { 115 1.1 joerg if (BitsInit *BI = R.getValueAsBitsInit("DevelopmentStage")) { 116 1.1 joerg switch(getValueFromBitsInit(BI, R)) { 117 1.1 joerg case 0: 118 1.1 joerg return "alpha"; 119 1.1 joerg case 1: 120 1.1 joerg return "released"; 121 1.1 joerg } 122 1.1 joerg } 123 1.1 joerg 124 1.1 joerg PrintFatalError(R.getLoc(), 125 1.1 joerg "unable to parse command line option type for " 126 1.1 joerg + getCheckerFullName(&R)); 127 1.1 joerg return ""; 128 1.1 joerg } 129 1.1 joerg 130 1.1 joerg static bool isHidden(const Record *R) { 131 1.1 joerg if (R->getValueAsBit("Hidden")) 132 1.1 joerg return true; 133 1.1 joerg 134 1.1 joerg // Not declared as hidden, check the parent package if it is hidden. 135 1.1 joerg if (DefInit *DI = dyn_cast<DefInit>(R->getValueInit("ParentPackage"))) 136 1.1 joerg return isHidden(DI->getDef()); 137 1.1 joerg 138 1.1 joerg return false; 139 1.1 joerg } 140 1.1 joerg 141 1.1 joerg static void printChecker(llvm::raw_ostream &OS, const Record &R) { 142 1.1 joerg OS << "CHECKER(" << "\""; 143 1.1 joerg OS.write_escaped(getCheckerFullName(&R)) << "\", "; 144 1.1 joerg OS << R.getName() << ", "; 145 1.1 joerg OS << "\""; 146 1.1 joerg OS.write_escaped(getStringValue(R, "HelpText")) << "\", "; 147 1.1 joerg OS << "\""; 148 1.1 joerg OS.write_escaped(getCheckerDocs(R)); 149 1.1 joerg OS << "\", "; 150 1.1 joerg 151 1.1 joerg if (!isHidden(&R)) 152 1.1 joerg OS << "false"; 153 1.1 joerg else 154 1.1 joerg OS << "true"; 155 1.1 joerg 156 1.1 joerg OS << ")\n"; 157 1.1 joerg } 158 1.1 joerg 159 1.1 joerg static void printOption(llvm::raw_ostream &OS, StringRef FullName, 160 1.1 joerg const Record &R) { 161 1.1 joerg OS << "\""; 162 1.1 joerg OS.write_escaped(getCheckerOptionType(R)) << "\", \""; 163 1.1 joerg OS.write_escaped(FullName) << "\", "; 164 1.1 joerg OS << '\"' << getStringValue(R, "CmdFlag") << "\", "; 165 1.1 joerg OS << '\"'; 166 1.1 joerg OS.write_escaped(getStringValue(R, "Desc")) << "\", "; 167 1.1 joerg OS << '\"'; 168 1.1 joerg OS.write_escaped(getStringValue(R, "DefaultVal")) << "\", "; 169 1.1 joerg OS << '\"'; 170 1.1 joerg OS << getDevelopmentStage(R) << "\", "; 171 1.1 joerg 172 1.1 joerg if (!R.getValueAsBit("Hidden")) 173 1.1 joerg OS << "false"; 174 1.1 joerg else 175 1.1 joerg OS << "true"; 176 1.1 joerg } 177 1.1 joerg 178 1.1 joerg void clang::EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) { 179 1.1 joerg std::vector<Record*> checkers = Records.getAllDerivedDefinitions("Checker"); 180 1.1 joerg std::vector<Record*> packages = Records.getAllDerivedDefinitions("Package"); 181 1.1 joerg 182 1.1 joerg using SortedRecords = llvm::StringMap<const Record *>; 183 1.1 joerg 184 1.1 joerg OS << "// This file is automatically generated. Do not edit this file by " 185 1.1 joerg "hand.\n"; 186 1.1 joerg 187 1.1 joerg // Emit packages. 188 1.1 joerg // 189 1.1 joerg // PACKAGE(PACKAGENAME) 190 1.1 joerg // - PACKAGENAME: The name of the package. 191 1.1 joerg OS << "\n" 192 1.1 joerg "#ifdef GET_PACKAGES\n"; 193 1.1 joerg { 194 1.1 joerg SortedRecords sortedPackages; 195 1.1 joerg for (unsigned i = 0, e = packages.size(); i != e; ++i) 196 1.1 joerg sortedPackages[getPackageFullName(packages[i])] = packages[i]; 197 1.1 joerg 198 1.1 joerg for (SortedRecords::iterator 199 1.1 joerg I = sortedPackages.begin(), E = sortedPackages.end(); I != E; ++I) { 200 1.1 joerg const Record &R = *I->second; 201 1.1 joerg 202 1.1 joerg OS << "PACKAGE(" << "\""; 203 1.1 joerg OS.write_escaped(getPackageFullName(&R)) << '\"'; 204 1.1 joerg OS << ")\n"; 205 1.1 joerg } 206 1.1 joerg } 207 1.1 joerg OS << "#endif // GET_PACKAGES\n" 208 1.1 joerg "\n"; 209 1.1 joerg 210 1.1 joerg // Emit a package option. 211 1.1 joerg // 212 1.1 joerg // PACKAGE_OPTION(OPTIONTYPE, PACKAGENAME, OPTIONNAME, DESCRIPTION, DEFAULT) 213 1.1 joerg // - OPTIONTYPE: Type of the option, whether it's integer or boolean etc. 214 1.1 joerg // This is important for validating user input. Note that 215 1.1 joerg // it's a string, rather than an actual type: since we can 216 1.1 joerg // load checkers runtime, we can't use template hackery for 217 1.1 joerg // sorting this out compile-time. 218 1.1 joerg // - PACKAGENAME: Name of the package. 219 1.1 joerg // - OPTIONNAME: Name of the option. 220 1.1 joerg // - DESCRIPTION 221 1.1 joerg // - DEFAULT: The default value for this option. 222 1.1 joerg // 223 1.1 joerg // The full option can be specified in the command like like this: 224 1.1 joerg // -analyzer-config PACKAGENAME:OPTIONNAME=VALUE 225 1.1 joerg OS << "\n" 226 1.1 joerg "#ifdef GET_PACKAGE_OPTIONS\n"; 227 1.1 joerg for (const Record *Package : packages) { 228 1.1 joerg 229 1.1 joerg if (Package->isValueUnset("PackageOptions")) 230 1.1 joerg continue; 231 1.1 joerg 232 1.1 joerg std::vector<Record *> PackageOptions = Package 233 1.1 joerg ->getValueAsListOfDefs("PackageOptions"); 234 1.1 joerg for (Record *PackageOpt : PackageOptions) { 235 1.1 joerg OS << "PACKAGE_OPTION("; 236 1.1 joerg printOption(OS, getPackageFullName(Package), *PackageOpt); 237 1.1 joerg OS << ")\n"; 238 1.1 joerg } 239 1.1 joerg } 240 1.1 joerg OS << "#endif // GET_PACKAGE_OPTIONS\n" 241 1.1 joerg "\n"; 242 1.1 joerg 243 1.1 joerg // Emit checkers. 244 1.1 joerg // 245 1.1 joerg // CHECKER(FULLNAME, CLASS, HELPTEXT) 246 1.1 joerg // - FULLNAME: The full name of the checker, including packages, e.g.: 247 1.1 joerg // alpha.cplusplus.UninitializedObject 248 1.1 joerg // - CLASS: The name of the checker, with "Checker" appended, e.g.: 249 1.1 joerg // UninitializedObjectChecker 250 1.1 joerg // - HELPTEXT: The description of the checker. 251 1.1 joerg OS << "\n" 252 1.1 joerg "#ifdef GET_CHECKERS\n" 253 1.1 joerg "\n"; 254 1.1 joerg for (const Record *checker : checkers) { 255 1.1 joerg printChecker(OS, *checker); 256 1.1 joerg } 257 1.1 joerg OS << "\n" 258 1.1 joerg "#endif // GET_CHECKERS\n" 259 1.1 joerg "\n"; 260 1.1 joerg 261 1.1 joerg // Emit dependencies. 262 1.1 joerg // 263 1.1 joerg // CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY) 264 1.1 joerg // - FULLNAME: The full name of the checker that depends on another checker. 265 1.1 joerg // - DEPENDENCY: The full name of the checker FULLNAME depends on. 266 1.1 joerg OS << "\n" 267 1.1 joerg "#ifdef GET_CHECKER_DEPENDENCIES\n"; 268 1.1 joerg for (const Record *Checker : checkers) { 269 1.1 joerg if (Checker->isValueUnset("Dependencies")) 270 1.1 joerg continue; 271 1.1 joerg 272 1.1 joerg for (const Record *Dependency : 273 1.1 joerg Checker->getValueAsListOfDefs("Dependencies")) { 274 1.1 joerg OS << "CHECKER_DEPENDENCY("; 275 1.1 joerg OS << '\"'; 276 1.1 joerg OS.write_escaped(getCheckerFullName(Checker)) << "\", "; 277 1.1 joerg OS << '\"'; 278 1.1 joerg OS.write_escaped(getCheckerFullName(Dependency)) << '\"'; 279 1.1 joerg OS << ")\n"; 280 1.1 joerg } 281 1.1 joerg } 282 1.1 joerg OS << "\n" 283 1.1 joerg "#endif // GET_CHECKER_DEPENDENCIES\n"; 284 1.1 joerg 285 1.1.1.2 joerg // Emit weak dependencies. 286 1.1.1.2 joerg // 287 1.1.1.2 joerg // CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY) 288 1.1.1.2 joerg // - FULLNAME: The full name of the checker that is supposed to be 289 1.1.1.2 joerg // registered first. 290 1.1.1.2 joerg // - DEPENDENCY: The full name of the checker FULLNAME weak depends on. 291 1.1.1.2 joerg OS << "\n" 292 1.1.1.2 joerg "#ifdef GET_CHECKER_WEAK_DEPENDENCIES\n"; 293 1.1.1.2 joerg for (const Record *Checker : checkers) { 294 1.1.1.2 joerg if (Checker->isValueUnset("WeakDependencies")) 295 1.1.1.2 joerg continue; 296 1.1.1.2 joerg 297 1.1.1.2 joerg for (const Record *Dependency : 298 1.1.1.2 joerg Checker->getValueAsListOfDefs("WeakDependencies")) { 299 1.1.1.2 joerg OS << "CHECKER_WEAK_DEPENDENCY("; 300 1.1.1.2 joerg OS << '\"'; 301 1.1.1.2 joerg OS.write_escaped(getCheckerFullName(Checker)) << "\", "; 302 1.1.1.2 joerg OS << '\"'; 303 1.1.1.2 joerg OS.write_escaped(getCheckerFullName(Dependency)) << '\"'; 304 1.1.1.2 joerg OS << ")\n"; 305 1.1.1.2 joerg } 306 1.1.1.2 joerg } 307 1.1.1.2 joerg OS << "\n" 308 1.1.1.2 joerg "#endif // GET_CHECKER_WEAK_DEPENDENCIES\n"; 309 1.1.1.2 joerg 310 1.1 joerg // Emit a package option. 311 1.1 joerg // 312 1.1 joerg // CHECKER_OPTION(OPTIONTYPE, CHECKERNAME, OPTIONNAME, DESCRIPTION, DEFAULT) 313 1.1 joerg // - OPTIONTYPE: Type of the option, whether it's integer or boolean etc. 314 1.1 joerg // This is important for validating user input. Note that 315 1.1 joerg // it's a string, rather than an actual type: since we can 316 1.1 joerg // load checkers runtime, we can't use template hackery for 317 1.1 joerg // sorting this out compile-time. 318 1.1 joerg // - CHECKERNAME: Name of the package. 319 1.1 joerg // - OPTIONNAME: Name of the option. 320 1.1 joerg // - DESCRIPTION 321 1.1 joerg // - DEFAULT: The default value for this option. 322 1.1 joerg // 323 1.1 joerg // The full option can be specified in the command like like this: 324 1.1 joerg // -analyzer-config CHECKERNAME:OPTIONNAME=VALUE 325 1.1 joerg OS << "\n" 326 1.1 joerg "#ifdef GET_CHECKER_OPTIONS\n"; 327 1.1 joerg for (const Record *Checker : checkers) { 328 1.1 joerg 329 1.1 joerg if (Checker->isValueUnset("CheckerOptions")) 330 1.1 joerg continue; 331 1.1 joerg 332 1.1 joerg std::vector<Record *> CheckerOptions = Checker 333 1.1 joerg ->getValueAsListOfDefs("CheckerOptions"); 334 1.1 joerg for (Record *CheckerOpt : CheckerOptions) { 335 1.1 joerg OS << "CHECKER_OPTION("; 336 1.1 joerg printOption(OS, getCheckerFullName(Checker), *CheckerOpt); 337 1.1 joerg OS << ")\n"; 338 1.1 joerg } 339 1.1 joerg } 340 1.1 joerg OS << "#endif // GET_CHECKER_OPTIONS\n" 341 1.1 joerg "\n"; 342 1.1 joerg } 343