1//===- unittests/Analysis/FlowSensitive/MatchSwitchTest.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/MatchSwitch.h"
10#include "clang/AST/ASTContext.h"
11#include "clang/AST/Decl.h"
12#include "clang/AST/DeclCXX.h"
13#include "clang/AST/Expr.h"
14#include "clang/AST/Stmt.h"
15#include "clang/ASTMatchers/ASTMatchFinder.h"
16#include "clang/ASTMatchers/ASTMatchers.h"
17#include "clang/Tooling/Tooling.h"
18#include "llvm/ADT/StringRef.h"
19#include "gtest/gtest.h"
20#include <cstdint>
21#include <memory>
22#include <ostream>
23#include <string>
24#include <utility>
25
26using namespace clang;
27using namespace dataflow;
28using namespace ast_matchers;
29
30namespace {
31
32TEST(MatchSwitchTest, Stmts) {
33 std::string Code = R"(
34 void Foo();
35 void Bar();
36 void f() {
37 int X = 1;
38 Foo();
39 Bar();
40 }
41 )";
42 auto Unit = tooling::buildASTFromCode(Code);
43 auto &Ctx = Unit->getASTContext();
44
45 llvm::StringRef XStr = "X";
46 llvm::StringRef FooStr = "Foo";
47 llvm::StringRef BarStr = "Bar";
48
49 auto XMatcher = declStmt(hasSingleDecl(InnerMatcher: varDecl(hasName(Name: XStr))));
50 auto FooMatcher = callExpr(callee(InnerMatcher: functionDecl(hasName(Name: FooStr))));
51 auto BarMatcher = callExpr(callee(InnerMatcher: functionDecl(hasName(Name: BarStr))));
52
53 ASTMatchSwitch<Stmt, llvm::StringRef> MS =
54 ASTMatchSwitchBuilder<Stmt, llvm::StringRef>()
55 .CaseOf<Stmt>(M: XMatcher,
56 A: [&XStr](const Stmt *, const MatchFinder::MatchResult &,
57 llvm::StringRef &State) { State = XStr; })
58 .CaseOf<Stmt>(M: FooMatcher,
59 A: [&FooStr](const Stmt *,
60 const MatchFinder::MatchResult &,
61 llvm::StringRef &State) { State = FooStr; })
62 .Build();
63 llvm::StringRef State;
64
65 // State modified from the first case of the switch
66 const auto *X = selectFirst<Stmt>(BoundTo: XStr, Results: match(Matcher: XMatcher.bind(ID: XStr), Context&: Ctx));
67 MS(*X, Ctx, State);
68 EXPECT_EQ(State, XStr);
69
70 // State modified from the second case of the switch
71 const auto *Foo =
72 selectFirst<Stmt>(BoundTo: FooStr, Results: match(Matcher: FooMatcher.bind(ID: FooStr), Context&: Ctx));
73 MS(*Foo, Ctx, State);
74 EXPECT_EQ(State, FooStr);
75
76 // State unmodified, no case defined for calling Bar
77 const auto *Bar =
78 selectFirst<Stmt>(BoundTo: BarStr, Results: match(Matcher: BarMatcher.bind(ID: BarStr), Context&: Ctx));
79 MS(*Bar, Ctx, State);
80 EXPECT_EQ(State, FooStr);
81}
82
83TEST(MatchSwitchTest, CtorInitializers) {
84 std::string Code = R"(
85 struct f {
86 int i;
87 int j;
88 int z;
89 f(): i(1), j(1), z(1) {}
90 };
91 )";
92 auto Unit = tooling::buildASTFromCode(Code);
93 auto &Ctx = Unit->getASTContext();
94
95 llvm::StringRef IStr = "i";
96 llvm::StringRef JStr = "j";
97 llvm::StringRef ZStr = "z";
98
99 auto InitI = cxxCtorInitializer(forField(InnerMatcher: hasName(Name: IStr)));
100 auto InitJ = cxxCtorInitializer(forField(InnerMatcher: hasName(Name: JStr)));
101 auto InitZ = cxxCtorInitializer(forField(InnerMatcher: hasName(Name: ZStr)));
102
103 ASTMatchSwitch<CXXCtorInitializer, llvm::StringRef> MS =
104 ASTMatchSwitchBuilder<CXXCtorInitializer, llvm::StringRef>()
105 .CaseOf<CXXCtorInitializer>(
106 M: InitI, A: [&IStr](const CXXCtorInitializer *,
107 const MatchFinder::MatchResult &,
108 llvm::StringRef &State) { State = IStr; })
109 .CaseOf<CXXCtorInitializer>(
110 M: InitJ, A: [&JStr](const CXXCtorInitializer *,
111 const MatchFinder::MatchResult &,
112 llvm::StringRef &State) { State = JStr; })
113 .Build();
114 llvm::StringRef State;
115
116 // State modified from the first case of the switch
117 const auto *I =
118 selectFirst<CXXCtorInitializer>(BoundTo: IStr, Results: match(Matcher: InitI.bind(ID: IStr), Context&: Ctx));
119 MS(*I, Ctx, State);
120 EXPECT_EQ(State, IStr);
121
122 // State modified from the second case of the switch
123 const auto *J =
124 selectFirst<CXXCtorInitializer>(BoundTo: JStr, Results: match(Matcher: InitJ.bind(ID: JStr), Context&: Ctx));
125 MS(*J, Ctx, State);
126 EXPECT_EQ(State, JStr);
127
128 // State unmodified, no case defined for the initializer of z
129 const auto *Z =
130 selectFirst<CXXCtorInitializer>(BoundTo: ZStr, Results: match(Matcher: InitZ.bind(ID: ZStr), Context&: Ctx));
131 MS(*Z, Ctx, State);
132 EXPECT_EQ(State, JStr);
133}
134
135TEST(MatchSwitchTest, ReturnNonVoid) {
136 auto Unit =
137 tooling::buildASTFromCode(Code: "void f() { int x = 42; }", FileName: "input.cc",
138 PCHContainerOps: std::make_shared<PCHContainerOperations>());
139 auto &Context = Unit->getASTContext();
140 const auto *S =
141 selectFirst<FunctionDecl>(
142 BoundTo: "f",
143 Results: match(Matcher: functionDecl(isDefinition(), hasName(Name: "f")).bind(ID: "f"), Context))
144 ->getBody();
145
146 ASTMatchSwitch<Stmt, const int, std::vector<int>> Switch =
147 ASTMatchSwitchBuilder<Stmt, const int, std::vector<int>>()
148 .CaseOf<Stmt>(M: stmt(),
149 A: [](const Stmt *, const MatchFinder::MatchResult &,
150 const int &State) -> std::vector<int> {
151 return {1, State, 3};
152 })
153 .Build();
154 std::vector<int> Actual = Switch(*S, Context, 7);
155 std::vector<int> Expected{1, 7, 3};
156 EXPECT_EQ(Actual, Expected);
157}
158
159} // namespace
160

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