xref: /llvm-project/clang/unittests/Analysis/FlowSensitive/LoggerTest.cpp (revision 59ff3adcc1310e22ab31163767e49b4edb20c6b4)
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 
9 namespace clang::dataflow::test {
10 namespace {
11 using testing::HasSubstr;
12 
13 struct TestLattice {
14   int Elements = 0;
15   int Branches = 0;
16   int Joins = 0;
17 
joinclang::dataflow::test::__anond8cfe21d0111::TestLattice18   LatticeJoinEffect join(const TestLattice &Other) {
19     if (Joins < 3) {
20       ++Joins;
21       Elements += Other.Elements;
22       Branches += Other.Branches;
23       return LatticeJoinEffect::Changed;
24     }
25     return LatticeJoinEffect::Unchanged;
26   }
operator ==(const TestLattice & LHS,const TestLattice & RHS)27   friend bool operator==(const TestLattice &LHS, const TestLattice &RHS) {
28     return std::tie(LHS.Elements, LHS.Branches, LHS.Joins) ==
29            std::tie(RHS.Elements, RHS.Branches, RHS.Joins);
30   }
31 };
32 
33 class TestAnalysis : public DataflowAnalysis<TestAnalysis, TestLattice> {
34 public:
35   using DataflowAnalysis::DataflowAnalysis;
36 
initialElement()37   static TestLattice initialElement() { return TestLattice{}; }
transfer(const CFGElement &,TestLattice & L,Environment & E)38   void transfer(const CFGElement &, TestLattice &L, Environment &E) {
39     E.getDataflowAnalysisContext().getOptions().Log->log(
40         [](llvm::raw_ostream &OS) { OS << "transfer()"; });
41     ++L.Elements;
42   }
transferBranch(bool Branch,const Stmt * S,TestLattice & L,Environment & E)43   void transferBranch(bool Branch, const Stmt *S, TestLattice &L,
44                       Environment &E) {
45     E.getDataflowAnalysisContext().getOptions().Log->log(
46         [&](llvm::raw_ostream &OS) {
47           OS << "transferBranch(" << Branch << ")";
48         });
49     ++L.Branches;
50   }
51 };
52 
53 class TestLogger : public Logger {
54 public:
TestLogger(std::string & S)55   TestLogger(std::string &S) : OS(S) {}
56 
57 private:
58   llvm::raw_string_ostream OS;
59 
beginAnalysis(const AdornedCFG &,TypeErasedDataflowAnalysis &)60   void beginAnalysis(const AdornedCFG &,
61                      TypeErasedDataflowAnalysis &) override {
62     logText("beginAnalysis()");
63   }
endAnalysis()64   void endAnalysis() override { logText("\nendAnalysis()"); }
65 
enterBlock(const CFGBlock & B,bool PostVisit)66   void enterBlock(const CFGBlock &B, bool PostVisit) override {
67     OS << "\nenterBlock(" << B.BlockID << ", " << (PostVisit ? "true" : "false")
68        << ")\n";
69   }
enterElement(const CFGElement & E)70   void enterElement(const CFGElement &E) override {
71     // we don't want the trailing \n
72     std::string S;
73     llvm::raw_string_ostream SS(S);
74     E.dumpToStream(SS);
75 
76     OS << "enterElement(" << llvm::StringRef(S).trim() << ")\n";
77   }
recordState(TypeErasedDataflowAnalysisState & S)78   void recordState(TypeErasedDataflowAnalysisState &S) override {
79     const TestLattice &L = llvm::any_cast<TestLattice>(S.Lattice.Value);
80     OS << "recordState(Elements=" << L.Elements << ", Branches=" << L.Branches
81        << ", Joins=" << L.Joins << ")\n";
82   }
83   /// Records that the analysis state for the current block is now final.
blockConverged()84   void blockConverged() override { logText("blockConverged()"); }
85 
logText(llvm::StringRef Text)86   void logText(llvm::StringRef Text) override { OS << Text << "\n"; }
87 };
88 
makeInputs()89 AnalysisInputs<TestAnalysis> makeInputs() {
90   const char *Code = R"cpp(
91 int target(bool b, int p, int q) {
92   return b ? p : q;
93 }
94 )cpp";
95   static const std::vector<std::string> Args = {
96       "-fsyntax-only", "-fno-delayed-template-parsing", "-std=c++17"};
97 
98   auto Inputs = AnalysisInputs<TestAnalysis>(
99       Code, ast_matchers::hasName("target"),
100       [](ASTContext &C, Environment &) { return TestAnalysis(C); });
101   Inputs.ASTBuildArgs = Args;
102   return Inputs;
103 }
104 
TEST(LoggerTest,Sequence)105 TEST(LoggerTest, Sequence) {
106   auto Inputs = makeInputs();
107   std::string Log;
108   TestLogger Logger(Log);
109   Inputs.BuiltinOptions.Log = &Logger;
110 
111   ASSERT_THAT_ERROR(checkDataflow<TestAnalysis>(std::move(Inputs),
112                                                 [](const AnalysisOutputs &) {}),
113                     llvm::Succeeded());
114 
115   EXPECT_EQ(Log, R"(beginAnalysis()
116 
117 enterBlock(4, false)
118 recordState(Elements=0, Branches=0, Joins=0)
119 enterElement(b)
120 transfer()
121 recordState(Elements=1, Branches=0, Joins=0)
122 enterElement(b (ImplicitCastExpr, LValueToRValue, _Bool))
123 transfer()
124 recordState(Elements=2, Branches=0, Joins=0)
125 recordState(Elements=2, Branches=0, Joins=0)
126 
127 enterBlock(2, false)
128 transferBranch(1)
129 recordState(Elements=2, Branches=1, Joins=0)
130 enterElement(p)
131 transfer()
132 recordState(Elements=3, Branches=1, Joins=0)
133 
134 enterBlock(3, false)
135 transferBranch(0)
136 recordState(Elements=2, Branches=1, Joins=0)
137 enterElement(q)
138 transfer()
139 recordState(Elements=3, Branches=1, Joins=0)
140 
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)
152 
153 enterBlock(0, false)
154 recordState(Elements=9, Branches=2, Joins=1)
155 
156 endAnalysis()
157 )");
158 }
159 
TEST(LoggerTest,HTML)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());
166   });
167   Inputs.BuiltinOptions.Log = Logger.get();
168 
169   ASSERT_THAT_ERROR(checkDataflow<TestAnalysis>(std::move(Inputs),
170                                                 [](const AnalysisOutputs &) {}),
171                     llvm::Succeeded());
172 
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";
183 }
184 
185 } // namespace
186 } // namespace clang::dataflow::test
187