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