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
18using namespace clang;
19using namespace dataflow;
20using namespace ast_matchers;
21
22namespace {
23// State for tracking the number of matches on each kind of CFGElement by the
24// CFGMatchSwitch. Currently only tracks CFGStmt and CFGInitializer.
25struct 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.
32auto buildCFGMatchSwitch() {
33 return CFGMatchSwitchBuilder<CFGElementMatches>()
34 .CaseOfCFGStmt<DeclStmt>(
35 M: declStmt(hasSingleDecl(
36 InnerMatcher: varDecl(hasInitializer(InnerMatcher: integerLiteral(equals(Value: 42)))))),
37 A: [](const DeclStmt *, const MatchFinder::MatchResult &,
38 CFGElementMatches &Counter) { Counter.StmtMatches++; })
39 .CaseOfCFGInit<CXXCtorInitializer>(
40 M: cxxCtorInitializer(withInitializer(InnerMatcher: integerLiteral(equals(Value: 42)))),
41 A: [](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.
55template <typename State>
56void applySwitchToCode(CFGMatchSwitch<State> &MS, State &S,
57 llvm::StringRef Code) {
58 auto Unit = tooling::buildASTFromCodeWithArgs(Code, Args: {"-Wno-unused-value"});
59 auto &Ctx = Unit->getASTContext();
60 const auto *F = selectFirst<FunctionDecl>(
61 BoundTo: "f", Results: match(Matcher: functionDecl(isDefinition(), hasName(Name: "f")).bind(ID: "f"), Context&: 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
73TEST(CFGMatchSwitchTest, NoInitializationTo42) {
74 CFGMatchSwitch<CFGElementMatches> Switch = buildCFGMatchSwitch();
75 CFGElementMatches Counter;
76 applySwitchToCode(MS&: Switch, S&: Counter, Code: R"(
77 void f() {
78 42;
79 }
80 )");
81 EXPECT_EQ(Counter.StmtMatches, 0u);
82 EXPECT_EQ(Counter.InitializerMatches, 0u);
83}
84
85TEST(CFGMatchSwitchTest, SingleLocalVarInitializationTo42) {
86 CFGMatchSwitch<CFGElementMatches> Switch = buildCFGMatchSwitch();
87 CFGElementMatches Counter;
88 applySwitchToCode(MS&: Switch, S&: Counter, Code: R"(
89 void f() {
90 int i = 42;
91 }
92 )");
93 EXPECT_EQ(Counter.StmtMatches, 1u);
94 EXPECT_EQ(Counter.InitializerMatches, 0u);
95}
96
97TEST(CFGMatchSwitchTest, SingleFieldInitializationTo42) {
98 CFGMatchSwitch<CFGElementMatches> Switch = buildCFGMatchSwitch();
99 CFGElementMatches Counter;
100 applySwitchToCode(MS&: Switch, S&: Counter, Code: 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
110TEST(CFGMatchSwitchTest, LocalVarAndFieldInitializationTo42) {
111 CFGMatchSwitch<CFGElementMatches> Switch = buildCFGMatchSwitch();
112 CFGElementMatches Counter;
113 applySwitchToCode(MS&: Switch, S&: Counter, Code: 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

source code of clang/unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp