Index: include/clang/Basic/DiagnosticCommonKinds.td =================================================================== --- include/clang/Basic/DiagnosticCommonKinds.td +++ include/clang/Basic/DiagnosticCommonKinds.td @@ -292,7 +292,7 @@ // Static Analyzer Core def err_unknown_analyzer_checker : Error< - "no analyzer checkers are associated with '%0'">; + "no analyzer checkers or packages are associated with '%0'">; def note_suggest_disabling_all_checkers : Note< "use -analyzer-disable-all-checks to disable all static analyzer checkers">; } Index: include/clang/StaticAnalyzer/Checkers/CheckerBase.td =================================================================== --- include/clang/StaticAnalyzer/Checkers/CheckerBase.td +++ include/clang/StaticAnalyzer/Checkers/CheckerBase.td @@ -10,14 +10,42 @@ // //===----------------------------------------------------------------------===// +/// Describes a checker or package option type. This is important for validating +/// user supplied inputs. +class CmdLineOptionTypeEnum val> { + bits<2> Type = val; +} +def Integer : CmdLineOptionTypeEnum<0>; +def String : CmdLineOptionTypeEnum<1>; +def Boolean : CmdLineOptionTypeEnum<2>; + +class Type { + bits<2> Type = val.Type; +} + +/// Describes an option for a checker or a package. +class CmdLineOption { + bits<2> Type = type.Type; + string CmdFlag = cmdFlag; + string Desc = desc; + string DefaultVal = defaultVal; +} + +/// Describes a list of package options. +class PackageOptions opts> { + list PackageOptions = opts; +} + /// Describes a package. Every checker is a part of a package, for example, /// 'NullDereference' is part of the 'core' package, hence it's full name is /// 'core.NullDereference'. /// Example: /// def Core : Package<"core">; class Package { - string PackageName = name; - Package ParentPackage; + string PackageName = name; + list PackageOptions; + Package ParentPackage; } /// Describes a 'super' package that holds another package inside it. This is @@ -52,11 +80,17 @@ /// def DereferenceChecker : Checker<"NullDereference">, /// HelpText<"Check for dereferences of null pointers">; class Checker { - string CheckerName = name; - string HelpText; - list Dependencies; - bits<2> Documentation; - Package ParentPackage; + string CheckerName = name; + string HelpText; + list CheckerOptions; + list Dependencies; + bits<2> Documentation; + Package ParentPackage; +} + +/// Describes a list of checker options. +class CheckerOptions opts> { + list CheckerOptions = opts; } /// Describes dependencies in between checkers. For example, InnerPointerChecker Index: include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- include/clang/StaticAnalyzer/Checkers/Checkers.td +++ include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -42,7 +42,18 @@ // development, but unwanted for developers who target only a single platform. def PortabilityOptIn : Package<"portability">, ParentPackage; -def Nullability : Package<"nullability">; +def Nullability : Package<"nullability">, + PackageOptions<[ + CmdLineOption, + CmdLineOption + ]>; def Cplusplus : Package<"cplusplus">; def CplusplusAlpha : Package<"cplusplus">, ParentPackage; @@ -369,6 +380,13 @@ HelpText<"The base of several malloc() related checkers. On it's own it " "emits no reports, but adds valuable information to the analysis " "when enabled.">, + CheckerOptions<[ + CmdLineOption + ]>, Dependencies<[CStringModeling]>, Documentation; @@ -445,7 +463,28 @@ Documentation; def MoveChecker: Checker<"Move">, - HelpText<"Find use-after-move bugs in C++">, + HelpText<"Find use-after-move bugs in C++">, + CheckerOptions<[ + CmdLineOption + ]>, Documentation; } // end: "cplusplus" @@ -454,6 +493,12 @@ def VirtualCallChecker : Checker<"VirtualCall">, HelpText<"Check virtual function calls during construction or destruction">, + CheckerOptions<[ + CmdLineOption + ]>, Documentation; } // end: "optin.cplusplus" @@ -490,7 +535,40 @@ Documentation; def UninitializedObjectChecker: Checker<"UninitializedObject">, - HelpText<"Reports uninitialized fields after object construction">, + HelpText<"Reports uninitialized fields after object construction">, + CheckerOptions<[ + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption + ]>, Documentation; } // end: "alpha.cplusplus" @@ -551,6 +629,13 @@ def PaddingChecker : Checker<"Padding">, HelpText<"Check for excessively padded structs.">, + CheckerOptions<[ + CmdLineOption + ]>, Documentation; } // end: "padding" @@ -652,11 +737,18 @@ HelpText<"Check for overflows in the arguments to malloc()">, Documentation; -// Operating systems specific PROT_READ/PROT_WRITE values is not implemented, -// the defaults are correct for several common operating systems though, -// but may need to be overridden via the related analyzer-config flags. def MmapWriteExecChecker : Checker<"MmapWriteExec">, HelpText<"Warn on mmap() calls that are both writable and executable">, + CheckerOptions<[ + CmdLineOption, + CmdLineOption + ]>, Documentation; } // end "alpha.security" @@ -694,6 +786,14 @@ def NumberObjectConversionChecker : Checker<"NumberObjectConversion">, HelpText<"Check for erroneous conversions of objects representing numbers " "into numbers">, + CheckerOptions<[ + CmdLineOption + ]>, Documentation; def MacOSXAPIChecker : Checker<"API">, @@ -780,6 +880,22 @@ def RetainCountChecker : Checker<"RetainCount">, HelpText<"Check for leaks and improper reference count management">, + CheckerOptions<[ + CmdLineOption, + CmdLineOption, + CmdLineOption + ]>, Dependencies<[RetainCountBase]>, Documentation; @@ -880,6 +996,17 @@ def NonLocalizedStringChecker : Checker<"NonLocalizedStringChecker">, HelpText<"Warns about uses of non-localized NSStrings passed to UI methods " "expecting localized NSStrings">, + CheckerOptions<[ + CmdLineOption + ]>, Documentation; def EmptyLocalizationContextChecker : @@ -938,6 +1065,72 @@ def AnalysisOrderChecker : Checker<"AnalysisOrder">, HelpText<"Print callbacks that are called during analysis in order">, + CheckerOptions<[ + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption + ]>, Documentation; def DominatorsTreeDumper : Checker<"DumpDominators">, @@ -1007,6 +1200,25 @@ def CloneChecker : Checker<"CloneChecker">, HelpText<"Reports similar pieces of code.">, + CheckerOptions<[ + CmdLineOption, + CmdLineOption, + CmdLineOption + ]>, Documentation; } // end "clone" Index: include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h =================================================================== --- include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h +++ include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h @@ -92,6 +92,26 @@ using InitializationFunction = void (*)(CheckerManager &); using ShouldRegisterFunction = bool (*)(const LangOptions &); + /// Specifies a command line option. It may either belong to a checker or a + /// package. + struct CmdLineOption { + StringRef OptionType; + StringRef OptionName; + StringRef DefaultValStr; + StringRef Description; + + CmdLineOption(StringRef OptionType, StringRef OptionName, + StringRef DefaultValStr, StringRef Description) + : OptionType(OptionType), OptionName(OptionName), + DefaultValStr(DefaultValStr), Description(Description) { + + assert((OptionType == "bool" || OptionType == "string" || + OptionType == "int") && "Unknown command line option type!"); + } + }; + + using CmdLineOptionList = llvm::SmallVector; + struct CheckerInfo; using CheckerInfoList = std::vector; @@ -99,6 +119,8 @@ using ConstCheckerInfoList = llvm::SmallVector; using CheckerInfoSet = llvm::SetVector; + /// Specifies a checker. Note that this isn't what we call a checker object, + /// it merely contains everything required to create one. struct CheckerInfo { enum class StateFromCmdLine { // This checker wasn't explicitly enabled or disabled. @@ -114,6 +136,7 @@ StringRef FullName; StringRef Desc; StringRef DocumentationUri; + CmdLineOptionList CmdLineOptions; StateFromCmdLine State = StateFromCmdLine::State_Unspecified; ConstCheckerInfoList Dependencies; @@ -134,6 +157,17 @@ using StateFromCmdLine = CheckerInfo::StateFromCmdLine; + /// Specifies a package. Each package option is implicitly an option for all + /// checkers within the package. + struct PackageInfo { + StringRef FullName; + CmdLineOptionList CmdLineOptions; + + PackageInfo(StringRef FullName) : FullName(FullName) {} + }; + + using PackageInfoList = llvm::SmallVector; + private: template static void initializeManager(CheckerManager &mgr) { @@ -164,25 +198,40 @@ /// Makes the checker with the full name \p fullName depends on the checker /// called \p dependency. - void addDependency(StringRef fullName, StringRef dependency) { - auto CheckerThatNeedsDeps = - [&fullName](const CheckerInfo &Chk) { return Chk.FullName == fullName; }; - auto Dependency = - [&dependency](const CheckerInfo &Chk) { - return Chk.FullName == dependency; - }; - - auto CheckerIt = llvm::find_if(Checkers, CheckerThatNeedsDeps); - assert(CheckerIt != Checkers.end() && - "Failed to find the checker while attempting to set up it's " - "dependencies!"); - - auto DependencyIt = llvm::find_if(Checkers, Dependency); - assert(DependencyIt != Checkers.end() && - "Failed to find the dependency of a checker!"); - - CheckerIt->Dependencies.push_back(&*DependencyIt); - } + void addDependency(StringRef fullName, StringRef dependency); + + /// Registers an option to a given checker. A checker option will always have + /// the following format: + /// CheckerFullName:OptionName=Value + /// And can be specified from the command line like this: + /// -analyzer-config CheckerFullName:OptionName=Value + /// + /// Options for unknown checkers, or unknown options for a given checker, or + /// invalid value types for that given option are reported as an error in + /// non-compatibility mode. + void addCheckerOption(StringRef OptionType, + StringRef CheckerFullName, + StringRef OptionName, + StringRef DefaultValStr, + StringRef Description); + + /// Adds a package to the registry. + void addPackage(StringRef FullName); + + /// Registers an option to a given package. A package option will always have + /// the following format: + /// PackageFullName:OptionName=Value + /// And can be specified from the command line like this: + /// -analyzer-config PackageFullName:OptionName=Value + /// + /// Options for unknown packages, or unknown options for a given package, or + /// invalid value types for that given option are reported as an error in + /// non-compatibility mode. + void addPackageOption(StringRef OptionType, + StringRef PackageFullName, + StringRef OptionName, + StringRef DefaultValStr, + StringRef Description); // FIXME: This *really* should be added to the frontend flag descriptions. /// Initializes a CheckerManager by calling the initialization functions for @@ -196,8 +245,9 @@ /// Prints the name and description of all checkers in this registry. /// This output is not intended to be machine-parseable. - void printHelp(raw_ostream &out, size_t maxNameChars = 30) const; - void printList(raw_ostream &out) const; + void printCheckerWithDescList(raw_ostream &out, + size_t maxNameChars = 30) const; + void printEnabledCheckerList(raw_ostream &out) const; private: /// Collect all enabled checkers. The returned container preserves the order @@ -211,7 +261,10 @@ CheckerInfoListRange getMutableCheckersForCmdLineArg(StringRef CmdLineArg); CheckerInfoList Checkers; - llvm::StringMap Packages; + PackageInfoList Packages; + /// Used for couting how many checkers belong to a certain package in the + /// \c Checkers field. For convenience purposes. + llvm::StringMap PackageSizes; DiagnosticsEngine &Diags; AnalyzerOptions &AnOpts; @@ -219,7 +272,6 @@ }; } // namespace ento - } // namespace clang #endif // LLVM_CLANG_STATICANALYZER_CORE_CHECKERREGISTRY_H Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -422,7 +422,7 @@ OptionField = DefaultVal; bool HasFailed = getStringOption(Config, Name, std::to_string(DefaultVal)) - .getAsInteger(10, OptionField); + .getAsInteger(0, OptionField); if (Diags && HasFailed) Diags->Report(diag::err_analyzer_config_invalid_input) << Name << "an unsigned"; Index: lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp =================================================================== --- lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp +++ lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp @@ -50,7 +50,8 @@ out << "OVERVIEW: Clang Static Analyzer Checkers List\n\n"; out << "USAGE: -analyzer-checker \n\n"; - CheckerRegistry(plugins, diags, anopts, langOpts).printHelp(out); + CheckerRegistry(plugins, diags, anopts, langOpts) + .printCheckerWithDescList(out); } void ento::printEnabledCheckerList(raw_ostream &out, @@ -60,7 +61,8 @@ const LangOptions &langOpts) { out << "OVERVIEW: Clang Static Analyzer Enabled Checkers List\n\n"; - CheckerRegistry(plugins, diags, anopts, langOpts).printList(out); + CheckerRegistry(plugins, diags, anopts, langOpts) + .printEnabledCheckerList(out); } void ento::printAnalyzerConfigList(raw_ostream &out) { Index: lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp =================================================================== --- lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp +++ lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp @@ -9,6 +9,7 @@ #include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/LLVM.h" +#include "clang/Driver/DriverDiagnostic.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" @@ -18,6 +19,7 @@ #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/DynamicLibrary.h" +#include "llvm/Support/FormattedStream.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" #include @@ -43,6 +45,11 @@ return a.FullName < b.FullName; } +static bool packageNameLT(const CheckerRegistry::PackageInfo &a, + const CheckerRegistry::PackageInfo &b) { + return a.FullName < b.FullName; +} + static constexpr char PackageSeparator = '.'; static bool isInPackage(const CheckerRegistry::CheckerInfo &checker, @@ -83,9 +90,9 @@ // checker. size_t size = 1; llvm::StringMap::const_iterator packageSize = - Packages.find(CmdLineArg); + PackageSizes.find(CmdLineArg); - if (packageSize != Packages.end()) + if (packageSize != PackageSizes.end()) size = packageSize->getValue(); return { it, it + size }; @@ -103,9 +110,16 @@ #define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI) \ addChecker(register##CLASS, shouldRegister##CLASS, FULLNAME, HELPTEXT, \ DOC_URI); + +#define GET_PACKAGES +#define PACKAGE(FULLNAME) \ + addPackage(FULLNAME); + #include "clang/StaticAnalyzer/Checkers/Checkers.inc" #undef CHECKER #undef GET_CHECKERS +#undef PACKAGE +#undef GET_PACKAGES // Register checkers from plugins. for (ArrayRef::iterator i = plugins.begin(), e = plugins.end(); @@ -150,15 +164,28 @@ // Would it be better to name it '~experimental' or something else // that's ASCIIbetically last? llvm::sort(Checkers, checkerNameLT); + llvm::sort(Packages, packageNameLT); #define GET_CHECKER_DEPENDENCIES #define CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY) \ addDependency(FULLNAME, DEPENDENCY); +#define GET_CHECKER_OPTIONS +#define CHECKER_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL) \ + addCheckerOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC); + +#define GET_PACKAGE_OPTIONS +#define PACKAGE_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL) \ + addPackageOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC); + #include "clang/StaticAnalyzer/Checkers/Checkers.inc" #undef CHECKER_DEPENDENCY #undef GET_CHECKER_DEPENDENCIES +#undef CHECKER_OPTION +#undef GET_CHECKER_OPTIONS +#undef PACKAGE_OPTION +#undef GET_PACKAGE_OPTIONS // Parse '-analyzer-checker' and '-analyzer-disable-checker' options from the // command line. @@ -255,11 +282,74 @@ StringRef packageName, leafName; std::tie(packageName, leafName) = Name.rsplit(PackageSeparator); while (!leafName.empty()) { - Packages[packageName] += 1; + PackageSizes[packageName] += 1; std::tie(packageName, leafName) = packageName.rsplit(PackageSeparator); } } +namespace { +template +struct SameFullName { + StringRef RequestedName; + + SameFullName(StringRef RequestedName) : RequestedName(RequestedName) {} + + bool operator()(const CheckerOrPackageInfo &CheckerOrPackage) { + return CheckerOrPackage.FullName == RequestedName; + } +}; + +using SameCheckerName = SameFullName; +using SamePackageName = SameFullName; +} // end of anonymous namespace + +void CheckerRegistry::addDependency(StringRef fullName, StringRef dependency) { + auto CheckerIt = llvm::find_if(Checkers, SameCheckerName(fullName)); + assert(CheckerIt != Checkers.end() && + "Failed to find the checker while attempting to set up it's " + "dependencies!"); + + auto DependencyIt = llvm::find_if(Checkers, SameCheckerName(dependency)); + assert(DependencyIt != Checkers.end() && + "Failed to find the dependency of a checker!"); + + CheckerIt->Dependencies.push_back(&*DependencyIt); +} + +void CheckerRegistry::addCheckerOption(StringRef OptionType, + StringRef CheckerFullName, + StringRef OptionName, + StringRef DefaultValStr, + StringRef Description) { + + auto CheckerIt = llvm::find_if(Checkers, SameCheckerName(CheckerFullName)); + assert(CheckerIt != Checkers.end() && + "Failed to find the checker while attempting to add a command line " + "option to it!"); + + CheckerIt->CmdLineOptions.push_back( + {OptionType, OptionName, DefaultValStr, Description}); +} + +void CheckerRegistry::addPackage(StringRef FullName) { + Packages.push_back(FullName); +} + +void CheckerRegistry::addPackageOption(StringRef OptionType, + StringRef CheckerFullName, + StringRef OptionName, + StringRef DefaultValStr, + StringRef Description) { + + auto PackageIt = llvm::find_if(Packages, SamePackageName(CheckerFullName)); + assert(PackageIt != Packages.end() && + "Failed to find the package while attempting to add a command line " + "option to it!"); + + PackageIt->CmdLineOptions.push_back( + {OptionType, OptionName, DefaultValStr, Description}); +} + void CheckerRegistry::initializeManager(CheckerManager &checkerMgr) const { // Collect checkers enabled by the options. CheckerInfoSet enabledCheckers = getEnabledCheckers(); @@ -277,21 +367,23 @@ if (pos == StringRef::npos) continue; - bool hasChecker = false; - StringRef checkerName = config.getKey().substr(0, pos); - for (const auto &checker : Checkers) { - if (checker.FullName.startswith(checkerName) && - (checker.FullName.size() == pos || checker.FullName[pos] == '.')) { - hasChecker = true; - break; - } - } - if (!hasChecker) - Diags.Report(diag::err_unknown_analyzer_checker) << checkerName; + StringRef SuppliedChecker(config.first().substr(0, pos)); + + auto CheckerIt = + llvm::find_if(Checkers, SameCheckerName(SuppliedChecker)); + if (CheckerIt != Checkers.end()) + continue; + + auto PackageIt = + llvm::find_if(Packages, SamePackageName(SuppliedChecker)); + if (PackageIt != Packages.end()) + continue; + + Diags.Report(diag::err_unknown_analyzer_checker) << SuppliedChecker; } } -void CheckerRegistry::printHelp(raw_ostream &out, +void CheckerRegistry::printCheckerWithDescList(raw_ostream &out, size_t maxNameChars) const { // FIXME: Print available packages. @@ -324,7 +416,7 @@ } } -void CheckerRegistry::printList(raw_ostream &out) const { +void CheckerRegistry::printEnabledCheckerList(raw_ostream &out) const { // Collect checkers enabled by the options. CheckerInfoSet enabledCheckers = getEnabledCheckers(); Index: test/Analysis/disable-all-checks.c =================================================================== --- test/Analysis/disable-all-checks.c +++ test/Analysis/disable-all-checks.c @@ -12,7 +12,7 @@ // // expected-no-diagnostics -// CHECK: no analyzer checkers are associated with 'non.existant.Checker' +// CHECK: no analyzer checkers or packages are associated with 'non.existant.Checker' // CHECK: use -analyzer-disable-all-checks to disable all static analyzer checkers int buggy() { int x = 0; Index: test/Analysis/invalid-checker-option.c =================================================================== --- /dev/null +++ test/Analysis/invalid-checker-option.c @@ -0,0 +1,19 @@ +// RUN: not %clang_analyze_cc1 -verify %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-config RetainOneTwoThree:CheckOSObject=false \ +// RUN: 2>&1 | FileCheck %s -check-prefix=CHECK-NON-EXISTENT-CHECKER + +// Note that non-existent packages and checkers were always reported. + +// RUN: not %clang_analyze_cc1 -verify %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-config-compatibility-mode=true \ +// RUN: -analyzer-config RetainOneTwoThree:CheckOSObject=false \ +// RUN: 2>&1 | FileCheck %s -check-prefix=CHECK-NON-EXISTENT-CHECKER + +// CHECK-NON-EXISTENT-CHECKER: (frontend): no analyzer checkers or packages +// CHECK-NON-EXISTENT-CHECKER-SAME: are associated with 'RetainOneTwoThree' + +// expected-no-diagnostics + +int main() {} Index: utils/TableGen/ClangSACheckersEmitter.cpp =================================================================== --- utils/TableGen/ClangSACheckersEmitter.cpp +++ utils/TableGen/ClangSACheckersEmitter.cpp @@ -90,6 +90,23 @@ .str(); } +static std::string getCheckerOptionType(const Record &R) { + if (BitsInit *BI = R.getValueAsBitsInit("Type")) { + switch(getValueFromBitsInit(BI, R)) { + case 0: + return "int"; + case 1: + return "string"; + case 2: + return "bool"; + } + } + PrintFatalError(R.getLoc(), + "unable to parse command line option type for " + + getCheckerFullName(&R)); + return ""; +} + static void printChecker(llvm::raw_ostream &OS, const Record &R) { OS << "CHECKER(" << "\""; OS.write_escaped(getCheckerFullName(&R)) << "\", "; @@ -134,6 +151,45 @@ OS << "#endif // GET_PACKAGES\n" "\n"; + // Emit a package option. + // + // PACKAGE_OPTION(OPTIONTYPE, PACKAGENAME, OPTIONNAME, DESCRIPTION, DEFAULT) + // - OPTIONTYPE: Type of the option, whether it's integer or boolean etc. + // This is important for validating user input. Note that + // it's a string, rather than an actual type: since we can + // load checkers runtime, we can't use template hackery for + // sorting this out compile-time. + // - PACKAGENAME: Name of the package. + // - OPTIONNAME: Name of the option. + // - DESCRIPTION + // - DEFAULT: The default value for this option. + // + // The full option can be specified in the command like like this: + // -analyzer-config PACKAGENAME:OPTIONNAME=VALUE + OS << "\n" + "#ifdef GET_PACKAGE_OPTIONS\n"; + for (const Record *package : packages) { + + if (package->isValueUnset("PackageOptions")) + continue; + + std::vector PackageOptions = package + ->getValueAsListOfDefs("PackageOptions"); + for (Record *PackageOpt : PackageOptions) { + OS << "PACKAGE_OPTION(\""; + OS.write_escaped(getCheckerOptionType(*PackageOpt)) << "\", \""; + OS.write_escaped(getPackageFullName(package)) << "\", "; + OS << '\"' << getStringValue(*PackageOpt, "CmdFlag") << "\", "; + OS << '\"'; + OS.write_escaped(getStringValue(*PackageOpt, "Desc")) << "\", "; + OS << '\"'; + OS.write_escaped(getStringValue(*PackageOpt, "DefaultVal")) << "\""; + OS << ")\n"; + } + } + OS << "#endif // GET_PACKAGE_OPTIONS\n" + "\n"; + // Emit checkers. // // CHECKER(FULLNAME, CLASS, HELPTEXT) @@ -176,5 +232,46 @@ } OS << "\n" "#endif // GET_CHECKER_DEPENDENCIES\n"; + + // Emit a package option. + // + // CHECKER_OPTION(OPTIONTYPE, CHECKERNAME, OPTIONNAME, DESCRIPTION, DEFAULT) + // - OPTIONTYPE: Type of the option, whether it's integer or boolean etc. + // This is important for validating user input. Note that + // it's a string, rather than an actual type: since we can + // load checkers runtime, we can't use template hackery for + // sorting this out compile-time. + // - CHECKERNAME: Name of the package. + // - OPTIONNAME: Name of the option. + // - DESCRIPTION + // - DEFAULT: The default value for this option. + // + // The full option can be specified in the command like like this: + // -analyzer-config CHECKERNAME:OPTIONNAME=VALUE + OS << "\n" + "#ifdef GET_CHECKER_OPTIONS\n"; + for (const Record *checker : checkers) { + + if (checker->isValueUnset("CheckerOptions")) + continue; + + + std::vector CheckerOptions = checker + ->getValueAsListOfDefs("CheckerOptions"); + for (Record *CheckerOpt : CheckerOptions) { + OS << "CHECKER_OPTION(\""; + OS << getCheckerOptionType(*CheckerOpt) << "\", \""; + OS.write_escaped(getCheckerFullName(checker)) << "\", "; + OS << '\"' << getStringValue(*CheckerOpt, "CmdFlag") << "\", "; + OS << '\"'; + OS.write_escaped(getStringValue(*CheckerOpt, "Desc")) << "\", "; + OS << '\"'; + OS.write_escaped(getStringValue(*CheckerOpt, "DefaultVal")) << "\""; + OS << ")"; + OS << '\n'; + } + } + OS << "#endif // GET_CHECKER_OPTIONS\n" + "\n"; } } // end namespace clang