1 //===- unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp ------------===//
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
7 //===----------------------------------------------------------------------===//
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"
18 using namespace clang
;
19 using namespace dataflow
;
20 using namespace ast_matchers
;
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;
30 // Returns a match switch that counts the number of local variables
31 // (singly-declared) and fields initialized to the integer literal 42.
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
++; })
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).
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
>
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
));
64 BO
.AddInitializers
= true;
66 auto CFG
= CFG::buildCFG(F
, F
->getBody(), &Ctx
, BO
);
67 auto CFGBlock
= *CFG
->getEntry().succ_begin();
68 for (auto &Elt
: CFGBlock
->Elements
) {
73 TEST(CFGMatchSwitchTest
, NoInitializationTo42
) {
74 CFGMatchSwitch
<CFGElementMatches
> Switch
= buildCFGMatchSwitch();
75 CFGElementMatches Counter
;
76 applySwitchToCode(Switch
, Counter
, R
"(
81 EXPECT_EQ(Counter
.StmtMatches
, 0u);
82 EXPECT_EQ(Counter
.InitializerMatches
, 0u);
85 TEST(CFGMatchSwitchTest
, SingleLocalVarInitializationTo42
) {
86 CFGMatchSwitch
<CFGElementMatches
> Switch
= buildCFGMatchSwitch();
87 CFGElementMatches Counter
;
88 applySwitchToCode(Switch
, Counter
, R
"(
93 EXPECT_EQ(Counter
.StmtMatches
, 1u);
94 EXPECT_EQ(Counter
.InitializerMatches
, 0u);
97 TEST(CFGMatchSwitchTest
, SingleFieldInitializationTo42
) {
98 CFGMatchSwitch
<CFGElementMatches
> Switch
= buildCFGMatchSwitch();
99 CFGElementMatches Counter
;
100 applySwitchToCode(Switch
, Counter
, R
"(
106 EXPECT_EQ(Counter
.StmtMatches
, 0u);
107 EXPECT_EQ(Counter
.InitializerMatches
, 1u);
110 TEST(CFGMatchSwitchTest
, LocalVarAndFieldInitializationTo42
) {
111 CFGMatchSwitch
<CFGElementMatches
> Switch
= buildCFGMatchSwitch();
112 CFGElementMatches Counter
;
113 applySwitchToCode(Switch
, Counter
, R
"(
121 EXPECT_EQ(Counter
.StmtMatches
, 1u);
122 EXPECT_EQ(Counter
.InitializerMatches
, 1u);