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 
250 bool AnalyzerOptions::mayInlineObjCMethod() {
251   return getBooleanOption(ObjCInliningMode,
252                           "objc-inlining",
253                           /* Default = */ true);
254 }
255 
256 bool AnalyzerOptions::shouldSuppressNullReturnPaths() {
257   return getBooleanOption(SuppressNullReturnPaths,
258                           "suppress-null-return-paths",
259                           /* Default = */ true);
260 }
261 
262 bool AnalyzerOptions::shouldAvoidSuppressingNullArgumentPaths() {
263   return getBooleanOption(AvoidSuppressingNullArgumentPaths,
264                           "avoid-suppressing-null-argument-paths",
265                           /* Default = */ false);
266 }
267 
268 bool AnalyzerOptions::shouldSuppressInlinedDefensiveChecks() {
269   return getBooleanOption(SuppressInlinedDefensiveChecks,
270                           "suppress-inlined-defensive-checks",
271                           /* Default = */ true);
272 }
273 
274 bool AnalyzerOptions::shouldSuppressFromCXXStandardLibrary() {
275   return getBooleanOption(SuppressFromCXXStandardLibrary,
276                           "suppress-c++-stdlib",
277                           /* Default = */ true);
278 }
279 
280 bool AnalyzerOptions::shouldReportIssuesInMainSourceFile() {
281   return getBooleanOption(ReportIssuesInMainSourceFile,
282                           "report-in-main-source-file",
283                           /* Default = */ false);
284 }
285 
286 
287 bool AnalyzerOptions::shouldWriteStableReportFilename() {
288   return getBooleanOption(StableReportFilename,
289                           "stable-report-filename",
290                           /* Default = */ false);
291 }
292 
293 bool AnalyzerOptions::shouldSerializeStats() {
294   return getBooleanOption(SerializeStats,
295                           "serialize-stats",
296                           /* Default = */ false);
297 }
298 
299 int AnalyzerOptions::getOptionAsInteger(StringRef Name, int DefaultVal,
300                                         const CheckerBase *C,
301                                         bool SearchInParents) {
302   SmallString<10> StrBuf;
303   llvm::raw_svector_ostream OS(StrBuf);
304   OS << DefaultVal;
305 
306   StringRef V = C ? getCheckerOption(C->getTagDescription(), Name, OS.str(),
307                                      SearchInParents)
308                   : StringRef(Config.insert(std::make_pair(Name, OS.str()))
309                                   .first->second);
310 
311   int Res = DefaultVal;
312   bool b = V.getAsInteger(10, Res);
313   assert(!b && "analyzer-config option should be numeric");
314   (void)b;
315   return Res;
316 }
317 
318 StringRef AnalyzerOptions::getOptionAsString(StringRef Name,
319                                              StringRef DefaultVal,
320                                              const CheckerBase *C,
321                                              bool SearchInParents) {
322   return C ? getCheckerOption(C->getTagDescription(), Name, DefaultVal,
323                               SearchInParents)
324            : StringRef(
325                  Config.insert(std::make_pair(Name, DefaultVal)).first->second);
326 }
327 
328 unsigned AnalyzerOptions::getAlwaysInlineSize() {
329   if (!AlwaysInlineSize.hasValue())
330     AlwaysInlineSize = getOptionAsInteger("ipa-always-inline-size", 3);
331   return AlwaysInlineSize.getValue();
332 }
333 
334 unsigned AnalyzerOptions::getMaxInlinableSize() {
335   if (!MaxInlinableSize.hasValue()) {
336 
337     int DefaultValue = 0;
338     UserModeKind HighLevelMode = getUserMode();
339     switch (HighLevelMode) {
340       default:
341         llvm_unreachable("Invalid mode.");
342       case UMK_Shallow:
343         DefaultValue = 4;
344         break;
345       case UMK_Deep:
346         DefaultValue = 100;
347         break;
348     }
349 
350     MaxInlinableSize = getOptionAsInteger("max-inlinable-size", DefaultValue);
351   }
352   return MaxInlinableSize.getValue();
353 }
354 
355 unsigned AnalyzerOptions::getGraphTrimInterval() {
356   if (!GraphTrimInterval.hasValue())
357     GraphTrimInterval = getOptionAsInteger("graph-trim-interval", 1000);
358   return GraphTrimInterval.getValue();
359 }
360 
361 unsigned AnalyzerOptions::getMaxTimesInlineLarge() {
362   if (!MaxTimesInlineLarge.hasValue())
363     MaxTimesInlineLarge = getOptionAsInteger("max-times-inline-large", 32);
364   return MaxTimesInlineLarge.getValue();
365 }
366 
367 unsigned AnalyzerOptions::getMinCFGSizeTreatFunctionsAsLarge() {
368   if (!MinCFGSizeTreatFunctionsAsLarge.hasValue())
369     MinCFGSizeTreatFunctionsAsLarge = getOptionAsInteger(
370       "min-cfg-size-treat-functions-as-large", 14);
371   return MinCFGSizeTreatFunctionsAsLarge.getValue();
372 }
373 
374 unsigned AnalyzerOptions::getMaxNodesPerTopLevelFunction() {
375   if (!MaxNodesPerTopLevelFunction.hasValue()) {
376     int DefaultValue = 0;
377     UserModeKind HighLevelMode = getUserMode();
378     switch (HighLevelMode) {
379       default:
380         llvm_unreachable("Invalid mode.");
381       case UMK_Shallow:
382         DefaultValue = 75000;
383         break;
384       case UMK_Deep:
385         DefaultValue = 225000;
386         break;
387     }
388     MaxNodesPerTopLevelFunction = getOptionAsInteger("max-nodes", DefaultValue);
389   }
390   return MaxNodesPerTopLevelFunction.getValue();
391 }
392 
393 bool AnalyzerOptions::shouldSynthesizeBodies() {
394   return getBooleanOption("faux-bodies", true);
395 }
396 
397 bool AnalyzerOptions::shouldPrunePaths() {
398   return getBooleanOption("prune-paths", true);
399 }
400 
401 bool AnalyzerOptions::shouldConditionalizeStaticInitializers() {
402   return getBooleanOption("cfg-conditional-static-initializers", true);
403 }
404 
405 bool AnalyzerOptions::shouldInlineLambdas() {
406   if (!InlineLambdas.hasValue())
407     InlineLambdas = getBooleanOption("inline-lambdas", /*Default=*/true);
408   return InlineLambdas.getValue();
409 }
410 
411 bool AnalyzerOptions::shouldWidenLoops() {
412   if (!WidenLoops.hasValue())
413     WidenLoops = getBooleanOption("widen-loops", /*Default=*/false);
414   return WidenLoops.getValue();
415 }
416 
417 bool AnalyzerOptions::shouldUnrollLoops() {
418   if (!UnrollLoops.hasValue())
419     UnrollLoops = getBooleanOption("unroll-loops", /*Default=*/false);
420   return UnrollLoops.getValue();
421 }
422 
423 bool AnalyzerOptions::shouldDisplayNotesAsEvents() {
424   if (!DisplayNotesAsEvents.hasValue())
425     DisplayNotesAsEvents =
426         getBooleanOption("notes-as-events", /*Default=*/false);
427   return DisplayNotesAsEvents.getValue();
428 }
429