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