Index: include/clang/StaticAnalyzer/Core/AnalyzerOptions.h =================================================================== --- include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -20,12 +20,15 @@ #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" #include #include #include namespace clang { +class DiagnosticsEngine; + namespace ento { class CheckerBase; @@ -165,6 +168,7 @@ std::vector> CheckersControlList; /// A key-value table of use-specified configuration values. + // TODO: This shouldn't be public. ConfigTable Config; AnalysisStores AnalysisStoreOpt = RegionStoreModel; AnalysisConstraints AnalysisConstraintsOpt = RangeConstraintsModel; @@ -214,24 +218,25 @@ /// The mode of function selection used during inlining. AnalysisInliningMode InliningMode = NoRedundancy; -private: - -#define ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, DEFAULT_VAL) \ - Optional NAME; #define ANALYZER_OPTION_DEPENDS_ON_USER_MODE(TYPE, NAME, CMDFLAG, DESC, \ SHALLOW_VAL, DEEP_VAL) \ + ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, SHALLOW_VAL) + +#define ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, DEFAULT_VAL) \ Optional NAME; + #include "clang/StaticAnalyzer/Core/AnalyzerOptions.def" #undef ANALYZER_OPTION #undef ANALYZER_OPTION_DEPENDS_ON_USER_MODE +private: /// 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); + StringRef getStringOption(StringRef OptionName, StringRef DefaultVal) const; void initOption(Optional &V, StringRef Name, StringRef DefaultVal); @@ -250,6 +255,8 @@ UnoptimizedCFG(false), PrintStats(false), NoRetryExhausted(false) {} + void parseConfigs(ConfigTable config, DiagnosticsEngine &Diags); + /// Interprets an option's string value as a boolean. The "true" string is /// interpreted as true and the "false" string is interpreted as false. /// @@ -303,42 +310,15 @@ const ento::CheckerBase *C, bool SearchInParents = false) const; -#define ANALYZER_OPTION_GEN_FN(TYPE, NAME, CMDFLAG, DESC, DEFAULT_VAL, \ - 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() { \ - 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" - -#undef ANALYZER_OPTION_GEN_FN_DEPENDS_ON_USER_MODE -#undef ANALYZER_OPTION_WITH_FN - /// Retrieves and sets the UserMode. This is a high-level option, /// which is used to set other low-level options. It is not accessible /// outside of AnalyzerOptions. - UserModeKind getUserMode(); + UserModeKind getUserMode() const; - ExplorationStrategyKind getExplorationStrategy(); + ExplorationStrategyKind getExplorationStrategy() const; /// Returns the inter-procedural analysis mode. - IPAKind getIPAMode(); + IPAKind getIPAMode() const; /// Returns the option controlling which C++ member functions will be /// considered for inlining. @@ -346,13 +326,98 @@ /// This is controlled by the 'c++-inlining' config option. /// /// \sa CXXMemberInliningMode - bool mayInlineCXXMemberFunction(CXXInlineableMemberKind K); - - StringRef getCTUDir(); + bool mayInlineCXXMemberFunction(CXXInlineableMemberKind K) const; }; using AnalyzerOptionsRef = IntrusiveRefCntPtr; +//===----------------------------------------------------------------------===// +// We'll use AnalyzerOptions in the frontend, but we can't link the frontend +// with clangStaticAnalyzerCore, because clangStaticAnalyzerCore depends on +// clangFrontend. +// +// For this reason, implement some methods in this header file. +//===----------------------------------------------------------------------===// + +inline void AnalyzerOptions::parseConfigs(ConfigTable config, + DiagnosticsEngine &Diags) { + // TODO: Emit warnings for incorrect options. + // TODO: There's no need to store the entire configtable, it'd be plenty + // enough tostore checker options. + Config = std::move(config); + +#define ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, DEFAULT_VAL) \ + initOption(NAME, CMDFLAG, DEFAULT_VAL); \ + assert(NAME.hasValue()); + +#define ANALYZER_OPTION_DEPENDS_ON_USER_MODE(TYPE, NAME, CMDFLAG, DESC, \ + SHALLOW_VAL, DEEP_VAL) \ + switch (getUserMode()) { \ + case UMK_Shallow: \ + initOption(NAME, CMDFLAG, SHALLOW_VAL); \ + break; \ + case UMK_Deep: \ + initOption(NAME, CMDFLAG, DEEP_VAL); \ + break; \ + } \ + assert(NAME.hasValue()); + +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.def" +#undef ANALYZER_OPTION +#undef ANALYZER_OPTION_DEPENDS_ON_USER_MODE +} + +inline UserModeKind AnalyzerOptions::getUserMode() const { + auto K = llvm::StringSwitch>(*UserMode) + .Case("shallow", UMK_Shallow) + .Case("deep", UMK_Deep) + .Default(None); + assert(UserMode.hasValue() && "User mode is invalid."); + return K.getValue(); +} + +inline StringRef toString(bool b) { return b ? "true" : "false"; } + +inline StringRef AnalyzerOptions::getStringOption(StringRef OptionName, + StringRef DefaultVal) const { + if (Config.count(OptionName)) + return Config.find(OptionName)->second; + return DefaultVal; +} + +inline void AnalyzerOptions::initOption(Optional &V, StringRef Name, + StringRef DefaultVal) { + if (V.hasValue()) + return; + + V = getStringOption(Name, DefaultVal); +} + +inline 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); +} + +inline 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; +} } // namespace clang #endif // LLVM_CLANG_STATICANALYZER_CORE_ANALYZEROPTIONS_H Index: include/clang/StaticAnalyzer/Core/AnalyzerOptions.def =================================================================== --- include/clang/StaticAnalyzer/Core/AnalyzerOptions.def +++ include/clang/StaticAnalyzer/Core/AnalyzerOptions.def @@ -9,29 +9,6 @@ // // This file defines the analyzer options avaible with -analyzer-config. // -// This file is in part intended for method generation. If it's not included -// for that purpose, the following function-like macros should be predefined, -// through which all registered options are accessible: -// -// * ANALYZER_OPTION: Register a new option. -// * ANALYZER_OPTION_DEPENDS_ON_USER_MODE: Register a new option, default -// value depends on the "user-mode" option. -// -// Options where a simple getter method is sufficient are registered with the -// following macros: -// -// * ANALYZER_OPTION_GEN_FN: Register a new option, and generate a getter -// method for it in AnalyzerOptions. -// -// * ANALYZER_OPTION_GEN_FN_DEPENDS_ON_USER_MODE: Same as above, but -// generates a getter function that depends on the "user-mode" option. -// -// You can only include this file when both or none of the above two macros -// are defined! -// When they are defined, entries that do not generate functions won't appear, -// and when they aren't, all entries are converted to ANALYZER_OPTION or to -// ANALYZER_OPTION_DEPENDS_ON_USER_MODE. -// //===----------------------------------------------------------------------===// #ifndef LLVM_ADT_STRINGREF_H @@ -53,22 +30,6 @@ #endif #endif -#ifdef ANALYZER_OPTION_GEN_FN -#ifndef ANALYZER_OPTION_GEN_FN_DEPENDS_ON_USER_MODE -#error If you include this file with the intent of generating functions, \ -define both 'ANALYZER_OPTION_GEN_FN' and \ -'ANALYZER_OPTION_GEN_FN_DEPENDS_ON_USER_MODE' macros! -#endif -#endif - -#ifdef ANALYZER_OPTION_GEN_FN_DEPENDS_ON_USER_MODE -#ifndef ANALYZER_OPTION_GEN_FN -#error If you include this file with the intent of generating functions, \ -define both 'ANALYZER_OPTION_GEN_FN' and \ -'ANALYZER_OPTION_GEN_FN_DEPENDS_ON_USER_MODE' macros! -#endif -#endif - #ifndef ANALYZER_OPTION /// Create a new analyzer option, but dont generate a method for it in /// AnalyzerOptions. @@ -105,231 +66,187 @@ SHALLOW_VAL, DEEP_VAL) #endif -#ifndef ANALYZER_OPTION_GEN_FN -/// Create a new analyzer option, and generate a getter method for it in -/// AnalyzerOptions. -/// -/// TYPE - The type of the option object that will be stored in -/// AnalyzerOptions. This file is expected to be icluded in translation -/// units where AnalyzerOptions.h is included, so types from that -/// header should be used. -/// NAME - The name of the option object. -/// CMDFLAG - The command line flag for the option. -/// (-analyzer-config CMDFLAG=VALUE) -/// DESC - Description of the flag. -/// DEFAULT_VAL - The default value for CMDFLAG. -/// CREATE_FN - Name of the getter function. -// -// If this def file wasn't included with the intent of generating functions, -// regard all entries as ANALYZER_OPTION. -#define ANALYZER_OPTION_GEN_FN(TYPE, NAME, CMDFLAG, DESC, DEFAULT_VAL, \ - CREATE_FN) \ - ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, DEFAULT_VAL) -#endif +//===----------------------------------------------------------------------===// +// The "mode" option. Since some options depend on this, we list it on top of +// this file in order to make sure that the generated field for it is +// initialized before the rest. +//===----------------------------------------------------------------------===// -#ifndef ANALYZER_OPTION_GEN_FN_DEPENDS_ON_USER_MODE -/// Create a new analyzer option, and generate a getter method for it in -/// AnalyzerOptions, and make it's default value depend on the "user-mode" -/// option. -/// -/// TYPE - The type of the option object that will be stored in -/// AnalyzerOptions. This file is expected to be icluded in translation -/// units where AnalyzerOptions.h is included, so types from that -/// header should be used. -/// NAME - The name of the option object. -/// CMDFLAG - The command line flag for the option. -/// (-analyzer-config CMDFLAG=VALUE) -/// DESC - Description of the flago. -/// SHALLOW_VAL - The default value for CMDFLAG, when "user-mode" was set to -/// "shallow". -/// DEEP_VAL - The default value for CMDFLAG, when "user-mode" was set to -/// "deep". -/// CREATE_FN - Name of the getter function. -// -// If this def file wasn't included with the intent of generating functions, -// regard all entries as ANALYZER_OPTION_DEPENDS_ON_USER_MODE. -#define ANALYZER_OPTION_GEN_FN_DEPENDS_ON_USER_MODE( \ - TYPE, NAME, CMDFLAG, DESC, SHALLOW_VAL, DEEP_VAL, CREATE_FN) \ - ANALYZER_OPTION_DEPENDS_ON_USER_MODE(TYPE, NAME, CMDFLAG, DESC, SHALLOW_VAL, \ - DEEP_VAL) -#endif +ANALYZER_OPTION( + StringRef, UserMode, "mode", + "(string) Controls the high-level analyzer mode, which influences the " + "default settings for some of the lower-level config options (such as " + "IPAMode). Value: \"deep\", \"shallow\".", + "deep") //===----------------------------------------------------------------------===// // Boolean analyzer options. //===----------------------------------------------------------------------===// -ANALYZER_OPTION_GEN_FN(bool, IncludeImplicitDtorsInCFG, "cfg-implicit-dtors", - "Whether or not implicit destructors for C++ objects " - "should be included in the CFG.", - true, includeImplicitDtorsInCFG) +ANALYZER_OPTION(bool, ShouldIncludeImplicitDtorsInCFG, "cfg-implicit-dtors", + "Whether or not implicit destructors for C++ objects " + "should be included in the CFG.", + true) -ANALYZER_OPTION_GEN_FN(bool, IncludeTemporaryDtorsInCFG, "cfg-temporary-dtors", - "Whether or not the destructors for C++ temporary " - "objects should be included in the CFG.", - true, includeTemporaryDtorsInCFG) +ANALYZER_OPTION(bool, ShouldIncludeTemporaryDtorsInCFG, "cfg-temporary-dtors", + "Whether or not the destructors for C++ temporary " + "objects should be included in the CFG.", + true) -ANALYZER_OPTION_GEN_FN( - bool, IncludeLifetimeInCFG, "cfg-lifetime", +ANALYZER_OPTION( + bool, ShouldIncludeLifetimeInCFG, "cfg-lifetime", "Whether or not end-of-lifetime information should be included in the CFG.", - false, includeLifetimeInCFG) - -ANALYZER_OPTION_GEN_FN(bool, IncludeLoopExitInCFG, "cfg-loopexit", - "Whether or not the end of the loop information should " - "be included in the CFG.", - false, includeLoopExitInCFG) - -ANALYZER_OPTION_GEN_FN(bool, IncludeRichConstructorsInCFG, - "cfg-rich-constructors", - "Whether or not construction site information should be " - "included in the CFG C++ constructor elements.", - true, includeRichConstructorsInCFG) - -ANALYZER_OPTION_GEN_FN( - bool, IncludeScopesInCFG, "cfg-scopes", - "Whether or not scope information should be included in the CFG.", false, - includeScopesInCFG) - -ANALYZER_OPTION_GEN_FN( - bool, InlineTemplateFunctions, "c++-template-inlining", - "Whether or not templated functions may be considered for inlining.", true, - mayInlineTemplateFunctions) - -ANALYZER_OPTION_GEN_FN(bool, InlineCXXStandardLibrary, "c++-stdlib-inlining", - "Whether or not C++ standard library functions may be " - "considered for inlining.", - true, mayInlineCXXStandardLibrary) - -ANALYZER_OPTION_GEN_FN( - bool, InlineCXXAllocator, "c++-allocator-inlining", - "Whether or not allocator call may be considered for inlining.", true, - mayInlineCXXAllocator) - -ANALYZER_OPTION_GEN_FN( - bool, InlineCXXSharedPtrDtor, "c++-shared_ptr-inlining", + false) + +ANALYZER_OPTION(bool, ShouldIncludeLoopExitInCFG, "cfg-loopexit", + "Whether or not the end of the loop information should " + "be included in the CFG.", + false) + +ANALYZER_OPTION(bool, ShouldIncludeRichConstructorsInCFG, + "cfg-rich-constructors", + "Whether or not construction site information should be " + "included in the CFG C++ constructor elements.", + true) + +ANALYZER_OPTION( + bool, ShouldIncludeScopesInCFG, "cfg-scopes", + "Whether or not scope information should be included in the CFG.", false) + +ANALYZER_OPTION( + bool, MayInlineTemplateFunctions, "c++-template-inlining", + "Whether or not templated functions may be considered for inlining.", true) + +ANALYZER_OPTION(bool, MayInlineCXXStandardLibrary, "c++-stdlib-inlining", + "Whether or not C++ standard library functions may be " + "considered for inlining.", + true) + +ANALYZER_OPTION(bool, MayInlineCXXAllocator, "c++-allocator-inlining", + "Whether or not allocator call may be considered for inlining.", + true) + +ANALYZER_OPTION( + bool, MayInlineCXXSharedPtrDtor, "c++-shared_ptr-inlining", "Whether or not the destructor of C++ 'shared_ptr' may be considered for " "inlining. This covers std::shared_ptr, std::tr1::shared_ptr, and " "boost::shared_ptr, and indeed any destructor named '~shared_ptr'.", - false, mayInlineCXXSharedPtrDtor) + false) -ANALYZER_OPTION_GEN_FN(bool, InlineCXXTemporaryDtors, "c++-temp-dtor-inlining", - "Whether C++ temporary destructors should be inlined " - "during analysis. If temporary destructors are disabled " - "in the CFG via the 'cfg-temporary-dtors' option, " - "temporary destructors would not be inlined anyway.", - true, mayInlineCXXTemporaryDtors) +ANALYZER_OPTION(bool, MayInlineCXXTemporaryDtors, "c++-temp-dtor-inlining", + "Whether C++ temporary destructors should be inlined " + "during analysis. If temporary destructors are disabled " + "in the CFG via the 'cfg-temporary-dtors' option, " + "temporary destructors would not be inlined anyway.", + true) -ANALYZER_OPTION_GEN_FN( - bool, SuppressNullReturnPaths, "suppress-null-return-paths", +ANALYZER_OPTION( + bool, ShouldSuppressNullReturnPaths, "suppress-null-return-paths", "Whether or not paths that go through null returns should be suppressed. " "This is a heuristic for avoiding bug reports with paths that go through " "inlined functions that are more defensive than their callers.", - true, shouldSuppressNullReturnPaths) + true) -ANALYZER_OPTION_GEN_FN( - bool, AvoidSuppressingNullArgumentPaths, +ANALYZER_OPTION( + bool, ShouldAvoidSuppressingNullArgumentPaths, "avoid-suppressing-null-argument-paths", "Whether a bug report should not be suppressed if its path includes a call " "with a null argument, even if that call has a null return. This option " - "has no effect when #shouldSuppressNullReturnPaths() is false. This is a " + "has no effect when ShouldSuppressNullReturnPaths is false. This is a " "counter-heuristic to avoid false negatives.", - false, shouldAvoidSuppressingNullArgumentPaths) - -ANALYZER_OPTION_GEN_FN(bool, SuppressInlinedDefensiveChecks, - "suppress-inlined-defensive-checks", - "Whether or not diagnostics containing inlined " - "defensive NULL checks should be suppressed.", - true, shouldSuppressInlinedDefensiveChecks) - -ANALYZER_OPTION_GEN_FN(bool, InlineCXXContainerMethods, - "c++-container-inlining", - "Whether or not methods of C++ container objects may be " - "considered for inlining.", - false, mayInlineCXXContainerMethods) - -ANALYZER_OPTION_GEN_FN(bool, SuppressFromCXXStandardLibrary, - "suppress-c++-stdlib", - "Whether or not diagnostics reported within the C++ " - "standard library should be suppressed.", - true, shouldSuppressFromCXXStandardLibrary) - -ANALYZER_OPTION_GEN_FN(bool, CrosscheckWithZ3, "crosscheck-with-z3", - "Whether bug reports should be crosschecked with the Z3 " - "constraint manager backend.", - false, shouldCrosscheckWithZ3) - -ANALYZER_OPTION_GEN_FN(bool, ReportIssuesInMainSourceFile, - "report-in-main-source-file", - "Whether or not the diagnostic report should be always " - "reported in the main source file and not the headers.", - false, shouldReportIssuesInMainSourceFile) - -ANALYZER_OPTION_GEN_FN( - bool, WriteStableReportFilename, "stable-report-filename", - "Whether or not the report filename should be random or not.", false, - shouldWriteStableReportFilename) - -ANALYZER_OPTION_GEN_FN( - bool, SerializeStats, "serialize-stats", + false) + +ANALYZER_OPTION(bool, ShouldSuppressInlinedDefensiveChecks, + "suppress-inlined-defensive-checks", + "Whether or not diagnostics containing inlined " + "defensive NULL checks should be suppressed.", + true) + +ANALYZER_OPTION(bool, MayInlineCXXContainerMethods, "c++-container-inlining", + "Whether or not methods of C++ container objects may be " + "considered for inlining.", + false) + +ANALYZER_OPTION(bool, ShouldSuppressFromCXXStandardLibrary, + "suppress-c++-stdlib", + "Whether or not diagnostics reported within the C++ " + "standard library should be suppressed.", + true) + +ANALYZER_OPTION(bool, ShouldCrosscheckWithZ3, "crosscheck-with-z3", + "Whether bug reports should be crosschecked with the Z3 " + "constraint manager backend.", + false) + +ANALYZER_OPTION(bool, ShouldReportIssuesInMainSourceFile, + "report-in-main-source-file", + "Whether or not the diagnostic report should be always " + "reported in the main source file and not the headers.", + false) + +ANALYZER_OPTION(bool, ShouldWriteStableReportFilename, "stable-report-filename", + "Whether or not the report filename should be random or not.", + false) + +ANALYZER_OPTION( + bool, ShouldSerializeStats, "serialize-stats", "Whether the analyzer should serialize statistics to plist output. " "Statistics would be serialized in JSON format inside the main dictionary " "under the statistics key. Available only if compiled in assert mode or " "with LLVM statistics explicitly enabled.", - false, shouldSerializeStats) + false) -ANALYZER_OPTION_GEN_FN( - bool, InlineObjCMethod, "objc-inlining", - "Whether ObjectiveC inlining is enabled, false otherwise.", true, - mayInlineObjCMethod) +ANALYZER_OPTION(bool, MayInlineObjCMethod, "objc-inlining", + "Whether ObjectiveC inlining is enabled, false otherwise.", + true) -ANALYZER_OPTION_GEN_FN(bool, PrunePaths, "prune-paths", - "Whether irrelevant parts of a bug report path should " - "be pruned out of the final output.", - true, shouldPrunePaths) +ANALYZER_OPTION(bool, ShouldPrunePaths, "prune-paths", + "Whether irrelevant parts of a bug report path should " + "be pruned out of the final output.", + true) -ANALYZER_OPTION_GEN_FN( - bool, ConditionalizeStaticInitializers, +ANALYZER_OPTION( + bool, ShouldConditionalizeStaticInitializers, "cfg-conditional-static-initializers", "Whether 'static' initializers should be in conditional logic in the CFG.", - true, shouldConditionalizeStaticInitializers) + true) -ANALYZER_OPTION_GEN_FN(bool, SynthesizeBodies, "faux-bodies", - "Whether the analyzer engine should synthesize fake " - "bodies for well-known functions.", - true, shouldSynthesizeBodies) +ANALYZER_OPTION(bool, ShouldSynthesizeBodies, "faux-bodies", + "Whether the analyzer engine should synthesize fake " + "bodies for well-known functions.", + true) -ANALYZER_OPTION_GEN_FN( - bool, ElideConstructors, "elide-constructors", +ANALYZER_OPTION( + bool, ShouldElideConstructors, "elide-constructors", "Whether elidable C++ copy-constructors and move-constructors should be " "actually elided during analysis. Both behaviors are allowed by the C++ " "standard, and the analyzer, like CodeGen, defaults to eliding. Starting " "with C++17 some elisions become mandatory, and in these cases the option " "will be ignored.", - true, shouldElideConstructors) + true) -ANALYZER_OPTION_GEN_FN( - bool, InlineLambdas, "inline-lambdas", +ANALYZER_OPTION( + bool, ShouldInlineLambdas, "inline-lambdas", "Whether lambdas should be inlined. Otherwise a sink node will be " "generated each time a LambdaExpr is visited.", - true, shouldInlineLambdas) + true) -ANALYZER_OPTION_GEN_FN(bool, WidenLoops, "widen-loops", - "Whether the analysis should try to widen loops.", - false, shouldWidenLoops) +ANALYZER_OPTION(bool, ShouldWidenLoops, "widen-loops", + "Whether the analysis should try to widen loops.", false) -ANALYZER_OPTION_GEN_FN( - bool, UnrollLoops, "unroll-loops", - "Whether the analysis should try to unroll loops with known bounds.", false, - shouldUnrollLoops) +ANALYZER_OPTION( + bool, ShouldUnrollLoops, "unroll-loops", + "Whether the analysis should try to unroll loops with known bounds.", false) -ANALYZER_OPTION_GEN_FN( - bool, DisplayNotesAsEvents, "notes-as-events", +ANALYZER_OPTION( + bool, ShouldDisplayNotesAsEvents, "notes-as-events", "Whether the bug reporter should transparently treat extra note diagnostic " "pieces as event diagnostic pieces. Useful when the diagnostic consumer " "doesn't support the extra note pieces.", - false, shouldDisplayNotesAsEvents) + false) -ANALYZER_OPTION_GEN_FN( - bool, AggressivelySimplifyBinaryOperation, +ANALYZER_OPTION( + bool, ShouldAggressivelySimplifyBinaryOperation, "aggressive-binary-operation-simplification", "Whether SValBuilder should rearrange comparisons and additive operations " "of symbolic expressions which consist of a sum of a symbol and a concrete " @@ -342,10 +259,10 @@ "'<', '<=', '>', '>=', '+' or '-'. The rearrangement also happens with '-' " "instead of '+' on either or both side and also if any or both integers " "are missing.", - false, shouldAggressivelySimplifyBinaryOperation) + false) -ANALYZER_OPTION_GEN_FN( - bool, EagerlyAssume, "eagerly-assume", +ANALYZER_OPTION( + bool, ShouldEagerlyAssume, "eagerly-assume", "Whether we should eagerly assume evaluations of conditionals, thus, " "bifurcating the path. This indicates how the engine should handle " "expressions such as: 'x = (y != 0)'. When this is true then the " @@ -353,63 +270,60 @@ "evaluating it to the integers 0 or 1 respectively. The upside is that " "this can increase analysis precision until we have a better way to lazily " "evaluate such logic. The downside is that it eagerly bifurcates paths.", - true, shouldEagerlyAssume) + true) -ANALYZER_OPTION_GEN_FN( - bool, NaiveCTU, "experimental-enable-naive-ctu-analysis", +ANALYZER_OPTION( + bool, IsNaiveCTUEnabled, "experimental-enable-naive-ctu-analysis", "Whether naive cross translation unit analysis is enabled. This is an " "experimental feature to inline functions from another translation units.", - false, naiveCTUEnabled) + false) //===----------------------------------------------------------------------===// // Unsinged analyzer options. //===----------------------------------------------------------------------===// -ANALYZER_OPTION_GEN_FN( +ANALYZER_OPTION( unsigned, AlwaysInlineSize, "ipa-always-inline-size", "The size of the functions (in basic blocks), which should be considered " "to be small enough to always inline.", - 3, getAlwaysInlineSize) + 3) -ANALYZER_OPTION_GEN_FN( +ANALYZER_OPTION( unsigned, GraphTrimInterval, "graph-trim-interval", "How often nodes in the ExplodedGraph should be recycled to save memory. " "To disable node reclamation, set the option to 0.", - 1000, getGraphTrimInterval) + 1000) -ANALYZER_OPTION_GEN_FN( +ANALYZER_OPTION( unsigned, MinCFGSizeTreatFunctionsAsLarge, "min-cfg-size-treat-functions-as-large", "The number of basic blocks a function needs to have to be considered " "large for the 'max-times-inline-large' config option.", - 14, getMinCFGSizeTreatFunctionsAsLarge) + 14) -ANALYZER_OPTION_GEN_FN(unsigned, MaxSymbolComplexity, "max-symbol-complexity", - "The maximum complexity of symbolic constraint.", 35, - getMaxSymbolComplexity) +ANALYZER_OPTION(unsigned, MaxSymbolComplexity, "max-symbol-complexity", + "The maximum complexity of symbolic constraint.", 35) -ANALYZER_OPTION_GEN_FN(unsigned, MaxTimesInlineLarge, "max-times-inline-large", - "The maximum times a large function could be inlined.", - 32, getMaxTimesInlineLarge) +ANALYZER_OPTION(unsigned, MaxTimesInlineLarge, "max-times-inline-large", + "The maximum times a large function could be inlined.", 32) -ANALYZER_OPTION_GEN_FN_DEPENDS_ON_USER_MODE( +ANALYZER_OPTION_DEPENDS_ON_USER_MODE( unsigned, MaxInlinableSize, "max-inlinable-size", "The bound on the number of basic blocks in an inlined function.", - /* SHALLOW_VAL */ 4, /* DEEP_VAL */ 100, getMaxInlinableSize) + /* SHALLOW_VAL */ 4, /* DEEP_VAL */ 100) -ANALYZER_OPTION_GEN_FN_DEPENDS_ON_USER_MODE( +ANALYZER_OPTION_DEPENDS_ON_USER_MODE( unsigned, MaxNodesPerTopLevelFunction, "max-nodes", "The maximum number of nodes the analyzer can generate while exploring a " "top level function (for each exploded graph). 0 means no limit.", - /* SHALLOW_VAL */ 75000, /* DEEP_VAL */ 225000, - getMaxNodesPerTopLevelFunction) + /* SHALLOW_VAL */ 75000, /* DEEP_VAL */ 225000) -ANALYZER_OPTION_GEN_FN(unsigned, RegionStoreSmallStructLimit, - "region-store-small-struct-limit", - "Returns the maximum number of nodes the analyzer can " - "generate while exploring a top level function (for " - "each exploded graph). 0 means no limit.", - 2, getRegionStoreSmallStructLimit) +ANALYZER_OPTION(unsigned, RegionStoreSmallStructLimit, + "region-store-small-struct-limit", + "Returns the maximum number of nodes the analyzer can " + "generate while exploring a top level function (for " + "each exploded graph). 0 means no limit.", + 2) //===----------------------------------------------------------------------===// // String analyzer options. @@ -418,23 +332,16 @@ ANALYZER_OPTION(StringRef, CTUDir, "ctu-dir", "The directory containing the CTU related files.", "") -ANALYZER_OPTION_GEN_FN( - StringRef, CTUIndexName, "ctu-index-name", - "the name of the file containing the CTU index of functions.", - "externalFnMap.txt", getCTUIndexName) +ANALYZER_OPTION(StringRef, CTUIndexName, "ctu-index-name", + "the name of the file containing the CTU index of functions.", + "externalFnMap.txt") -ANALYZER_OPTION_GEN_FN( +ANALYZER_OPTION( StringRef, ModelPath, "model-path", "The analyzer can inline an alternative implementation written in C at the " "call site if the called function's body is not available. This is a path " "where to look for those alternative implementations (called models).", - "", getModelPath) - -ANALYZER_OPTION(StringRef, UserMode, "mode", - "Controls the high-level analyzer mode, which influences the " - "default settings for some of the lower-level config options " - "(such as IPAMode). Value: \"deep\", \"shallow\".", - "deep") + "") ANALYZER_OPTION( StringRef, CXXMemberInliningMode, "c++-inlining", @@ -455,7 +362,5 @@ "\"bfs_block_dfs_contents\".", "unexplored_first_queue") -#undef ANALYZER_OPTION_GEN_FN_DEPENDS_ON_USER_MODE -#undef ANALYZER_OPTION_GEN_FN #undef ANALYZER_OPTION_DEPENDS_ON_USER_MODE #undef ANALYZER_OPTION Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -314,6 +314,8 @@ Opts.CheckersControlList.emplace_back(checker, enable); } + AnalyzerOptions::ConfigTable Config; + // Go through the analyzer configuration options. for (const auto *A : Args.filtered(OPT_analyzer_config)) { A->claim(); @@ -338,9 +340,10 @@ Success = false; break; } - Opts.Config[key] = val; + Config[key] = val; } } + Opts.parseConfigs(Config, Diags); llvm::raw_string_ostream os(Opts.FullCompilerInvocation); for (unsigned i = 0; i < Args.getNumInputArgStrings(); ++i) { Index: lib/StaticAnalyzer/Checkers/DebugCheckers.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/DebugCheckers.cpp +++ lib/StaticAnalyzer/Checkers/DebugCheckers.cpp @@ -161,30 +161,61 @@ namespace { class ConfigDumper : public Checker< check::EndOfTranslationUnit > { - typedef AnalyzerOptions::ConfigTable Table; - static int compareEntry(const Table::MapEntryTy *const *LHS, - const Table::MapEntryTy *const *RHS) { - return (*LHS)->getKey().compare((*RHS)->getKey()); + static constexpr size_t getNonCheckerConfigCount() { +#define ANALYZER_OPTION_DEPENDS_ON_USER_MODE(TYPE, NAME, CMDFLAG, DESC, \ + SHALLOW_VAL, DEEP_VAL) \ + ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, SHALLOW_VAL) + + return 0 +#define ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, DEFAULT_VAL) \ + + 1 +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.def" + ; +#undef ANALYZER_OPTION +#undef ANALYZER_OPTION_DEPENDS_ON_USER_MODE + } + + static std::string getStr(unsigned U) { + return std::to_string(U); + } + + static std::string getStr(bool B) { + return B ? "true" : "false"; + } + + static std::string getStr(StringRef Str) { + return Str.empty() ? "\"\"" : Str; } public: void checkEndOfTranslationUnit(const TranslationUnitDecl *TU, AnalysisManager& mgr, BugReporter &BR) const { - const Table &Config = mgr.options.Config; + llvm::errs() << "[config]\n"; - SmallVector Keys; - for (Table::const_iterator I = Config.begin(), E = Config.end(); I != E; - ++I) - Keys.push_back(&*I); - llvm::array_pod_sort(Keys.begin(), Keys.end(), compareEntry); + const AnalyzerOptions &AnOpts = mgr.getAnalyzerOptions(); - llvm::errs() << "[config]\n"; - for (unsigned I = 0, E = Keys.size(); I != E; ++I) - llvm::errs() << Keys[I]->getKey() << " = " << Keys[I]->second << '\n'; + std::string Options[] = { +#define ANALYZER_OPTION_DEPENDS_ON_USER_MODE(TYPE, NAME, CMDFLAG, DESC, \ + SHALLOW_VAL, DEEP_VAL) \ + ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, SHALLOW_VAL) + +#define ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, DEFAULT_VAL) \ + (llvm::Twine(CMDFLAG " = ") + getStr(AnOpts.NAME.getValue())).str(), + +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.def" +#undef ANALYZER_OPTION +#undef ANALYZER_OPTION_DEPENDS_ON_USER_MODE + }; + + std::sort(std::begin(Options), std::end(Options)); + + for (const std::string &Option : Options) + llvm::errs() << Option << '\n'; - llvm::errs() << "[stats]\n" << "num-entries = " << Keys.size() << '\n'; + llvm::errs() << "[stats]\n" << "num-entries = " + << getNonCheckerConfigCount() << '\n'; } }; } Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -1114,7 +1114,7 @@ void MallocChecker::checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const { - if (!C.getAnalysisManager().getAnalyzerOptions().mayInlineCXXAllocator()) + if (!C.getAnalysisManager().getAnalyzerOptions().MayInlineCXXAllocator.getValue()) processNewAllocation(NE, C, C.getSVal(NE)); } Index: lib/StaticAnalyzer/Core/AnalysisManager.cpp =================================================================== --- lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -22,16 +22,21 @@ AnalyzerOptions &Options, CodeInjector *injector) : AnaCtxMgr( - ASTCtx, Options.UnoptimizedCFG, Options.includeImplicitDtorsInCFG(), - /*AddInitializers=*/true, Options.includeTemporaryDtorsInCFG(), - Options.includeLifetimeInCFG(), + ASTCtx, Options.UnoptimizedCFG, + Options.ShouldIncludeImplicitDtorsInCFG.getValue(), + /*AddInitializers=*/true, + Options.ShouldIncludeTemporaryDtorsInCFG.getValue(), + Options.ShouldIncludeLifetimeInCFG.getValue(), // Adding LoopExit elements to the CFG is a requirement for loop // unrolling. - Options.includeLoopExitInCFG() || Options.shouldUnrollLoops(), - Options.includeScopesInCFG(), Options.shouldSynthesizeBodies(), - Options.shouldConditionalizeStaticInitializers(), - /*addCXXNewAllocator=*/true, Options.includeRichConstructorsInCFG(), - Options.shouldElideConstructors(), injector), + Options.ShouldIncludeLoopExitInCFG.getValue() || + Options.ShouldUnrollLoops.getValue(), + Options.ShouldIncludeScopesInCFG.getValue(), + Options.ShouldSynthesizeBodies.getValue(), + Options.ShouldConditionalizeStaticInitializers.getValue(), + /*addCXXNewAllocator=*/true, + Options.ShouldIncludeRichConstructorsInCFG.getValue(), + Options.ShouldElideConstructors.getValue(), injector), Ctx(ASTCtx), Diags(diags), LangOpts(ASTCtx.getLangOpts()), PathConsumers(PDC), CreateStoreMgr(storemgr), CreateConstraintMgr(constraintmgr), CheckerMgr(checkerMgr), Index: lib/StaticAnalyzer/Core/AnalyzerOptions.cpp =================================================================== --- lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -12,6 +12,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/Basic/Diagnostic.h" #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "llvm/ADT/SmallString.h" @@ -31,7 +32,8 @@ using namespace llvm; std::vector -AnalyzerOptions::getRegisteredCheckers(bool IncludeExperimental /* = false */) { +AnalyzerOptions::getRegisteredCheckers(bool IncludeExperimental) { + static const StringRef StaticAnalyzerChecks[] = { #define GET_CHECKERS #define CHECKER(FULLNAME, CLASS, DESCFILE, HELPTEXT, GROUPINDEX, HIDDEN) \ @@ -49,25 +51,8 @@ return Result; } -UserModeKind AnalyzerOptions::getUserMode() { - if (!UserMode.hasValue()) { - UserMode = getStringOption("mode", "deep"); - } - - auto K = llvm::StringSwitch>(*UserMode) - .Case("shallow", UMK_Shallow) - .Case("deep", UMK_Deep) - .Default(None); - assert(UserMode.hasValue() && "User mode is invalid."); - return K.getValue(); -} - ExplorationStrategyKind -AnalyzerOptions::getExplorationStrategy() { - if (!ExplorationStrategy.hasValue()) { - ExplorationStrategy = getStringOption("exploration_strategy", - "unexplored_first_queue"); - } +AnalyzerOptions::getExplorationStrategy() const { auto K = llvm::StringSwitch>( *ExplorationStrategy) @@ -86,17 +71,7 @@ return K.getValue(); } -IPAKind AnalyzerOptions::getIPAMode() { - if (!IPAMode.hasValue()) { - switch (getUserMode()) { - case UMK_Shallow: - IPAMode = getStringOption("ipa", "inlining"); - break; - case UMK_Deep: - IPAMode = getStringOption("ipa", "dynamic-bifurcate"); - break; - } - } +IPAKind AnalyzerOptions::getIPAMode() const { auto K = llvm::StringSwitch>(*IPAMode) .Case("none", IPAK_None) .Case("basic-inlining", IPAK_BasicInlining) @@ -110,11 +85,8 @@ } bool -AnalyzerOptions::mayInlineCXXMemberFunction(CXXInlineableMemberKind Param) { - if (!CXXMemberInliningMode.hasValue()) { - CXXMemberInliningMode = getStringOption("c++-inlining", "destructors"); - } - +AnalyzerOptions::mayInlineCXXMemberFunction( + CXXInlineableMemberKind Param) const { if (getIPAMode() < IPAK_Inlining) return false; @@ -132,47 +104,6 @@ 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"; } - -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, @@ -203,7 +134,8 @@ // but the AnalyzerOptions doesn't have access to a diagnostic engine. assert(C); return llvm::StringSwitch( - getCheckerStringOption(Name, toString(DefaultVal), C, SearchInParents)) + getCheckerStringOption(Name, DefaultVal ? "true" : "false", C, + SearchInParents)) .Case("true", true) .Case("false", false) .Default(DefaultVal); @@ -220,12 +152,3 @@ (void)HasFailed; return Ret; } - -StringRef AnalyzerOptions::getCTUDir() { - if (!CTUDir.hasValue()) { - CTUDir = getStringOption("ctu-dir", ""); - if (!llvm::sys::fs::is_directory(*CTUDir)) - CTUDir = ""; - } - return CTUDir.getValue(); -} Index: lib/StaticAnalyzer/Core/BugReporter.cpp =================================================================== --- lib/StaticAnalyzer/Core/BugReporter.cpp +++ lib/StaticAnalyzer/Core/BugReporter.cpp @@ -1977,7 +1977,7 @@ // Finally, prune the diagnostic path of uninteresting stuff. if (!PD->path.empty()) { - if (R->shouldPrunePath() && Opts.shouldPrunePaths()) { + if (R->shouldPrunePath() && Opts.ShouldPrunePaths.getValue()) { bool stillHasNotes = removeUnneededCalls(PD->getMutablePieces(), R, LCM); assert(stillHasNotes); @@ -2617,7 +2617,7 @@ generateVisitorsDiagnostics(R, ErrorNode, BRC); if (R->isValid()) { - if (Opts.shouldCrosscheckWithZ3()) { + if (Opts.ShouldCrosscheckWithZ3.getValue()) { // If crosscheck is enabled, remove all visitors, add the refutation // visitor and check again R->clearVisitors(); @@ -2959,7 +2959,7 @@ } PathPieces &Pieces = PD->getMutablePieces(); - if (getAnalyzerOptions().shouldDisplayNotesAsEvents()) { + if (getAnalyzerOptions().ShouldDisplayNotesAsEvents.getValue()) { // For path diagnostic consumers that don't support extra notes, // we may optionally convert those to path notes. for (auto I = report->getNotes().rbegin(), @@ -3096,7 +3096,7 @@ // report location to the last piece in the main source file. AnalyzerOptions &Opts = getAnalyzerOptions(); for (auto const &P : *Out) - if (Opts.shouldReportIssuesInMainSourceFile() && !Opts.AnalyzeAll) + if (Opts.ShouldReportIssuesInMainSourceFile.getValue() && !Opts.AnalyzeAll) P.second->resetDiagnosticLocationToMainFile(); return Out; Index: lib/StaticAnalyzer/Core/BugReporterVisitors.cpp =================================================================== --- lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -696,8 +696,8 @@ bool EnableNullFPSuppression, BugReport &BR, const SVal V) { AnalyzerOptions &Options = N->getState()->getAnalysisManager().options; - if (EnableNullFPSuppression && Options.shouldSuppressNullReturnPaths() - && V.getAs()) + if (EnableNullFPSuppression && + Options.ShouldSuppressNullReturnPaths.getValue() && V.getAs()) BR.addVisitor(llvm::make_unique( R->getAs(), V)); } @@ -828,7 +828,8 @@ AnalyzerOptions &Options = State->getAnalysisManager().options; bool EnableNullFPSuppression = false; - if (InEnableNullFPSuppression && Options.shouldSuppressNullReturnPaths()) + if (InEnableNullFPSuppression && + Options.ShouldSuppressNullReturnPaths.getValue()) if (Optional RetLoc = RetVal.getAs()) EnableNullFPSuppression = State->isNull(*RetLoc).isConstrainedTrue(); @@ -897,7 +898,7 @@ // future nodes. We want to emit a path note as well, in case // the report is resurrected as valid later on. if (EnableNullFPSuppression && - Options.shouldAvoidSuppressingNullArgumentPaths()) + Options.ShouldAvoidSuppressingNullArgumentPaths.getValue()) Mode = MaybeUnsuppress; if (RetE->getType()->isObjCObjectPointerType()) { @@ -945,7 +946,7 @@ visitNodeMaybeUnsuppress(const ExplodedNode *N, BugReporterContext &BRC, BugReport &BR) { #ifndef NDEBUG - assert(Options.shouldAvoidSuppressingNullArgumentPaths()); + assert(Options.ShouldAvoidSuppressingNullArgumentPaths.getValue()); #endif // Are we at the entry node for this call? @@ -1399,7 +1400,7 @@ : V(Value) { // Check if the visitor is disabled. AnalyzerOptions &Options = N->getState()->getAnalysisManager().options; - if (!Options.shouldSuppressInlinedDefensiveChecks()) + if (!Options.ShouldSuppressInlinedDefensiveChecks.getValue()) IsSatisfied = true; assert(N->getState()->isNull(V).isConstrainedTrue() && @@ -2241,7 +2242,7 @@ // the user's fault, we currently don't report them very well, and // Note that this will not help for any other data structure libraries, like // TR1, Boost, or llvm/ADT. - if (Options.shouldSuppressFromCXXStandardLibrary()) { + if (Options.ShouldSuppressFromCXXStandardLibrary.getValue()) { BR.markInvalid(getTag(), nullptr); return; } else { Index: lib/StaticAnalyzer/Core/CallEvent.cpp =================================================================== --- lib/StaticAnalyzer/Core/CallEvent.cpp +++ lib/StaticAnalyzer/Core/CallEvent.cpp @@ -550,13 +550,14 @@ AnalyzerOptions &Opts = Engine->getAnalysisManager().options; // Try to get CTU definition only if CTUDir is provided. - if (!Opts.naiveCTUEnabled()) + if (!Opts.IsNaiveCTUEnabled.getValue()) return {}; cross_tu::CrossTranslationUnitContext &CTUCtx = *Engine->getCrossTranslationUnitContext(); llvm::Expected CTUDeclOrError = - CTUCtx.getCrossTUDefinition(FD, Opts.getCTUDir(), Opts.getCTUIndexName()); + CTUCtx.getCrossTUDefinition(FD, Opts.CTUDir.getValue(), + Opts.CTUIndexName.getValue()); if (!CTUDeclOrError) { handleAllErrors(CTUDeclOrError.takeError(), Index: lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngine.cpp +++ lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -191,7 +191,7 @@ svalBuilder(StateMgr.getSValBuilder()), ObjCNoRet(mgr.getASTContext()), BR(mgr, *this), VisitedCallees(VisitedCalleesIn), HowToInline(HowToInlineIn) { - unsigned TrimInterval = mgr.options.getGraphTrimInterval(); + unsigned TrimInterval = mgr.options.GraphTrimInterval.getValue(); if (TrimInterval != 0) { // Enable eager node reclaimation when constructing the ExplodedGraph. G.enableNodeReclamation(TrimInterval); @@ -753,7 +753,7 @@ NodeBuilder Bldr(Pred, Dst, *currBldrCtx); ProgramStateRef NewState = Pred->getState(); - if(AMgr.options.shouldUnrollLoops()) + if(AMgr.options.ShouldUnrollLoops.getValue()) NewState = processLoopEnd(S, NewState); LoopExit PP(S, Pred->getLocationContext()); @@ -885,7 +885,7 @@ // TODO: We're not evaluating allocators for all cases just yet as // we're not handling the return value correctly, which causes false // positives when the alpha.cplusplus.NewDeleteLeaks check is on. - if (Opts.mayInlineCXXAllocator()) + if (Opts.MayInlineCXXAllocator.getValue()) VisitCXXNewAllocatorCall(NE, Pred, Dst); else { NodeBuilder Bldr(Pred, Dst, *currBldrCtx); @@ -1100,7 +1100,7 @@ // This is a fallback solution in case we didn't have a construction // context when we were constructing the temporary. Otherwise the map should // have been populated there. - if (!getAnalysisManager().options.includeTemporaryDtorsInCFG()) { + if (!getAnalysisManager().options.ShouldIncludeTemporaryDtorsInCFG.getValue()) { // In case we don't have temporary destructors in the CFG, do not mark // the initialization - we would otherwise never clean it up. Dst = PreVisit; @@ -1460,7 +1460,7 @@ break; case Stmt::LambdaExprClass: - if (AMgr.options.shouldInlineLambdas()) { + if (AMgr.options.ShouldInlineLambdas.getValue()) { Bldr.takeNodes(Pred); VisitLambdaExpr(cast(S), Pred, Dst); Bldr.addNodes(Dst); @@ -1489,7 +1489,7 @@ Bldr.takeNodes(Pred); - if (AMgr.options.shouldEagerlyAssume() && + if (AMgr.options.ShouldEagerlyAssume.getValue() && (B->isRelationalOp() || B->isEqualityOp())) { ExplodedNodeSet Tmp; VisitBinaryOperator(cast(S), Pred, Tmp); @@ -1753,7 +1753,7 @@ case Stmt::UnaryOperatorClass: { Bldr.takeNodes(Pred); const auto *U = cast(S); - if (AMgr.options.shouldEagerlyAssume() && (U->getOpcode() == UO_LNot)) { + if (AMgr.options.ShouldEagerlyAssume.getValue() && (U->getOpcode() == UO_LNot)) { ExplodedNodeSet Tmp; VisitUnaryOperator(U, Pred, Tmp); evalEagerlyAssumeBinOpBifurcation(Dst, Tmp, U); @@ -1854,7 +1854,7 @@ PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext()); // If we reach a loop which has a known bound (and meets // other constraints) then consider completely unrolling it. - if(AMgr.options.shouldUnrollLoops()) { + if(AMgr.options.ShouldUnrollLoops.getValue()) { unsigned maxBlockVisitOnPath = AMgr.options.maxBlockVisitOnPath; const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminator(); if (Term) { @@ -1876,7 +1876,7 @@ // maximum number of times, widen the loop. unsigned int BlockCount = nodeBuilder.getContext().blockCount(); if (BlockCount == AMgr.options.maxBlockVisitOnPath - 1 && - AMgr.options.shouldWidenLoops()) { + AMgr.options.ShouldWidenLoops.getValue()) { const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminator(); if (!(Term && (isa(Term) || isa(Term) || isa(Term)))) @@ -2377,7 +2377,7 @@ const auto *DeclRefEx = dyn_cast(Ex); Optional> VInfo; - if (AMgr.options.shouldInlineLambdas() && DeclRefEx && + if (AMgr.options.ShouldInlineLambdas.getValue() && DeclRefEx && DeclRefEx->refersToEnclosingVariableOrCapture() && MD && MD->getParent()->isLambda()) { // Lookup the field of the lambda. Index: lib/StaticAnalyzer/Core/ExprEngineCXX.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -159,7 +159,7 @@ return std::make_pair(State, FieldVal); } case ConstructionContext::NewAllocatedObjectKind: { - if (AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) { + if (AMgr.getAnalyzerOptions().MayInlineCXXAllocator.getValue()) { const auto *NECC = cast(CC); const auto *NE = NECC->getCXXNewExpr(); SVal V = *getObjectUnderConstruction(State, NE, LCtx); @@ -210,7 +210,7 @@ llvm_unreachable("Unhandled return value construction context!"); } case ConstructionContext::ElidedTemporaryObjectKind: { - assert(AMgr.getAnalyzerOptions().shouldElideConstructors()); + assert(AMgr.getAnalyzerOptions().ShouldElideConstructors.getValue()); const auto *TCC = cast(CC); const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr(); const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr(); @@ -706,7 +706,7 @@ ProgramStateRef State = Pred->getState(); // Retrieve the stored operator new() return value. - if (AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) { + if (AMgr.getAnalyzerOptions().MayInlineCXXAllocator.getValue()) { symVal = *getObjectUnderConstruction(State, CNE, LCtx); State = finishObjectConstruction(State, CNE, LCtx); } @@ -726,7 +726,7 @@ CallEventRef Call = CEMgr.getCXXAllocatorCall(CNE, State, LCtx); - if (!AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) { + if (!AMgr.getAnalyzerOptions().MayInlineCXXAllocator.getValue()) { // Invalidate placement args. // FIXME: Once we figure out how we want allocators to work, // we should be using the usual pre-/(default-)eval-/post-call checks here. Index: lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -349,7 +349,7 @@ /*WasInlined=*/true); } else if (CE && !(isa(CE) && // Called when visiting CXXNewExpr. - AMgr.getAnalyzerOptions().mayInlineCXXAllocator())) { + AMgr.getAnalyzerOptions().MayInlineCXXAllocator.getValue())) { getCheckerManager().runCheckersForPostStmt(Dst, DstPostCall, CE, *this, /*WasInlined=*/true); } else { @@ -386,7 +386,7 @@ // Do not count the small functions when determining the stack depth. AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(DI); const CFG *CalleeCFG = CalleeADC->getCFG(); - if (CalleeCFG->getNumBlockIDs() > AMgr.options.getAlwaysInlineSize()) + if (CalleeCFG->getNumBlockIDs() > AMgr.options.AlwaysInlineSize.getValue()) ++StackDepth; } LCtx = LCtx->getParent(); @@ -683,7 +683,7 @@ : nullptr; if (CC && isa(CC) && - !Opts.mayInlineCXXAllocator()) + !Opts.MayInlineCXXAllocator.getValue()) return CIP_DisallowedOnce; // FIXME: We don't handle constructors or destructors for arrays properly. @@ -712,7 +712,7 @@ // If we don't handle temporary destructors, we shouldn't inline // their constructors. if (CallOpts.IsTemporaryCtorOrDtor && - !Opts.includeTemporaryDtorsInCFG()) + !Opts.ShouldIncludeTemporaryDtorsInCFG.getValue()) return CIP_DisallowedOnce; // If we did not find the correct this-region, it would be pointless @@ -743,7 +743,8 @@ return CIP_DisallowedOnce; // Allow disabling temporary destructor inlining with a separate option. - if (CallOpts.IsTemporaryCtorOrDtor && !Opts.mayInlineCXXTemporaryDtors()) + if (CallOpts.IsTemporaryCtorOrDtor && + !Opts.MayInlineCXXTemporaryDtors.getValue()) return CIP_DisallowedOnce; // If we did not find the correct this-region, it would be pointless @@ -754,13 +755,13 @@ break; } case CE_CXXAllocator: - if (Opts.mayInlineCXXAllocator()) + if (Opts.MayInlineCXXAllocator.getValue()) break; // Do not inline allocators until we model deallocators. // This is unfortunate, but basically necessary for smart pointers and such. return CIP_DisallowedAlways; case CE_ObjCMessage: - if (!Opts.mayInlineObjCMethod()) + if (!Opts.MayInlineObjCMethod.getValue()) return CIP_DisallowedAlways; if (!(Opts.getIPAMode() == IPAK_DynamicDispatch || Opts.getIPAMode() == IPAK_DynamicDispatchBifurcate)) @@ -844,19 +845,19 @@ if (Ctx.getLangOpts().CPlusPlus) { if (const FunctionDecl *FD = dyn_cast(CalleeADC->getDecl())) { // Conditionally control the inlining of template functions. - if (!Opts.mayInlineTemplateFunctions()) + if (!Opts.MayInlineTemplateFunctions.getValue()) if (FD->getTemplatedKind() != FunctionDecl::TK_NonTemplate) return false; // Conditionally control the inlining of C++ standard library functions. - if (!Opts.mayInlineCXXStandardLibrary()) + if (!Opts.MayInlineCXXStandardLibrary.getValue()) if (Ctx.getSourceManager().isInSystemHeader(FD->getLocation())) if (AnalysisDeclContext::isInStdNamespace(FD)) return false; // Conditionally control the inlining of methods on objects that look // like C++ containers. - if (!Opts.mayInlineCXXContainerMethods()) + if (!Opts.MayInlineCXXContainerMethods.getValue()) if (!AMgr.isInCodeFile(FD->getLocation())) if (isContainerMethod(Ctx, FD)) return false; @@ -865,7 +866,7 @@ // We don't currently do a good job modeling shared_ptr because we can't // see the reference count, so treating as opaque is probably the best // idea. - if (!Opts.mayInlineCXXSharedPtrDtor()) + if (!Opts.MayInlineCXXSharedPtrDtor.getValue()) if (isCXXSharedPtrDtor(FD)) return false; } @@ -878,7 +879,7 @@ return false; // Do not inline large functions. - if (CalleeCFG->getNumBlockIDs() > Opts.getMaxInlinableSize()) + if (CalleeCFG->getNumBlockIDs() > Opts.MaxInlinableSize.getValue()) return false; // It is possible that the live variables analysis cannot be @@ -946,21 +947,21 @@ unsigned StackDepth = 0; examineStackFrames(D, Pred->getLocationContext(), IsRecursive, StackDepth); if ((StackDepth >= Opts.InlineMaxStackDepth) && - ((CalleeCFG->getNumBlockIDs() > Opts.getAlwaysInlineSize()) + ((CalleeCFG->getNumBlockIDs() > Opts.AlwaysInlineSize.getValue()) || IsRecursive)) return false; // Do not inline large functions too many times. if ((Engine.FunctionSummaries->getNumTimesInlined(D) > - Opts.getMaxTimesInlineLarge()) && + Opts.MaxTimesInlineLarge.getValue()) && CalleeCFG->getNumBlockIDs() >= - Opts.getMinCFGSizeTreatFunctionsAsLarge()) { + Opts.MinCFGSizeTreatFunctionsAsLarge.getValue()) { NumReachedInlineCountMax++; return false; } if (HowToInline == Inline_Minimal && - (CalleeCFG->getNumBlockIDs() > Opts.getAlwaysInlineSize() + (CalleeCFG->getNumBlockIDs() > Opts.AlwaysInlineSize.getValue() || IsRecursive)) return false; Index: lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp =================================================================== --- lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -218,7 +218,7 @@ int FD; SmallString<128> Model, ResultPath; - if (!AnalyzerOpts.shouldWriteStableReportFilename()) { + if (!AnalyzerOpts.ShouldWriteStableReportFilename.getValue()) { llvm::sys::path::append(Model, Directory, "report-%%%%%%.html"); if (std::error_code EC = llvm::sys::fs::make_absolute(Model)) { Index: lib/StaticAnalyzer/Core/PlistDiagnostics.cpp =================================================================== --- lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -605,7 +605,7 @@ EmitString(o << " ", SM.getFileEntryForID(FID)->getName()) << '\n'; o << " \n"; - if (llvm::AreStatisticsEnabled() && AnOpts.shouldSerializeStats()) { + if (llvm::AreStatisticsEnabled() && AnOpts.ShouldSerializeStats.getValue()) { o << " statistics\n"; std::string stats; llvm::raw_string_ostream os(stats); Index: lib/StaticAnalyzer/Core/RegionStore.cpp =================================================================== --- lib/StaticAnalyzer/Core/RegionStore.cpp +++ lib/StaticAnalyzer/Core/RegionStore.cpp @@ -350,7 +350,7 @@ if (SubEngine *Eng = StateMgr.getOwningEngine()) { AnalyzerOptions &Options = Eng->getAnalysisManager().options; SmallStructLimit = - Options.getRegionStoreSmallStructLimit(); + Options.RegionStoreSmallStructLimit.getValue(); } } Index: lib/StaticAnalyzer/Core/SValBuilder.cpp =================================================================== --- lib/StaticAnalyzer/Core/SValBuilder.cpp +++ lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -385,7 +385,7 @@ // instead of generating an Unknown value and propagate the taint info to it. const unsigned MaxComp = StateMgr.getOwningEngine() ->getAnalysisManager() - .options.getMaxSymbolComplexity(); + .options.MaxSymbolComplexity.getValue(); if (symLHS && symRHS && (symLHS->computeComplexity() + symRHS->computeComplexity()) < MaxComp) Index: lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp =================================================================== --- lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -459,7 +459,7 @@ // FIXME: After putting complexity threshold to the symbols we can always // rearrange additive operations but rearrange comparisons only if // option is set. - if(!Opts.shouldAggressivelySimplifyBinaryOperation()) + if(!Opts.ShouldAggressivelySimplifyBinaryOperation.getValue()) return None; SymbolRef LSym = Lhs.getAsSymbol(); Index: lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp =================================================================== --- lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -204,7 +204,7 @@ PP(CI.getPreprocessor()), OutDir(outdir), Opts(std::move(opts)), Plugins(plugins), Injector(injector), CTU(CI) { DigestAnalyzerOptions(); - if (Opts->PrintStats || Opts->shouldSerializeStats()) { + if (Opts->PrintStats || Opts->ShouldSerializeStats.getValue()) { AnalyzerTimers = llvm::make_unique( "analyzer", "Analyzer timers"); TUTotalTimer = llvm::make_unique( @@ -739,7 +739,7 @@ // Execute the worklist algorithm. Eng.ExecuteWorkList(Mgr->getAnalysisDeclContextManager().getStackFrame(D), - Mgr->options.getMaxNodesPerTopLevelFunction()); + Mgr->options.MaxNodesPerTopLevelFunction.getValue()); if (!Mgr->options.DumpExplodedGraphTo.empty()) Eng.DumpGraph(Mgr->options.TrimGraph, Mgr->options.DumpExplodedGraphTo); Index: lib/StaticAnalyzer/Frontend/ModelInjector.cpp =================================================================== --- lib/StaticAnalyzer/Frontend/ModelInjector.cpp +++ lib/StaticAnalyzer/Frontend/ModelInjector.cpp @@ -48,7 +48,7 @@ FileID mainFileID = SM.getMainFileID(); AnalyzerOptionsRef analyzerOpts = CI.getAnalyzerOpts(); - llvm::StringRef modelPath = analyzerOpts->getModelPath(); + llvm::StringRef modelPath = analyzerOpts->ModelPath.getValue(); llvm::SmallString<128> fileName; Index: test/Analysis/analyzer-config.c =================================================================== --- test/Analysis/analyzer-config.c +++ test/Analysis/analyzer-config.c @@ -11,6 +11,15 @@ } // CHECK: [config] +// CHECK-NEXT: aggressive-binary-operation-simplification = false +// CHECK-NEXT: avoid-suppressing-null-argument-paths = false +// CHECK-NEXT: c++-allocator-inlining = true +// CHECK-NEXT: c++-container-inlining = false +// CHECK-NEXT: c++-inlining = destructors +// CHECK-NEXT: c++-shared_ptr-inlining = false +// CHECK-NEXT: c++-stdlib-inlining = true +// CHECK-NEXT: c++-temp-dtor-inlining = true +// CHECK-NEXT: c++-template-inlining = true // CHECK-NEXT: cfg-conditional-static-initializers = true // CHECK-NEXT: cfg-implicit-dtors = true // CHECK-NEXT: cfg-lifetime = false @@ -18,8 +27,12 @@ // CHECK-NEXT: cfg-rich-constructors = true // CHECK-NEXT: cfg-scopes = false // CHECK-NEXT: cfg-temporary-dtors = true +// CHECK-NEXT: crosscheck-with-z3 = false +// CHECK-NEXT: ctu-dir = "" +// CHECK-NEXT: ctu-index-name = externalFnMap.txt // CHECK-NEXT: eagerly-assume = true // CHECK-NEXT: elide-constructors = true +// CHECK-NEXT: experimental-enable-naive-ctu-analysis = false // CHECK-NEXT: exploration_strategy = unexplored_first_queue // CHECK-NEXT: faux-bodies = true // CHECK-NEXT: graph-trim-interval = 1000 @@ -28,12 +41,22 @@ // CHECK-NEXT: ipa-always-inline-size = 3 // CHECK-NEXT: max-inlinable-size = 100 // CHECK-NEXT: max-nodes = 225000 +// CHECK-NEXT: max-symbol-complexity = 35 // CHECK-NEXT: max-times-inline-large = 32 // CHECK-NEXT: min-cfg-size-treat-functions-as-large = 14 // CHECK-NEXT: mode = deep +// CHECK-NEXT: model-path = "" +// CHECK-NEXT: notes-as-events = false +// CHECK-NEXT: objc-inlining = true +// CHECK-NEXT: prune-paths = true // CHECK-NEXT: region-store-small-struct-limit = 2 +// CHECK-NEXT: report-in-main-source-file = false // CHECK-NEXT: serialize-stats = false +// CHECK-NEXT: stable-report-filename = false +// CHECK-NEXT: suppress-c++-stdlib = true +// CHECK-NEXT: suppress-inlined-defensive-checks = true +// CHECK-NEXT: suppress-null-return-paths = true // CHECK-NEXT: unroll-loops = false // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 24 +// CHECK-NEXT: num-entries = 47 Index: test/Analysis/analyzer-config.cpp =================================================================== --- test/Analysis/analyzer-config.cpp +++ test/Analysis/analyzer-config.cpp @@ -19,6 +19,9 @@ }; // CHECK: [config] +// CHECK-NEXT: aggressive-binary-operation-simplification = false +// CHECK-NEXT: avoid-suppressing-null-argument-paths = false +// CHECK-NEXT: c++-allocator-inlining = true // CHECK-NEXT: c++-container-inlining = false // CHECK-NEXT: c++-inlining = destructors // CHECK-NEXT: c++-shared_ptr-inlining = false @@ -32,6 +35,9 @@ // CHECK-NEXT: cfg-rich-constructors = true // CHECK-NEXT: cfg-scopes = false // CHECK-NEXT: cfg-temporary-dtors = true +// CHECK-NEXT: crosscheck-with-z3 = false +// CHECK-NEXT: ctu-dir = "" +// CHECK-NEXT: ctu-index-name = externalFnMap.txt // CHECK-NEXT: eagerly-assume = true // CHECK-NEXT: elide-constructors = true // CHECK-NEXT: experimental-enable-naive-ctu-analysis = false @@ -43,12 +49,22 @@ // CHECK-NEXT: ipa-always-inline-size = 3 // CHECK-NEXT: max-inlinable-size = 100 // CHECK-NEXT: max-nodes = 225000 +// CHECK-NEXT: max-symbol-complexity = 35 // CHECK-NEXT: max-times-inline-large = 32 // CHECK-NEXT: min-cfg-size-treat-functions-as-large = 14 // CHECK-NEXT: mode = deep +// CHECK-NEXT: model-path = "" +// CHECK-NEXT: notes-as-events = false +// CHECK-NEXT: objc-inlining = true +// CHECK-NEXT: prune-paths = true // CHECK-NEXT: region-store-small-struct-limit = 2 +// CHECK-NEXT: report-in-main-source-file = false // CHECK-NEXT: serialize-stats = false +// CHECK-NEXT: stable-report-filename = false +// CHECK-NEXT: suppress-c++-stdlib = true +// CHECK-NEXT: suppress-inlined-defensive-checks = true +// CHECK-NEXT: suppress-null-return-paths = true // CHECK-NEXT: unroll-loops = false // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 31 +// CHECK-NEXT: num-entries = 47