Path: blob/main/contrib/llvm-project/clang/utils/TableGen/ClangSACheckersEmitter.cpp
35231 views
//=- ClangSACheckersEmitter.cpp - Generate Clang SA checkers tables -*- C++ -*-1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This tablegen backend emits Clang Static Analyzer checkers tables.9//10//===----------------------------------------------------------------------===//1112#include "TableGenBackends.h"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>1920using namespace llvm;2122//===----------------------------------------------------------------------===//23// Static Analyzer Checkers Tables generation24//===----------------------------------------------------------------------===//2526static std::string getPackageFullName(const Record *R, StringRef Sep = ".");2728static std::string getParentPackageFullName(const Record *R,29StringRef Sep = ".") {30std::string name;31if (DefInit *DI = dyn_cast<DefInit>(R->getValueInit("ParentPackage")))32name = getPackageFullName(DI->getDef(), Sep);33return name;34}3536static std::string getPackageFullName(const Record *R, StringRef Sep) {37std::string name = getParentPackageFullName(R, Sep);38if (!name.empty())39name += Sep;40assert(!R->getValueAsString("PackageName").empty());41name += R->getValueAsString("PackageName");42return name;43}4445static std::string getCheckerFullName(const Record *R, StringRef Sep = ".") {46std::string name = getParentPackageFullName(R, Sep);47if (!name.empty())48name += Sep;49assert(!R->getValueAsString("CheckerName").empty());50name += R->getValueAsString("CheckerName");51return name;52}5354static std::string getStringValue(const Record &R, StringRef field) {55if (StringInit *SI = dyn_cast<StringInit>(R.getValueInit(field)))56return std::string(SI->getValue());57return std::string();58}5960// Calculates the integer value representing the BitsInit object61static inline uint64_t getValueFromBitsInit(const BitsInit *B, const Record &R) {62assert(B->getNumBits() <= sizeof(uint64_t) * 8 && "BitInits' too long!");6364uint64_t Value = 0;65for (unsigned i = 0, e = B->getNumBits(); i != e; ++i) {66const auto *Bit = dyn_cast<BitInit>(B->getBit(i));67if (Bit)68Value |= uint64_t(Bit->getValue()) << i;69else70PrintFatalError(R.getLoc(),71"missing Documentation for " + getCheckerFullName(&R));72}73return Value;74}7576static std::string getCheckerDocs(const Record &R) {77const BitsInit *BI = R.getValueAsBitsInit("Documentation");78if (!BI)79PrintFatalError(R.getLoc(), "missing Documentation<...> member for " +80getCheckerFullName(&R));8182// Ignore 'Documentation<NotDocumented>' checkers.83if (getValueFromBitsInit(BI, R) == 0)84return "";8586std::string CheckerFullName = StringRef(getCheckerFullName(&R, "-")).lower();87return (llvm::Twine("https://clang.llvm.org/docs/analyzer/checkers.html#") +88CheckerFullName)89.str();90}9192/// Retrieves the type from a CmdOptionTypeEnum typed Record object. Note that93/// the class itself has to be modified for adding a new option type in94/// CheckerBase.td.95static std::string getCheckerOptionType(const Record &R) {96if (BitsInit *BI = R.getValueAsBitsInit("Type")) {97switch(getValueFromBitsInit(BI, R)) {98case 0:99return "int";100case 1:101return "string";102case 2:103return "bool";104}105}106PrintFatalError(R.getLoc(),107"unable to parse command line option type for "108+ getCheckerFullName(&R));109return "";110}111112static std::string getDevelopmentStage(const Record &R) {113if (BitsInit *BI = R.getValueAsBitsInit("DevelopmentStage")) {114switch(getValueFromBitsInit(BI, R)) {115case 0:116return "alpha";117case 1:118return "released";119}120}121122PrintFatalError(R.getLoc(),123"unable to parse command line option type for "124+ getCheckerFullName(&R));125return "";126}127128static bool isHidden(const Record *R) {129if (R->getValueAsBit("Hidden"))130return true;131132// Not declared as hidden, check the parent package if it is hidden.133if (DefInit *DI = dyn_cast<DefInit>(R->getValueInit("ParentPackage")))134return isHidden(DI->getDef());135136return false;137}138139static void printChecker(llvm::raw_ostream &OS, const Record &R) {140OS << "CHECKER(" << "\"";141OS.write_escaped(getCheckerFullName(&R)) << "\", ";142OS << R.getName() << ", ";143OS << "\"";144OS.write_escaped(getStringValue(R, "HelpText")) << "\", ";145OS << "\"";146OS.write_escaped(getCheckerDocs(R));147OS << "\", ";148149if (!isHidden(&R))150OS << "false";151else152OS << "true";153154OS << ")\n";155}156157static void printOption(llvm::raw_ostream &OS, StringRef FullName,158const Record &R) {159OS << "\"";160OS.write_escaped(getCheckerOptionType(R)) << "\", \"";161OS.write_escaped(FullName) << "\", ";162OS << '\"' << getStringValue(R, "CmdFlag") << "\", ";163OS << '\"';164OS.write_escaped(getStringValue(R, "Desc")) << "\", ";165OS << '\"';166OS.write_escaped(getStringValue(R, "DefaultVal")) << "\", ";167OS << '\"';168OS << getDevelopmentStage(R) << "\", ";169170if (!R.getValueAsBit("Hidden"))171OS << "false";172else173OS << "true";174}175176void clang::EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) {177std::vector<Record*> checkers = Records.getAllDerivedDefinitions("Checker");178std::vector<Record*> packages = Records.getAllDerivedDefinitions("Package");179180using SortedRecords = llvm::StringMap<const Record *>;181182OS << "// This file is automatically generated. Do not edit this file by "183"hand.\n";184185// Emit packages.186//187// PACKAGE(PACKAGENAME)188// - PACKAGENAME: The name of the package.189OS << "\n"190"#ifdef GET_PACKAGES\n";191{192SortedRecords sortedPackages;193for (unsigned i = 0, e = packages.size(); i != e; ++i)194sortedPackages[getPackageFullName(packages[i])] = packages[i];195196for (SortedRecords::iterator197I = sortedPackages.begin(), E = sortedPackages.end(); I != E; ++I) {198const Record &R = *I->second;199200OS << "PACKAGE(" << "\"";201OS.write_escaped(getPackageFullName(&R)) << '\"';202OS << ")\n";203}204}205OS << "#endif // GET_PACKAGES\n"206"\n";207208// Emit a package option.209//210// PACKAGE_OPTION(OPTIONTYPE, PACKAGENAME, OPTIONNAME, DESCRIPTION, DEFAULT)211// - OPTIONTYPE: Type of the option, whether it's integer or boolean etc.212// This is important for validating user input. Note that213// it's a string, rather than an actual type: since we can214// load checkers runtime, we can't use template hackery for215// sorting this out compile-time.216// - PACKAGENAME: Name of the package.217// - OPTIONNAME: Name of the option.218// - DESCRIPTION219// - DEFAULT: The default value for this option.220//221// The full option can be specified in the command like this:222// -analyzer-config PACKAGENAME:OPTIONNAME=VALUE223OS << "\n"224"#ifdef GET_PACKAGE_OPTIONS\n";225for (const Record *Package : packages) {226227if (Package->isValueUnset("PackageOptions"))228continue;229230std::vector<Record *> PackageOptions = Package231->getValueAsListOfDefs("PackageOptions");232for (Record *PackageOpt : PackageOptions) {233OS << "PACKAGE_OPTION(";234printOption(OS, getPackageFullName(Package), *PackageOpt);235OS << ")\n";236}237}238OS << "#endif // GET_PACKAGE_OPTIONS\n"239"\n";240241// Emit checkers.242//243// CHECKER(FULLNAME, CLASS, HELPTEXT)244// - FULLNAME: The full name of the checker, including packages, e.g.:245// alpha.cplusplus.UninitializedObject246// - CLASS: The name of the checker, with "Checker" appended, e.g.:247// UninitializedObjectChecker248// - HELPTEXT: The description of the checker.249OS << "\n"250"#ifdef GET_CHECKERS\n"251"\n";252for (const Record *checker : checkers) {253printChecker(OS, *checker);254}255OS << "\n"256"#endif // GET_CHECKERS\n"257"\n";258259// Emit dependencies.260//261// CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY)262// - FULLNAME: The full name of the checker that depends on another checker.263// - DEPENDENCY: The full name of the checker FULLNAME depends on.264OS << "\n"265"#ifdef GET_CHECKER_DEPENDENCIES\n";266for (const Record *Checker : checkers) {267if (Checker->isValueUnset("Dependencies"))268continue;269270for (const Record *Dependency :271Checker->getValueAsListOfDefs("Dependencies")) {272OS << "CHECKER_DEPENDENCY(";273OS << '\"';274OS.write_escaped(getCheckerFullName(Checker)) << "\", ";275OS << '\"';276OS.write_escaped(getCheckerFullName(Dependency)) << '\"';277OS << ")\n";278}279}280OS << "\n"281"#endif // GET_CHECKER_DEPENDENCIES\n";282283// Emit weak dependencies.284//285// CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY)286// - FULLNAME: The full name of the checker that is supposed to be287// registered first.288// - DEPENDENCY: The full name of the checker FULLNAME weak depends on.289OS << "\n"290"#ifdef GET_CHECKER_WEAK_DEPENDENCIES\n";291for (const Record *Checker : checkers) {292if (Checker->isValueUnset("WeakDependencies"))293continue;294295for (const Record *Dependency :296Checker->getValueAsListOfDefs("WeakDependencies")) {297OS << "CHECKER_WEAK_DEPENDENCY(";298OS << '\"';299OS.write_escaped(getCheckerFullName(Checker)) << "\", ";300OS << '\"';301OS.write_escaped(getCheckerFullName(Dependency)) << '\"';302OS << ")\n";303}304}305OS << "\n"306"#endif // GET_CHECKER_WEAK_DEPENDENCIES\n";307308// Emit a package option.309//310// CHECKER_OPTION(OPTIONTYPE, CHECKERNAME, OPTIONNAME, DESCRIPTION, DEFAULT)311// - OPTIONTYPE: Type of the option, whether it's integer or boolean etc.312// This is important for validating user input. Note that313// it's a string, rather than an actual type: since we can314// load checkers runtime, we can't use template hackery for315// sorting this out compile-time.316// - CHECKERNAME: Name of the package.317// - OPTIONNAME: Name of the option.318// - DESCRIPTION319// - DEFAULT: The default value for this option.320//321// The full option can be specified in the command like this:322// -analyzer-config CHECKERNAME:OPTIONNAME=VALUE323OS << "\n"324"#ifdef GET_CHECKER_OPTIONS\n";325for (const Record *Checker : checkers) {326327if (Checker->isValueUnset("CheckerOptions"))328continue;329330std::vector<Record *> CheckerOptions = Checker331->getValueAsListOfDefs("CheckerOptions");332for (Record *CheckerOpt : CheckerOptions) {333OS << "CHECKER_OPTION(";334printOption(OS, getCheckerFullName(Checker), *CheckerOpt);335OS << ")\n";336}337}338OS << "#endif // GET_CHECKER_OPTIONS\n"339"\n";340}341342343