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