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