ClangSACheckersEmitter.cpp revision 355940
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 20using namespace llvm; 21 22//===----------------------------------------------------------------------===// 23// Static Analyzer Checkers Tables generation 24//===----------------------------------------------------------------------===// 25 26static std::string getPackageFullName(const Record *R); 27 28static 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 35static 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 44static 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 53static 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 60static 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 75static 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/// Retrieves the type from a CmdOptionTypeEnum typed Record object. Note that 94/// the class itself has to be modified for adding a new option type in 95/// CheckerBase.td. 96static std::string getCheckerOptionType(const Record &R) { 97 if (BitsInit *BI = R.getValueAsBitsInit("Type")) { 98 switch(getValueFromBitsInit(BI, R)) { 99 case 0: 100 return "int"; 101 case 1: 102 return "string"; 103 case 2: 104 return "bool"; 105 } 106 } 107 PrintFatalError(R.getLoc(), 108 "unable to parse command line option type for " 109 + getCheckerFullName(&R)); 110 return ""; 111} 112 113static std::string getDevelopmentStage(const Record &R) { 114 if (BitsInit *BI = R.getValueAsBitsInit("DevelopmentStage")) { 115 switch(getValueFromBitsInit(BI, R)) { 116 case 0: 117 return "alpha"; 118 case 1: 119 return "released"; 120 } 121 } 122 123 PrintFatalError(R.getLoc(), 124 "unable to parse command line option type for " 125 + getCheckerFullName(&R)); 126 return ""; 127} 128 129static bool isHidden(const Record *R) { 130 if (R->getValueAsBit("Hidden")) 131 return true; 132 133 // Not declared as hidden, check the parent package if it is hidden. 134 if (DefInit *DI = dyn_cast<DefInit>(R->getValueInit("ParentPackage"))) 135 return isHidden(DI->getDef()); 136 137 return false; 138} 139 140static void printChecker(llvm::raw_ostream &OS, const Record &R) { 141 OS << "CHECKER(" << "\""; 142 OS.write_escaped(getCheckerFullName(&R)) << "\", "; 143 OS << R.getName() << ", "; 144 OS << "\""; 145 OS.write_escaped(getStringValue(R, "HelpText")) << "\", "; 146 OS << "\""; 147 OS.write_escaped(getCheckerDocs(R)); 148 OS << "\", "; 149 150 if (!isHidden(&R)) 151 OS << "false"; 152 else 153 OS << "true"; 154 155 OS << ")\n"; 156} 157 158static void printOption(llvm::raw_ostream &OS, StringRef FullName, 159 const Record &R) { 160 OS << "\""; 161 OS.write_escaped(getCheckerOptionType(R)) << "\", \""; 162 OS.write_escaped(FullName) << "\", "; 163 OS << '\"' << getStringValue(R, "CmdFlag") << "\", "; 164 OS << '\"'; 165 OS.write_escaped(getStringValue(R, "Desc")) << "\", "; 166 OS << '\"'; 167 OS.write_escaped(getStringValue(R, "DefaultVal")) << "\", "; 168 OS << '\"'; 169 OS << getDevelopmentStage(R) << "\", "; 170 171 if (!R.getValueAsBit("Hidden")) 172 OS << "false"; 173 else 174 OS << "true"; 175} 176 177namespace clang { 178void EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) { 179 std::vector<Record*> checkers = Records.getAllDerivedDefinitions("Checker"); 180 std::vector<Record*> packages = Records.getAllDerivedDefinitions("Package"); 181 182 using SortedRecords = llvm::StringMap<const Record *>; 183 184 OS << "// This file is automatically generated. Do not edit this file by " 185 "hand.\n"; 186 187 // Emit packages. 188 // 189 // PACKAGE(PACKAGENAME) 190 // - PACKAGENAME: The name of the package. 191 OS << "\n" 192 "#ifdef GET_PACKAGES\n"; 193 { 194 SortedRecords sortedPackages; 195 for (unsigned i = 0, e = packages.size(); i != e; ++i) 196 sortedPackages[getPackageFullName(packages[i])] = packages[i]; 197 198 for (SortedRecords::iterator 199 I = sortedPackages.begin(), E = sortedPackages.end(); I != E; ++I) { 200 const Record &R = *I->second; 201 202 OS << "PACKAGE(" << "\""; 203 OS.write_escaped(getPackageFullName(&R)) << '\"'; 204 OS << ")\n"; 205 } 206 } 207 OS << "#endif // GET_PACKAGES\n" 208 "\n"; 209 210 // Emit a package option. 211 // 212 // PACKAGE_OPTION(OPTIONTYPE, PACKAGENAME, OPTIONNAME, DESCRIPTION, DEFAULT) 213 // - OPTIONTYPE: Type of the option, whether it's integer or boolean etc. 214 // This is important for validating user input. Note that 215 // it's a string, rather than an actual type: since we can 216 // load checkers runtime, we can't use template hackery for 217 // sorting this out compile-time. 218 // - PACKAGENAME: Name of the package. 219 // - OPTIONNAME: Name of the option. 220 // - DESCRIPTION 221 // - DEFAULT: The default value for this option. 222 // 223 // The full option can be specified in the command like like this: 224 // -analyzer-config PACKAGENAME:OPTIONNAME=VALUE 225 OS << "\n" 226 "#ifdef GET_PACKAGE_OPTIONS\n"; 227 for (const Record *Package : packages) { 228 229 if (Package->isValueUnset("PackageOptions")) 230 continue; 231 232 std::vector<Record *> PackageOptions = Package 233 ->getValueAsListOfDefs("PackageOptions"); 234 for (Record *PackageOpt : PackageOptions) { 235 OS << "PACKAGE_OPTION("; 236 printOption(OS, getPackageFullName(Package), *PackageOpt); 237 OS << ")\n"; 238 } 239 } 240 OS << "#endif // GET_PACKAGE_OPTIONS\n" 241 "\n"; 242 243 // Emit checkers. 244 // 245 // CHECKER(FULLNAME, CLASS, HELPTEXT) 246 // - FULLNAME: The full name of the checker, including packages, e.g.: 247 // alpha.cplusplus.UninitializedObject 248 // - CLASS: The name of the checker, with "Checker" appended, e.g.: 249 // UninitializedObjectChecker 250 // - HELPTEXT: The description of the checker. 251 OS << "\n" 252 "#ifdef GET_CHECKERS\n" 253 "\n"; 254 for (const Record *checker : checkers) { 255 printChecker(OS, *checker); 256 } 257 OS << "\n" 258 "#endif // GET_CHECKERS\n" 259 "\n"; 260 261 // Emit dependencies. 262 // 263 // CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY) 264 // - FULLNAME: The full name of the checker that depends on another checker. 265 // - DEPENDENCY: The full name of the checker FULLNAME depends on. 266 OS << "\n" 267 "#ifdef GET_CHECKER_DEPENDENCIES\n"; 268 for (const Record *Checker : checkers) { 269 if (Checker->isValueUnset("Dependencies")) 270 continue; 271 272 for (const Record *Dependency : 273 Checker->getValueAsListOfDefs("Dependencies")) { 274 OS << "CHECKER_DEPENDENCY("; 275 OS << '\"'; 276 OS.write_escaped(getCheckerFullName(Checker)) << "\", "; 277 OS << '\"'; 278 OS.write_escaped(getCheckerFullName(Dependency)) << '\"'; 279 OS << ")\n"; 280 } 281 } 282 OS << "\n" 283 "#endif // GET_CHECKER_DEPENDENCIES\n"; 284 285 // Emit a package option. 286 // 287 // CHECKER_OPTION(OPTIONTYPE, CHECKERNAME, OPTIONNAME, DESCRIPTION, DEFAULT) 288 // - OPTIONTYPE: Type of the option, whether it's integer or boolean etc. 289 // This is important for validating user input. Note that 290 // it's a string, rather than an actual type: since we can 291 // load checkers runtime, we can't use template hackery for 292 // sorting this out compile-time. 293 // - CHECKERNAME: Name of the package. 294 // - OPTIONNAME: Name of the option. 295 // - DESCRIPTION 296 // - DEFAULT: The default value for this option. 297 // 298 // The full option can be specified in the command like like this: 299 // -analyzer-config CHECKERNAME:OPTIONNAME=VALUE 300 OS << "\n" 301 "#ifdef GET_CHECKER_OPTIONS\n"; 302 for (const Record *Checker : checkers) { 303 304 if (Checker->isValueUnset("CheckerOptions")) 305 continue; 306 307 std::vector<Record *> CheckerOptions = Checker 308 ->getValueAsListOfDefs("CheckerOptions"); 309 for (Record *CheckerOpt : CheckerOptions) { 310 OS << "CHECKER_OPTION("; 311 printOption(OS, getCheckerFullName(Checker), *CheckerOpt); 312 OS << ")\n"; 313 } 314 } 315 OS << "#endif // GET_CHECKER_OPTIONS\n" 316 "\n"; 317} 318} // end namespace clang 319