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::mayInlineCXXStandardLibrary() {
228   return getBooleanOption(InlineCXXStandardLibrary,
229                           "c++-stdlib-inlining",
230                           /*Default=*/true);
231 }
232 
233 bool AnalyzerOptions::mayInlineTemplateFunctions() {
234   return getBooleanOption(InlineTemplateFunctions,
235                           "c++-template-inlining",
236                           /*Default=*/true);
237 }
238 
239 bool AnalyzerOptions::mayInlineCXXAllocator() {
240   return getBooleanOption(InlineCXXAllocator,
241                           "c++-allocator-inlining",
242                           /*Default=*/true);
243 }
244 
245 bool AnalyzerOptions::mayInlineCXXContainerMethods() {
246   return getBooleanOption(InlineCXXContainerMethods,
247                           "c++-container-inlining",
248                           /*Default=*/false);
249 }
250 
251 bool AnalyzerOptions::mayInlineCXXSharedPtrDtor() {
252   return getBooleanOption(InlineCXXSharedPtrDtor,
253                           "c++-shared_ptr-inlining",
254                           /*Default=*/false);
255 }
256 
257 bool AnalyzerOptions::mayInlineCXXTemporaryDtors() {
258   return getBooleanOption(InlineCXXTemporaryDtors,
259                           "c++-temp-dtor-inlining",
260                           /*Default=*/false);
261 }
262 
263 bool AnalyzerOptions::mayInlineObjCMethod() {
264   return getBooleanOption(ObjCInliningMode,
265                           "objc-inlining",
266                           /* Default = */ true);
267 }
268 
269 bool AnalyzerOptions::shouldSuppressNullReturnPaths() {
270   return getBooleanOption(SuppressNullReturnPaths,
271                           "suppress-null-return-paths",
272                           /* Default = */ true);
273 }
274 
275 bool AnalyzerOptions::shouldAvoidSuppressingNullArgumentPaths() {
276   return getBooleanOption(AvoidSuppressingNullArgumentPaths,
277                           "avoid-suppressing-null-argument-paths",
278                           /* Default = */ false);
279 }
280 
281 bool AnalyzerOptions::shouldSuppressInlinedDefensiveChecks() {
282   return getBooleanOption(SuppressInlinedDefensiveChecks,
283                           "suppress-inlined-defensive-checks",
284                           /* Default = */ true);
285 }
286 
287 bool AnalyzerOptions::shouldSuppressFromCXXStandardLibrary() {
288   return getBooleanOption(SuppressFromCXXStandardLibrary,
289                           "suppress-c++-stdlib",
290                           /* Default = */ true);
291 }
292 
293 bool AnalyzerOptions::shouldReportIssuesInMainSourceFile() {
294   return getBooleanOption(ReportIssuesInMainSourceFile,
295                           "report-in-main-source-file",
296                           /* Default = */ false);
297 }
298 
299 
300 bool AnalyzerOptions::shouldWriteStableReportFilename() {
301   return getBooleanOption(StableReportFilename,
302                           "stable-report-filename",
303                           /* Default = */ false);
304 }
305 
306 bool AnalyzerOptions::shouldSerializeStats() {
307   return getBooleanOption(SerializeStats,
308                           "serialize-stats",
309                           /* Default = */ false);
310 }
311 
312 int AnalyzerOptions::getOptionAsInteger(StringRef Name, int DefaultVal,
313                                         const CheckerBase *C,
314                                         bool SearchInParents) {
315   SmallString<10> StrBuf;
316   llvm::raw_svector_ostream OS(StrBuf);
317   OS << DefaultVal;
318 
319   StringRef V = C ? getCheckerOption(C->getTagDescription(), Name, OS.str(),
320                                      SearchInParents)
321                   : StringRef(Config.insert(std::make_pair(Name, OS.str()))
322                                   .first->second);
323 
324   int Res = DefaultVal;
325   bool b = V.getAsInteger(10, Res);
326   assert(!b && "analyzer-config option should be numeric");
327   (void)b;
328   return Res;
329 }
330 
331 StringRef AnalyzerOptions::getOptionAsString(StringRef Name,
332                                              StringRef DefaultVal,
333                                              const CheckerBase *C,
334                                              bool SearchInParents) {
335   return C ? getCheckerOption(C->getTagDescription(), Name, DefaultVal,
336                               SearchInParents)
337            : StringRef(
338                  Config.insert(std::make_pair(Name, DefaultVal)).first->second);
339 }
340 
341 unsigned AnalyzerOptions::getAlwaysInlineSize() {
342   if (!AlwaysInlineSize.hasValue())
343     AlwaysInlineSize = getOptionAsInteger("ipa-always-inline-size", 3);
344   return AlwaysInlineSize.getValue();
345 }
346 
347 unsigned AnalyzerOptions::getMaxInlinableSize() {
348   if (!MaxInlinableSize.hasValue()) {
349     int DefaultValue = 0;
350     UserModeKind HighLevelMode = getUserMode();
351     switch (HighLevelMode) {
352       default:
353         llvm_unreachable("Invalid mode.");
354       case UMK_Shallow:
355         DefaultValue = 4;
356         break;
357       case UMK_Deep:
358         DefaultValue = 100;
359         break;
360     }
361 
362     MaxInlinableSize = getOptionAsInteger("max-inlinable-size", DefaultValue);
363   }
364   return MaxInlinableSize.getValue();
365 }
366 
367 unsigned AnalyzerOptions::getGraphTrimInterval() {
368   if (!GraphTrimInterval.hasValue())
369     GraphTrimInterval = getOptionAsInteger("graph-trim-interval", 1000);
370   return GraphTrimInterval.getValue();
371 }
372 
373 unsigned AnalyzerOptions::getMaxTimesInlineLarge() {
374   if (!MaxTimesInlineLarge.hasValue())
375     MaxTimesInlineLarge = getOptionAsInteger("max-times-inline-large", 32);
376   return MaxTimesInlineLarge.getValue();
377 }
378 
379 unsigned AnalyzerOptions::getMinCFGSizeTreatFunctionsAsLarge() {
380   if (!MinCFGSizeTreatFunctionsAsLarge.hasValue())
381     MinCFGSizeTreatFunctionsAsLarge = getOptionAsInteger(
382       "min-cfg-size-treat-functions-as-large", 14);
383   return MinCFGSizeTreatFunctionsAsLarge.getValue();
384 }
385 
386 unsigned AnalyzerOptions::getMaxNodesPerTopLevelFunction() {
387   if (!MaxNodesPerTopLevelFunction.hasValue()) {
388     int DefaultValue = 0;
389     UserModeKind HighLevelMode = getUserMode();
390     switch (HighLevelMode) {
391       default:
392         llvm_unreachable("Invalid mode.");
393       case UMK_Shallow:
394         DefaultValue = 75000;
395         break;
396       case UMK_Deep:
397         DefaultValue = 225000;
398         break;
399     }
400     MaxNodesPerTopLevelFunction = getOptionAsInteger("max-nodes", DefaultValue);
401   }
402   return MaxNodesPerTopLevelFunction.getValue();
403 }
404 
405 bool AnalyzerOptions::shouldSynthesizeBodies() {
406   return getBooleanOption("faux-bodies", true);
407 }
408 
409 bool AnalyzerOptions::shouldPrunePaths() {
410   return getBooleanOption("prune-paths", true);
411 }
412 
413 bool AnalyzerOptions::shouldConditionalizeStaticInitializers() {
414   return getBooleanOption("cfg-conditional-static-initializers", true);
415 }
416 
417 bool AnalyzerOptions::shouldInlineLambdas() {
418   if (!InlineLambdas.hasValue())
419     InlineLambdas = getBooleanOption("inline-lambdas", /*Default=*/true);
420   return InlineLambdas.getValue();
421 }
422 
423 bool AnalyzerOptions::shouldWidenLoops() {
424   if (!WidenLoops.hasValue())
425     WidenLoops = getBooleanOption("widen-loops", /*Default=*/false);
426   return WidenLoops.getValue();
427 }
428 
429 bool AnalyzerOptions::shouldUnrollLoops() {
430   if (!UnrollLoops.hasValue())
431     UnrollLoops = getBooleanOption("unroll-loops", /*Default=*/false);
432   return UnrollLoops.getValue();
433 }
434 
435 bool AnalyzerOptions::shouldDisplayNotesAsEvents() {
436   if (!DisplayNotesAsEvents.hasValue())
437     DisplayNotesAsEvents =
438         getBooleanOption("notes-as-events", /*Default=*/false);
439   return DisplayNotesAsEvents.getValue();
440 }
441 
442 StringRef AnalyzerOptions::getCTUDir() {
443   if (!CTUDir.hasValue()) {
444     CTUDir = getOptionAsString("ctu-dir", "");
445     if (!llvm::sys::fs::is_directory(*CTUDir))
446       CTUDir = "";
447   }
448   return CTUDir.getValue();
449 }
450 
451 bool AnalyzerOptions::naiveCTUEnabled() {
452   if (!NaiveCTU.hasValue()) {
453     NaiveCTU = getBooleanOption("experimental-enable-naive-ctu-analysis",
454                                 /*Default=*/false);
455   }
456   return NaiveCTU.getValue();
457 }
458 
459 StringRef AnalyzerOptions::getCTUIndexName() {
460   if (!CTUIndexName.hasValue())
461     CTUIndexName = getOptionAsString("ctu-index-name", "externalFnMap.txt");
462   return CTUIndexName.getValue();
463 }
464