1 //===- AnalyzerOptions.cpp - Analysis Engine Options ----------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file contains special accessors for analyzer configuration options
10 // with string representations.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
15 #include "clang/StaticAnalyzer/Core/Checker.h"
16 #include "llvm/ADT/SmallString.h"
17 #include "llvm/ADT/StringSwitch.h"
18 #include "llvm/ADT/StringRef.h"
19 #include "llvm/ADT/Twine.h"
20 #include "llvm/Support/ErrorHandling.h"
21 #include "llvm/Support/FileSystem.h"
22 #include "llvm/Support/FormattedStream.h"
23 #include "llvm/Support/raw_ostream.h"
24 #include <cassert>
25 #include <cstddef>
26 #include <optional>
27 #include <utility>
28 #include <vector>
29 
30 using namespace clang;
31 using namespace ento;
32 using namespace llvm;
33 
34 void AnalyzerOptions::printFormattedEntry(
35     llvm::raw_ostream &Out,
36     std::pair<StringRef, StringRef> EntryDescPair,
37     size_t InitialPad, size_t EntryWidth, size_t MinLineWidth) {
38 
39   llvm::formatted_raw_ostream FOut(Out);
40 
41   const size_t PadForDesc = InitialPad + EntryWidth;
42 
43   FOut.PadToColumn(InitialPad) << EntryDescPair.first;
44   // If the buffer's length is greater than PadForDesc, print a newline.
45   if (FOut.getColumn() > PadForDesc)
46     FOut << '\n';
47 
48   FOut.PadToColumn(PadForDesc);
49 
50   if (MinLineWidth == 0) {
51     FOut << EntryDescPair.second;
52     return;
53   }
54 
55   for (char C : EntryDescPair.second) {
56     if (FOut.getColumn() > MinLineWidth && C == ' ') {
57       FOut << '\n';
58       FOut.PadToColumn(PadForDesc);
59       continue;
60     }
61     FOut << C;
62   }
63 }
64 
65 ExplorationStrategyKind
66 AnalyzerOptions::getExplorationStrategy() const {
67   auto K =
68       llvm::StringSwitch<std::optional<ExplorationStrategyKind>>(
69           ExplorationStrategy)
70           .Case("dfs", ExplorationStrategyKind::DFS)
71           .Case("bfs", ExplorationStrategyKind::BFS)
72           .Case("unexplored_first", ExplorationStrategyKind::UnexploredFirst)
73           .Case("unexplored_first_queue",
74                 ExplorationStrategyKind::UnexploredFirstQueue)
75           .Case("unexplored_first_location_queue",
76                 ExplorationStrategyKind::UnexploredFirstLocationQueue)
77           .Case("bfs_block_dfs_contents",
78                 ExplorationStrategyKind::BFSBlockDFSContents)
79           .Default(std::nullopt);
80   assert(K && "User mode is invalid.");
81   return *K;
82 }
83 
84 CTUPhase1InliningKind AnalyzerOptions::getCTUPhase1Inlining() const {
85   auto K = llvm::StringSwitch<std::optional<CTUPhase1InliningKind>>(
86                CTUPhase1InliningMode)
87                .Case("none", CTUPhase1InliningKind::None)
88                .Case("small", CTUPhase1InliningKind::Small)
89                .Case("all", CTUPhase1InliningKind::All)
90                .Default(std::nullopt);
91   assert(K && "CTU inlining mode is invalid.");
92   return *K;
93 }
94 
95 IPAKind AnalyzerOptions::getIPAMode() const {
96   auto K = llvm::StringSwitch<std::optional<IPAKind>>(IPAMode)
97                .Case("none", IPAK_None)
98                .Case("basic-inlining", IPAK_BasicInlining)
99                .Case("inlining", IPAK_Inlining)
100                .Case("dynamic", IPAK_DynamicDispatch)
101                .Case("dynamic-bifurcate", IPAK_DynamicDispatchBifurcate)
102                .Default(std::nullopt);
103   assert(K && "IPA Mode is invalid.");
104 
105   return *K;
106 }
107 
108 bool
109 AnalyzerOptions::mayInlineCXXMemberFunction(
110                                           CXXInlineableMemberKind Param) const {
111   if (getIPAMode() < IPAK_Inlining)
112     return false;
113 
114   auto K = llvm::StringSwitch<std::optional<CXXInlineableMemberKind>>(
115                CXXMemberInliningMode)
116                .Case("constructors", CIMK_Constructors)
117                .Case("destructors", CIMK_Destructors)
118                .Case("methods", CIMK_MemberFunctions)
119                .Case("none", CIMK_None)
120                .Default(std::nullopt);
121 
122   assert(K && "Invalid c++ member function inlining mode.");
123 
124   return *K >= Param;
125 }
126 
127 StringRef AnalyzerOptions::getCheckerStringOption(StringRef CheckerName,
128                                                   StringRef OptionName,
129                                                   bool SearchInParents) const {
130   assert(!CheckerName.empty() &&
131          "Empty checker name! Make sure the checker object (including it's "
132          "bases!) if fully initialized before calling this function!");
133 
134   ConfigTable::const_iterator E = Config.end();
135   do {
136     ConfigTable::const_iterator I =
137         Config.find((Twine(CheckerName) + ":" + OptionName).str());
138     if (I != E)
139       return StringRef(I->getValue());
140     size_t Pos = CheckerName.rfind('.');
141     if (Pos == StringRef::npos)
142       break;
143 
144     CheckerName = CheckerName.substr(0, Pos);
145   } while (!CheckerName.empty() && SearchInParents);
146 
147   llvm_unreachable("Unknown checker option! Did you call getChecker*Option "
148                    "with incorrect parameters? User input must've been "
149                    "verified by CheckerRegistry.");
150 
151   return "";
152 }
153 
154 StringRef AnalyzerOptions::getCheckerStringOption(const ento::CheckerBase *C,
155                                                   StringRef OptionName,
156                                                   bool SearchInParents) const {
157   return getCheckerStringOption(
158                            C->getTagDescription(), OptionName, SearchInParents);
159 }
160 
161 bool AnalyzerOptions::getCheckerBooleanOption(StringRef CheckerName,
162                                               StringRef OptionName,
163                                               bool SearchInParents) const {
164   auto Ret =
165       llvm::StringSwitch<std::optional<bool>>(
166           getCheckerStringOption(CheckerName, OptionName, SearchInParents))
167           .Case("true", true)
168           .Case("false", false)
169           .Default(std::nullopt);
170 
171   assert(Ret &&
172          "This option should be either 'true' or 'false', and should've been "
173          "validated by CheckerRegistry!");
174 
175   return *Ret;
176 }
177 
178 bool AnalyzerOptions::getCheckerBooleanOption(const ento::CheckerBase *C,
179                                               StringRef OptionName,
180                                               bool SearchInParents) const {
181   return getCheckerBooleanOption(
182              C->getTagDescription(), OptionName, SearchInParents);
183 }
184 
185 int AnalyzerOptions::getCheckerIntegerOption(StringRef CheckerName,
186                                              StringRef OptionName,
187                                              bool SearchInParents) const {
188   int Ret = 0;
189   bool HasFailed = getCheckerStringOption(CheckerName, OptionName,
190                                           SearchInParents)
191                      .getAsInteger(0, Ret);
192   assert(!HasFailed &&
193          "This option should be numeric, and should've been validated by "
194          "CheckerRegistry!");
195   (void)HasFailed;
196   return Ret;
197 }
198 
199 int AnalyzerOptions::getCheckerIntegerOption(const ento::CheckerBase *C,
200                                              StringRef OptionName,
201                                              bool SearchInParents) const {
202   return getCheckerIntegerOption(
203                            C->getTagDescription(), OptionName, SearchInParents);
204 }
205