
Before C++17 copy elision was optional, even if the elidable copy/move constructor had arbitrary side effects. The elidable constructor is present in the AST, but marked as elidable. In these cases CFG now contains additional information that allows its clients to figure out if a temporary object is only being constructed so that to pass it to an elidable constructor. If so, it includes a reference to the elidable constructor's construction context, so that the client could elide the elidable constructor and construct the object directly at its final destination. Differential Revision: https://reviews.llvm.org/D47616 llvm-svn: 335795
490 lines
17 KiB
C++
490 lines
17 KiB
C++
//===- AnalyzerOptions.cpp - Analysis Engine Options ----------------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file contains special accessors for analyzer configuration options
|
|
// with string representations.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
|
|
#include "clang/StaticAnalyzer/Core/Checker.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/ADT/StringSwitch.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/ADT/Twine.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <cassert>
|
|
#include <cstddef>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
using namespace clang;
|
|
using namespace ento;
|
|
using namespace llvm;
|
|
|
|
std::vector<StringRef>
|
|
AnalyzerOptions::getRegisteredCheckers(bool IncludeExperimental /* = false */) {
|
|
static const StringRef StaticAnalyzerChecks[] = {
|
|
#define GET_CHECKERS
|
|
#define CHECKER(FULLNAME, CLASS, DESCFILE, HELPTEXT, GROUPINDEX, HIDDEN) \
|
|
FULLNAME,
|
|
#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
|
|
#undef CHECKER
|
|
#undef GET_CHECKERS
|
|
};
|
|
std::vector<StringRef> Result;
|
|
for (StringRef CheckName : StaticAnalyzerChecks) {
|
|
if (!CheckName.startswith("debug.") &&
|
|
(IncludeExperimental || !CheckName.startswith("alpha.")))
|
|
Result.push_back(CheckName);
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
AnalyzerOptions::UserModeKind AnalyzerOptions::getUserMode() {
|
|
if (UserMode == UMK_NotSet) {
|
|
StringRef ModeStr =
|
|
Config.insert(std::make_pair("mode", "deep")).first->second;
|
|
UserMode = llvm::StringSwitch<UserModeKind>(ModeStr)
|
|
.Case("shallow", UMK_Shallow)
|
|
.Case("deep", UMK_Deep)
|
|
.Default(UMK_NotSet);
|
|
assert(UserMode != UMK_NotSet && "User mode is invalid.");
|
|
}
|
|
return UserMode;
|
|
}
|
|
|
|
AnalyzerOptions::ExplorationStrategyKind
|
|
AnalyzerOptions::getExplorationStrategy() {
|
|
if (ExplorationStrategy == ExplorationStrategyKind::NotSet) {
|
|
StringRef StratStr =
|
|
Config
|
|
.insert(std::make_pair("exploration_strategy", "unexplored_first_queue"))
|
|
.first->second;
|
|
ExplorationStrategy =
|
|
llvm::StringSwitch<ExplorationStrategyKind>(StratStr)
|
|
.Case("dfs", ExplorationStrategyKind::DFS)
|
|
.Case("bfs", ExplorationStrategyKind::BFS)
|
|
.Case("unexplored_first",
|
|
ExplorationStrategyKind::UnexploredFirst)
|
|
.Case("unexplored_first_queue",
|
|
ExplorationStrategyKind::UnexploredFirstQueue)
|
|
.Case("bfs_block_dfs_contents",
|
|
ExplorationStrategyKind::BFSBlockDFSContents)
|
|
.Default(ExplorationStrategyKind::NotSet);
|
|
assert(ExplorationStrategy != ExplorationStrategyKind::NotSet &&
|
|
"User mode is invalid.");
|
|
}
|
|
return ExplorationStrategy;
|
|
}
|
|
|
|
IPAKind AnalyzerOptions::getIPAMode() {
|
|
if (IPAMode == IPAK_NotSet) {
|
|
// Use the User Mode to set the default IPA value.
|
|
// Note, we have to add the string to the Config map for the ConfigDumper
|
|
// checker to function properly.
|
|
const char *DefaultIPA = nullptr;
|
|
UserModeKind HighLevelMode = getUserMode();
|
|
if (HighLevelMode == UMK_Shallow)
|
|
DefaultIPA = "inlining";
|
|
else if (HighLevelMode == UMK_Deep)
|
|
DefaultIPA = "dynamic-bifurcate";
|
|
assert(DefaultIPA);
|
|
|
|
// Lookup the ipa configuration option, use the default from User Mode.
|
|
StringRef ModeStr =
|
|
Config.insert(std::make_pair("ipa", DefaultIPA)).first->second;
|
|
IPAKind IPAConfig = llvm::StringSwitch<IPAKind>(ModeStr)
|
|
.Case("none", IPAK_None)
|
|
.Case("basic-inlining", IPAK_BasicInlining)
|
|
.Case("inlining", IPAK_Inlining)
|
|
.Case("dynamic", IPAK_DynamicDispatch)
|
|
.Case("dynamic-bifurcate", IPAK_DynamicDispatchBifurcate)
|
|
.Default(IPAK_NotSet);
|
|
assert(IPAConfig != IPAK_NotSet && "IPA Mode is invalid.");
|
|
|
|
// Set the member variable.
|
|
IPAMode = IPAConfig;
|
|
}
|
|
|
|
return IPAMode;
|
|
}
|
|
|
|
bool
|
|
AnalyzerOptions::mayInlineCXXMemberFunction(CXXInlineableMemberKind K) {
|
|
if (getIPAMode() < IPAK_Inlining)
|
|
return false;
|
|
|
|
if (!CXXMemberInliningMode) {
|
|
static const char *ModeKey = "c++-inlining";
|
|
|
|
StringRef ModeStr =
|
|
Config.insert(std::make_pair(ModeKey, "destructors")).first->second;
|
|
|
|
CXXInlineableMemberKind &MutableMode =
|
|
const_cast<CXXInlineableMemberKind &>(CXXMemberInliningMode);
|
|
|
|
MutableMode = llvm::StringSwitch<CXXInlineableMemberKind>(ModeStr)
|
|
.Case("constructors", CIMK_Constructors)
|
|
.Case("destructors", CIMK_Destructors)
|
|
.Case("none", CIMK_None)
|
|
.Case("methods", CIMK_MemberFunctions)
|
|
.Default(CXXInlineableMemberKind());
|
|
|
|
if (!MutableMode) {
|
|
// FIXME: We should emit a warning here about an unknown inlining kind,
|
|
// but the AnalyzerOptions doesn't have access to a diagnostic engine.
|
|
MutableMode = CIMK_None;
|
|
}
|
|
}
|
|
|
|
return CXXMemberInliningMode >= K;
|
|
}
|
|
|
|
static StringRef toString(bool b) { return b ? "true" : "false"; }
|
|
|
|
StringRef AnalyzerOptions::getCheckerOption(StringRef CheckerName,
|
|
StringRef OptionName,
|
|
StringRef Default,
|
|
bool SearchInParents) {
|
|
// Search for a package option if the option for the checker is not specified
|
|
// and search in parents is enabled.
|
|
ConfigTable::const_iterator E = Config.end();
|
|
do {
|
|
ConfigTable::const_iterator I =
|
|
Config.find((Twine(CheckerName) + ":" + OptionName).str());
|
|
if (I != E)
|
|
return StringRef(I->getValue());
|
|
size_t Pos = CheckerName.rfind('.');
|
|
if (Pos == StringRef::npos)
|
|
return Default;
|
|
CheckerName = CheckerName.substr(0, Pos);
|
|
} while (!CheckerName.empty() && SearchInParents);
|
|
return Default;
|
|
}
|
|
|
|
bool AnalyzerOptions::getBooleanOption(StringRef Name, bool DefaultVal,
|
|
const CheckerBase *C,
|
|
bool SearchInParents) {
|
|
// 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)
|
|
: StringRef(Config.insert(std::make_pair(Name, Default)).first->second);
|
|
return llvm::StringSwitch<bool>(V)
|
|
.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();
|
|
}
|
|
|
|
bool AnalyzerOptions::includeTemporaryDtorsInCFG() {
|
|
return getBooleanOption(IncludeTemporaryDtorsInCFG,
|
|
"cfg-temporary-dtors",
|
|
/* Default = */ true);
|
|
}
|
|
|
|
bool AnalyzerOptions::includeImplicitDtorsInCFG() {
|
|
return getBooleanOption(IncludeImplicitDtorsInCFG,
|
|
"cfg-implicit-dtors",
|
|
/* Default = */ true);
|
|
}
|
|
|
|
bool AnalyzerOptions::includeLifetimeInCFG() {
|
|
return getBooleanOption(IncludeLifetimeInCFG, "cfg-lifetime",
|
|
/* Default = */ false);
|
|
}
|
|
|
|
bool AnalyzerOptions::includeLoopExitInCFG() {
|
|
return getBooleanOption(IncludeLoopExitInCFG, "cfg-loopexit",
|
|
/* Default = */ false);
|
|
}
|
|
|
|
bool AnalyzerOptions::includeRichConstructorsInCFG() {
|
|
return getBooleanOption(IncludeRichConstructorsInCFG,
|
|
"cfg-rich-constructors",
|
|
/* Default = */ true);
|
|
}
|
|
|
|
bool AnalyzerOptions::includeScopesInCFG() {
|
|
return getBooleanOption(IncludeScopesInCFG,
|
|
"cfg-scopes",
|
|
/* Default = */ false);
|
|
}
|
|
|
|
bool AnalyzerOptions::mayInlineCXXStandardLibrary() {
|
|
return getBooleanOption(InlineCXXStandardLibrary,
|
|
"c++-stdlib-inlining",
|
|
/*Default=*/true);
|
|
}
|
|
|
|
bool AnalyzerOptions::mayInlineTemplateFunctions() {
|
|
return getBooleanOption(InlineTemplateFunctions,
|
|
"c++-template-inlining",
|
|
/*Default=*/true);
|
|
}
|
|
|
|
bool AnalyzerOptions::mayInlineCXXAllocator() {
|
|
return getBooleanOption(InlineCXXAllocator,
|
|
"c++-allocator-inlining",
|
|
/*Default=*/true);
|
|
}
|
|
|
|
bool AnalyzerOptions::mayInlineCXXContainerMethods() {
|
|
return getBooleanOption(InlineCXXContainerMethods,
|
|
"c++-container-inlining",
|
|
/*Default=*/false);
|
|
}
|
|
|
|
bool AnalyzerOptions::mayInlineCXXSharedPtrDtor() {
|
|
return getBooleanOption(InlineCXXSharedPtrDtor,
|
|
"c++-shared_ptr-inlining",
|
|
/*Default=*/false);
|
|
}
|
|
|
|
bool AnalyzerOptions::mayInlineCXXTemporaryDtors() {
|
|
return getBooleanOption(InlineCXXTemporaryDtors,
|
|
"c++-temp-dtor-inlining",
|
|
/*Default=*/true);
|
|
}
|
|
|
|
bool AnalyzerOptions::mayInlineObjCMethod() {
|
|
return getBooleanOption(ObjCInliningMode,
|
|
"objc-inlining",
|
|
/* Default = */ true);
|
|
}
|
|
|
|
bool AnalyzerOptions::shouldSuppressNullReturnPaths() {
|
|
return getBooleanOption(SuppressNullReturnPaths,
|
|
"suppress-null-return-paths",
|
|
/* Default = */ true);
|
|
}
|
|
|
|
bool AnalyzerOptions::shouldAvoidSuppressingNullArgumentPaths() {
|
|
return getBooleanOption(AvoidSuppressingNullArgumentPaths,
|
|
"avoid-suppressing-null-argument-paths",
|
|
/* Default = */ false);
|
|
}
|
|
|
|
bool AnalyzerOptions::shouldSuppressInlinedDefensiveChecks() {
|
|
return getBooleanOption(SuppressInlinedDefensiveChecks,
|
|
"suppress-inlined-defensive-checks",
|
|
/* Default = */ true);
|
|
}
|
|
|
|
bool AnalyzerOptions::shouldSuppressFromCXXStandardLibrary() {
|
|
return getBooleanOption(SuppressFromCXXStandardLibrary,
|
|
"suppress-c++-stdlib",
|
|
/* Default = */ true);
|
|
}
|
|
|
|
bool AnalyzerOptions::shouldCrosscheckWithZ3() {
|
|
return getBooleanOption(CrosscheckWithZ3,
|
|
"crosscheck-with-z3",
|
|
/* Default = */ false);
|
|
}
|
|
|
|
bool AnalyzerOptions::shouldReportIssuesInMainSourceFile() {
|
|
return getBooleanOption(ReportIssuesInMainSourceFile,
|
|
"report-in-main-source-file",
|
|
/* Default = */ false);
|
|
}
|
|
|
|
|
|
bool AnalyzerOptions::shouldWriteStableReportFilename() {
|
|
return getBooleanOption(StableReportFilename,
|
|
"stable-report-filename",
|
|
/* Default = */ false);
|
|
}
|
|
|
|
bool AnalyzerOptions::shouldSerializeStats() {
|
|
return getBooleanOption(SerializeStats,
|
|
"serialize-stats",
|
|
/* Default = */ false);
|
|
}
|
|
|
|
bool AnalyzerOptions::shouldElideConstructors() {
|
|
return getBooleanOption(ElideConstructors,
|
|
"elide-constructors",
|
|
/* Default = */ true);
|
|
}
|
|
|
|
int AnalyzerOptions::getOptionAsInteger(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)
|
|
: StringRef(Config.insert(std::make_pair(Name, OS.str()))
|
|
.first->second);
|
|
|
|
int Res = DefaultVal;
|
|
bool b = V.getAsInteger(10, Res);
|
|
assert(!b && "analyzer-config option should be numeric");
|
|
(void)b;
|
|
return Res;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
unsigned AnalyzerOptions::getAlwaysInlineSize() {
|
|
if (!AlwaysInlineSize.hasValue())
|
|
AlwaysInlineSize = getOptionAsInteger("ipa-always-inline-size", 3);
|
|
return AlwaysInlineSize.getValue();
|
|
}
|
|
|
|
unsigned AnalyzerOptions::getMaxInlinableSize() {
|
|
if (!MaxInlinableSize.hasValue()) {
|
|
int DefaultValue = 0;
|
|
UserModeKind HighLevelMode = getUserMode();
|
|
switch (HighLevelMode) {
|
|
default:
|
|
llvm_unreachable("Invalid mode.");
|
|
case UMK_Shallow:
|
|
DefaultValue = 4;
|
|
break;
|
|
case UMK_Deep:
|
|
DefaultValue = 100;
|
|
break;
|
|
}
|
|
|
|
MaxInlinableSize = getOptionAsInteger("max-inlinable-size", DefaultValue);
|
|
}
|
|
return MaxInlinableSize.getValue();
|
|
}
|
|
|
|
unsigned AnalyzerOptions::getGraphTrimInterval() {
|
|
if (!GraphTrimInterval.hasValue())
|
|
GraphTrimInterval = getOptionAsInteger("graph-trim-interval", 1000);
|
|
return GraphTrimInterval.getValue();
|
|
}
|
|
|
|
unsigned AnalyzerOptions::getMaxTimesInlineLarge() {
|
|
if (!MaxTimesInlineLarge.hasValue())
|
|
MaxTimesInlineLarge = getOptionAsInteger("max-times-inline-large", 32);
|
|
return MaxTimesInlineLarge.getValue();
|
|
}
|
|
|
|
unsigned AnalyzerOptions::getMinCFGSizeTreatFunctionsAsLarge() {
|
|
if (!MinCFGSizeTreatFunctionsAsLarge.hasValue())
|
|
MinCFGSizeTreatFunctionsAsLarge = getOptionAsInteger(
|
|
"min-cfg-size-treat-functions-as-large", 14);
|
|
return MinCFGSizeTreatFunctionsAsLarge.getValue();
|
|
}
|
|
|
|
unsigned AnalyzerOptions::getMaxNodesPerTopLevelFunction() {
|
|
if (!MaxNodesPerTopLevelFunction.hasValue()) {
|
|
int DefaultValue = 0;
|
|
UserModeKind HighLevelMode = getUserMode();
|
|
switch (HighLevelMode) {
|
|
default:
|
|
llvm_unreachable("Invalid mode.");
|
|
case UMK_Shallow:
|
|
DefaultValue = 75000;
|
|
break;
|
|
case UMK_Deep:
|
|
DefaultValue = 225000;
|
|
break;
|
|
}
|
|
MaxNodesPerTopLevelFunction = getOptionAsInteger("max-nodes", DefaultValue);
|
|
}
|
|
return MaxNodesPerTopLevelFunction.getValue();
|
|
}
|
|
|
|
bool AnalyzerOptions::shouldSynthesizeBodies() {
|
|
return getBooleanOption("faux-bodies", true);
|
|
}
|
|
|
|
bool AnalyzerOptions::shouldPrunePaths() {
|
|
return getBooleanOption("prune-paths", true);
|
|
}
|
|
|
|
bool AnalyzerOptions::shouldConditionalizeStaticInitializers() {
|
|
return getBooleanOption("cfg-conditional-static-initializers", true);
|
|
}
|
|
|
|
bool AnalyzerOptions::shouldInlineLambdas() {
|
|
if (!InlineLambdas.hasValue())
|
|
InlineLambdas = getBooleanOption("inline-lambdas", /*Default=*/true);
|
|
return InlineLambdas.getValue();
|
|
}
|
|
|
|
bool AnalyzerOptions::shouldWidenLoops() {
|
|
if (!WidenLoops.hasValue())
|
|
WidenLoops = getBooleanOption("widen-loops", /*Default=*/false);
|
|
return WidenLoops.getValue();
|
|
}
|
|
|
|
bool AnalyzerOptions::shouldUnrollLoops() {
|
|
if (!UnrollLoops.hasValue())
|
|
UnrollLoops = getBooleanOption("unroll-loops", /*Default=*/false);
|
|
return UnrollLoops.getValue();
|
|
}
|
|
|
|
bool AnalyzerOptions::shouldDisplayNotesAsEvents() {
|
|
if (!DisplayNotesAsEvents.hasValue())
|
|
DisplayNotesAsEvents =
|
|
getBooleanOption("notes-as-events", /*Default=*/false);
|
|
return DisplayNotesAsEvents.getValue();
|
|
}
|
|
|
|
bool AnalyzerOptions::shouldAggressivelySimplifyRelationalComparison() {
|
|
if (!AggressiveRelationalComparisonSimplification.hasValue())
|
|
AggressiveRelationalComparisonSimplification =
|
|
getBooleanOption("aggressive-relational-comparison-simplification",
|
|
/*Default=*/false);
|
|
return AggressiveRelationalComparisonSimplification.getValue();
|
|
}
|
|
|
|
StringRef AnalyzerOptions::getCTUDir() {
|
|
if (!CTUDir.hasValue()) {
|
|
CTUDir = getOptionAsString("ctu-dir", "");
|
|
if (!llvm::sys::fs::is_directory(*CTUDir))
|
|
CTUDir = "";
|
|
}
|
|
return CTUDir.getValue();
|
|
}
|
|
|
|
bool AnalyzerOptions::naiveCTUEnabled() {
|
|
if (!NaiveCTU.hasValue()) {
|
|
NaiveCTU = getBooleanOption("experimental-enable-naive-ctu-analysis",
|
|
/*Default=*/false);
|
|
}
|
|
return NaiveCTU.getValue();
|
|
}
|
|
|
|
StringRef AnalyzerOptions::getCTUIndexName() {
|
|
if (!CTUIndexName.hasValue())
|
|
CTUIndexName = getOptionAsString("ctu-index-name", "externalFnMap.txt");
|
|
return CTUIndexName.getValue();
|
|
}
|