]> granicus.if.org Git - clang/commitdiff
[analyzer] Restrict AnalyzerOptions' interface so that non-checker objects have to...
authorKristof Umann <dkszelethus@gmail.com>
Mon, 5 Nov 2018 03:50:37 +0000 (03:50 +0000)
committerKristof Umann <dkszelethus@gmail.com>
Mon, 5 Nov 2018 03:50:37 +0000 (03:50 +0000)
One of the reasons why AnalyzerOptions is so chaotic is that options can be
retrieved from the command line whenever and wherever. This allowed for some
options to be forgotten for a looooooong time. Have you ever heard of
"region-store-small-struct-limit"? In order to prevent this in the future, I'm
proposing to restrict AnalyzerOptions' interface so that only checker options
can be retrieved without special getters. I would like to make every option be
accessible only through a getter, but checkers from plugins are a thing, so I'll
have to figure something out for that.

This also forces developers who'd like to add a new option to register it
properly in the .def file.

This is done by

* making the third checker pointer parameter non-optional, and checked by an
  assert to be non-null.
* I added new, but private non-checkers option initializers, meant only for
  internal use,
* Renamed these methods accordingly (mind the consistent name for once with
  getBooleanOption!):
  - getOptionAsString -> getCheckerStringOption,
  - getOptionAsInteger -> getCheckerIntegerOption
* The 3 functions meant for initializing data members (with the not very
  descriptive getBooleanOption, getOptionAsString and getOptionAsUInt names)
  were renamed to be overloads of the getAndInitOption function name.
* All options were in some way retrieved via getCheckerOption. I removed it, and
  moved the logic to getStringOption and getCheckerStringOption. This did cause
  some code duplication, but that's the only way I could do it, now that checker
  and non-checker options are separated. Note that the non-checker version
  inserts the new option to the ConfigTable with the default value, but the
  checker version only attempts to find already existing entries. This is how
  it always worked, but this is clunky and I might end reworking that too, so we
  can eventually get a ConfigTable that contains the entire configuration of the
  analyzer.

Differential Revision: https://reviews.llvm.org/D53483

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@346113 91177308-0d34-0410-b5e6-96231b3b80d8

14 files changed:
include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp
lib/StaticAnalyzer/Checkers/CloneChecker.cpp
lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
lib/StaticAnalyzer/Checkers/MallocChecker.cpp
lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp
lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp
lib/StaticAnalyzer/Checkers/PaddingChecker.cpp
lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
unittests/StaticAnalyzer/AnalyzerOptionsTest.cpp

index 635106c991a39736ac9a27865f4ebaeb1ce72813..d1bbd71bb5a74fc8407b7d9877143ee6d366f439 100644 (file)
@@ -137,6 +137,28 @@ enum UserModeKind {
   UMK_Deep = 2
 };
 
+/// Stores options for the analyzer from the command line.
+///
+/// Some options are frontend 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 frontend 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.
+///
+/// Some of the options are controlled by raw frontend flags for no good reason,
+/// and should be eventually converted into -analyzer-config flags. New analyzer
+/// options should not be implemented as frontend flags. Frontend flags still
+/// make sense for things that do not affect the actual analysis.
 class AnalyzerOptions : public RefCountedBase<AnalyzerOptions> {
 public:
   using ConfigTable = llvm::StringMap<std::string>;
@@ -209,32 +231,21 @@ private:
 #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<StringRef> &V, StringRef Name,
+                                          StringRef DefaultVal);
 
+  void initOption(Optional<bool> &V, StringRef Name, bool DefaultVal);
+
+  void initOption(Optional<unsigned> &V, StringRef Name,
+                                         unsigned DefaultVal);
 public:
   AnalyzerOptions()
       : DisableAllChecks(false), ShowCheckerHelp(false),
@@ -252,34 +263,17 @@ public:
   /// @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 <package and checker name>: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<bool> &V, StringRef Name, bool DefaultVal,
-                        const ento::CheckerBase *C  = nullptr,
-                        bool SearchInParents = false);
 
   /// Interprets an option's string value as an integer value.
   ///
@@ -287,21 +281,16 @@ public:
   /// @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 <package and checker name>: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<unsigned> &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.
   ///
@@ -309,29 +298,38 @@ public:
   /// @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 <package and checker name>: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<StringRef> &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 cc306329dc1fe2ebac9dfd476eb648eb514b0b4c..f9cf97e5083574e308454f2aacc0080437283838 100644 (file)
@@ -45,8 +45,8 @@ class AnalysisOrderChecker
                      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 ee517ed97770006cec8449ef99b3f583e9d7614f..309a29b2a45c52a32d0b6dd1c2b5c9a29299885d 100644 (file)
