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 int AnalyzerOptions::getOptionAsInteger(StringRef Name, int DefaultVal,
325                                         const CheckerBase *C,
326                                         bool SearchInParents) {
327   SmallString<10> StrBuf;
328   llvm::raw_svector_ostream OS(StrBuf);
329   OS << DefaultVal;
330 
331   StringRef V = C ? getCheckerOption(C->getTagDescription(), Name, OS.str(),
332                                      SearchInParents)
333                   : StringRef(Config.insert(std::make_pair(Name, OS.str()))
334                                   .first->second);
335 
336   int Res = DefaultVal;
337   bool b = V.getAsInteger(10, Res);
338   assert(!b && "analyzer-config option should be numeric");
339   (void)b;
340   return Res;
341 }
342 
343 StringRef AnalyzerOptions::getOptionAsString(StringRef Name,
344                                              StringRef DefaultVal,
345                                              const CheckerBase *C,
346                                              bool SearchInParents) {
347   return C ? getCheckerOption(C->getTagDescription(), Name, DefaultVal,
348                               SearchInParents)
349            : StringRef(
350                  Config.insert(std::make_pair(Name, DefaultVal)).first->second);
351 }
352 
353 unsigned AnalyzerOptions::getAlwaysInlineSize() {
354   if (!AlwaysInlineSize.hasValue())
355     AlwaysInlineSize = getOptionAsInteger("ipa-always-inline-size", 3);
356   return AlwaysInlineSize.getValue();
357 }
358 
359 unsigned AnalyzerOptions::getMaxInlinableSize() {
360   if (!MaxInlinableSize.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 = 4;
368         break;
369       case UMK_Deep:
370         DefaultValue = 100;
371         break;
372     }
373 
374     MaxInlinableSize = getOptionAsInteger("max-inlinable-size", DefaultValue);
375   }
376   return MaxInlinableSize.getValue();
377 }
378 
379 unsigned AnalyzerOptions::getGraphTrimInterval() {
380   if (!GraphTrimInterval.hasValue())
381     GraphTrimInterval = getOptionAsInteger("graph-trim-interval", 1000);
382   return GraphTrimInterval.getValue();
383 }
384 
385 unsigned AnalyzerOptions::getMaxTimesInlineLarge() {
386   if (!MaxTimesInlineLarge.hasValue())
387     MaxTimesInlineLarge = getOptionAsInteger("max-times-inline-large", 32);
388   return MaxTimesInlineLarge.getValue();
389 }
390 
391 unsigned AnalyzerOptions::getMinCFGSizeTreatFunctionsAsLarge() {
392   if (!MinCFGSizeTreatFunctionsAsLarge.hasValue())
393     MinCFGSizeTreatFunctionsAsLarge = getOptionAsInteger(
394       "min-cfg-size-treat-functions-as-large", 14);
395   return MinCFGSizeTreatFunctionsAsLarge.getValue();
396 }
397 
398 unsigned AnalyzerOptions::getMaxNodesPerTopLevelFunction() {
399   if (!MaxNodesPerTopLevelFunction.hasValue()) {
400     int DefaultValue = 0;
401     UserModeKind HighLevelMode = getUserMode();
402     switch (HighLevelMode) {
403       default:
404         llvm_unreachable("Invalid mode.");
405       case UMK_Shallow:
406         DefaultValue = 75000;
407         break;
408       case UMK_Deep:
409         DefaultValue = 225000;
410         break;
411     }
412     MaxNodesPerTopLevelFunction = getOptionAsInteger("max-nodes", DefaultValue);
413   }
414   return MaxNodesPerTopLevelFunction.getValue();
415 }
416 
417 bool AnalyzerOptions::shouldSynthesizeBodies() {
418   return getBooleanOption("faux-bodies", true);
419 }
420 
421 bool AnalyzerOptions::shouldPrunePaths() {
422   return getBooleanOption("prune-paths", true);
423 }
424 
425 bool AnalyzerOptions::shouldConditionalizeStaticInitializers() {
426   return getBooleanOption("cfg-conditional-static-initializers", true);
427 }
428 
429 bool AnalyzerOptions::shouldInlineLambdas() {
430   if (!InlineLambdas.hasValue())
431     InlineLambdas = getBooleanOption("inline-lambdas", /*Default=*/true);
432   return InlineLambdas.getValue();
433 }
434 
435 bool AnalyzerOptions::shouldWidenLoops() {
436   if (!WidenLoops.hasValue())
437     WidenLoops = getBooleanOption("widen-loops", /*Default=*/false);
438   return WidenLoops.getValue();
439 }
440 
441 bool AnalyzerOptions::shouldUnrollLoops() {
442   if (!UnrollLoops.hasValue())
443     UnrollLoops = getBooleanOption("unroll-loops", /*Default=*/false);
444   return UnrollLoops.getValue();
445 }
446 
447 bool AnalyzerOptions::shouldDisplayNotesAsEvents() {
448   if (!DisplayNotesAsEvents.hasValue())
449     DisplayNotesAsEvents =
450         getBooleanOption("notes-as-events", /*Default=*/false);
451   return DisplayNotesAsEvents.getValue();
452 }
453 
454 bool AnalyzerOptions::shouldAggressivelySimplifyRelationalComparison() {
455   if (!AggressiveRelationalComparisonSimplification.hasValue())
456     AggressiveRelationalComparisonSimplification =
457       getBooleanOption("aggressive-relational-comparison-simplification",
458                        /*Default=*/false);
459   return AggressiveRelationalComparisonSimplification.getValue();
460 }
461 
462 StringRef AnalyzerOptions::getCTUDir() {
463   if (!CTUDir.hasValue()) {
464     CTUDir = getOptionAsString("ctu-dir", "");
465     if (!llvm::sys::fs::is_directory(*CTUDir))
466       CTUDir = "";
467   }
468   return CTUDir.getValue();
469 }
470 
471 bool AnalyzerOptions::naiveCTUEnabled() {
472   if (!NaiveCTU.hasValue()) {
473     NaiveCTU = getBooleanOption("experimental-enable-naive-ctu-analysis",
474                                 /*Default=*/false);
475   }
476   return NaiveCTU.getValue();
477 }
478 
479 StringRef AnalyzerOptions::getCTUIndexName() {
480   if (!CTUIndexName.hasValue())
481     CTUIndexName = getOptionAsString("ctu-index-name", "externalFnMap.txt");
482   return CTUIndexName.getValue();
483 }
484