brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.6 KiB · 8353ef7 Raw
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