@@ -63,18 +63,18 @@ void CloneChecker::checkEndOfTranslationUnit(const TranslationUnitDecl *TU,
   // 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 ec05c737ac26b7a6359f5df3dc494b6b96049df3..103a33d39f9658479038417ed1caba45b270b41a 100644 (file)
@@ -1398,8 +1398,8 @@ void ento::registerNonLocalizedStringChecker(CheckerManager &mgr) {
   NonLocalizedStringChecker *checker =
       mgr.registerChecker<NonLocalizedStringChecker>();
   checker->IsAggressive =
-      mgr.getAnalyzerOptions().getBooleanOption("AggressiveReport", false,
-                                                checker);
+      mgr.getAnalyzerOptions().getCheckerBooleanOption("AggressiveReport",
+                                                       false, checker);
 }
 
 void ento::registerEmptyLocalizationContextChecker(CheckerManager &mgr) {
index fbd8a9d4d872c472a9a14676db1bb6ca7390bb18..07483cd471473f3ddeee15c6bd6fc9c35ad26a10 100644 (file)
@@ -3084,7 +3084,7 @@ markReleased(ProgramStateRef State, SymbolRef Sym, const Expr *Origin) {
 void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) {
   registerCStringCheckerBasic(mgr);
   MallocChecker *checker = mgr.registerChecker<MallocChecker>();
-  checker->IsOptimistic = mgr.getAnalyzerOptions().getBooleanOption(
+  checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption(
       "Optimistic", false, checker);
   checker->ChecksEnabled[MallocChecker::CK_NewDeleteLeaksChecker] = true;
   checker->CheckNames[MallocChecker::CK_NewDeleteLeaksChecker] =
@@ -3105,7 +3105,7 @@ void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) {
 void ento::registerInnerPointerCheckerAux(CheckerManager &mgr) {
     registerCStringCheckerBasic(mgr);
     MallocChecker *checker = mgr.registerChecker<MallocChecker>();
-    checker->IsOptimistic = mgr.getAnalyzerOptions().getBooleanOption(
+    checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption(
         "Optimistic", false, checker);
     checker->ChecksEnabled[MallocChecker::CK_InnerPointerChecker] = true;
     checker->CheckNames[MallocChecker::CK_InnerPointerChecker] =
@@ -3116,7 +3116,7 @@ void ento::registerInnerPointerCheckerAux(CheckerManager &mgr) {
   void ento::register##name(CheckerManager &mgr) {                             \
     registerCStringCheckerBasic(mgr);                                          \
     MallocChecker *checker = mgr.registerChecker<MallocChecker>();             \
-    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 5060b0e0a6e0726c5e9719cc8df2372284432273..0d63cfe93767eba394978b3db2ff6f5e636d1dae 100644 (file)
@@ -82,7 +82,9 @@ void ento::registerMmapWriteExecChecker(CheckerManager &mgr) {
   MmapWriteExecChecker *Mwec =
       mgr.registerChecker<MmapWriteExecChecker>();
   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 ad68dc2f49db66167a805dec597895fc37b4c56b..6345c6aac5d2d7802725a20230ce4b9fafa1f563 100644 (file)
@@ -1193,7 +1193,7 @@ void NullabilityChecker::printState(raw_ostream &Out, ProgramStateRef State,
     checker->NeedTracking = checker->NeedTracking || trackingRequired;         \
     checker->NoDiagnoseCallsToSystemHeaders =                                  \
         checker->NoDiagnoseCallsToSystemHeaders ||                             \
-        mgr.getAnalyzerOptions().getBooleanOption(                             \
+        mgr.getAnalyzerOptions().getCheckerBooleanOption(                             \
                       "NoDiagnoseCallsToSystemHeaders", false, checker, true); \
   }
 
index 56b42239d7c38b0bd11e6c6d9c772e8b3301a6cd..d342bd072a4e8157c1628468d98a24a4c33eb262 100644 (file)
@@ -346,5 +346,5 @@ void ento::registerNumberObjectConversionChecker(CheckerManager &Mgr) {
   NumberObjectConversionChecker *Chk =
       Mgr.registerChecker<NumberObjectConversionChecker>();
   Chk->Pedantic =
-      Mgr.getAnalyzerOptions().getBooleanOption("Pedantic", false, Chk);
+      Mgr.getAnalyzerOptions().getCheckerBooleanOption("Pedantic", false, Chk);
 }
index 04164a97569f7836cc8db8a3893686fbbc7311e8..dc361ad537ada4c7b6d80b84c0931fad4c3a8d3f 100644 (file)
@@ -41,7 +41,8 @@ public:
                     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 89d3795b1b011e49bed752ede5f3dfa99dce3aac..c75a7b046b9ec13a039dec8646aefd8bd453bd44 100644 (file)
@@ -1397,8 +1397,8 @@ void ento::registerRetainCountChecker(CheckerManager &Mgr) {
 
   AnalyzerOptions &Options = Mgr.getAnalyzerOptions();
 
-  Chk->IncludeAllocationLine = Options.getBooleanOption(
+  Chk->IncludeAllocationLine = Options.getCheckerBooleanOption(
                            "leak-diagnostics-reference-allocation", false, Chk);
-  Chk->ShouldCheckOSObjectRetainCount = Options.getBooleanOption(
-                                                   "CheckOSObject", true, Chk);
+  Chk->ShouldCheckOSObjectRetainCount = Options.getCheckerBooleanOption(
+                                                    "CheckOSObject", true, Chk);
 }
index d31d3a2112172578c4561a8d309db89c2dbc3e38..e4a17a9dd28ca6d98c0044dd08a3f27a19ec35ac 100644 (file)
@@ -488,12 +488,12 @@ void ento::registerUninitializedObjectChecker(CheckerManager &Mgr) {
   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 cf673de6d475614334805fca230412d3cc8e1e15..902b325dec8772c39d31cadc2eb70db00747d92b 100644 (file)
@@ -280,5 +280,6 @@ void ento::registerVirtualCallChecker(CheckerManager &mgr) {
   VirtualCallChecker *checker = mgr.registerChecker<VirtualCallChecker>();
 
   checker->IsPureOnly =
-      mgr.getAnalyzerOptions().getBooleanOption("PureOnly", false, checker);
+      mgr.getAnalyzerOptions().getCheckerBooleanOption("PureOnly", false,
+                                                       checker);
 }
index 0700bd08959c28dde1126aa7c1b95cbed76a3d1d..37bbcb4219fbdb1c1da5531a0bc950fa0fbf004e 100644 (file)
@@ -51,7 +51,7 @@ AnalyzerOptions::getRegisteredCheckers(bool IncludeExperimental /* = false */) {
 
 UserModeKind AnalyzerOptions::getUserMode() {
   if (!UserMode.hasValue()) {
-    UserMode = getOptionAsString("mode", "deep");
+    UserMode = getStringOption("mode", "deep");
   }
 
   auto K = llvm::StringSwitch<llvm::Optional<UserModeKind>>(*UserMode)
@@ -65,7 +65,7 @@ UserModeKind AnalyzerOptions::getUserMode() {
 ExplorationStrategyKind
 AnalyzerOptions::getExplorationStrategy() {
   if (!ExplorationStrategy.hasValue()) {
-    ExplorationStrategy = getOptionAsString("exploration_strategy",
+    ExplorationStrategy = getStringOption("exploration_strategy",
                                             "unexplored_first_queue");
   }
   auto K =
@@ -90,10 +90,10 @@ IPAKind AnalyzerOptions::getIPAMode() {
   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 @@ IPAKind AnalyzerOptions::getIPAMode() {
 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,62 @@ AnalyzerOptions::mayInlineCXXMemberFunction(CXXInlineableMemberKind Param) {
   return *K >= Param;
 }
 
-static StringRef toString(bool b) { return b ? "true" : "false"; }
+StringRef AnalyzerOptions::getStringOption(StringRef OptionName,
+                                           StringRef DefaultVal) {
+  return Config.insert({OptionName, DefaultVal}).first->second;
+}
+
+static StringRef toString(bool B) { return (B ? "true" : "false"); }
+
+template <typename T>
+static StringRef toString(T) = delete;
+
+void AnalyzerOptions::initOption(Optional<StringRef> &V, StringRef Name,
+                                                         StringRef DefaultVal) {
+  if (V.hasValue())
+    return;
+
+  V = getStringOption(Name, DefaultVal);
+}
 
-StringRef AnalyzerOptions::getCheckerOption(StringRef CheckerName,
-                                            StringRef OptionName,
-                                            StringRef Default,
-                                            bool SearchInParents) {
+void AnalyzerOptions::initOption(Optional<bool> &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<bool>(getStringOption(Name, toString(DefaultVal)))
+      .Case("true", true)
+      .Case("false", false)
+      .Default(DefaultVal);
+}
+
+void AnalyzerOptions::initOption(Optional<unsigned> &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();
+
+  assert(!CheckerName.empty() &&
+         "Empty checker name! Make sure the checker object (including it's "
+         "bases!) if fully initialized before calling this function!");
   ConfigTable::const_iterator E = Config.end();
   do {
     ConfigTable::const_iterator I =
@@ -148,127 +196,41 @@ StringRef AnalyzerOptions::getCheckerOption(StringRef CheckerName,
       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<bool>(V)
+  assert(C);
+  return llvm::StringSwitch<bool>(
+      getCheckerStringOption(Name, toString(DefaultVal), C, SearchInParents))
       .Case("true", true)
       .Case("false", false)
       .Default(DefaultVal);
 }
 
-bool AnalyzerOptions::getBooleanOption(Optional<bool> &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<unsigned> &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<StringRef> &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<bool> &V, StringRef Name,
-                      bool DefaultVal) {
-  return A.getBooleanOption(V, Name, DefaultVal);
+                                        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;
 }
 
-static unsigned getOption(AnalyzerOptions &A, Optional<unsigned> &V,
-                          StringRef Name, unsigned DefaultVal) {
-  return A.getOptionAsUInt(V, Name, DefaultVal);
-}
-
-static StringRef getOption(AnalyzerOptions &A, Optional<StringRef> &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);                  \
-}
-
-#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 3d8332554fbc104fcdfbf44490d0d8eec3667ec2..de41874bdeecc8488eab8f6f2ea57d052be5cf76 100644 (file)
@@ -52,23 +52,25 @@ TEST(StaticAnalyzerOptions, SearchInParentPackageTests) {
   // 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 @@ TEST(StaticAnalyzerOptions, StringOptions) {
 
   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