xref: /llvm-project/clang/unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp (revision d931ac9e27cab964c65078c80e4488185c62b3d8)
1 //===- unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp ------------===//
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 #include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/AST/Decl.h"
12 #include "clang/AST/Stmt.h"
13 #include "clang/Analysis/CFG.h"
14 #include "clang/Tooling/Tooling.h"
15 #include "llvm/ADT/StringRef.h"
16 #include "gtest/gtest.h"
17 
18 using namespace clang;
19 using namespace dataflow;
20 using namespace ast_matchers;
21 
22 namespace {
23 // State for tracking the number of matches on each kind of CFGElement by the
24 // CFGMatchSwitch. Currently only tracks CFGStmt and CFGInitializer.
25 struct CFGElementMatches {
26   unsigned StmtMatches = 0;
27   unsigned InitializerMatches = 0;
28 };
29 
30 // Returns a match switch that counts the number of local variables
31 // (singly-declared) and fields initialized to the integer literal 42.
buildCFGMatchSwitch()32 auto buildCFGMatchSwitch() {
33   return CFGMatchSwitchBuilder<CFGElementMatches>()
34       .CaseOfCFGStmt<DeclStmt>(
35           declStmt(hasSingleDecl(
36               varDecl(hasInitializer(integerLiteral(equals(42)))))),
37           [](const DeclStmt *, const MatchFinder::MatchResult &,
38              CFGElementMatches &Counter) { Counter.StmtMatches++; })
39       .CaseOfCFGInit<CXXCtorInitializer>(
40           cxxCtorInitializer(withInitializer(integerLiteral(equals(42)))),
41           [](const CXXCtorInitializer *, const MatchFinder::MatchResult &,
42              CFGElementMatches &Counter) { Counter.InitializerMatches++; })
43       .Build();
44 }
45 
46 // Runs the match switch `MS` on the control flow graph generated from `Code`,
47 // tracking information in state `S`. For simplicity, this test utility is
48 // restricted to CFGs with a single control flow block (excluding entry and
49 // exit blocks) - generated by `Code` with sequential flow (i.e. no branching).
50 //
51 // Requirements:
52 //
53 //  `Code` must contain a function named `f`, the body of this function will be
54 //  used to generate the CFG.
55 template <typename State>
applySwitchToCode(CFGMatchSwitch<State> & MS,State & S,llvm::StringRef Code)56 void applySwitchToCode(CFGMatchSwitch<State> &MS, State &S,
57                        llvm::StringRef Code) {
58   auto Unit = tooling::buildASTFromCodeWithArgs(Code, {"-Wno-unused-value"});
59   auto &Ctx = Unit->getASTContext();
60   const auto *F = selectFirst<FunctionDecl>(
61       "f", match(functionDecl(isDefinition(), hasName("f")).bind("f"), Ctx));
62 
63   CFG::BuildOptions BO;
64   BO.AddInitializers = true;
65 
66   auto CFG = CFG::buildCFG(F, F->getBody(), &Ctx, BO);
67   auto CFGBlock = *CFG->getEntry().succ_begin();
68   for (auto &Elt : CFGBlock->Elements) {
69     MS(Elt, Ctx, S);
70   }
71 }
72 
TEST(CFGMatchSwitchTest,NoInitializationTo42)73 TEST(CFGMatchSwitchTest, NoInitializationTo42) {
74   CFGMatchSwitch<CFGElementMatches> Switch = buildCFGMatchSwitch();
75   CFGElementMatches Counter;
76   applySwitchToCode(Switch, Counter, R"(
77     void f() {
78       42;
79     }
80   )");
81   EXPECT_EQ(Counter.StmtMatches, 0u);
82   EXPECT_EQ(Counter.InitializerMatches, 0u);
83 }
84 
TEST(CFGMatchSwitchTest,SingleLocalVarInitializationTo42)85 TEST(CFGMatchSwitchTest, SingleLocalVarInitializationTo42) {
86   CFGMatchSwitch<CFGElementMatches> Switch = buildCFGMatchSwitch();
87   CFGElementMatches Counter;
88   applySwitchToCode(Switch, Counter, R"(
89     void f() {
90       int i = 42;
91     }
92   )");
93   EXPECT_EQ(Counter.StmtMatches, 1u);
94   EXPECT_EQ(Counter.InitializerMatches, 0u);
95 }
96 
TEST(CFGMatchSwitchTest,SingleFieldInitializationTo42)97 TEST(CFGMatchSwitchTest, SingleFieldInitializationTo42) {
98   CFGMatchSwitch<CFGElementMatches> Switch = buildCFGMatchSwitch();
99   CFGElementMatches Counter;
100   applySwitchToCode(Switch, Counter, R"(
101     struct f {
102       int i;
103       f(): i(42) {}
104     };
105   )");
106   EXPECT_EQ(Counter.StmtMatches, 0u);
107   EXPECT_EQ(Counter.InitializerMatches, 1u);
108 }
109 
TEST(CFGMatchSwitchTest,LocalVarAndFieldInitializationTo42)110 TEST(CFGMatchSwitchTest, LocalVarAndFieldInitializationTo42) {
111   CFGMatchSwitch<CFGElementMatches> Switch = buildCFGMatchSwitch();
112   CFGElementMatches Counter;
113   applySwitchToCode(Switch, Counter, R"(
114     struct f {
115       int i;
116       f(): i(42) {
117         int j = 42;
118       }
119     };
120   )");
121   EXPECT_EQ(Counter.StmtMatches, 1u);
122   EXPECT_EQ(Counter.InitializerMatches, 1u);
123 }
124 } // namespace
125