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"
10 namespace clang::dataflow::test
{
12 using testing::HasSubstr
;
19 LatticeJoinEffect
join(const TestLattice
&Other
) {
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
> {
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()"; });
44 void transferBranch(bool Branch
, const Stmt
*S
, TestLattice
&L
,
46 E
.getDataflowAnalysisContext().getOptions().Log
->log(
47 [&](llvm::raw_ostream
&OS
) {
48 OS
<< "transferBranch(" << Branch
<< ")";
54 class TestLogger
: public Logger
{
56 TestLogger(std::string
&S
) : OS(S
) {}
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")
71 void enterElement(const CFGElement
&E
) override
{
72 // we don't want the trailing \n
74 llvm::raw_string_ostream
SS(S
);
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) {
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
;
106 TEST(LoggerTest
, Sequence
) {
107 auto Inputs
= makeInputs();
109 TestLogger
Logger(Log
);
110 Inputs
.BuiltinOptions
.Log
= &Logger
;
112 ASSERT_THAT_ERROR(checkDataflow
<TestAnalysis
>(std::move(Inputs
),
113 [](const AnalysisOutputs
&) {}),
116 EXPECT_EQ(Log
, R
"(beginAnalysis()
119 recordState(Elements=0, Branches=0, Joins=0)
122 recordState(Elements=1, Branches=0, Joins=0)
123 enterElement(b (ImplicitCastExpr, LValueToRValue, _Bool))
125 recordState(Elements=2, Branches=0, Joins=0)
129 recordState(Elements=2, Branches=1, Joins=0)
132 recordState(Elements=3, Branches=1, Joins=0)
136 recordState(Elements=2, Branches=1, Joins=0)
139 recordState(Elements=3, Branches=1, Joins=0)
142 recordState(Elements=6, Branches=2, Joins=1)
143 enterElement(b ? p : q)
145 recordState(Elements=7, Branches=2, Joins=1)
146 enterElement(b ? p : q (ImplicitCastExpr, LValueToRValue, int))
148 recordState(Elements=8, Branches=2, Joins=1)
149 enterElement(return b ? p : q;)
151 recordState(Elements=9, Branches=2, Joins=1)
154 recordState(Elements=9, Branches=2, Joins=1)
160 TEST(LoggerTest
, HTML
) {
161 auto Inputs
= makeInputs();
162 std::vector
<std::string
> Logs
;
163 auto Logger
= Logger::html([&]() {
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
&) {}),
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";
186 } // namespace clang::dataflow::test