10b57cec5SDimitry Andric //===- AnalyzerOptions.cpp - Analysis Engine Options ----------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file contains special accessors for analyzer configuration options
100b57cec5SDimitry Andric // with string representations.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric
140b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
150b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
160b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
170b57cec5SDimitry Andric #include "llvm/ADT/StringSwitch.h"
180b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
190b57cec5SDimitry Andric #include "llvm/ADT/Twine.h"
200b57cec5SDimitry Andric #include "llvm/Support/ErrorHandling.h"
210b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h"
220b57cec5SDimitry Andric #include "llvm/Support/FormattedStream.h"
230b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
240b57cec5SDimitry Andric #include <cassert>
250b57cec5SDimitry Andric #include <cstddef>
26*bdd1243dSDimitry Andric #include <optional>
270b57cec5SDimitry Andric #include <utility>
280b57cec5SDimitry Andric #include <vector>
290b57cec5SDimitry Andric
300b57cec5SDimitry Andric using namespace clang;
310b57cec5SDimitry Andric using namespace ento;
320b57cec5SDimitry Andric using namespace llvm;
330b57cec5SDimitry Andric
printFormattedEntry(llvm::raw_ostream & Out,std::pair<StringRef,StringRef> EntryDescPair,size_t InitialPad,size_t EntryWidth,size_t MinLineWidth)340b57cec5SDimitry Andric void AnalyzerOptions::printFormattedEntry(
350b57cec5SDimitry Andric llvm::raw_ostream &Out,
360b57cec5SDimitry Andric std::pair<StringRef, StringRef> EntryDescPair,
370b57cec5SDimitry Andric size_t InitialPad, size_t EntryWidth, size_t MinLineWidth) {
380b57cec5SDimitry Andric
390b57cec5SDimitry Andric llvm::formatted_raw_ostream FOut(Out);
400b57cec5SDimitry Andric
410b57cec5SDimitry Andric const size_t PadForDesc = InitialPad + EntryWidth;
420b57cec5SDimitry Andric
430b57cec5SDimitry Andric FOut.PadToColumn(InitialPad) << EntryDescPair.first;
44e8d8bef9SDimitry Andric // If the buffer's length is greater than PadForDesc, print a newline.
450b57cec5SDimitry Andric if (FOut.getColumn() > PadForDesc)
460b57cec5SDimitry Andric FOut << '\n';
470b57cec5SDimitry Andric
480b57cec5SDimitry Andric FOut.PadToColumn(PadForDesc);
490b57cec5SDimitry Andric
500b57cec5SDimitry Andric if (MinLineWidth == 0) {
510b57cec5SDimitry Andric FOut << EntryDescPair.second;
520b57cec5SDimitry Andric return;
530b57cec5SDimitry Andric }
540b57cec5SDimitry Andric
550b57cec5SDimitry Andric for (char C : EntryDescPair.second) {
560b57cec5SDimitry Andric if (FOut.getColumn() > MinLineWidth && C == ' ') {
570b57cec5SDimitry Andric FOut << '\n';
580b57cec5SDimitry Andric FOut.PadToColumn(PadForDesc);
590b57cec5SDimitry Andric continue;
600b57cec5SDimitry Andric }
610b57cec5SDimitry Andric FOut << C;
620b57cec5SDimitry Andric }
630b57cec5SDimitry Andric }
640b57cec5SDimitry Andric
650b57cec5SDimitry Andric ExplorationStrategyKind
getExplorationStrategy() const660b57cec5SDimitry Andric AnalyzerOptions::getExplorationStrategy() const {
670b57cec5SDimitry Andric auto K =
68*bdd1243dSDimitry Andric llvm::StringSwitch<std::optional<ExplorationStrategyKind>>(
690b57cec5SDimitry Andric ExplorationStrategy)
700b57cec5SDimitry Andric .Case("dfs", ExplorationStrategyKind::DFS)
710b57cec5SDimitry Andric .Case("bfs", ExplorationStrategyKind::BFS)
72*bdd1243dSDimitry Andric .Case("unexplored_first", ExplorationStrategyKind::UnexploredFirst)
730b57cec5SDimitry Andric .Case("unexplored_first_queue",
740b57cec5SDimitry Andric ExplorationStrategyKind::UnexploredFirstQueue)
750b57cec5SDimitry Andric .Case("unexplored_first_location_queue",
760b57cec5SDimitry Andric ExplorationStrategyKind::UnexploredFirstLocationQueue)
770b57cec5SDimitry Andric .Case("bfs_block_dfs_contents",
780b57cec5SDimitry Andric ExplorationStrategyKind::BFSBlockDFSContents)
79*bdd1243dSDimitry Andric .Default(std::nullopt);
8081ad6265SDimitry Andric assert(K && "User mode is invalid.");
81*bdd1243dSDimitry Andric return *K;
8281ad6265SDimitry Andric }
8381ad6265SDimitry Andric
getCTUPhase1Inlining() const8481ad6265SDimitry Andric CTUPhase1InliningKind AnalyzerOptions::getCTUPhase1Inlining() const {
85*bdd1243dSDimitry Andric auto K = llvm::StringSwitch<std::optional<CTUPhase1InliningKind>>(
8681ad6265SDimitry Andric CTUPhase1InliningMode)
8781ad6265SDimitry Andric .Case("none", CTUPhase1InliningKind::None)
8881ad6265SDimitry Andric .Case("small", CTUPhase1InliningKind::Small)
8981ad6265SDimitry Andric .Case("all", CTUPhase1InliningKind::All)
90*bdd1243dSDimitry Andric .Default(std::nullopt);
9181ad6265SDimitry Andric assert(K && "CTU inlining mode is invalid.");
92*bdd1243dSDimitry Andric return *K;
930b57cec5SDimitry Andric }
940b57cec5SDimitry Andric
getIPAMode() const950b57cec5SDimitry Andric IPAKind AnalyzerOptions::getIPAMode() const {
96*bdd1243dSDimitry Andric auto K = llvm::StringSwitch<std::optional<IPAKind>>(IPAMode)
970b57cec5SDimitry Andric .Case("none", IPAK_None)
980b57cec5SDimitry Andric .Case("basic-inlining", IPAK_BasicInlining)
990b57cec5SDimitry Andric .Case("inlining", IPAK_Inlining)
1000b57cec5SDimitry Andric .Case("dynamic", IPAK_DynamicDispatch)
1010b57cec5SDimitry Andric .Case("dynamic-bifurcate", IPAK_DynamicDispatchBifurcate)
102*bdd1243dSDimitry Andric .Default(std::nullopt);
10381ad6265SDimitry Andric assert(K && "IPA Mode is invalid.");
1040b57cec5SDimitry Andric
105*bdd1243dSDimitry Andric return *K;
1060b57cec5SDimitry Andric }
1070b57cec5SDimitry Andric
1080b57cec5SDimitry Andric bool
mayInlineCXXMemberFunction(CXXInlineableMemberKind Param) const1090b57cec5SDimitry Andric AnalyzerOptions::mayInlineCXXMemberFunction(
1100b57cec5SDimitry Andric CXXInlineableMemberKind Param) const {
1110b57cec5SDimitry Andric if (getIPAMode() < IPAK_Inlining)
1120b57cec5SDimitry Andric return false;
1130b57cec5SDimitry Andric
114*bdd1243dSDimitry Andric auto K = llvm::StringSwitch<std::optional<CXXInlineableMemberKind>>(
1150b57cec5SDimitry Andric CXXMemberInliningMode)
1160b57cec5SDimitry Andric .Case("constructors", CIMK_Constructors)
1170b57cec5SDimitry Andric .Case("destructors", CIMK_Destructors)
1180b57cec5SDimitry Andric .Case("methods", CIMK_MemberFunctions)
1190b57cec5SDimitry Andric .Case("none", CIMK_None)
120*bdd1243dSDimitry Andric .Default(std::nullopt);
1210b57cec5SDimitry Andric
12281ad6265SDimitry Andric assert(K && "Invalid c++ member function inlining mode.");
1230b57cec5SDimitry Andric
1240b57cec5SDimitry Andric return *K >= Param;
1250b57cec5SDimitry Andric }
1260b57cec5SDimitry Andric
getCheckerStringOption(StringRef CheckerName,StringRef OptionName,bool SearchInParents) const1270b57cec5SDimitry Andric StringRef AnalyzerOptions::getCheckerStringOption(StringRef CheckerName,
1280b57cec5SDimitry Andric StringRef OptionName,
1290b57cec5SDimitry Andric bool SearchInParents) const {
1300b57cec5SDimitry Andric assert(!CheckerName.empty() &&
1310b57cec5SDimitry Andric "Empty checker name! Make sure the checker object (including it's "
1320b57cec5SDimitry Andric "bases!) if fully initialized before calling this function!");
1330b57cec5SDimitry Andric
1340b57cec5SDimitry Andric ConfigTable::const_iterator E = Config.end();
1350b57cec5SDimitry Andric do {
1360b57cec5SDimitry Andric ConfigTable::const_iterator I =
1370b57cec5SDimitry Andric Config.find((Twine(CheckerName) + ":" + OptionName).str());
1380b57cec5SDimitry Andric if (I != E)
1390b57cec5SDimitry Andric return StringRef(I->getValue());
1400b57cec5SDimitry Andric size_t Pos = CheckerName.rfind('.');
1410b57cec5SDimitry Andric if (Pos == StringRef::npos)
1420b57cec5SDimitry Andric break;
1430b57cec5SDimitry Andric
1440b57cec5SDimitry Andric CheckerName = CheckerName.substr(0, Pos);
1450b57cec5SDimitry Andric } while (!CheckerName.empty() && SearchInParents);
1460b57cec5SDimitry Andric
1470b57cec5SDimitry Andric llvm_unreachable("Unknown checker option! Did you call getChecker*Option "
1480b57cec5SDimitry Andric "with incorrect parameters? User input must've been "
1490b57cec5SDimitry Andric "verified by CheckerRegistry.");
1500b57cec5SDimitry Andric
1510b57cec5SDimitry Andric return "";
1520b57cec5SDimitry Andric }
1530b57cec5SDimitry Andric
getCheckerStringOption(const ento::CheckerBase * C,StringRef OptionName,bool SearchInParents) const1540b57cec5SDimitry Andric StringRef AnalyzerOptions::getCheckerStringOption(const ento::CheckerBase *C,
1550b57cec5SDimitry Andric StringRef OptionName,
1560b57cec5SDimitry Andric bool SearchInParents) const {
1570b57cec5SDimitry Andric return getCheckerStringOption(
1580b57cec5SDimitry Andric C->getTagDescription(), OptionName, SearchInParents);
1590b57cec5SDimitry Andric }
1600b57cec5SDimitry Andric
getCheckerBooleanOption(StringRef CheckerName,StringRef OptionName,bool SearchInParents) const1610b57cec5SDimitry Andric bool AnalyzerOptions::getCheckerBooleanOption(StringRef CheckerName,
1620b57cec5SDimitry Andric StringRef OptionName,
1630b57cec5SDimitry Andric bool SearchInParents) const {
164*bdd1243dSDimitry Andric auto Ret =
165*bdd1243dSDimitry Andric llvm::StringSwitch<std::optional<bool>>(
166*bdd1243dSDimitry Andric getCheckerStringOption(CheckerName, OptionName, SearchInParents))
1670b57cec5SDimitry Andric .Case("true", true)
1680b57cec5SDimitry Andric .Case("false", false)
169*bdd1243dSDimitry Andric .Default(std::nullopt);
1700b57cec5SDimitry Andric
1710b57cec5SDimitry Andric assert(Ret &&
1720b57cec5SDimitry Andric "This option should be either 'true' or 'false', and should've been "
1730b57cec5SDimitry Andric "validated by CheckerRegistry!");
1740b57cec5SDimitry Andric
1750b57cec5SDimitry Andric return *Ret;
1760b57cec5SDimitry Andric }
1770b57cec5SDimitry Andric
getCheckerBooleanOption(const ento::CheckerBase * C,StringRef OptionName,bool SearchInParents) const1780b57cec5SDimitry Andric bool AnalyzerOptions::getCheckerBooleanOption(const ento::CheckerBase *C,
1790b57cec5SDimitry Andric StringRef OptionName,
1800b57cec5SDimitry Andric bool SearchInParents) const {
1810b57cec5SDimitry Andric return getCheckerBooleanOption(
1820b57cec5SDimitry Andric C->getTagDescription(), OptionName, SearchInParents);
1830b57cec5SDimitry Andric }
1840b57cec5SDimitry Andric
getCheckerIntegerOption(StringRef CheckerName,StringRef OptionName,bool SearchInParents) const1850b57cec5SDimitry Andric int AnalyzerOptions::getCheckerIntegerOption(StringRef CheckerName,
1860b57cec5SDimitry Andric StringRef OptionName,
1870b57cec5SDimitry Andric bool SearchInParents) const {
1880b57cec5SDimitry Andric int Ret = 0;
1890b57cec5SDimitry Andric bool HasFailed = getCheckerStringOption(CheckerName, OptionName,
1900b57cec5SDimitry Andric SearchInParents)
1910b57cec5SDimitry Andric .getAsInteger(0, Ret);
1920b57cec5SDimitry Andric assert(!HasFailed &&
1930b57cec5SDimitry Andric "This option should be numeric, and should've been validated by "
1940b57cec5SDimitry Andric "CheckerRegistry!");
1950b57cec5SDimitry Andric (void)HasFailed;
1960b57cec5SDimitry Andric return Ret;
1970b57cec5SDimitry Andric }
1980b57cec5SDimitry Andric
getCheckerIntegerOption(const ento::CheckerBase * C,StringRef OptionName,bool SearchInParents) const1990b57cec5SDimitry Andric int AnalyzerOptions::getCheckerIntegerOption(const ento::CheckerBase *C,
2000b57cec5SDimitry Andric StringRef OptionName,
2010b57cec5SDimitry Andric bool SearchInParents) const {
2020b57cec5SDimitry Andric return getCheckerIntegerOption(
2030b57cec5SDimitry Andric C->getTagDescription(), OptionName, SearchInParents);
2040b57cec5SDimitry Andric }
205