1 //===-- AnalyzerOptions.cpp - Analysis Engine Options -----------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This file contains special accessors for analyzer configuration options 11 // with string representations. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" 16 #include "clang/StaticAnalyzer/Core/Checker.h" 17 #include "llvm/ADT/SmallString.h" 18 #include "llvm/ADT/StringSwitch.h" 19 #include "llvm/Support/ErrorHandling.h" 20 #include "llvm/Support/raw_ostream.h" 21 22 using namespace clang; 23 using namespace ento; 24 using namespace llvm; 25 26 std::vector<StringRef> 27 AnalyzerOptions::getRegisteredCheckers(bool IncludeExperimental /* = false */) { 28 static const StringRef StaticAnalyzerChecks[] = { 29 #define GET_CHECKERS 30 #define CHECKER(FULLNAME, CLASS, DESCFILE, HELPTEXT, GROUPINDEX, HIDDEN) \ 31 FULLNAME, 32 #include "clang/StaticAnalyzer/Checkers/Checkers.inc" 33 #undef CHECKER 34 #undef GET_CHECKERS 35 }; 36 std::vector<StringRef> Result; 37 for (StringRef CheckName : StaticAnalyzerChecks) { 38 if (!CheckName.startswith("debug.") && 39 (IncludeExperimental || !CheckName.startswith("alpha."))) 40 Result.push_back(CheckName); 41 } 42 return Result; 43 } 44 45 AnalyzerOptions::UserModeKind AnalyzerOptions::getUserMode() { 46 if (UserMode == UMK_NotSet) { 47 StringRef ModeStr = 48 Config.insert(std::make_pair("mode", "deep")).first->second; 49 UserMode = llvm::StringSwitch<UserModeKind>(ModeStr) 50 .Case("shallow", UMK_Shallow) 51 .Case("deep", UMK_Deep) 52 .Default(UMK_NotSet); 53 assert(UserMode != UMK_NotSet && "User mode is invalid."); 54 } 55 return UserMode; 56 } 57 58 AnalyzerOptions::ExplorationStrategyKind 59 AnalyzerOptions::getExplorationStrategy() { 60 if (ExplorationStrategy == ExplorationStrategyKind::NotSet) { 61 StringRef StratStr = Config.insert( 62 std::make_pair("exploration_strategy", "dfs")).first->second; 63 ExplorationStrategy = llvm::StringSwitch<ExplorationStrategyKind>(StratStr) 64 .Case("dfs", ExplorationStrategyKind::DFS) 65 .Case("bfs", ExplorationStrategyKind::BFS) 66 .Case("bfs_block_dfs_contents", ExplorationStrategyKind::BFSBlockDFSContents) 67 .Default(ExplorationStrategyKind::NotSet); 68 assert(ExplorationStrategy != ExplorationStrategyKind::NotSet 69 && "User mode is invalid."); 70 } 71 return ExplorationStrategy; 72 73 } 74 75 76 77 IPAKind AnalyzerOptions::getIPAMode() { 78 if (IPAMode == IPAK_NotSet) { 79 80 // Use the User Mode to set the default IPA value. 81 // Note, we have to add the string to the Config map for the ConfigDumper 82 // checker to function properly. 83 const char *DefaultIPA = nullptr; 84 UserModeKind HighLevelMode = getUserMode(); 85 if (HighLevelMode == UMK_Shallow) 86 DefaultIPA = "inlining"; 87 else if (HighLevelMode == UMK_Deep) 88 DefaultIPA = "dynamic-bifurcate"; 89 assert(DefaultIPA); 90 91 // Lookup the ipa configuration option, use the default from User Mode. 92 StringRef ModeStr = 93 Config.insert(std::make_pair("ipa", DefaultIPA)).first->second; 94 IPAKind IPAConfig = llvm::StringSwitch<IPAKind>(ModeStr) 95 .Case("none", IPAK_None) 96 .Case("basic-inlining", IPAK_BasicInlining) 97 .Case("inlining", IPAK_Inlining) 98 .Case("dynamic", IPAK_DynamicDispatch) 99 .Case("dynamic-bifurcate", IPAK_DynamicDispatchBifurcate) 100 .Default(IPAK_NotSet); 101 assert(IPAConfig != IPAK_NotSet && "IPA Mode is invalid."); 102 103 // Set the member variable. 104 IPAMode = IPAConfig; 105 } 106 107 return IPAMode; 108 } 109 110 bool 111 AnalyzerOptions::mayInlineCXXMemberFunction(CXXInlineableMemberKind K) { 112 if (getIPAMode() < IPAK_Inlining) 113 return false; 114 115 if (!CXXMemberInliningMode) { 116 static const char *ModeKey = "c++-inlining"; 117 118 StringRef ModeStr = 119 Config.insert(std::make_pair(ModeKey, "destructors")).first->second; 120 121 CXXInlineableMemberKind &MutableMode = 122 const_cast<CXXInlineableMemberKind &>(CXXMemberInliningMode); 123 124 MutableMode = llvm::StringSwitch<CXXInlineableMemberKind>(ModeStr) 125 .Case("constructors", CIMK_Constructors) 126 .Case("destructors", CIMK_Destructors) 127 .Case("none", CIMK_None) 128 .Case("methods", CIMK_MemberFunctions) 129 .Default(CXXInlineableMemberKind()); 130 131 if (!MutableMode) { 132 // FIXME: We should emit a warning here about an unknown inlining kind, 133 // but the AnalyzerOptions doesn't have access to a diagnostic engine. 134 MutableMode = CIMK_None; 135 } 136 } 137 138 return CXXMemberInliningMode >= K; 139 } 140 141 static StringRef toString(bool b) { return b ? "true" : "false"; } 142 143 StringRef AnalyzerOptions::getCheckerOption(StringRef CheckerName, 144 StringRef OptionName, 145 StringRef Default, 146 bool SearchInParents) { 147 // Search for a package option if the option for the checker is not specified 148 // and search in parents is enabled. 149 ConfigTable::const_iterator E = Config.end(); 150 do { 151 ConfigTable::const_iterator I = 152 Config.find((Twine(CheckerName) + ":" + OptionName).str()); 153 if (I != E) 154 return StringRef(I->getValue()); 155 size_t Pos = CheckerName.rfind('.'); 156 if (Pos == StringRef::npos) 157 return Default; 158 CheckerName = CheckerName.substr(0, Pos); 159 } while (!CheckerName.empty() && SearchInParents); 160 return Default; 161 } 162 163 bool AnalyzerOptions::getBooleanOption(StringRef Name, bool DefaultVal, 164 const CheckerBase *C, 165 bool SearchInParents) { 166 // FIXME: We should emit a warning here if the value is something other than 167 // "true", "false", or the empty string (meaning the default value), 168 // but the AnalyzerOptions doesn't have access to a diagnostic engine. 169 StringRef Default = toString(DefaultVal); 170 StringRef V = 171 C ? getCheckerOption(C->getTagDescription(), Name, Default, 172 SearchInParents) 173 : StringRef(Config.insert(std::make_pair(Name, Default)).first->second); 174 return llvm::StringSwitch<bool>(V) 175 .Case("true", true) 176 .Case("false", false) 177 .Default(DefaultVal); 178 } 179 180 bool AnalyzerOptions::getBooleanOption(Optional<bool> &V, StringRef Name, 181 bool DefaultVal, const CheckerBase *C, 182 bool SearchInParents) { 183 if (!V.hasValue()) 184 V = getBooleanOption(Name, DefaultVal, C, SearchInParents); 185 return V.getValue(); 186 } 187 188 bool AnalyzerOptions::includeTemporaryDtorsInCFG() { 189 return getBooleanOption(IncludeTemporaryDtorsInCFG, 190 "cfg-temporary-dtors", 191 /* Default = */ false); 192 } 193 194 bool AnalyzerOptions::includeImplicitDtorsInCFG() { 195 return getBooleanOption(IncludeImplicitDtorsInCFG, 196 "cfg-implicit-dtors", 197 /* Default = */ true); 198 } 199 200 bool AnalyzerOptions::includeLifetimeInCFG() { 201 return getBooleanOption(IncludeLifetimeInCFG, "cfg-lifetime", 202 /* Default = */ false); 203 } 204 205 bool AnalyzerOptions::includeLoopExitInCFG() { 206 return getBooleanOption(IncludeLoopExitInCFG, "cfg-loopexit", 207 /* Default = */ false); 208 } 209 210 bool AnalyzerOptions::mayInlineCXXStandardLibrary() { 211 return getBooleanOption(InlineCXXStandardLibrary, 212 "c++-stdlib-inlining", 213 /*Default=*/true); 214 } 215 216 bool AnalyzerOptions::mayInlineTemplateFunctions() { 217 return getBooleanOption(InlineTemplateFunctions, 218 "c++-template-inlining", 219 /*Default=*/true); 220 } 221 222 bool AnalyzerOptions::mayInlineCXXAllocator() { 223 return getBooleanOption(InlineCXXAllocator, 224 "c++-allocator-inlining", 225 /*Default=*/true); 226 } 227 228 bool AnalyzerOptions::mayInlineCXXContainerMethods() { 229 return getBooleanOption(InlineCXXContainerMethods, 230 "c++-container-inlining", 231 /*Default=*/false); 232 } 233 234 bool AnalyzerOptions::mayInlineCXXSharedPtrDtor() { 235 return getBooleanOption(InlineCXXSharedPtrDtor, 236 "c++-shared_ptr-inlining", 237 /*Default=*/false); 238 } 239 240 241 bool AnalyzerOptions::mayInlineObjCMethod() { 242 return getBooleanOption(ObjCInliningMode, 243 "objc-inlining", 244 /* Default = */ true); 245 } 246 247 bool AnalyzerOptions::shouldSuppressNullReturnPaths() { 248 return getBooleanOption(SuppressNullReturnPaths, 249 "suppress-null-return-paths", 250 /* Default = */ true); 251 } 252 253 bool AnalyzerOptions::shouldAvoidSuppressingNullArgumentPaths() { 254 return getBooleanOption(AvoidSuppressingNullArgumentPaths, 255 "avoid-suppressing-null-argument-paths", 256 /* Default = */ false); 257 } 258 259 bool AnalyzerOptions::shouldSuppressInlinedDefensiveChecks() { 260 return getBooleanOption(SuppressInlinedDefensiveChecks, 261 "suppress-inlined-defensive-checks", 262 /* Default = */ true); 263 } 264 265 bool AnalyzerOptions::shouldSuppressFromCXXStandardLibrary() { 266 return getBooleanOption(SuppressFromCXXStandardLibrary, 267 "suppress-c++-stdlib", 268 /* Default = */ true); 269 } 270 271 bool AnalyzerOptions::shouldReportIssuesInMainSourceFile() { 272 return getBooleanOption(ReportIssuesInMainSourceFile, 273 "report-in-main-source-file", 274 /* Default = */ false); 275 } 276 277 278 bool AnalyzerOptions::shouldWriteStableReportFilename() { 279 return getBooleanOption(StableReportFilename, 280 "stable-report-filename", 281 /* Default = */ false); 282 } 283 284 int AnalyzerOptions::getOptionAsInteger(StringRef Name, int DefaultVal, 285 const CheckerBase *C, 286 bool SearchInParents) { 287 SmallString<10> StrBuf; 288 llvm::raw_svector_ostream OS(StrBuf); 289 OS << DefaultVal; 290 291 StringRef V = C ? getCheckerOption(C->getTagDescription(), Name, OS.str(), 292 SearchInParents) 293 : StringRef(Config.insert(std::make_pair(Name, OS.str())) 294 .first->second); 295 296 int Res = DefaultVal; 297 bool b = V.getAsInteger(10, Res); 298 assert(!b && "analyzer-config option should be numeric"); 299 (void)b; 300 return Res; 301 } 302 303 StringRef AnalyzerOptions::getOptionAsString(StringRef Name, 304 StringRef DefaultVal, 305 const CheckerBase *C, 306 bool SearchInParents) { 307 return C ? getCheckerOption(C->getTagDescription(), Name, DefaultVal, 308 SearchInParents) 309 : StringRef( 310 Config.insert(std::make_pair(Name, DefaultVal)).first->second); 311 } 312 313 unsigned AnalyzerOptions::getAlwaysInlineSize() { 314 if (!AlwaysInlineSize.hasValue()) 315 AlwaysInlineSize = getOptionAsInteger("ipa-always-inline-size", 3); 316 return AlwaysInlineSize.getValue(); 317 } 318 319 unsigned AnalyzerOptions::getMaxInlinableSize() { 320 if (!MaxInlinableSize.hasValue()) { 321 322 int DefaultValue = 0; 323 UserModeKind HighLevelMode = getUserMode(); 324 switch (HighLevelMode) { 325 default: 326 llvm_unreachable("Invalid mode."); 327 case UMK_Shallow: 328 DefaultValue = 4; 329 break; 330 case UMK_Deep: 331 DefaultValue = 100; 332 break; 333 } 334 335 MaxInlinableSize = getOptionAsInteger("max-inlinable-size", DefaultValue); 336 } 337 return MaxInlinableSize.getValue(); 338 } 339 340 unsigned AnalyzerOptions::getGraphTrimInterval() { 341 if (!GraphTrimInterval.hasValue()) 342 GraphTrimInterval = getOptionAsInteger("graph-trim-interval", 1000); 343 return GraphTrimInterval.getValue(); 344 } 345 346 unsigned AnalyzerOptions::getMaxTimesInlineLarge() { 347 if (!MaxTimesInlineLarge.hasValue()) 348 MaxTimesInlineLarge = getOptionAsInteger("max-times-inline-large", 32); 349 return MaxTimesInlineLarge.getValue(); 350 } 351 352 unsigned AnalyzerOptions::getMinCFGSizeTreatFunctionsAsLarge() { 353 if (!MinCFGSizeTreatFunctionsAsLarge.hasValue()) 354 MinCFGSizeTreatFunctionsAsLarge = getOptionAsInteger( 355 "min-cfg-size-treat-functions-as-large", 14); 356 return MinCFGSizeTreatFunctionsAsLarge.getValue(); 357 } 358 359 unsigned AnalyzerOptions::getMaxNodesPerTopLevelFunction() { 360 if (!MaxNodesPerTopLevelFunction.hasValue()) { 361 int DefaultValue = 0; 362 UserModeKind HighLevelMode = getUserMode(); 363 switch (HighLevelMode) { 364 default: 365 llvm_unreachable("Invalid mode."); 366 case UMK_Shallow: 367 DefaultValue = 75000; 368 break; 369 case UMK_Deep: 370 DefaultValue = 225000; 371 break; 372 } 373 MaxNodesPerTopLevelFunction = getOptionAsInteger("max-nodes", DefaultValue); 374 } 375 return MaxNodesPerTopLevelFunction.getValue(); 376 } 377 378 bool AnalyzerOptions::shouldSynthesizeBodies() { 379 return getBooleanOption("faux-bodies", true); 380 } 381 382 bool AnalyzerOptions::shouldPrunePaths() { 383 return getBooleanOption("prune-paths", true); 384 } 385 386 bool AnalyzerOptions::shouldConditionalizeStaticInitializers() { 387 return getBooleanOption("cfg-conditional-static-initializers", true); 388 } 389 390 bool AnalyzerOptions::shouldInlineLambdas() { 391 if (!InlineLambdas.hasValue()) 392 InlineLambdas = getBooleanOption("inline-lambdas", /*Default=*/true); 393 return InlineLambdas.getValue(); 394 } 395 396 bool AnalyzerOptions::shouldWidenLoops() { 397 if (!WidenLoops.hasValue()) 398 WidenLoops = getBooleanOption("widen-loops", /*Default=*/false); 399 return WidenLoops.getValue(); 400 } 401 402 bool AnalyzerOptions::shouldUnrollLoops() { 403 if (!UnrollLoops.hasValue()) 404 UnrollLoops = getBooleanOption("unroll-loops", /*Default=*/false); 405 return UnrollLoops.getValue(); 406 } 407 408 bool AnalyzerOptions::shouldDisplayNotesAsEvents() { 409 if (!DisplayNotesAsEvents.hasValue()) 410 DisplayNotesAsEvents = 411 getBooleanOption("notes-as-events", /*Default=*/false); 412 return DisplayNotesAsEvents.getValue(); 413 } 414