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,45 @@ // //===----------------------------------------------------------------------===// +/// Describes a checker or package option type. This is important for validating +/// user supplied inputs. +/// New option types can be added by modifying this enum. Note that this +/// requires changes in the TableGen emitter file ClangSACheckersEmitter.cpp. +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; + // This field is optional. + list PackageOptions; + Package ParentPackage; } /// Describes a 'super' package that holds another package inside it. This is @@ -52,11 +83,19 @@ /// 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; + // This field is optional. + list CheckerOptions; + // This field is optional. + 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,17 @@ // 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 + ]>; def Cplusplus : Package<"cplusplus">; def CplusplusAlpha : Package<"cplusplus">, ParentPackage; @@ -371,6 +381,14 @@ 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; @@ -447,7 +465,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" @@ -456,6 +495,12 @@ def VirtualCallChecker : Checker<"VirtualCall">, HelpText<"Check virtual function calls during construction or destruction">, + CheckerOptions<[ + CmdLineOption + ]>, Documentation; } // end: "optin.cplusplus" @@ -492,7 +537,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" @@ -554,6 +632,13 @@ def PaddingChecker : Checker<"Padding">, HelpText<"Check for excessively padded structs.">, + CheckerOptions<[ + CmdLineOption + ]>, Documentation; } // end: "padding" @@ -662,11 +747,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" @@ -704,6 +796,14 @@ def NumberObjectConversionChecker : Checker<"NumberObjectConversion">, HelpText<"Check for erroneous conversions of objects representing numbers " "into numbers">, + CheckerOptions<[ + CmdLineOption + ]>, Documentation; def MacOSXAPIChecker : Checker<"API">, @@ -795,6 +895,23 @@ def RetainCountChecker : Checker<"RetainCount">, HelpText<"Check for leaks and improper reference count management">, + CheckerOptions<[ + CmdLineOption, + CmdLineOption + ]>, Dependencies<[RetainCountBase]>, Documentation; @@ -903,6 +1020,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 : @@ -961,6 +1089,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">, @@ -1034,6 +1228,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 @@ -91,6 +91,27 @@ 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; @@ -98,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. @@ -113,6 +136,7 @@ StringRef FullName; StringRef Desc; StringRef DocumentationUri; + CmdLineOptionList CmdLineOptions; StateFromCmdLine State = StateFromCmdLine::State_Unspecified; ConstCheckerInfoList Dependencies; @@ -136,6 +160,23 @@ 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; + + // Since each package must have a different full name, we can identify + // CheckerInfo objects by them. + bool operator==(const PackageInfo &Rhs) const { + return FullName == Rhs.FullName; + } + + explicit PackageInfo(StringRef FullName) : FullName(FullName) {} + }; + + using PackageInfoList = llvm::SmallVector; + private: template static void initializeManager(CheckerManager &mgr) { mgr.registerChecker(); @@ -165,6 +206,35 @@ /// called \p dependency. 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 /// all checkers specified by the given CheckerOptInfo list. The order of this @@ -193,21 +263,30 @@ CheckerInfoListRange getMutableCheckersForCmdLineArg(StringRef CmdLineArg); CheckerInfoList Checkers; + PackageInfoList Packages; + /// Used for couting how many checkers belong to a certain package in the + /// \c Checkers field. For convenience purposes. llvm::StringMap PackageSizes; /// Contains all (Dependendent checker, Dependency) pairs. We need this, as /// we'll resolve dependencies after all checkers were added first. llvm::SmallVector, 0> Dependencies; - void resolveDependencies(); + /// Contains all (FullName, CmdLineOption) pairs. Similarly to dependencies, + /// we only modify the actual CheckerInfo and PackageInfo objects once all + /// of them have been added. + llvm::SmallVector, 0> PackageOptions; + llvm::SmallVector, 0> CheckerOptions; + + void resolveCheckerAndPackageOptions(); + DiagnosticsEngine &Diags; AnalyzerOptions &AnOpts; const LangOptions &LangOpts; }; } // 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/CheckerRegistry.cpp =================================================================== --- lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp +++ lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp @@ -45,6 +45,7 @@ } }; +using PackageNameLT = FullNameLT; using CheckerNameLT = FullNameLT; } // end of anonymous namespace @@ -118,6 +119,9 @@ 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 @@ -166,6 +170,7 @@ // FIXME: Alphabetical sort puts 'experimental' in the middle. // Would it be better to name it '~experimental' or something else // that's ASCIIbetically last? + llvm::sort(Packages, PackageNameLT{}); llvm::sort(Checkers, CheckerNameLT{}); #define GET_CHECKER_DEPENDENCIES @@ -173,11 +178,24 @@ #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 resolveDependencies(); + resolveCheckerAndPackageOptions(); // Parse '-analyzer-checker' and '-analyzer-disable-checker' options from the // command line. @@ -266,20 +284,6 @@ return EnabledCheckers; } -void CheckerRegistry::addChecker(InitializationFunction Rfn, - ShouldRegisterFunction Sfn, StringRef Name, - StringRef Desc, StringRef DocsUri) { - Checkers.emplace_back(Rfn, Sfn, Name, Desc, DocsUri); - - // Record the presence of the checker in its packages. - StringRef PackageName, LeafName; - std::tie(PackageName, LeafName) = Name.rsplit(PackageSeparator); - while (!LeafName.empty()) { - PackageSizes[PackageName] += 1; - std::tie(PackageName, LeafName) = PackageName.rsplit(PackageSeparator); - } -} - void CheckerRegistry::resolveDependencies() { for (const std::pair &Entry : Dependencies) { auto CheckerIt = binaryFind(Checkers, Entry.first); @@ -302,6 +306,72 @@ Dependencies.emplace_back(FullName, Dependency); } +template +static void +insertOptionToCollection(StringRef FullName, T &Collection, + const CheckerRegistry::CmdLineOption &&Option) { + auto It = binaryFind(Collection, FullName); + assert(It != Collection.end() && + "Failed to find the checker while attempting to add a command line " + "option to it!"); + + It->CmdLineOptions.emplace_back(std::move(Option)); +} + +void CheckerRegistry::resolveCheckerAndPackageOptions() { + for (const std::pair &CheckerOptEntry : + CheckerOptions) { + insertOptionToCollection(CheckerOptEntry.first, Checkers, + std::move(CheckerOptEntry.second)); + } + CheckerOptions.clear(); + + for (const std::pair &PackageOptEntry : + PackageOptions) { + insertOptionToCollection(PackageOptEntry.first, Checkers, + std::move(PackageOptEntry.second)); + } + PackageOptions.clear(); +} + +void CheckerRegistry::addPackage(StringRef FullName) { + Packages.emplace_back(PackageInfo(FullName)); +} + +void CheckerRegistry::addPackageOption(StringRef OptionType, + StringRef PackageFullName, + StringRef OptionName, + StringRef DefaultValStr, + StringRef Description) { + PackageOptions.emplace_back( + PackageFullName, + CmdLineOption{OptionType, OptionName, DefaultValStr, Description}); +} + +void CheckerRegistry::addChecker(InitializationFunction Rfn, + ShouldRegisterFunction Sfn, StringRef Name, + StringRef Desc, StringRef DocsUri) { + Checkers.emplace_back(Rfn, Sfn, Name, Desc, DocsUri); + + // Record the presence of the checker in its packages. + StringRef PackageName, LeafName; + std::tie(PackageName, LeafName) = Name.rsplit(PackageSeparator); + while (!LeafName.empty()) { + PackageSizes[PackageName] += 1; + std::tie(PackageName, LeafName) = PackageName.rsplit(PackageSeparator); + } +} + +void CheckerRegistry::addCheckerOption(StringRef OptionType, + StringRef CheckerFullName, + StringRef OptionName, + StringRef DefaultValStr, + StringRef Description) { + CheckerOptions.emplace_back( + CheckerFullName, + CmdLineOption{OptionType, OptionName, DefaultValStr, Description}); +} + void CheckerRegistry::initializeManager(CheckerManager &CheckerMgr) 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 =================================================================== --- test/Analysis/invalid-checker-option.c +++ 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,26 @@ .str(); } +/// Retrieves the type from a CmdOptionTypeEnum typed Record object. Note that +/// the class itself has to be modified for adding a new option type in +/// CheckerBase.td. +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 +154,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) @@ -160,15 +219,15 @@ // - DEPENDENCY: The full name of the checker FULLNAME depends on. OS << "\n" "#ifdef GET_CHECKER_DEPENDENCIES\n"; - for (const Record *checker : checkers) { - if (checker->isValueUnset("Dependencies")) + for (const Record *Checker : checkers) { + if (Checker->isValueUnset("Dependencies")) continue; for (const Record *Dependency : - checker->getValueAsListOfDefs("Dependencies")) { + Checker->getValueAsListOfDefs("Dependencies")) { OS << "CHECKER_DEPENDENCY("; OS << '\"'; - OS.write_escaped(getCheckerFullName(checker)) << "\", "; + OS.write_escaped(getCheckerFullName(Checker)) << "\", "; OS << '\"'; OS.write_escaped(getCheckerFullName(Dependency)) << '\"'; OS << ")\n"; @@ -176,5 +235,45 @@ } 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