160 lines
5.2 KiB
C++
160 lines
5.2 KiB
C++
//===- unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp ---------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/Analysis/FlowSensitive/MatchSwitch.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/Decl.h"
|
|
#include "clang/AST/DeclCXX.h"
|
|
#include "clang/AST/Expr.h"
|
|
#include "clang/AST/Stmt.h"
|
|
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
|
#include "clang/ASTMatchers/ASTMatchers.h"
|
|
#include "clang/Tooling/Tooling.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "gtest/gtest.h"
|
|
#include <cstdint>
|
|
#include <memory>
|
|
#include <ostream>
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
using namespace clang;
|
|
using namespace dataflow;
|
|
using namespace ast_matchers;
|
|
|
|
namespace {
|
|
|
|
TEST(MatchSwitchTest, Stmts) {
|
|
std::string Code = R"(
|
|
void Foo();
|
|
void Bar();
|
|
void f() {
|
|
int X = 1;
|
|
Foo();
|
|
Bar();
|
|
}
|
|
)";
|
|
auto Unit = tooling::buildASTFromCode(Code);
|
|
auto &Ctx = Unit->getASTContext();
|
|
|
|
llvm::StringRef XStr = "X";
|
|
llvm::StringRef FooStr = "Foo";
|
|
llvm::StringRef BarStr = "Bar";
|
|
|
|
auto XMatcher = declStmt(hasSingleDecl(varDecl(hasName(XStr))));
|
|
auto FooMatcher = callExpr(callee(functionDecl(hasName(FooStr))));
|
|
auto BarMatcher = callExpr(callee(functionDecl(hasName(BarStr))));
|
|
|
|
ASTMatchSwitch<Stmt, llvm::StringRef> MS =
|
|
ASTMatchSwitchBuilder<Stmt, llvm::StringRef>()
|
|
.CaseOf<Stmt>(XMatcher,
|
|
[&XStr](const Stmt *, const MatchFinder::MatchResult &,
|
|
llvm::StringRef &State) { State = XStr; })
|
|
.CaseOf<Stmt>(FooMatcher,
|
|
[&FooStr](const Stmt *,
|
|
const MatchFinder::MatchResult &,
|
|
llvm::StringRef &State) { State = FooStr; })
|
|
.Build();
|
|
llvm::StringRef State;
|
|
|
|
// State modified from the first case of the switch
|
|
const auto *X = selectFirst<Stmt>(XStr, match(XMatcher.bind(XStr), Ctx));
|
|
MS(*X, Ctx, State);
|
|
EXPECT_EQ(State, XStr);
|
|
|
|
// State modified from the second case of the switch
|
|
const auto *Foo =
|
|
selectFirst<Stmt>(FooStr, match(FooMatcher.bind(FooStr), Ctx));
|
|
MS(*Foo, Ctx, State);
|
|
EXPECT_EQ(State, FooStr);
|
|
|
|
// State unmodified, no case defined for calling Bar
|
|
const auto *Bar =
|
|
selectFirst<Stmt>(BarStr, match(BarMatcher.bind(BarStr), Ctx));
|
|
MS(*Bar, Ctx, State);
|
|
EXPECT_EQ(State, FooStr);
|
|
}
|
|
|
|
TEST(MatchSwitchTest, CtorInitializers) {
|
|
std::string Code = R"(
|
|
struct f {
|
|
int i;
|
|
int j;
|
|
int z;
|
|
f(): i(1), j(1), z(1) {}
|
|
};
|
|
)";
|
|
auto Unit = tooling::buildASTFromCode(Code);
|
|
auto &Ctx = Unit->getASTContext();
|
|
|
|
llvm::StringRef IStr = "i";
|
|
llvm::StringRef JStr = "j";
|
|
llvm::StringRef ZStr = "z";
|
|
|
|
auto InitI = cxxCtorInitializer(forField(hasName(IStr)));
|
|
auto InitJ = cxxCtorInitializer(forField(hasName(JStr)));
|
|
auto InitZ = cxxCtorInitializer(forField(hasName(ZStr)));
|
|
|
|
ASTMatchSwitch<CXXCtorInitializer, llvm::StringRef> MS =
|
|
ASTMatchSwitchBuilder<CXXCtorInitializer, llvm::StringRef>()
|
|
.CaseOf<CXXCtorInitializer>(
|
|
InitI, [&IStr](const CXXCtorInitializer *,
|
|
const MatchFinder::MatchResult &,
|
|
llvm::StringRef &State) { State = IStr; })
|
|
.CaseOf<CXXCtorInitializer>(
|
|
InitJ, [&JStr](const CXXCtorInitializer *,
|
|
const MatchFinder::MatchResult &,
|
|
llvm::StringRef &State) { State = JStr; })
|
|
.Build();
|
|
llvm::StringRef State;
|
|
|
|
// State modified from the first case of the switch
|
|
const auto *I =
|
|
selectFirst<CXXCtorInitializer>(IStr, match(InitI.bind(IStr), Ctx));
|
|
MS(*I, Ctx, State);
|
|
EXPECT_EQ(State, IStr);
|
|
|
|
// State modified from the second case of the switch
|
|
const auto *J =
|
|
selectFirst<CXXCtorInitializer>(JStr, match(InitJ.bind(JStr), Ctx));
|
|
MS(*J, Ctx, State);
|
|
EXPECT_EQ(State, JStr);
|
|
|
|
// State unmodified, no case defined for the initializer of z
|
|
const auto *Z =
|
|
selectFirst<CXXCtorInitializer>(ZStr, match(InitZ.bind(ZStr), Ctx));
|
|
MS(*Z, Ctx, State);
|
|
EXPECT_EQ(State, JStr);
|
|
}
|
|
|
|
TEST(MatchSwitchTest, ReturnNonVoid) {
|
|
auto Unit =
|
|
tooling::buildASTFromCode("void f() { int x = 42; }", "input.cc",
|
|
std::make_shared<PCHContainerOperations>());
|
|
auto &Context = Unit->getASTContext();
|
|
const auto *S =
|
|
selectFirst<FunctionDecl>(
|
|
"f",
|
|
match(functionDecl(isDefinition(), hasName("f")).bind("f"), Context))
|
|
->getBody();
|
|
|
|
ASTMatchSwitch<Stmt, const int, std::vector<int>> Switch =
|
|
ASTMatchSwitchBuilder<Stmt, const int, std::vector<int>>()
|
|
.CaseOf<Stmt>(stmt(),
|
|
[](const Stmt *, const MatchFinder::MatchResult &,
|
|
const int &State) -> std::vector<int> {
|
|
return {1, State, 3};
|
|
})
|
|
.Build();
|
|
std::vector<int> Actual = Switch(*S, Context, 7);
|
|
std::vector<int> Expected{1, 7, 3};
|
|
EXPECT_EQ(Actual, Expected);
|
|
}
|
|
|
|
} // namespace
|