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 | |
26 | using namespace clang; |
27 | using namespace dataflow; |
28 | using namespace ast_matchers; |
29 | |
30 | namespace { |
31 | |
32 | TEST(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 | |
83 | TEST(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 | |
135 | TEST(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 | |