xref: /llvm-project/clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp (revision 88210b81eed803598afeeaa2a60eb26ddbb65435)
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 
TEST(MatchSwitchTest,Stmts)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(varDecl(hasName(XStr))));
50   auto FooMatcher = callExpr(callee(functionDecl(hasName(FooStr))));
51   auto BarMatcher = callExpr(callee(functionDecl(hasName(BarStr))));
52 
53   ASTMatchSwitch<Stmt, llvm::StringRef> MS =
54       ASTMatchSwitchBuilder<Stmt, llvm::StringRef>()
55           .CaseOf<Stmt>(XMatcher,
56                         [&XStr](const Stmt *, const MatchFinder::MatchResult &,
57                                 llvm::StringRef &State) { State = XStr; })
58           .CaseOf<Stmt>(FooMatcher,
59                         [&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>(XStr, match(XMatcher.bind(XStr), 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>(FooStr, match(FooMatcher.bind(FooStr), 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>(BarStr, match(BarMatcher.bind(BarStr), Ctx));
79   MS(*Bar, Ctx, State);
80   EXPECT_EQ(State, FooStr);
81 }
82 
TEST(MatchSwitchTest,CtorInitializers)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(hasName(IStr)));
100   auto InitJ = cxxCtorInitializer(forField(hasName(JStr)));
101   auto InitZ = cxxCtorInitializer(forField(hasName(ZStr)));
102 
103   ASTMatchSwitch<CXXCtorInitializer, llvm::StringRef> MS =
104       ASTMatchSwitchBuilder<CXXCtorInitializer, llvm::StringRef>()
105           .CaseOf<CXXCtorInitializer>(
106               InitI, [&IStr](const CXXCtorInitializer *,
107                              const MatchFinder::MatchResult &,
108                              llvm::StringRef &State) { State = IStr; })
109           .CaseOf<CXXCtorInitializer>(
110               InitJ, [&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>(IStr, match(InitI.bind(IStr), 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>(JStr, match(InitJ.bind(JStr), 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>(ZStr, match(InitZ.bind(ZStr), Ctx));
131   MS(*Z, Ctx, State);
132   EXPECT_EQ(State, JStr);
133 }
134 
TEST(MatchSwitchTest,ReturnNonVoid)135 TEST(MatchSwitchTest, ReturnNonVoid) {
136   auto Unit =
137       tooling::buildASTFromCode("void f() { int x = 42; }", "input.cc",
138                                 std::make_shared<PCHContainerOperations>());
139   auto &Context = Unit->getASTContext();
140   const auto *S =
141       selectFirst<FunctionDecl>(
142           "f",
143           match(functionDecl(isDefinition(), hasName("f")).bind("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>(stmt(),
149                         [](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