[clang-format] Fix a bug in aligning comments above PPDirective (#72791)
[llvm-project.git] / clang / unittests / Analysis / FlowSensitive / LoggerTest.cpp
bloba60dbe1f61f6d6e10c19a42dad7ee544471711e5
1 #include "TestingSupport.h"
2 #include "clang/ASTMatchers/ASTMatchers.h"
3 #include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
4 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
5 #include "clang/Analysis/FlowSensitive/DataflowLattice.h"
6 #include "llvm/Testing/Support/Error.h"
7 #include "gtest/gtest.h"
8 #include <optional>
10 namespace clang::dataflow::test {
11 namespace {
12 using testing::HasSubstr;
14 struct TestLattice {
15 int Elements = 0;
16 int Branches = 0;
17 int Joins = 0;
19 LatticeJoinEffect join(const TestLattice &Other) {
20 if (Joins < 3) {
21 ++Joins;
22 Elements += Other.Elements;
23 Branches += Other.Branches;
24 return LatticeJoinEffect::Changed;
26 return LatticeJoinEffect::Unchanged;
28 friend bool operator==(const TestLattice &LHS, const TestLattice &RHS) {
29 return std::tie(LHS.Elements, LHS.Branches, LHS.Joins) ==
30 std::tie(RHS.Elements, RHS.Branches, RHS.Joins);
34 class TestAnalysis : public DataflowAnalysis<TestAnalysis, TestLattice> {
35 public:
36 using DataflowAnalysis::DataflowAnalysis;
38 static TestLattice initialElement() { return TestLattice{}; }
39 void transfer(const CFGElement &, TestLattice &L, Environment &E) {
40 E.getDataflowAnalysisContext().getOptions().Log->log(
41 [](llvm::raw_ostream &OS) { OS << "transfer()"; });
42 ++L.Elements;
44 void transferBranch(bool Branch, const Stmt *S, TestLattice &L,
45 Environment &E) {
46 E.getDataflowAnalysisContext().getOptions().Log->log(
47 [&](llvm::raw_ostream &OS) {
48 OS << "transferBranch(" << Branch << ")";
49 });
50 ++L.Branches;
54 class TestLogger : public Logger {
55 public:
56 TestLogger(std::string &S) : OS(S) {}
58 private:
59 llvm::raw_string_ostream OS;
61 void beginAnalysis(const ControlFlowContext &,
62 TypeErasedDataflowAnalysis &) override {
63 logText("beginAnalysis()");
65 void endAnalysis() override { logText("\nendAnalysis()"); }
67 void enterBlock(const CFGBlock &B, bool PostVisit) override {
68 OS << "\nenterBlock(" << B.BlockID << ", " << (PostVisit ? "true" : "false")
69 << ")\n";
71 void enterElement(const CFGElement &E) override {
72 // we don't want the trailing \n
73 std::string S;
74 llvm::raw_string_ostream SS(S);
75 E.dumpToStream(SS);
77 OS << "enterElement(" << llvm::StringRef(S).trim() << ")\n";
79 void recordState(TypeErasedDataflowAnalysisState &S) override {
80 const TestLattice &L = llvm::any_cast<TestLattice>(S.Lattice.Value);
81 OS << "recordState(Elements=" << L.Elements << ", Branches=" << L.Branches
82 << ", Joins=" << L.Joins << ")\n";
84 /// Records that the analysis state for the current block is now final.
85 void blockConverged() override { logText("blockConverged()"); }
87 void logText(llvm::StringRef Text) override { OS << Text << "\n"; }
90 AnalysisInputs<TestAnalysis> makeInputs() {
91 const char *Code = R"cpp(
92 int target(bool b, int p, int q) {
93 return b ? p : q;
95 )cpp";
96 static const std::vector<std::string> Args = {
97 "-fsyntax-only", "-fno-delayed-template-parsing", "-std=c++17"};
99 auto Inputs = AnalysisInputs<TestAnalysis>(
100 Code, ast_matchers::hasName("target"),
101 [](ASTContext &C, Environment &) { return TestAnalysis(C); });
102 Inputs.ASTBuildArgs = Args;
103 return Inputs;
106 TEST(LoggerTest, Sequence) {
107 auto Inputs = makeInputs();
108 std::string Log;
109 TestLogger Logger(Log);
110 Inputs.BuiltinOptions.Log = &Logger;
112 ASSERT_THAT_ERROR(checkDataflow<TestAnalysis>(std::move(Inputs),
113 [](const AnalysisOutputs &) {}),
114 llvm::Succeeded());
116 EXPECT_EQ(Log, R"(beginAnalysis()
118 enterBlock(4, false)
119 recordState(Elements=0, Branches=0, Joins=0)
120 enterElement(b)
121 transfer()
122 recordState(Elements=1, Branches=0, Joins=0)
123 enterElement(b (ImplicitCastExpr, LValueToRValue, _Bool))
124 transfer()
125 recordState(Elements=2, Branches=0, Joins=0)
127 enterBlock(3, false)
128 transferBranch(0)
129 recordState(Elements=2, Branches=1, Joins=0)
130 enterElement(q)
131 transfer()
132 recordState(Elements=3, Branches=1, Joins=0)
134 enterBlock(2, false)
135 transferBranch(1)
136 recordState(Elements=2, Branches=1, Joins=0)
137 enterElement(p)
138 transfer()
139 recordState(Elements=3, Branches=1, Joins=0)
141 enterBlock(1, false)
142 recordState(Elements=6, Branches=2, Joins=1)
143 enterElement(b ? p : q)
144 transfer()
145 recordState(Elements=7, Branches=2, Joins=1)
146 enterElement(b ? p : q (ImplicitCastExpr, LValueToRValue, int))
147 transfer()
148 recordState(Elements=8, Branches=2, Joins=1)
149 enterElement(return b ? p : q;)
150 transfer()
151 recordState(Elements=9, Branches=2, Joins=1)
153 enterBlock(0, false)
154 recordState(Elements=9, Branches=2, Joins=1)
156 endAnalysis()
157 )");
160 TEST(LoggerTest, HTML) {
161 auto Inputs = makeInputs();
162 std::vector<std::string> Logs;
163 auto Logger = Logger::html([&]() {
164 Logs.emplace_back();
165 return std::make_unique<llvm::raw_string_ostream>(Logs.back());
167 Inputs.BuiltinOptions.Log = Logger.get();
169 ASSERT_THAT_ERROR(checkDataflow<TestAnalysis>(std::move(Inputs),
170 [](const AnalysisOutputs &) {}),
171 llvm::Succeeded());
173 // Simple smoke tests: we can't meaningfully test the behavior.
174 ASSERT_THAT(Logs, testing::SizeIs(1));
175 EXPECT_THAT(Logs[0], HasSubstr("function updateSelection")) << "embeds JS";
176 EXPECT_THAT(Logs[0], HasSubstr("html {")) << "embeds CSS";
177 EXPECT_THAT(Logs[0], HasSubstr("b (ImplicitCastExpr")) << "has CFG elements";
178 EXPECT_THAT(Logs[0], HasSubstr("\"B3:1_B3.1\":"))
179 << "has analysis point state";
180 EXPECT_THAT(Logs[0], HasSubstr("transferBranch(0)")) << "has analysis logs";
181 EXPECT_THAT(Logs[0], HasSubstr("LocToVal")) << "has built-in lattice dump";
182 EXPECT_THAT(Logs[0], HasSubstr("\"type\": \"int\"")) << "has value dump";
185 } // namespace
186 } // namespace clang::dataflow::test