1 //===- AnalyzerOptions.cpp - Analysis Engine Options ----------------------===// 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/ADT/StringRef.h" 20 #include "llvm/ADT/Twine.h" 21 #include "llvm/Support/ErrorHandling.h" 22 #include "llvm/Support/FileSystem.h" 23 #include "llvm/Support/raw_ostream.h" 24 #include <cassert> 25 #include <cstddef> 26 #include <utility> 27 #include <vector> 28 29 using namespace clang; 30 using namespace ento; 31 using namespace llvm; 32 33 std::vector<StringRef> 34 AnalyzerOptions::getRegisteredCheckers(bool IncludeExperimental /* = false */) { 35 static const StringRef StaticAnalyzerChecks[] = { 36 #define GET_CHECKERS 37 #define CHECKER(FULLNAME, CLASS, DESCFILE, HELPTEXT, GROUPINDEX, HIDDEN) \ 38 FULLNAME, 39 #include "clang/StaticAnalyzer/Checkers/Checkers.inc" 40 #undef CHECKER 41 #undef GET_CHECKERS 42 }; 43 std::vector<StringRef> Result; 44 for (StringRef CheckName : StaticAnalyzerChecks) { 45 if (!CheckName.startswith("debug.") && 46 (IncludeExperimental || !CheckName.startswith("alpha."))) 47 Result.push_back(CheckName); 48 } 49 return Result; 50 } 51 52 AnalyzerOptions::UserModeKind AnalyzerOptions::getUserMode() { 53 if (!UserMode.hasValue()) { 54 StringRef ModeStr = getOptionAsString("mode", "deep"); 55 UserMode = llvm::StringSwitch<llvm::Optional<UserModeKind>>(ModeStr) 56 .Case("shallow", UMK_Shallow) 57 .Case("deep", UMK_Deep) 58 .Default(None); 59 assert(UserMode.getValue() && "User mode is invalid."); 60 } 61 return UserMode.getValue(); 62 } 63 64 AnalyzerOptions::ExplorationStrategyKind 65 AnalyzerOptions::getExplorationStrategy() { 66 if (!ExplorationStrategy.hasValue()) { 67 StringRef StratStr = getOptionAsString("exploration_strategy", 68 "unexplored_first_queue"); 69 ExplorationStrategy = 70 llvm::StringSwitch<llvm::Optional<ExplorationStrategyKind>>(StratStr) 71 .Case("dfs", ExplorationStrategyKind::DFS) 72 .Case("bfs", ExplorationStrategyKind::BFS) 73 .Case("unexplored_first", 74 ExplorationStrategyKind::UnexploredFirst) 75 .Case("unexplored_first_queue", 76 ExplorationStrategyKind::UnexploredFirstQueue) 77 .Case("unexplored_first_location_queue", 78 ExplorationStrategyKind::UnexploredFirstLocationQueue) 79 .Case("bfs_block_dfs_contents", 80 ExplorationStrategyKind::BFSBlockDFSContents) 81 .Default(None); 82 assert(ExplorationStrategy.hasValue() && 83 "User mode is invalid."); 84 } 85 return ExplorationStrategy.getValue(); 86 } 87 88 IPAKind AnalyzerOptions::getIPAMode() { 89 if (!IPAMode.hasValue()) { 90 // Use the User Mode to set the default IPA value. 91 // Note, we have to add the string to the Config map for the ConfigDumper 92 // checker to function properly. 93 const char *DefaultIPA = nullptr; 94 UserModeKind HighLevelMode = getUserMode(); 95 if (HighLevelMode == UMK_Shallow) 96 DefaultIPA = "inlining"; 97 else if (HighLevelMode == UMK_Deep) 98 DefaultIPA = "dynamic-bifurcate"; 99 assert(DefaultIPA); 100 101 // Lookup the ipa configuration option, use the default from User Mode. 102 StringRef ModeStr = getOptionAsString("ipa", DefaultIPA); 103 IPAMode = llvm::StringSwitch<llvm::Optional<IPAKind>>(ModeStr) 104 .Case("none", IPAK_None) 105 .Case("basic-inlining", IPAK_BasicInlining) 106 .Case("inlining", IPAK_Inlining) 107 .Case("dynamic", IPAK_DynamicDispatch) 108 .Case("dynamic-bifurcate", IPAK_DynamicDispatchBifurcate) 109 .Default(None); 110 assert(IPAMode.hasValue() && "IPA Mode is invalid."); 111 } 112 113 return IPAMode.getValue(); 114 } 115 116 bool 117 AnalyzerOptions::mayInlineCXXMemberFunction(CXXInlineableMemberKind K) { 118 if (getIPAMode() < IPAK_Inlining) 119 return false; 120 121 if (!CXXMemberInliningMode) { 122 StringRef ModeStr = getOptionAsString("c++-inlining", "destructors"); 123 124 CXXMemberInliningMode = 125 llvm::StringSwitch<llvm::Optional<CXXInlineableMemberKind>>(ModeStr) 126 .Case("constructors", CIMK_Constructors) 127 .Case("destructors", CIMK_Destructors) 128 .Case("methods", CIMK_MemberFunctions) 129 .Case("none", CIMK_None) 130 .Default(None); 131 132 assert(CXXMemberInliningMode.hasValue() && 133 "Invalid c++ member function inlining mode."); 134 } 135 136 return *CXXMemberInliningMode >= K; 137 } 138 139 static StringRef toString(bool b) { return b ? "true" : "false"; } 140 141 StringRef AnalyzerOptions::getCheckerOption(StringRef CheckerName, 142 StringRef OptionName, 143 StringRef Default, 144 bool SearchInParents) { 145 // Search for a package option if the option for the checker is not specified 146 // and search in parents is enabled. 147 ConfigTable::const_iterator E = Config.end(); 148 do { 149 ConfigTable::const_iterator I = 150 Config.find((Twine(CheckerName) + ":" + OptionName).str()); 151 if (I != E) 152 return StringRef(I->getValue()); 153 size_t Pos = CheckerName.rfind('.'); 154 if (Pos == StringRef::npos) 155 return Default; 156 CheckerName = CheckerName.substr(0, Pos); 157 } while (!CheckerName.empty() && SearchInParents); 158 return Default; 159 } 160 161 bool AnalyzerOptions::getBooleanOption(StringRef Name, bool DefaultVal, 162 const CheckerBase *C, 163 bool SearchInParents) { 164 // FIXME: We should emit a warning here if the value is something other than 165 // "true", "false", or the empty string (meaning the default value), 166 // but the AnalyzerOptions doesn't have access to a diagnostic engine. 167 StringRef Default = toString(DefaultVal); 168 StringRef V = 169 C ? getCheckerOption(C->getTagDescription(), Name, Default, 170 SearchInParents) 171 : getOptionAsString(Name, Default); 172 return llvm::StringSwitch<bool>(V) 173 .Case("true", true) 174 .Case("false", false) 175 .Default(DefaultVal); 176 } 177 178 bool AnalyzerOptions::getBooleanOption(Optional<bool> &V, StringRef Name, 179 bool DefaultVal, const CheckerBase *C, 180 bool SearchInParents) { 181 if (!V.hasValue()) 182 V = getBooleanOption(Name, DefaultVal, C, SearchInParents); 183 return V.getValue(); 184 } 185 186 bool AnalyzerOptions::includeTemporaryDtorsInCFG() { 187 return getBooleanOption(IncludeTemporaryDtorsInCFG, 188 "cfg-temporary-dtors", 189 /* Default = */ true); 190 } 191 192 bool AnalyzerOptions::includeImplicitDtorsInCFG() { 193 return getBooleanOption(IncludeImplicitDtorsInCFG, 194 "cfg-implicit-dtors", 195 /* Default = */ true); 196 } 197 198 bool AnalyzerOptions::includeLifetimeInCFG() { 199 return getBooleanOption(IncludeLifetimeInCFG, "cfg-lifetime", 200 /* Default = */ false); 201 } 202 203 bool AnalyzerOptions::includeLoopExitInCFG() { 204 return getBooleanOption(IncludeLoopExitInCFG, "cfg-loopexit", 205 /* Default = */ false); 206 } 207 208 bool AnalyzerOptions::includeRichConstructorsInCFG() { 209 return getBooleanOption(IncludeRichConstructorsInCFG, 210 "cfg-rich-constructors", 211 /* Default = */ true); 212 } 213 214 bool AnalyzerOptions::includeScopesInCFG() { 215 return getBooleanOption(IncludeScopesInCFG, 216 "cfg-scopes", 217 /* Default = */ false); 218 } 219 220 bool AnalyzerOptions::mayInlineCXXStandardLibrary() { 221 return getBooleanOption(InlineCXXStandardLibrary, 222 "c++-stdlib-inlining", 223 /*Default=*/true); 224 } 225 226 bool AnalyzerOptions::mayInlineTemplateFunctions() { 227 return getBooleanOption(InlineTemplateFunctions, 228 "c++-template-inlining", 229 /*Default=*/true); 230 } 231 232 bool AnalyzerOptions::mayInlineCXXAllocator() { 233 return getBooleanOption(InlineCXXAllocator, 234 "c++-allocator-inlining", 235 /*Default=*/true); 236 } 237 238 bool AnalyzerOptions::mayInlineCXXContainerMethods() { 239 return getBooleanOption(InlineCXXContainerMethods, 240 "c++-container-inlining", 241 /*Default=*/false); 242 } 243 244 bool AnalyzerOptions::mayInlineCXXSharedPtrDtor() { 245 return getBooleanOption(InlineCXXSharedPtrDtor, 246 "c++-shared_ptr-inlining", 247 /*Default=*/false); 248 } 249 250 bool AnalyzerOptions::mayInlineCXXTemporaryDtors() { 251 return getBooleanOption(InlineCXXTemporaryDtors, 252 "c++-temp-dtor-inlining", 253 /*Default=*/true); 254 } 255 256 bool AnalyzerOptions::mayInlineObjCMethod() { 257 return getBooleanOption(ObjCInliningMode, 258 "objc-inlining", 259 /* Default = */ true); 260 } 261 262 bool AnalyzerOptions::shouldSuppressNullReturnPaths() { 263 return getBooleanOption(SuppressNullReturnPaths, 264 "suppress-null-return-paths", 265 /* Default = */ true); 266 } 267 268 bool AnalyzerOptions::shouldAvoidSuppressingNullArgumentPaths() { 269 return getBooleanOption(AvoidSuppressingNullArgumentPaths, 270 "avoid-suppressing-null-argument-paths", 271 /* Default = */ false); 272 } 273 274 bool AnalyzerOptions::shouldSuppressInlinedDefensiveChecks() { 275 return getBooleanOption(SuppressInlinedDefensiveChecks, 276 "suppress-inlined-defensive-checks", 277 /* Default = */ true); 278 } 279 280 bool AnalyzerOptions::shouldSuppressFromCXXStandardLibrary() { 281 return getBooleanOption(SuppressFromCXXStandardLibrary, 282 "suppress-c++-stdlib", 283 /* Default = */ true); 284 } 285 286 bool AnalyzerOptions::shouldCrosscheckWithZ3() { 287 return getBooleanOption(CrosscheckWithZ3, 288 "crosscheck-with-z3", 289 /* Default = */ false); 290 } 291 292 bool AnalyzerOptions::shouldReportIssuesInMainSourceFile() { 293 return getBooleanOption(ReportIssuesInMainSourceFile, 294 "report-in-main-source-file", 295 /* Default = */ false); 296 } 297 298 299 bool AnalyzerOptions::shouldWriteStableReportFilename() { 300 return getBooleanOption(StableReportFilename, 301 "stable-report-filename", 302 /* Default = */ false); 303 } 304 305 bool AnalyzerOptions::shouldSerializeStats() { 306 return getBooleanOption(SerializeStats, 307 "serialize-stats", 308 /* Default = */ false); 309 } 310 311 bool AnalyzerOptions::shouldElideConstructors() { 312 return getBooleanOption(ElideConstructors, 313 "elide-constructors", 314 /* Default = */ true); 315 } 316 317 int AnalyzerOptions::getOptionAsInteger(StringRef Name, int DefaultVal, 318 const CheckerBase *C, 319 bool SearchInParents) { 320 SmallString<10> StrBuf; 321 llvm::raw_svector_ostream OS(StrBuf); 322 OS << DefaultVal; 323 324 StringRef V = C ? getCheckerOption(C->getTagDescription(), Name, OS.str(), 325 SearchInParents) 326 : getOptionAsString(Name, OS.str()); 327 328 int Res = DefaultVal; 329 bool b = V.getAsInteger(10, Res); 330 assert(!b && "analyzer-config option should be numeric"); 331 (void)b; 332 return Res; 333 } 334 335 unsigned AnalyzerOptions::getOptionAsUInt(Optional<unsigned> &V, StringRef Name, 336 unsigned DefaultVal, 337 const CheckerBase *C, 338 bool SearchInParents) { 339 if (!V.hasValue()) 340 V = getOptionAsInteger(Name, DefaultVal, C, SearchInParents); 341 return V.getValue(); 342 } 343 344 StringRef AnalyzerOptions::getOptionAsString(StringRef Name, 345 StringRef DefaultVal, 346 const CheckerBase *C, 347 bool SearchInParents) { 348 return C ? getCheckerOption(C->getTagDescription(), Name, DefaultVal, 349 SearchInParents) 350 : StringRef( 351 Config.insert(std::make_pair(Name, DefaultVal)).first->second); 352 } 353 354 StringRef AnalyzerOptions::getOptionAsString(Optional<StringRef> &V, 355 StringRef Name, 356 StringRef DefaultVal, 357 const ento::CheckerBase *C, 358 bool SearchInParents) { 359 if (!V.hasValue()) 360 V = getOptionAsString(Name, DefaultVal, C, SearchInParents); 361 return V.getValue(); 362 } 363 364 unsigned AnalyzerOptions::getAlwaysInlineSize() { 365 if (!AlwaysInlineSize.hasValue()) 366 AlwaysInlineSize = getOptionAsInteger("ipa-always-inline-size", 3); 367 return AlwaysInlineSize.getValue(); 368 } 369 370 unsigned AnalyzerOptions::getMaxInlinableSize() { 371 if (!MaxInlinableSize.hasValue()) { 372 int DefaultValue = 0; 373 UserModeKind HighLevelMode = getUserMode(); 374 switch (HighLevelMode) { 375 case UMK_Shallow: 376 DefaultValue = 4; 377 break; 378 case UMK_Deep: 379 DefaultValue = 100; 380 break; 381 } 382 383 MaxInlinableSize = getOptionAsInteger("max-inlinable-size", DefaultValue); 384 } 385 return MaxInlinableSize.getValue(); 386 } 387 388 unsigned AnalyzerOptions::getGraphTrimInterval() { 389 if (!GraphTrimInterval.hasValue()) 390 GraphTrimInterval = getOptionAsInteger("graph-trim-interval", 1000); 391 return GraphTrimInterval.getValue(); 392 } 393 394 unsigned AnalyzerOptions::getMaxSymbolComplexity() { 395 if (!MaxSymbolComplexity.hasValue()) 396 MaxSymbolComplexity = getOptionAsInteger("max-symbol-complexity", 35); 397 return MaxSymbolComplexity.getValue(); 398 } 399 400 unsigned AnalyzerOptions::getMaxTimesInlineLarge() { 401 if (!MaxTimesInlineLarge.hasValue()) 402 MaxTimesInlineLarge = getOptionAsInteger("max-times-inline-large", 32); 403 return MaxTimesInlineLarge.getValue(); 404 } 405 406 unsigned AnalyzerOptions::getMinCFGSizeTreatFunctionsAsLarge() { 407 if (!MinCFGSizeTreatFunctionsAsLarge.hasValue()) 408 MinCFGSizeTreatFunctionsAsLarge = getOptionAsInteger( 409 "min-cfg-size-treat-functions-as-large", 14); 410 return MinCFGSizeTreatFunctionsAsLarge.getValue(); 411 } 412 413 unsigned AnalyzerOptions::getMaxNodesPerTopLevelFunction() { 414 if (!MaxNodesPerTopLevelFunction.hasValue()) { 415 int DefaultValue = 0; 416 UserModeKind HighLevelMode = getUserMode(); 417 switch (HighLevelMode) { 418 case UMK_Shallow: 419 DefaultValue = 75000; 420 break; 421 case UMK_Deep: 422 DefaultValue = 225000; 423 break; 424 } 425 MaxNodesPerTopLevelFunction = getOptionAsInteger("max-nodes", DefaultValue); 426 } 427 return MaxNodesPerTopLevelFunction.getValue(); 428 } 429 430 bool AnalyzerOptions::shouldSynthesizeBodies() { 431 return getBooleanOption("faux-bodies", true); 432 } 433 434 bool AnalyzerOptions::shouldPrunePaths() { 435 return getBooleanOption("prune-paths", true); 436 } 437 438 bool AnalyzerOptions::shouldConditionalizeStaticInitializers() { 439 return getBooleanOption("cfg-conditional-static-initializers", true); 440 } 441 442 bool AnalyzerOptions::shouldInlineLambdas() { 443 if (!InlineLambdas.hasValue()) 444 InlineLambdas = getBooleanOption("inline-lambdas", /*Default=*/true); 445 return InlineLambdas.getValue(); 446 } 447 448 bool AnalyzerOptions::shouldWidenLoops() { 449 if (!WidenLoops.hasValue()) 450 WidenLoops = getBooleanOption("widen-loops", /*Default=*/false); 451 return WidenLoops.getValue(); 452 } 453 454 bool AnalyzerOptions::shouldUnrollLoops() { 455 if (!UnrollLoops.hasValue()) 456 UnrollLoops = getBooleanOption("unroll-loops", /*Default=*/false); 457 return UnrollLoops.getValue(); 458 } 459 460 bool AnalyzerOptions::shouldDisplayNotesAsEvents() { 461 if (!DisplayNotesAsEvents.hasValue()) 462 DisplayNotesAsEvents = 463 getBooleanOption("notes-as-events", /*Default=*/false); 464 return DisplayNotesAsEvents.getValue(); 465 } 466 467 bool AnalyzerOptions::shouldAggressivelySimplifyBinaryOperation() { 468 if (!AggressiveBinaryOperationSimplification.hasValue()) 469 AggressiveBinaryOperationSimplification = 470 getBooleanOption("aggressive-binary-operation-simplification", 471 /*Default=*/false); 472 return AggressiveBinaryOperationSimplification.getValue(); 473 } 474 475 bool AnalyzerOptions::shouldEagerlyAssume() { 476 if (!EagerlyAssumeBinOpBifurcation.hasValue()) 477 EagerlyAssumeBinOpBifurcation = 478 getBooleanOption("eagerly-assume", true); 479 return EagerlyAssumeBinOpBifurcation.getValue(); 480 } 481 482 StringRef AnalyzerOptions::getCTUDir() { 483 if (!CTUDir.hasValue()) { 484 CTUDir = getOptionAsString("ctu-dir", ""); 485 if (!llvm::sys::fs::is_directory(*CTUDir)) 486 CTUDir = ""; 487 } 488 return CTUDir.getValue(); 489 } 490 491 bool AnalyzerOptions::naiveCTUEnabled() { 492 if (!NaiveCTU.hasValue()) { 493 NaiveCTU = getBooleanOption("experimental-enable-naive-ctu-analysis", 494 /*Default=*/false); 495 } 496 return NaiveCTU.getValue(); 497 } 498 499 StringRef AnalyzerOptions::getCTUIndexName() { 500 if (!CTUIndexName.hasValue()) 501 CTUIndexName = getOptionAsString("ctu-index-name", "externalFnMap.txt"); 502 return CTUIndexName.getValue(); 503 } 504