347 lines · cpp
1//===-- ClangSACheckersEmitter.cpp - Generate SA checkers tables ----------===//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-exception6//7//===----------------------------------------------------------------------===//8//9// This tablegen backend emits Clang Static Analyzer checkers tables.10//11//===----------------------------------------------------------------------===//12 13#include "TableGenBackends.h"14#include "llvm/ADT/StringMap.h"15#include "llvm/TableGen/Error.h"16#include "llvm/TableGen/Record.h"17#include "llvm/TableGen/TableGenBackend.h"18#include <string>19 20using namespace llvm;21 22//===----------------------------------------------------------------------===//23// Static Analyzer Checkers Tables generation24//===----------------------------------------------------------------------===//25 26static std::string getPackageFullName(const Record *R, StringRef Sep = ".");27 28static std::string getParentPackageFullName(const Record *R,29 StringRef Sep = ".") {30 if (const DefInit *DI = dyn_cast<DefInit>(R->getValueInit("ParentPackage")))31 return getPackageFullName(DI->getDef(), Sep);32 return "";33}34 35static std::string getPackageFullName(const Record *R, StringRef Sep) {36 std::string name = getParentPackageFullName(R, Sep);37 if (!name.empty())38 name += Sep;39 assert(!R->getValueAsString("PackageName").empty());40 name += R->getValueAsString("PackageName");41 return name;42}43 44static std::string getCheckerFullName(const Record *R, StringRef Sep = ".") {45 std::string name = getParentPackageFullName(R, Sep);46 if (!name.empty())47 name += Sep;48 assert(!R->getValueAsString("CheckerName").empty());49 name += R->getValueAsString("CheckerName");50 return name;51}52 53static StringRef getStringValue(const Record &R, StringRef field) {54 if (const StringInit *SI = dyn_cast<StringInit>(R.getValueInit(field)))55 return SI->getValue();56 return "";57}58 59// Calculates the integer value representing the BitsInit object60static 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 else69 PrintFatalError(R.getLoc(),70 "missing Documentation for " + getCheckerFullName(&R));71 }72 return Value;73}74 75static std::string getCheckerDocs(const Record &R) {76 const BitsInit *BI = R.getValueAsBitsInit("Documentation");77 if (!BI)78 PrintFatalError(R.getLoc(), "missing Documentation<...> member for " +79 getCheckerFullName(&R));80 81 // Ignore 'Documentation<NotDocumented>' checkers.82 if (getValueFromBitsInit(BI, R) == 0)83 return "";84 85 std::string CheckerFullName = StringRef(getCheckerFullName(&R, "-")).lower();86 return (Twine("https://clang.llvm.org/docs/analyzer/checkers.html#") +87 CheckerFullName)88 .str();89}90 91/// Retrieves the type from a CmdOptionTypeEnum typed Record object. Note that92/// the class itself has to be modified for adding a new option type in93/// CheckerBase.td.94static StringRef getCheckerOptionType(const Record &R) {95 if (const BitsInit *BI = R.getValueAsBitsInit("Type")) {96 switch(getValueFromBitsInit(BI, R)) {97 case 0:98 return "int";99 case 1:100 return "string";101 case 2:102 return "bool";103 }104 }105 PrintFatalError(R.getLoc(),106 "unable to parse command line option type for "107 + getCheckerFullName(&R));108 return "";109}110 111static StringRef getDevelopmentStage(const Record &R) {112 if (const BitsInit *BI = R.getValueAsBitsInit("DevelopmentStage")) {113 switch(getValueFromBitsInit(BI, R)) {114 case 0:115 return "alpha";116 case 1:117 return "released";118 }119 }120 121 PrintFatalError(R.getLoc(),122 "unable to parse command line option type for "123 + getCheckerFullName(&R));124 return "";125}126 127static bool isHidden(const Record *R) {128 if (R->getValueAsBit("Hidden"))129 return true;130 131 // Not declared as hidden, check the parent package if it is hidden.132 if (const DefInit *DI = dyn_cast<DefInit>(R->getValueInit("ParentPackage")))133 return isHidden(DI->getDef());134 135 return false;136}137 138static void printChecker(raw_ostream &OS, const Record &R) {139 OS << "CHECKER(" << "\"";140 OS.write_escaped(getCheckerFullName(&R)) << "\", ";141 OS << R.getName() << ", ";142 OS << "\"";143 OS.write_escaped(getStringValue(R, "HelpText")) << "\", ";144 OS << "\"";145 OS.write_escaped(getCheckerDocs(R));146 OS << "\", ";147 148 if (!isHidden(&R))149 OS << "false";150 else151 OS << "true";152 153 OS << ")\n";154}155 156static void printOption(raw_ostream &OS, StringRef FullName, const Record &R) {157 OS << "\"";158 OS.write_escaped(getCheckerOptionType(R)) << "\", \"";159 OS.write_escaped(FullName) << "\", ";160 OS << '\"' << getStringValue(R, "CmdFlag") << "\", ";161 OS << '\"';162 OS.write_escaped(getStringValue(R, "Desc")) << "\", ";163 OS << '\"';164 OS.write_escaped(getStringValue(R, "DefaultVal")) << "\", ";165 OS << '\"';166 OS << getDevelopmentStage(R) << "\", ";167 168 if (!R.getValueAsBit("Hidden"))169 OS << "false";170 else171 OS << "true";172}173 174void clang::EmitClangSACheckers(const RecordKeeper &Records, raw_ostream &OS) {175 ArrayRef<const Record *> checkers =176 Records.getAllDerivedDefinitions("Checker");177 ArrayRef<const Record *> packages =178 Records.getAllDerivedDefinitions("Package");179 180 OS << "// This file is automatically generated. Do not edit this file by "181 "hand.\n";182 183 // Emit packages.184 //185 // PACKAGE(PACKAGENAME)186 // - PACKAGENAME: The name of the package.187 OS << "\n"188 "#ifdef GET_PACKAGES\n";189 {190 StringMap<const Record *> sortedPackages;191 for (const Record *Package : packages)192 sortedPackages[getPackageFullName(Package)] = Package;193 194 for (const auto &[_, R] : sortedPackages) {195 OS << "PACKAGE(" << "\"";196 OS.write_escaped(getPackageFullName(R)) << '\"';197 OS << ")\n";198 }199 }200 OS << "#endif // GET_PACKAGES\n"201 "\n";202 203 // Emit a package option.204 //205 // PACKAGE_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL,206 // DEVELOPMENT_STATUS, IS_HIDDEN)207 // - TYPE: Type of the option, whether it's integer or boolean etc.208 // This is important for validating user input. Note that209 // it's a string, rather than an actual type: since we can210 // load checkers runtime, we can't use template hackery for211 // sorting this out compile-time.212 // - FULLNAME: Name of the package.213 // - CMDFLAG: Name of the option.214 // - DESC: The description of the option.215 // - DEFAULT_VAL: The default value for this option as a string.216 // - DEVELOPMENT_STATUS: "released" or "alpha".217 // - IS_HIDDEN: Boolean (true or false) marking if the option is hidden.218 //219 // The full option can be specified in the command like this:220 // -analyzer-config FULLNAME:CMDFLAG=VALUE221 OS << "\n"222 "#ifdef GET_PACKAGE_OPTIONS\n";223 for (const Record *Package : packages) {224 if (Package->isValueUnset("PackageOptions"))225 continue;226 227 for (const Record *PackageOpt :228 Package->getValueAsListOfDefs("PackageOptions")) {229 OS << "PACKAGE_OPTION(";230 printOption(OS, getPackageFullName(Package), *PackageOpt);231 OS << ")\n";232 }233 }234 OS << "#endif // GET_PACKAGE_OPTIONS\n"235 "\n";236 237 // Emit checkers.238 //239 // CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN)240 // - FULLNAME: The full name of the checker, including packages, e.g.:241 // alpha.cplusplus.UninitializedObject242 // - CLASS: The common ending of the name of the register... and243 // shouldRegister... functions. Traditionally coincides with the244 // name of the class that implements the checker, but can be245 // anything else e.g. in checker families.246 // - HELPTEXT: The description of the checker.247 // - DOC_URI: If the checker is NotDocumented, an empty string; if the248 // checker HasDocumentation, the concatenation of the fixed URL249 // https://clang.llvm.org/docs/analyzer/checkers.html# and250 // a html id derived from the full name by writing '-' instead251 // of '.' and lowercasing.252 // - IS_HIDDEN: Boolean (true or false) marking if the checker is hidden.253 254 OS << "\n"255 "#ifdef GET_CHECKERS\n"256 "\n";257 for (const Record *checker : checkers)258 printChecker(OS, *checker);259 260 OS << "\n"261 "#endif // GET_CHECKERS\n"262 "\n";263 264 // Emit dependencies.265 //266 // CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY)267 // - FULLNAME: The full name of the checker that depends on another checker.268 // - DEPENDENCY: The full name of the checker FULLNAME depends on.269 OS << "\n"270 "#ifdef GET_CHECKER_DEPENDENCIES\n";271 for (const Record *Checker : checkers) {272 if (Checker->isValueUnset("Dependencies"))273 continue;274 275 for (const Record *Dependency :276 Checker->getValueAsListOfDefs("Dependencies")) {277 OS << "CHECKER_DEPENDENCY(";278 OS << '\"';279 OS.write_escaped(getCheckerFullName(Checker)) << "\", ";280 OS << '\"';281 OS.write_escaped(getCheckerFullName(Dependency)) << '\"';282 OS << ")\n";283 }284 }285 OS << "\n"286 "#endif // GET_CHECKER_DEPENDENCIES\n";287 288 // Emit weak dependencies.289 //290 // CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY)291 // - FULLNAME: The full name of the checker that is supposed to be292 // registered first.293 // - DEPENDENCY: The full name of the checker FULLNAME weak depends on.294 OS << "\n"295 "#ifdef GET_CHECKER_WEAK_DEPENDENCIES\n";296 for (const Record *Checker : checkers) {297 if (Checker->isValueUnset("WeakDependencies"))298 continue;299 300 for (const Record *Dependency :301 Checker->getValueAsListOfDefs("WeakDependencies")) {302 OS << "CHECKER_WEAK_DEPENDENCY(";303 OS << '\"';304 OS.write_escaped(getCheckerFullName(Checker)) << "\", ";305 OS << '\"';306 OS.write_escaped(getCheckerFullName(Dependency)) << '\"';307 OS << ")\n";308 }309 }310 OS << "\n"311 "#endif // GET_CHECKER_WEAK_DEPENDENCIES\n";312 313 // Emit a package option.314 //315 // CHECKER_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL,316 // DEVELOPMENT_STATUS, IS_HIDDEN)317 // - TYPE: Type of the option, whether it's integer or boolean etc.318 // This is important for validating user input. Note that319 // it's a string, rather than an actual type: since we can320 // load checkers runtime, we can't use template hackery for321 // sorting this out compile-time.322 // - FULLNAME: Name of the checker.323 // - CMDFLAG: Name of the option.324 // - DESC: The description of the option.325 // - DEFAULT_VAL: The default value for this option as a string.326 // - DEVELOPMENT_STATUS: "released" or "alpha".327 // - IS_HIDDEN: Boolean (true or false) marking if the option is hidden.328 //329 // The full option can be specified in the command like this:330 // -analyzer-config FULLNAME:CMDFLAG=VALUE331 OS << "\n"332 "#ifdef GET_CHECKER_OPTIONS\n";333 for (const Record *Checker : checkers) {334 if (Checker->isValueUnset("CheckerOptions"))335 continue;336 337 for (const Record *CheckerOpt :338 Checker->getValueAsListOfDefs("CheckerOptions")) {339 OS << "CHECKER_OPTION(";340 printOption(OS, getCheckerFullName(Checker), *CheckerOpt);341 OS << ")\n";342 }343 }344 OS << "#endif // GET_CHECKER_OPTIONS\n"345 "\n";346}347