xref: /freebsd/contrib/llvm-project/clang/utils/TableGen/ClangSACheckersEmitter.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
10b57cec5SDimitry Andric //=- ClangSACheckersEmitter.cpp - Generate Clang SA checkers tables -*- C++ -*-
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This tablegen backend emits Clang Static Analyzer checkers tables.
100b57cec5SDimitry Andric //
110b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
120b57cec5SDimitry Andric 
13a7dea167SDimitry Andric #include "TableGenBackends.h"
140b57cec5SDimitry Andric #include "llvm/ADT/StringMap.h"
150b57cec5SDimitry Andric #include "llvm/TableGen/Error.h"
160b57cec5SDimitry Andric #include "llvm/TableGen/Record.h"
170b57cec5SDimitry Andric #include "llvm/TableGen/TableGenBackend.h"
180b57cec5SDimitry Andric #include <map>
190b57cec5SDimitry Andric #include <string>
200b57cec5SDimitry Andric 
210b57cec5SDimitry Andric using namespace llvm;
220b57cec5SDimitry Andric 
230b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
240b57cec5SDimitry Andric // Static Analyzer Checkers Tables generation
250b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
260b57cec5SDimitry Andric 
2781ad6265SDimitry Andric static std::string getPackageFullName(const Record *R, StringRef Sep = ".");
280b57cec5SDimitry Andric 
getParentPackageFullName(const Record * R,StringRef Sep=".")2981ad6265SDimitry Andric static std::string getParentPackageFullName(const Record *R,
3081ad6265SDimitry Andric                                             StringRef Sep = ".") {
310b57cec5SDimitry Andric   std::string name;
320b57cec5SDimitry Andric   if (DefInit *DI = dyn_cast<DefInit>(R->getValueInit("ParentPackage")))
3381ad6265SDimitry Andric     name = getPackageFullName(DI->getDef(), Sep);
340b57cec5SDimitry Andric   return name;
350b57cec5SDimitry Andric }
360b57cec5SDimitry Andric 
getPackageFullName(const Record * R,StringRef Sep)3781ad6265SDimitry Andric static std::string getPackageFullName(const Record *R, StringRef Sep) {
3881ad6265SDimitry Andric   std::string name = getParentPackageFullName(R, Sep);
390b57cec5SDimitry Andric   if (!name.empty())
4081ad6265SDimitry Andric     name += Sep;
410b57cec5SDimitry Andric   assert(!R->getValueAsString("PackageName").empty());
420b57cec5SDimitry Andric   name += R->getValueAsString("PackageName");
430b57cec5SDimitry Andric   return name;
440b57cec5SDimitry Andric }
450b57cec5SDimitry Andric 
getCheckerFullName(const Record * R,StringRef Sep=".")4681ad6265SDimitry Andric static std::string getCheckerFullName(const Record *R, StringRef Sep = ".") {
4781ad6265SDimitry Andric   std::string name = getParentPackageFullName(R, Sep);
480b57cec5SDimitry Andric   if (!name.empty())
4981ad6265SDimitry Andric     name += Sep;
500b57cec5SDimitry Andric   assert(!R->getValueAsString("CheckerName").empty());
510b57cec5SDimitry Andric   name += R->getValueAsString("CheckerName");
520b57cec5SDimitry Andric   return name;
530b57cec5SDimitry Andric }
540b57cec5SDimitry Andric 
getStringValue(const Record & R,StringRef field)550b57cec5SDimitry Andric static std::string getStringValue(const Record &R, StringRef field) {
560b57cec5SDimitry Andric   if (StringInit *SI = dyn_cast<StringInit>(R.getValueInit(field)))
575ffd83dbSDimitry Andric     return std::string(SI->getValue());
580b57cec5SDimitry Andric   return std::string();
590b57cec5SDimitry Andric }
600b57cec5SDimitry Andric 
610b57cec5SDimitry Andric // Calculates the integer value representing the BitsInit object
getValueFromBitsInit(const BitsInit * B,const Record & R)620b57cec5SDimitry Andric static inline uint64_t getValueFromBitsInit(const BitsInit *B, const Record &R) {
630b57cec5SDimitry Andric   assert(B->getNumBits() <= sizeof(uint64_t) * 8 && "BitInits' too long!");
640b57cec5SDimitry Andric 
650b57cec5SDimitry Andric   uint64_t Value = 0;
660b57cec5SDimitry Andric   for (unsigned i = 0, e = B->getNumBits(); i != e; ++i) {
670b57cec5SDimitry Andric     const auto *Bit = dyn_cast<BitInit>(B->getBit(i));
680b57cec5SDimitry Andric     if (Bit)
690b57cec5SDimitry Andric       Value |= uint64_t(Bit->getValue()) << i;
700b57cec5SDimitry Andric     else
710b57cec5SDimitry Andric       PrintFatalError(R.getLoc(),
720b57cec5SDimitry Andric                       "missing Documentation for " + getCheckerFullName(&R));
730b57cec5SDimitry Andric   }
740b57cec5SDimitry Andric   return Value;
750b57cec5SDimitry Andric }
760b57cec5SDimitry Andric 
getCheckerDocs(const Record & R)770b57cec5SDimitry Andric static std::string getCheckerDocs(const Record &R) {
7881ad6265SDimitry Andric   const BitsInit *BI = R.getValueAsBitsInit("Documentation");
7981ad6265SDimitry Andric   if (!BI)
8081ad6265SDimitry Andric     PrintFatalError(R.getLoc(), "missing Documentation<...> member for " +
8181ad6265SDimitry Andric                                     getCheckerFullName(&R));
820b57cec5SDimitry Andric 
8381ad6265SDimitry Andric   // Ignore 'Documentation<NotDocumented>' checkers.
8481ad6265SDimitry Andric   if (getValueFromBitsInit(BI, R) == 0)
850b57cec5SDimitry Andric     return "";
860b57cec5SDimitry Andric 
8781ad6265SDimitry Andric   std::string CheckerFullName = StringRef(getCheckerFullName(&R, "-")).lower();
8881ad6265SDimitry Andric   return (llvm::Twine("https://clang.llvm.org/docs/analyzer/checkers.html#") +
8981ad6265SDimitry Andric           CheckerFullName)
900b57cec5SDimitry Andric       .str();
910b57cec5SDimitry Andric }
920b57cec5SDimitry Andric 
930b57cec5SDimitry Andric /// Retrieves the type from a CmdOptionTypeEnum typed Record object. Note that
940b57cec5SDimitry Andric /// the class itself has to be modified for adding a new option type in
950b57cec5SDimitry Andric /// CheckerBase.td.
getCheckerOptionType(const Record & R)960b57cec5SDimitry Andric static std::string getCheckerOptionType(const Record &R) {
970b57cec5SDimitry Andric   if (BitsInit *BI = R.getValueAsBitsInit("Type")) {
980b57cec5SDimitry Andric     switch(getValueFromBitsInit(BI, R)) {
990b57cec5SDimitry Andric     case 0:
1000b57cec5SDimitry Andric       return "int";
1010b57cec5SDimitry Andric     case 1:
1020b57cec5SDimitry Andric       return "string";
1030b57cec5SDimitry Andric     case 2:
1040b57cec5SDimitry Andric       return "bool";
1050b57cec5SDimitry Andric     }
1060b57cec5SDimitry Andric   }
1070b57cec5SDimitry Andric   PrintFatalError(R.getLoc(),
1080b57cec5SDimitry Andric                   "unable to parse command line option type for "
1090b57cec5SDimitry Andric                   + getCheckerFullName(&R));
1100b57cec5SDimitry Andric   return "";
1110b57cec5SDimitry Andric }
1120b57cec5SDimitry Andric 
getDevelopmentStage(const Record & R)1130b57cec5SDimitry Andric static std::string getDevelopmentStage(const Record &R) {
1140b57cec5SDimitry Andric   if (BitsInit *BI = R.getValueAsBitsInit("DevelopmentStage")) {
1150b57cec5SDimitry Andric     switch(getValueFromBitsInit(BI, R)) {
1160b57cec5SDimitry Andric     case 0:
1170b57cec5SDimitry Andric       return "alpha";
1180b57cec5SDimitry Andric     case 1:
1190b57cec5SDimitry Andric       return "released";
1200b57cec5SDimitry Andric     }
1210b57cec5SDimitry Andric   }
1220b57cec5SDimitry Andric 
1230b57cec5SDimitry Andric   PrintFatalError(R.getLoc(),
1240b57cec5SDimitry Andric                   "unable to parse command line option type for "
1250b57cec5SDimitry Andric                   + getCheckerFullName(&R));
1260b57cec5SDimitry Andric   return "";
1270b57cec5SDimitry Andric }
1280b57cec5SDimitry Andric 
isHidden(const Record * R)1290b57cec5SDimitry Andric static bool isHidden(const Record *R) {
1300b57cec5SDimitry Andric   if (R->getValueAsBit("Hidden"))
1310b57cec5SDimitry Andric     return true;
1320b57cec5SDimitry Andric 
1330b57cec5SDimitry Andric   // Not declared as hidden, check the parent package if it is hidden.
1340b57cec5SDimitry Andric   if (DefInit *DI = dyn_cast<DefInit>(R->getValueInit("ParentPackage")))
1350b57cec5SDimitry Andric     return isHidden(DI->getDef());
1360b57cec5SDimitry Andric 
1370b57cec5SDimitry Andric   return false;
1380b57cec5SDimitry Andric }
1390b57cec5SDimitry Andric 
printChecker(llvm::raw_ostream & OS,const Record & R)1400b57cec5SDimitry Andric static void printChecker(llvm::raw_ostream &OS, const Record &R) {
1410b57cec5SDimitry Andric   OS << "CHECKER(" << "\"";
1420b57cec5SDimitry Andric   OS.write_escaped(getCheckerFullName(&R)) << "\", ";
1430b57cec5SDimitry Andric   OS << R.getName() << ", ";
1440b57cec5SDimitry Andric   OS << "\"";
1450b57cec5SDimitry Andric   OS.write_escaped(getStringValue(R, "HelpText")) << "\", ";
1460b57cec5SDimitry Andric   OS << "\"";
1470b57cec5SDimitry Andric   OS.write_escaped(getCheckerDocs(R));
1480b57cec5SDimitry Andric   OS << "\", ";
1490b57cec5SDimitry Andric 
1500b57cec5SDimitry Andric   if (!isHidden(&R))
1510b57cec5SDimitry Andric     OS << "false";
1520b57cec5SDimitry Andric   else
1530b57cec5SDimitry Andric     OS << "true";
1540b57cec5SDimitry Andric 
1550b57cec5SDimitry Andric   OS << ")\n";
1560b57cec5SDimitry Andric }
1570b57cec5SDimitry Andric 
printOption(llvm::raw_ostream & OS,StringRef FullName,const Record & R)1580b57cec5SDimitry Andric static void printOption(llvm::raw_ostream &OS, StringRef FullName,
1590b57cec5SDimitry Andric                         const Record &R) {
1600b57cec5SDimitry Andric   OS << "\"";
1610b57cec5SDimitry Andric   OS.write_escaped(getCheckerOptionType(R)) << "\", \"";
1620b57cec5SDimitry Andric   OS.write_escaped(FullName) << "\", ";
1630b57cec5SDimitry Andric   OS << '\"' << getStringValue(R, "CmdFlag") << "\", ";
1640b57cec5SDimitry Andric   OS << '\"';
1650b57cec5SDimitry Andric   OS.write_escaped(getStringValue(R, "Desc")) << "\", ";
1660b57cec5SDimitry Andric   OS << '\"';
1670b57cec5SDimitry Andric   OS.write_escaped(getStringValue(R, "DefaultVal")) << "\", ";
1680b57cec5SDimitry Andric   OS << '\"';
1690b57cec5SDimitry Andric   OS << getDevelopmentStage(R) << "\", ";
1700b57cec5SDimitry Andric 
1710b57cec5SDimitry Andric   if (!R.getValueAsBit("Hidden"))
1720b57cec5SDimitry Andric     OS << "false";
1730b57cec5SDimitry Andric   else
1740b57cec5SDimitry Andric     OS << "true";
1750b57cec5SDimitry Andric }
1760b57cec5SDimitry Andric 
EmitClangSACheckers(RecordKeeper & Records,raw_ostream & OS)177a7dea167SDimitry Andric void clang::EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) {
1780b57cec5SDimitry Andric   std::vector<Record*> checkers = Records.getAllDerivedDefinitions("Checker");
1790b57cec5SDimitry Andric   std::vector<Record*> packages = Records.getAllDerivedDefinitions("Package");
1800b57cec5SDimitry Andric 
1810b57cec5SDimitry Andric   using SortedRecords = llvm::StringMap<const Record *>;
1820b57cec5SDimitry Andric 
1830b57cec5SDimitry Andric   OS << "// This file is automatically generated. Do not edit this file by "
1840b57cec5SDimitry Andric         "hand.\n";
1850b57cec5SDimitry Andric 
1860b57cec5SDimitry Andric   // Emit packages.
1870b57cec5SDimitry Andric   //
1880b57cec5SDimitry Andric   // PACKAGE(PACKAGENAME)
1890b57cec5SDimitry Andric   //   - PACKAGENAME: The name of the package.
1900b57cec5SDimitry Andric   OS << "\n"
1910b57cec5SDimitry Andric         "#ifdef GET_PACKAGES\n";
1920b57cec5SDimitry Andric   {
1930b57cec5SDimitry Andric     SortedRecords sortedPackages;
1940b57cec5SDimitry Andric     for (unsigned i = 0, e = packages.size(); i != e; ++i)
1950b57cec5SDimitry Andric       sortedPackages[getPackageFullName(packages[i])] = packages[i];
1960b57cec5SDimitry Andric 
1970b57cec5SDimitry Andric     for (SortedRecords::iterator
1980b57cec5SDimitry Andric            I = sortedPackages.begin(), E = sortedPackages.end(); I != E; ++I) {
1990b57cec5SDimitry Andric       const Record &R = *I->second;
2000b57cec5SDimitry Andric 
2010b57cec5SDimitry Andric       OS << "PACKAGE(" << "\"";
2020b57cec5SDimitry Andric       OS.write_escaped(getPackageFullName(&R)) << '\"';
2030b57cec5SDimitry Andric       OS << ")\n";
2040b57cec5SDimitry Andric     }
2050b57cec5SDimitry Andric   }
2060b57cec5SDimitry Andric   OS << "#endif // GET_PACKAGES\n"
2070b57cec5SDimitry Andric         "\n";
2080b57cec5SDimitry Andric 
2090b57cec5SDimitry Andric   // Emit a package option.
2100b57cec5SDimitry Andric   //
2110b57cec5SDimitry Andric   // PACKAGE_OPTION(OPTIONTYPE, PACKAGENAME, OPTIONNAME, DESCRIPTION, DEFAULT)
2120b57cec5SDimitry Andric   //   - OPTIONTYPE: Type of the option, whether it's integer or boolean etc.
2130b57cec5SDimitry Andric   //                 This is important for validating user input. Note that
2140b57cec5SDimitry Andric   //                 it's a string, rather than an actual type: since we can
2150b57cec5SDimitry Andric   //                 load checkers runtime, we can't use template hackery for
2160b57cec5SDimitry Andric   //                 sorting this out compile-time.
2170b57cec5SDimitry Andric   //   - PACKAGENAME: Name of the package.
2180b57cec5SDimitry Andric   //   - OPTIONNAME: Name of the option.
2190b57cec5SDimitry Andric   //   - DESCRIPTION
2200b57cec5SDimitry Andric   //   - DEFAULT: The default value for this option.
2210b57cec5SDimitry Andric   //
222*bdd1243dSDimitry Andric   // The full option can be specified in the command like this:
2230b57cec5SDimitry Andric   //   -analyzer-config PACKAGENAME:OPTIONNAME=VALUE
2240b57cec5SDimitry Andric   OS << "\n"
2250b57cec5SDimitry Andric         "#ifdef GET_PACKAGE_OPTIONS\n";
2260b57cec5SDimitry Andric   for (const Record *Package : packages) {
2270b57cec5SDimitry Andric 
2280b57cec5SDimitry Andric     if (Package->isValueUnset("PackageOptions"))
2290b57cec5SDimitry Andric       continue;
2300b57cec5SDimitry Andric 
2310b57cec5SDimitry Andric     std::vector<Record *> PackageOptions = Package
2320b57cec5SDimitry Andric                                        ->getValueAsListOfDefs("PackageOptions");
2330b57cec5SDimitry Andric     for (Record *PackageOpt : PackageOptions) {
2340b57cec5SDimitry Andric       OS << "PACKAGE_OPTION(";
2350b57cec5SDimitry Andric       printOption(OS, getPackageFullName(Package), *PackageOpt);
2360b57cec5SDimitry Andric       OS << ")\n";
2370b57cec5SDimitry Andric     }
2380b57cec5SDimitry Andric   }
2390b57cec5SDimitry Andric   OS << "#endif // GET_PACKAGE_OPTIONS\n"
2400b57cec5SDimitry Andric         "\n";
2410b57cec5SDimitry Andric 
2420b57cec5SDimitry Andric   // Emit checkers.
2430b57cec5SDimitry Andric   //
2440b57cec5SDimitry Andric   // CHECKER(FULLNAME, CLASS, HELPTEXT)
2450b57cec5SDimitry Andric   //   - FULLNAME: The full name of the checker, including packages, e.g.:
2460b57cec5SDimitry Andric   //               alpha.cplusplus.UninitializedObject
2470b57cec5SDimitry Andric   //   - CLASS: The name of the checker, with "Checker" appended, e.g.:
2480b57cec5SDimitry Andric   //            UninitializedObjectChecker
2490b57cec5SDimitry Andric   //   - HELPTEXT: The description of the checker.
2500b57cec5SDimitry Andric   OS << "\n"
2510b57cec5SDimitry Andric         "#ifdef GET_CHECKERS\n"
2520b57cec5SDimitry Andric         "\n";
2530b57cec5SDimitry Andric   for (const Record *checker : checkers) {
2540b57cec5SDimitry Andric     printChecker(OS, *checker);
2550b57cec5SDimitry Andric   }
2560b57cec5SDimitry Andric   OS << "\n"
2570b57cec5SDimitry Andric         "#endif // GET_CHECKERS\n"
2580b57cec5SDimitry Andric         "\n";
2590b57cec5SDimitry Andric 
2600b57cec5SDimitry Andric   // Emit dependencies.
2610b57cec5SDimitry Andric   //
2620b57cec5SDimitry Andric   // CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY)
2630b57cec5SDimitry Andric   //   - FULLNAME: The full name of the checker that depends on another checker.
2640b57cec5SDimitry Andric   //   - DEPENDENCY: The full name of the checker FULLNAME depends on.
2650b57cec5SDimitry Andric   OS << "\n"
2660b57cec5SDimitry Andric         "#ifdef GET_CHECKER_DEPENDENCIES\n";
2670b57cec5SDimitry Andric   for (const Record *Checker : checkers) {
2680b57cec5SDimitry Andric     if (Checker->isValueUnset("Dependencies"))
2690b57cec5SDimitry Andric       continue;
2700b57cec5SDimitry Andric 
2710b57cec5SDimitry Andric     for (const Record *Dependency :
2720b57cec5SDimitry Andric                             Checker->getValueAsListOfDefs("Dependencies")) {
2730b57cec5SDimitry Andric       OS << "CHECKER_DEPENDENCY(";
2740b57cec5SDimitry Andric       OS << '\"';
2750b57cec5SDimitry Andric       OS.write_escaped(getCheckerFullName(Checker)) << "\", ";
2760b57cec5SDimitry Andric       OS << '\"';
2770b57cec5SDimitry Andric       OS.write_escaped(getCheckerFullName(Dependency)) << '\"';
2780b57cec5SDimitry Andric       OS << ")\n";
2790b57cec5SDimitry Andric     }
2800b57cec5SDimitry Andric   }
2810b57cec5SDimitry Andric   OS << "\n"
2820b57cec5SDimitry Andric         "#endif // GET_CHECKER_DEPENDENCIES\n";
2830b57cec5SDimitry Andric 
2845ffd83dbSDimitry Andric   // Emit weak dependencies.
2855ffd83dbSDimitry Andric   //
2865ffd83dbSDimitry Andric   // CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY)
2875ffd83dbSDimitry Andric   //   - FULLNAME: The full name of the checker that is supposed to be
2885ffd83dbSDimitry Andric   //     registered first.
2895ffd83dbSDimitry Andric   //   - DEPENDENCY: The full name of the checker FULLNAME weak depends on.
2905ffd83dbSDimitry Andric   OS << "\n"
2915ffd83dbSDimitry Andric         "#ifdef GET_CHECKER_WEAK_DEPENDENCIES\n";
2925ffd83dbSDimitry Andric   for (const Record *Checker : checkers) {
2935ffd83dbSDimitry Andric     if (Checker->isValueUnset("WeakDependencies"))
2945ffd83dbSDimitry Andric       continue;
2955ffd83dbSDimitry Andric 
2965ffd83dbSDimitry Andric     for (const Record *Dependency :
2975ffd83dbSDimitry Andric          Checker->getValueAsListOfDefs("WeakDependencies")) {
2985ffd83dbSDimitry Andric       OS << "CHECKER_WEAK_DEPENDENCY(";
2995ffd83dbSDimitry Andric       OS << '\"';
3005ffd83dbSDimitry Andric       OS.write_escaped(getCheckerFullName(Checker)) << "\", ";
3015ffd83dbSDimitry Andric       OS << '\"';
3025ffd83dbSDimitry Andric       OS.write_escaped(getCheckerFullName(Dependency)) << '\"';
3035ffd83dbSDimitry Andric       OS << ")\n";
3045ffd83dbSDimitry Andric     }
3055ffd83dbSDimitry Andric   }
3065ffd83dbSDimitry Andric   OS << "\n"
3075ffd83dbSDimitry Andric         "#endif // GET_CHECKER_WEAK_DEPENDENCIES\n";
3085ffd83dbSDimitry Andric 
3090b57cec5SDimitry Andric   // Emit a package option.
3100b57cec5SDimitry Andric   //
3110b57cec5SDimitry Andric   // CHECKER_OPTION(OPTIONTYPE, CHECKERNAME, OPTIONNAME, DESCRIPTION, DEFAULT)
3120b57cec5SDimitry Andric   //   - OPTIONTYPE: Type of the option, whether it's integer or boolean etc.
3130b57cec5SDimitry Andric   //                 This is important for validating user input. Note that
3140b57cec5SDimitry Andric   //                 it's a string, rather than an actual type: since we can
3150b57cec5SDimitry Andric   //                 load checkers runtime, we can't use template hackery for
3160b57cec5SDimitry Andric   //                 sorting this out compile-time.
3170b57cec5SDimitry Andric   //   - CHECKERNAME: Name of the package.
3180b57cec5SDimitry Andric   //   - OPTIONNAME: Name of the option.
3190b57cec5SDimitry Andric   //   - DESCRIPTION
3200b57cec5SDimitry Andric   //   - DEFAULT: The default value for this option.
3210b57cec5SDimitry Andric   //
322*bdd1243dSDimitry Andric   // The full option can be specified in the command like this:
3230b57cec5SDimitry Andric   //   -analyzer-config CHECKERNAME:OPTIONNAME=VALUE
3240b57cec5SDimitry Andric   OS << "\n"
3250b57cec5SDimitry Andric         "#ifdef GET_CHECKER_OPTIONS\n";
3260b57cec5SDimitry Andric   for (const Record *Checker : checkers) {
3270b57cec5SDimitry Andric 
3280b57cec5SDimitry Andric     if (Checker->isValueUnset("CheckerOptions"))
3290b57cec5SDimitry Andric       continue;
3300b57cec5SDimitry Andric 
3310b57cec5SDimitry Andric     std::vector<Record *> CheckerOptions = Checker
3320b57cec5SDimitry Andric                                        ->getValueAsListOfDefs("CheckerOptions");
3330b57cec5SDimitry Andric     for (Record *CheckerOpt : CheckerOptions) {
3340b57cec5SDimitry Andric       OS << "CHECKER_OPTION(";
3350b57cec5SDimitry Andric       printOption(OS, getCheckerFullName(Checker), *CheckerOpt);
3360b57cec5SDimitry Andric       OS << ")\n";
3370b57cec5SDimitry Andric     }
3380b57cec5SDimitry Andric   }
3390b57cec5SDimitry Andric   OS << "#endif // GET_CHECKER_OPTIONS\n"
3400b57cec5SDimitry Andric         "\n";
3410b57cec5SDimitry Andric }
342