//===--- ClangSACheckerProvider.cpp - Clang SA Checkers Provider ----------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Defines the CheckerProvider for the checkers defined in // libclangStaticAnalyzerCheckers. // //===----------------------------------------------------------------------===// #include "ClangSACheckerProvider.h" #include "ClangSACheckers.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/CheckerProvider.h" #include "llvm/Support/raw_ostream.h" #include "llvm/ADT/DenseSet.h" #include "map" using namespace clang; using namespace ento; namespace { /// \brief Provider for all the checkers in libclangStaticAnalyzerCheckers. class ClangSACheckerProvider : public CheckerProvider { public: virtual void registerCheckers(CheckerManager &checkerMgr, CheckerOptInfo *checkOpts, unsigned numCheckOpts); virtual void printHelp(llvm::raw_ostream &OS); }; } CheckerProvider *ento::createClangSACheckerProvider() { return new ClangSACheckerProvider(); } namespace { struct StaticCheckerInfoRec { const char *FullName; void (*RegFunc)(CheckerManager &mgr); const char *HelpText; int GroupIndex; bool Hidden; }; struct StaticPackageInfoRec { const char *FullName; int GroupIndex; bool Hidden; }; struct StaticGroupInfoRec { const char *FullName; }; } // end anonymous namespace. static const StaticPackageInfoRec StaticPackageInfo[] = { #define GET_PACKAGES #define PACKAGE(FULLNAME, GROUPINDEX, HIDDEN) \ { FULLNAME, GROUPINDEX, HIDDEN }, #include "Checkers.inc" { 0, -1, 0 } #undef PACKAGE #undef GET_PACKAGES }; static const unsigned NumPackages = sizeof(StaticPackageInfo) / sizeof(StaticPackageInfoRec) - 1; static const StaticGroupInfoRec StaticGroupInfo[] = { #define GET_GROUPS #define GROUP(FULLNAME) \ { FULLNAME }, #include "Checkers.inc" { 0 } #undef GROUP #undef GET_GROUPS }; static const unsigned NumGroups = sizeof(StaticGroupInfo) / sizeof(StaticGroupInfoRec) - 1; static const StaticCheckerInfoRec StaticCheckerInfo[] = { #define GET_CHECKERS #define CHECKER(FULLNAME,CLASS,DESCFILE,HELPTEXT,GROUPINDEX,HIDDEN) \ { FULLNAME, register##CLASS, HELPTEXT, GROUPINDEX, HIDDEN }, #include "Checkers.inc" { 0, 0, 0, -1, 0} #undef CHECKER #undef GET_CHECKERS }; static const unsigned NumCheckers = sizeof(StaticCheckerInfo) / sizeof(StaticCheckerInfoRec) - 1; namespace { struct CheckNameOption { const char *Name; const short *Members; const short *SubGroups; bool Hidden; }; } // end anonymous namespace. #define GET_MEMBER_ARRAYS #include "Checkers.inc" #undef GET_MEMBER_ARRAYS // The table of check name options, sorted by name for fast binary lookup. static const CheckNameOption CheckNameTable[] = { #define GET_CHECKNAME_TABLE #include "Checkers.inc" #undef GET_CHECKNAME_TABLE }; static const size_t CheckNameTableSize = sizeof(CheckNameTable) / sizeof(CheckNameTable[0]); static bool CheckNameOptionCompare(const CheckNameOption &LHS, const CheckNameOption &RHS) { return strcmp(LHS.Name, RHS.Name) < 0; } static void collectCheckers(const CheckNameOption *checkName, bool enable, llvm::DenseSet &checkers, bool collectHidden) { if (checkName->Hidden && !collectHidden) return; if (const short *member = checkName->Members) { if (enable) { for (; *member != -1; ++member) if (collectHidden || !StaticCheckerInfo[*member].Hidden) checkers.insert(&StaticCheckerInfo[*member]); } else { for (; *member != -1; ++member) checkers.erase(&StaticCheckerInfo[*member]); } } // Enable/disable all subgroups along with this one. if (const short *subGroups = checkName->SubGroups) { for (; *subGroups != -1; ++subGroups) { const CheckNameOption *sub = &CheckNameTable[*subGroups]; collectCheckers(sub, enable, checkers, collectHidden && !sub->Hidden); } } } static void collectCheckers(CheckerOptInfo &opt, llvm::DenseSet &checkers) { const char *optName = opt.getName(); CheckNameOption key = { optName, 0, 0, false }; const CheckNameOption *found = std::lower_bound(CheckNameTable, CheckNameTable + CheckNameTableSize, key, CheckNameOptionCompare); if (found == CheckNameTable + CheckNameTableSize || strcmp(found->Name, optName) != 0) return; // Check name not found. opt.claim(); collectCheckers(found, opt.isEnabled(), checkers, /*collectHidden=*/true); } void ClangSACheckerProvider::registerCheckers(CheckerManager &checkerMgr, CheckerOptInfo *checkOpts, unsigned numCheckOpts) { llvm::DenseSet enabledCheckers; for (unsigned i = 0; i != numCheckOpts; ++i) collectCheckers(checkOpts[i], enabledCheckers); for (llvm::DenseSet::iterator I = enabledCheckers.begin(), E = enabledCheckers.end(); I != E; ++I) { (*I)->RegFunc(checkerMgr); } } //===----------------------------------------------------------------------===// // Printing Help. //===----------------------------------------------------------------------===// static void printPackageOption(llvm::raw_ostream &OS) { // Find the maximum option length. unsigned OptionFieldWidth = 0; for (unsigned i = 0; i != NumPackages; ++i) { // Limit the amount of padding we are willing to give up for alignment. unsigned Length = strlen(StaticPackageInfo[i].FullName); if (Length <= 30) OptionFieldWidth = std::max(OptionFieldWidth, Length); } const unsigned InitialPad = 2; for (unsigned i = 0; i != NumPackages; ++i) { const StaticPackageInfoRec &package = StaticPackageInfo[i]; const std::string &Option = package.FullName; int Pad = OptionFieldWidth - int(Option.size()); OS.indent(InitialPad) << Option; if (package.GroupIndex != -1 || package.Hidden) { // Break on long option names. if (Pad < 0) { OS << "\n"; Pad = OptionFieldWidth + InitialPad; } OS.indent(Pad + 1) << "["; if (package.GroupIndex != -1) { OS << "Group=" << StaticGroupInfo[package.GroupIndex].FullName; if (package.Hidden) OS << ", "; } if (package.Hidden) OS << "Hidden"; OS << "]"; } OS << "\n"; } } typedef std::map SortedCheckers; static void printCheckerOption(llvm::raw_ostream &OS,SortedCheckers &checkers) { // Find the maximum option length. unsigned OptionFieldWidth = 0; for (SortedCheckers::iterator I = checkers.begin(), E = checkers.end(); I != E; ++I) { // Limit the amount of padding we are willing to give up for alignment. unsigned Length = strlen(I->second->FullName); if (Length <= 30) OptionFieldWidth = std::max(OptionFieldWidth, Length); } const unsigned InitialPad = 2; for (SortedCheckers::iterator I = checkers.begin(), E = checkers.end(); I != E; ++I) { const std::string &Option = I->first; const StaticCheckerInfoRec &checker = *I->second; int Pad = OptionFieldWidth - int(Option.size()); OS.indent(InitialPad) << Option; // Break on long option names. if (Pad < 0) { OS << "\n"; Pad = OptionFieldWidth + InitialPad; } OS.indent(Pad + 1) << checker.HelpText; if (checker.GroupIndex != -1 || checker.Hidden) { OS << " ["; if (checker.GroupIndex != -1) { OS << "Group=" << StaticGroupInfo[checker.GroupIndex].FullName; if (checker.Hidden) OS << ", "; } if (checker.Hidden) OS << "Hidden"; OS << "]"; } OS << "\n"; } } void ClangSACheckerProvider::printHelp(llvm::raw_ostream &OS) { OS << "USAGE: -analyzer-checker \n"; OS << "\nGROUPS:\n"; for (unsigned i = 0; i != NumGroups; ++i) OS.indent(2) << StaticGroupInfo[i].FullName << "\n"; OS << "\nPACKAGES:\n"; printPackageOption(OS); OS << "\nCHECKERS:\n"; // Sort checkers according to their full name. SortedCheckers checkers; for (unsigned i = 0; i != NumCheckers; ++i) checkers[StaticCheckerInfo[i].FullName] = &StaticCheckerInfo[i]; printCheckerOption(OS, checkers); }