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