Index: include/clang/StaticAnalyzer/Core/AnalyzerOptions.h =================================================================== --- include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -137,6 +137,23 @@ UMK_Deep = 2 }; +/// Stores options for the analyzer from the command line. +/// +/// Some options are -cc1 flags (e.g.: -analyzer-output), but some are analyzer +/// configuration options, which are preceded by -analyzer-config (e.g.: +/// -analyzer-config notes-as-events=true). +/// +/// If you'd like to add a new -cc1 flag, add it to +/// include/clang/Driver/CC1Options.td, add a new field to store the value of +/// that flag in this class, and initialize it in +/// lib/Frontend/CompilerInvocation.cpp. +/// +/// If you'd like to add a new non-checker configuration, register it in +/// include/clang/StaticAnalyzer/Core/AnalyzerOptions.def, and refer to the +/// top of the file for documentation. +/// +/// If you'd like to add a new checker option, call getChecker*Option() +/// whenever. class AnalyzerOptions : public RefCountedBase { public: using ConfigTable = llvm::StringMap; @@ -208,32 +225,21 @@ #undef ANALYZER_OPTION #undef ANALYZER_OPTION_DEPENDS_ON_USER_MODE - /// A helper function that retrieves option for a given full-qualified - /// checker name. - /// Options for checkers can be specified via 'analyzer-config' command-line - /// option. - /// Example: - /// @code-analyzer-config unix.Malloc:OptionName=CheckerOptionValue @endcode - /// or @code-analyzer-config unix:OptionName=GroupOptionValue @endcode - /// for groups of checkers. - /// @param [in] CheckerName Full-qualified checker name, like - /// alpha.unix.StreamChecker. - /// @param [in] OptionName Name of the option to get. - /// @param [in] Default Default value if no option is specified. - /// @param [in] SearchInParents If set to true and the searched option was not - /// specified for the given checker the options for the parent packages will - /// be searched as well. The inner packages take precedence over the outer - /// ones. - /// @retval CheckerOptionValue An option for a checker if it was specified. - /// @retval GroupOptionValue An option for group if it was specified and no - /// checker-specific options were found. The closer group to checker, - /// the more priority it has. For example, @c coregroup.subgroup has more - /// priority than @c coregroup for @c coregroup.subgroup.CheckerName checker. - /// @retval Default If nor checker option, nor group option was found. - StringRef getCheckerOption(StringRef CheckerName, StringRef OptionName, - StringRef Default, - bool SearchInParents = false); + /// Query an option's string value. + /// + /// If an option value is not provided, returns the given \p DefaultVal. + /// @param [in] Name Name for option to retrieve. + /// @param [in] DefaultVal Default value returned if no such option was + /// specified. + StringRef getStringOption(StringRef OptionName, StringRef DefaultVal); + + void initOption(Optional &V, StringRef Name, + StringRef DefaultVal); + void initOption(Optional &V, StringRef Name, bool DefaultVal); + + void initOption(Optional &V, StringRef Name, + unsigned DefaultVal); public: AnalyzerOptions() : DisableAllChecks(false), ShowCheckerHelp(false), @@ -251,34 +257,17 @@ /// @param [in] Name Name for option to retrieve. /// @param [in] DefaultVal Default value returned if no such option was /// specified. - /// @param [in] C The optional checker parameter that can be used to restrict - /// the search to the options of this particular checker (and its parents - /// depending on search mode). + /// @param [in] C The checker object the option belongs to. Checker options + /// are retrieved in the following format: + /// `-analyzer-config :OptionName=Value. /// @param [in] SearchInParents If set to true and the searched option was not /// specified for the given checker the options for the parent packages will /// be searched as well. The inner packages take precedence over the outer /// ones. - bool getBooleanOption(StringRef Name, bool DefaultVal, - const ento::CheckerBase *C = nullptr, - bool SearchInParents = false); + bool getCheckerBooleanOption(StringRef Name, bool DefaultVal, + const ento::CheckerBase *C, + bool SearchInParents = false) const; - /// Variant that accepts a Optional value to cache the result. - /// - /// @param [in,out] V Return value storage, returned if parameter contains - /// an existing valid option, else it is used to store a return value - /// @param [in] Name Name for option to retrieve. - /// @param [in] DefaultVal Default value returned if no such option was - /// specified. - /// @param [in] C The optional checker parameter that can be used to restrict - /// the search to the options of this particular checker (and its parents - /// depending on search mode). - /// @param [in] SearchInParents If set to true and the searched option was not - /// specified for the given checker the options for the parent packages will - /// be searched as well. The inner packages take precedence over the outer - /// ones. - bool getBooleanOption(Optional &V, StringRef Name, bool DefaultVal, - const ento::CheckerBase *C = nullptr, - bool SearchInParents = false); /// Interprets an option's string value as an integer value. /// @@ -286,21 +275,16 @@ /// @param [in] Name Name for option to retrieve. /// @param [in] DefaultVal Default value returned if no such option was /// specified. - /// @param [in] C The optional checker parameter that can be used to restrict - /// the search to the options of this particular checker (and its parents - /// depending on search mode). + /// @param [in] C The checker object the option belongs to. Checker options + /// are retrieved in the following format: + /// `-analyzer-config :OptionName=Value. /// @param [in] SearchInParents If set to true and the searched option was not /// specified for the given checker the options for the parent packages will /// be searched as well. The inner packages take precedence over the outer /// ones. - int getOptionAsInteger(StringRef Name, int DefaultVal, - const ento::CheckerBase *C = nullptr, - bool SearchInParents = false); - - unsigned getOptionAsUInt(Optional &V, StringRef Name, - unsigned DefaultVal, - const ento::CheckerBase *C = nullptr, - bool SearchInParents = false); + int getCheckerIntegerOption(StringRef Name, int DefaultVal, + const ento::CheckerBase *C, + bool SearchInParents = false) const; /// Query an option's string value. /// @@ -308,29 +292,38 @@ /// @param [in] Name Name for option to retrieve. /// @param [in] DefaultVal Default value returned if no such option was /// specified. - /// @param [in] C The optional checker parameter that can be used to restrict - /// the search to the options of this particular checker (and its parents - /// depending on search mode). + /// @param [in] C The checker object the option belongs to. Checker options + /// are retrieved in the following format: + /// `-analyzer-config :OptionName=Value. /// @param [in] SearchInParents If set to true and the searched option was not /// specified for the given checker the options for the parent packages will /// be searched as well. The inner packages take precedence over the outer /// ones. - StringRef getOptionAsString(StringRef Name, StringRef DefaultVal, - const ento::CheckerBase *C = nullptr, - bool SearchInParents = false); - - StringRef getOptionAsString(Optional &V, StringRef Name, - StringRef DefaultVal, - const ento::CheckerBase *C = nullptr, - bool SearchInParents = false); + StringRef getCheckerStringOption(StringRef Name, StringRef DefaultVal, + const ento::CheckerBase *C, + bool SearchInParents = false) const; #define ANALYZER_OPTION_GEN_FN(TYPE, NAME, CMDFLAG, DESC, DEFAULT_VAL, \ - CREATE_FN) \ - TYPE CREATE_FN(); + CREATE_FN) \ + TYPE CREATE_FN() { \ + initOption(NAME, CMDFLAG, DEFAULT_VAL); \ + return NAME.getValue(); \ + } #define ANALYZER_OPTION_GEN_FN_DEPENDS_ON_USER_MODE( \ TYPE, NAME, CMDFLAG, DESC, SHALLOW_VAL, DEEP_VAL, CREATE_FN) \ - TYPE CREATE_FN(); + TYPE CREATE_FN() { \ + switch (getUserMode()) { \ + case UMK_Shallow: \ + initOption(NAME, CMDFLAG, SHALLOW_VAL); \ + break; \ + case UMK_Deep: \ + initOption(NAME, CMDFLAG, DEEP_VAL); \ + break; \ + } \ + \ + return NAME.getValue(); \ + } #include "clang/StaticAnalyzer/Core/AnalyzerOptions.def" Index: lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp +++ lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp @@ -45,8 +45,8 @@ check::LiveSymbols> { bool isCallbackEnabled(AnalyzerOptions &Opts, StringRef CallbackName) const { - return Opts.getBooleanOption("*", false, this) || - Opts.getBooleanOption(CallbackName, false, this); + return Opts.getCheckerBooleanOption("*", false, this) || + Opts.getCheckerBooleanOption(CallbackName, false, this); } bool isCallbackEnabled(CheckerContext &C, StringRef CallbackName) const { Index: lib/StaticAnalyzer/Checkers/CloneChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/CloneChecker.cpp +++ lib/StaticAnalyzer/Checkers/CloneChecker.cpp @@ -63,18 +63,18 @@ // At this point, every statement in the translation unit has been analyzed by // the CloneDetector. The only thing left to do is to report the found clones. - int MinComplexity = Mgr.getAnalyzerOptions().getOptionAsInteger( + int MinComplexity = Mgr.getAnalyzerOptions().getCheckerIntegerOption( "MinimumCloneComplexity", 50, this); assert(MinComplexity >= 0); - bool ReportSuspiciousClones = Mgr.getAnalyzerOptions().getBooleanOption( - "ReportSuspiciousClones", true, this); + bool ReportSuspiciousClones = Mgr.getAnalyzerOptions() + .getCheckerBooleanOption("ReportSuspiciousClones", true, this); - bool ReportNormalClones = Mgr.getAnalyzerOptions().getBooleanOption( + bool ReportNormalClones = Mgr.getAnalyzerOptions().getCheckerBooleanOption( "ReportNormalClones", true, this); - StringRef IgnoredFilesPattern = Mgr.getAnalyzerOptions().getOptionAsString( - "IgnoredFilesPattern", "", this); + StringRef IgnoredFilesPattern = Mgr.getAnalyzerOptions() + .getCheckerStringOption("IgnoredFilesPattern", "", this); // Let the CloneDetector create a list of clones from all the analyzed // statements. We don't filter for matching variable patterns at this point Index: lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp +++ lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp @@ -1398,8 +1398,8 @@ NonLocalizedStringChecker *checker = mgr.registerChecker(); checker->IsAggressive = - mgr.getAnalyzerOptions().getBooleanOption("AggressiveReport", false, - checker); + mgr.getAnalyzerOptions().getCheckerBooleanOption("AggressiveReport", + false, checker); } void ento::registerEmptyLocalizationContextChecker(CheckerManager &mgr) { Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -3109,7 +3109,7 @@ void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) { registerCStringCheckerBasic(mgr); MallocChecker *checker = mgr.registerChecker(); - checker->IsOptimistic = mgr.getAnalyzerOptions().getBooleanOption( + checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption( "Optimistic", false, checker); checker->ChecksEnabled[MallocChecker::CK_NewDeleteLeaksChecker] = true; checker->CheckNames[MallocChecker::CK_NewDeleteLeaksChecker] = @@ -3130,7 +3130,7 @@ void ento::registerInnerPointerCheckerAux(CheckerManager &mgr) { registerCStringCheckerBasic(mgr); MallocChecker *checker = mgr.registerChecker(); - checker->IsOptimistic = mgr.getAnalyzerOptions().getBooleanOption( + checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption( "Optimistic", false, checker); checker->ChecksEnabled[MallocChecker::CK_InnerPointerChecker] = true; checker->CheckNames[MallocChecker::CK_InnerPointerChecker] = @@ -3141,7 +3141,7 @@ void ento::register##name(CheckerManager &mgr) { \ registerCStringCheckerBasic(mgr); \ MallocChecker *checker = mgr.registerChecker(); \ - checker->IsOptimistic = mgr.getAnalyzerOptions().getBooleanOption( \ + checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption( \ "Optimistic", false, checker); \ checker->ChecksEnabled[MallocChecker::CK_##name] = true; \ checker->CheckNames[MallocChecker::CK_##name] = mgr.getCurrentCheckName(); \ Index: lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp +++ lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp @@ -82,7 +82,9 @@ MmapWriteExecChecker *Mwec = mgr.registerChecker(); Mwec->ProtExecOv = - mgr.getAnalyzerOptions().getOptionAsInteger("MmapProtExec", 0x04, Mwec); + mgr.getAnalyzerOptions() + .getCheckerIntegerOption("MmapProtExec", 0x04, Mwec); Mwec->ProtReadOv = - mgr.getAnalyzerOptions().getOptionAsInteger("MmapProtRead", 0x01, Mwec); + mgr.getAnalyzerOptions() + .getCheckerIntegerOption("MmapProtRead", 0x01, Mwec); } Index: lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp +++ lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -1192,7 +1192,7 @@ checker->NeedTracking = checker->NeedTracking || trackingRequired; \ checker->NoDiagnoseCallsToSystemHeaders = \ checker->NoDiagnoseCallsToSystemHeaders || \ - mgr.getAnalyzerOptions().getBooleanOption( \ + mgr.getAnalyzerOptions().getCheckerBooleanOption( \ "NoDiagnoseCallsToSystemHeaders", false, checker, true); \ } Index: lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp +++ lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp @@ -346,5 +346,5 @@ NumberObjectConversionChecker *Chk = Mgr.registerChecker(); Chk->Pedantic = - Mgr.getAnalyzerOptions().getBooleanOption("Pedantic", false, Chk); + Mgr.getAnalyzerOptions().getCheckerBooleanOption("Pedantic", false, Chk); } Index: lib/StaticAnalyzer/Checkers/PaddingChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/PaddingChecker.cpp +++ lib/StaticAnalyzer/Checkers/PaddingChecker.cpp @@ -41,7 +41,8 @@ BugReporter &BRArg) const { BR = &BRArg; AllowedPad = - MGR.getAnalyzerOptions().getOptionAsInteger("AllowedPad", 24, this); + MGR.getAnalyzerOptions() + .getCheckerIntegerOption("AllowedPad", 24, this); assert(AllowedPad >= 0 && "AllowedPad option should be non-negative"); // The calls to checkAST* from AnalysisConsumer don't Index: lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h =================================================================== --- lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h +++ lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h @@ -278,13 +278,14 @@ RetainCountChecker(AnalyzerOptions &Options) : Options(Options), ShouldResetSummaryLog(false), IncludeAllocationLine( - Options.getBooleanOption("leak-diagnostics-reference-allocation", - false, this)) {} + Options.getCheckerBooleanOption( + "leak-diagnostics-reference-allocation", + false, this)) {} ~RetainCountChecker() override { DeleteContainerSeconds(DeadSymbolTags); } bool shouldCheckOSObjectRetainCount() const { - return Options.getBooleanOption("CheckOSObject", false, this); + return Options.getCheckerBooleanOption("CheckOSObject", false, this); } void checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, Index: lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp +++ lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp @@ -487,12 +487,12 @@ UninitObjCheckerOptions &ChOpts = Chk->Opts; ChOpts.IsPedantic = - AnOpts.getBooleanOption("Pedantic", /*DefaultVal*/ false, Chk); + AnOpts.getCheckerBooleanOption("Pedantic", /*DefaultVal*/ false, Chk); ChOpts.ShouldConvertNotesToWarnings = - AnOpts.getBooleanOption("NotesAsWarnings", /*DefaultVal*/ false, Chk); - ChOpts.CheckPointeeInitialization = AnOpts.getBooleanOption( + AnOpts.getCheckerBooleanOption("NotesAsWarnings", /*DefaultVal*/ false, Chk); + ChOpts.CheckPointeeInitialization = AnOpts.getCheckerBooleanOption( "CheckPointeeInitialization", /*DefaultVal*/ false, Chk); ChOpts.IgnoredRecordsWithFieldPattern = - AnOpts.getOptionAsString("IgnoreRecordsWithField", + AnOpts.getCheckerStringOption("IgnoreRecordsWithField", /*DefaultVal*/ "", Chk); } Index: lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp +++ lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp @@ -280,5 +280,6 @@ VirtualCallChecker *checker = mgr.registerChecker(); checker->IsPureOnly = - mgr.getAnalyzerOptions().getBooleanOption("PureOnly", false, checker); + mgr.getAnalyzerOptions().getCheckerBooleanOption("PureOnly", false, + checker); } Index: lib/StaticAnalyzer/Core/AnalyzerOptions.cpp =================================================================== --- lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -51,7 +51,7 @@ UserModeKind AnalyzerOptions::getUserMode() { if (!UserMode.hasValue()) { - UserMode = getOptionAsString("mode", "deep"); + UserMode = getStringOption("mode", "deep"); } auto K = llvm::StringSwitch>(*UserMode) @@ -65,7 +65,7 @@ ExplorationStrategyKind AnalyzerOptions::getExplorationStrategy() { if (!ExplorationStrategy.hasValue()) { - ExplorationStrategy = getOptionAsString("exploration_strategy", + ExplorationStrategy = getStringOption("exploration_strategy", "unexplored_first_queue"); } auto K = @@ -90,10 +90,10 @@ if (!IPAMode.hasValue()) { switch (getUserMode()) { case UMK_Shallow: - IPAMode = getOptionAsString("ipa", "inlining"); + IPAMode = getStringOption("ipa", "inlining"); break; case UMK_Deep: - IPAMode = getOptionAsString("ipa", "dynamic-bifurcate"); + IPAMode = getStringOption("ipa", "dynamic-bifurcate"); break; } } @@ -112,7 +112,7 @@ bool AnalyzerOptions::mayInlineCXXMemberFunction(CXXInlineableMemberKind Param) { if (!CXXMemberInliningMode.hasValue()) { - CXXMemberInliningMode = getOptionAsString("c++-inlining", "destructors"); + CXXMemberInliningMode = getStringOption("c++-inlining", "destructors"); } if (getIPAMode() < IPAK_Inlining) @@ -132,14 +132,55 @@ return *K >= Param; } +StringRef AnalyzerOptions::getStringOption(StringRef OptionName, + StringRef DefaultVal) { + return Config.insert({OptionName, DefaultVal}).first->second; +} + static StringRef toString(bool b) { return b ? "true" : "false"; } -StringRef AnalyzerOptions::getCheckerOption(StringRef CheckerName, - StringRef OptionName, - StringRef Default, - bool SearchInParents) { +void AnalyzerOptions::initOption(Optional &V, StringRef Name, + StringRef DefaultVal) { + if (V.hasValue()) + return; + + V = getStringOption(Name, DefaultVal); +} + +void AnalyzerOptions::initOption(Optional &V, StringRef Name, + bool DefaultVal) { + if (V.hasValue()) + return; + + // FIXME: We should emit a warning here if the value is something other than + // "true", "false", or the empty string (meaning the default value), + // but the AnalyzerOptions doesn't have access to a diagnostic engine. + V = llvm::StringSwitch(getStringOption(Name, toString(DefaultVal))) + .Case("true", true) + .Case("false", false) + .Default(DefaultVal); +} + +void AnalyzerOptions::initOption(Optional &V, StringRef Name, + unsigned DefaultVal) { + if (V.hasValue()) + return; + + V = DefaultVal; + bool HasFailed = getStringOption(Name, std::to_string(DefaultVal)) + .getAsInteger(10, *V); + assert(!HasFailed && "analyzer-config option should be numeric"); + (void)HasFailed; +} + +StringRef AnalyzerOptions::getCheckerStringOption(StringRef OptionName, + StringRef DefaultVal, + const CheckerBase *C, + bool SearchInParents) const { + assert(C); // Search for a package option if the option for the checker is not specified // and search in parents is enabled. + StringRef CheckerName = C->getTagDescription(); ConfigTable::const_iterator E = Config.end(); do { ConfigTable::const_iterator I = @@ -148,128 +189,41 @@ return StringRef(I->getValue()); size_t Pos = CheckerName.rfind('.'); if (Pos == StringRef::npos) - return Default; + return DefaultVal; CheckerName = CheckerName.substr(0, Pos); } while (!CheckerName.empty() && SearchInParents); - return Default; + return DefaultVal; } -bool AnalyzerOptions::getBooleanOption(StringRef Name, bool DefaultVal, - const CheckerBase *C, - bool SearchInParents) { +bool AnalyzerOptions::getCheckerBooleanOption(StringRef Name, bool DefaultVal, + const CheckerBase *C, + bool SearchInParents) const { // FIXME: We should emit a warning here if the value is something other than // "true", "false", or the empty string (meaning the default value), // but the AnalyzerOptions doesn't have access to a diagnostic engine. - StringRef Default = toString(DefaultVal); - StringRef V = - C ? getCheckerOption(C->getTagDescription(), Name, Default, - SearchInParents) - : getOptionAsString(Name, Default); - return llvm::StringSwitch(V) + assert(C); + return llvm::StringSwitch( + getCheckerStringOption(Name, toString(DefaultVal), C, SearchInParents)) .Case("true", true) .Case("false", false) .Default(DefaultVal); } -bool AnalyzerOptions::getBooleanOption(Optional &V, StringRef Name, - bool DefaultVal, const CheckerBase *C, - bool SearchInParents) { - if (!V.hasValue()) - V = getBooleanOption(Name, DefaultVal, C, SearchInParents); - return V.getValue(); -} - -int AnalyzerOptions::getOptionAsInteger(StringRef Name, int DefaultVal, +int AnalyzerOptions::getCheckerIntegerOption(StringRef Name, int DefaultVal, const CheckerBase *C, - bool SearchInParents) { - SmallString<10> StrBuf; - llvm::raw_svector_ostream OS(StrBuf); - OS << DefaultVal; - - StringRef V = C ? getCheckerOption(C->getTagDescription(), Name, OS.str(), - SearchInParents) - : getOptionAsString(Name, OS.str()); - - int Res = DefaultVal; - bool b = V.getAsInteger(10, Res); - assert(!b && "analyzer-config option should be numeric"); - (void)b; - return Res; -} - -unsigned AnalyzerOptions::getOptionAsUInt(Optional &V, StringRef Name, - unsigned DefaultVal, - const CheckerBase *C, - bool SearchInParents) { - if (!V.hasValue()) - V = getOptionAsInteger(Name, DefaultVal, C, SearchInParents); - return V.getValue(); -} - -StringRef AnalyzerOptions::getOptionAsString(StringRef Name, - StringRef DefaultVal, - const CheckerBase *C, - bool SearchInParents) { - return C ? getCheckerOption(C->getTagDescription(), Name, DefaultVal, - SearchInParents) - : StringRef( - Config.insert(std::make_pair(Name, DefaultVal)).first->second); -} - -StringRef AnalyzerOptions::getOptionAsString(Optional &V, - StringRef Name, - StringRef DefaultVal, - const ento::CheckerBase *C, - bool SearchInParents) { - if (!V.hasValue()) - V = getOptionAsString(Name, DefaultVal, C, SearchInParents); - return V.getValue(); -} - -static bool getOption(AnalyzerOptions &A, Optional &V, StringRef Name, - bool DefaultVal) { - return A.getBooleanOption(V, Name, DefaultVal); -} - -static unsigned getOption(AnalyzerOptions &A, Optional &V, - StringRef Name, unsigned DefaultVal) { - return A.getOptionAsUInt(V, Name, DefaultVal); -} - -static StringRef getOption(AnalyzerOptions &A, Optional &V, - StringRef Name, StringRef DefaultVal) { - return A.getOptionAsString(V, Name, DefaultVal); -} - -#define ANALYZER_OPTION_GEN_FN(TYPE, NAME, CMDFLAG, DESC, DEFAULT_VAL, \ - CREATE_FN) \ -TYPE AnalyzerOptions::CREATE_FN() { \ - return getOption(*this, NAME, CMDFLAG, DEFAULT_VAL); \ + bool SearchInParents) const { + int Ret = DefaultVal; + bool HasFailed = getCheckerStringOption(Name, std::to_string(DefaultVal), C, + SearchInParents) + .getAsInteger(10, Ret); + assert(!HasFailed && "analyzer-config option should be numeric"); + (void)HasFailed; + return Ret; } -#define ANALYZER_OPTION_GEN_FN_DEPENDS_ON_USER_MODE( \ - TYPE, NAME, CMDFLAG, DESC, SHALLOW_VAL, DEEP_VAL, CREATE_FN) \ -TYPE AnalyzerOptions::CREATE_FN() { \ - switch (getUserMode()) { \ - case UMK_Shallow: \ - return getOption(*this, NAME, CMDFLAG, SHALLOW_VAL); \ - case UMK_Deep: \ - return getOption(*this, NAME, CMDFLAG, DEEP_VAL); \ - } \ - \ - llvm_unreachable("Unknown usermode!"); \ - return {}; \ -} - -#include "clang/StaticAnalyzer/Core/AnalyzerOptions.def" - -#undef ANALYZER_OPTION_GEN_FN_DEPENDS_ON_USER_MODE -#undef ANALYZER_OPTION_WITH_FN - - StringRef AnalyzerOptions::getCTUDir() { if (!CTUDir.hasValue()) { - CTUDir = getOptionAsString("ctu-dir", ""); + CTUDir = getStringOption("ctu-dir", ""); if (!llvm::sys::fs::is_directory(*CTUDir)) CTUDir = ""; } Index: unittests/StaticAnalyzer/AnalyzerOptionsTest.cpp =================================================================== --- unittests/StaticAnalyzer/AnalyzerOptionsTest.cpp +++ unittests/StaticAnalyzer/AnalyzerOptionsTest.cpp @@ -52,23 +52,25 @@ // Checker one has Option specified as true. It should read true regardless of // search mode. CheckerOneMock CheckerOne; - EXPECT_TRUE(Opts.getBooleanOption("Option", false, &CheckerOne)); + EXPECT_TRUE(Opts.getCheckerBooleanOption("Option", false, &CheckerOne)); // The package option is overridden with a checker option. - EXPECT_TRUE(Opts.getBooleanOption("Option", false, &CheckerOne, true)); + EXPECT_TRUE(Opts.getCheckerBooleanOption("Option", false, &CheckerOne, + true)); // The Outer package option is overridden by the Inner package option. No // package option is specified. - EXPECT_TRUE(Opts.getBooleanOption("Option2", false, &CheckerOne, true)); + EXPECT_TRUE(Opts.getCheckerBooleanOption("Option2", false, &CheckerOne, + true)); // No package option is specified and search in packages is turned off. The // default value should be returned. - EXPECT_FALSE(Opts.getBooleanOption("Option2", false, &CheckerOne)); - EXPECT_TRUE(Opts.getBooleanOption("Option2", true, &CheckerOne)); + EXPECT_FALSE(Opts.getCheckerBooleanOption("Option2", false, &CheckerOne)); + EXPECT_TRUE(Opts.getCheckerBooleanOption("Option2", true, &CheckerOne)); // Checker true has no option specified. It should get the default value when // search in parents turned off and false when search in parents turned on. CheckerTwoMock CheckerTwo; - EXPECT_FALSE(Opts.getBooleanOption("Option", false, &CheckerTwo)); - EXPECT_TRUE(Opts.getBooleanOption("Option", true, &CheckerTwo)); - EXPECT_FALSE(Opts.getBooleanOption("Option", true, &CheckerTwo, true)); + EXPECT_FALSE(Opts.getCheckerBooleanOption("Option", false, &CheckerTwo)); + EXPECT_TRUE(Opts.getCheckerBooleanOption("Option", true, &CheckerTwo)); + EXPECT_FALSE(Opts.getCheckerBooleanOption("Option", true, &CheckerTwo, true)); } TEST(StaticAnalyzerOptions, StringOptions) { @@ -83,9 +85,9 @@ CheckerOneMock CheckerOne; EXPECT_TRUE("StringValue" == - Opts.getOptionAsString("Option", "DefaultValue", &CheckerOne)); + Opts.getCheckerStringOption("Option", "DefaultValue", &CheckerOne)); EXPECT_TRUE("DefaultValue" == - Opts.getOptionAsString("Option2", "DefaultValue", &CheckerOne)); + Opts.getCheckerStringOption("Option2", "DefaultValue", &CheckerOne)); } } // end namespace ento } // end namespace clang