diff --git a/clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp b/clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp --- a/clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp @@ -7,26 +7,15 @@ //===----------------------------------------------------------------------===// #include "clang/Analysis/FlowSensitive/MatchSwitch.h" -#include "TestingSupport.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/Analysis/FlowSensitive/DataflowAnalysis.h" -#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" -#include "clang/Analysis/FlowSensitive/DataflowLattice.h" -#include "clang/Analysis/FlowSensitive/MapLattice.h" #include "clang/Tooling/Tooling.h" -#include "llvm/ADT/None.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" -#include "llvm/ADT/Twine.h" -#include "llvm/Support/Error.h" -#include "llvm/Testing/Support/Annotations.h" -#include "llvm/Testing/Support/Error.h" -#include "gmock/gmock.h" #include "gtest/gtest.h" #include #include @@ -36,169 +25,114 @@ using namespace clang; using namespace dataflow; +using namespace ast_matchers; namespace { -using ::testing::Pair; -using ::testing::UnorderedElementsAre; -class BooleanLattice { -public: - BooleanLattice() : Value(false) {} - explicit BooleanLattice(bool B) : Value(B) {} - - static BooleanLattice bottom() { return BooleanLattice(false); } - - static BooleanLattice top() { return BooleanLattice(true); } - - LatticeJoinEffect join(BooleanLattice Other) { - auto Prev = Value; - Value = Value || Other.Value; - return Prev == Value ? LatticeJoinEffect::Unchanged - : LatticeJoinEffect::Changed; - } - - friend bool operator==(BooleanLattice LHS, BooleanLattice RHS) { - return LHS.Value == RHS.Value; - } - - friend std::ostream &operator<<(std::ostream &Os, const BooleanLattice &B) { - Os << B.Value; - return Os; - } - - bool value() const { return Value; } - -private: - bool Value; -}; -} // namespace - -MATCHER_P(Holds, m, - ((negation ? "doesn't hold" : "holds") + - llvm::StringRef(" a lattice element that ") + - ::testing::DescribeMatcher(m, negation)) - .str()) { - return ExplainMatchResult(m, arg.Lattice, result_listener); -} - -void TransferSetTrue(const DeclRefExpr *, - const ast_matchers::MatchFinder::MatchResult &, - TransferState &State) { - State.Lattice = BooleanLattice(true); -} - -void TransferSetFalse(const Stmt *, - const ast_matchers::MatchFinder::MatchResult &, - TransferState &State) { - State.Lattice = BooleanLattice(false); -} - -class TestAnalysis : public DataflowAnalysis { - MatchSwitch> TransferSwitch; - -public: - explicit TestAnalysis(ASTContext &Context) - : DataflowAnalysis(Context) { - using namespace ast_matchers; - TransferSwitch = - MatchSwitchBuilder>() - .CaseOf(declRefExpr(to(varDecl(hasName("X")))), - TransferSetTrue) - .CaseOf(callExpr(callee(functionDecl(hasName("Foo")))), - TransferSetFalse) - .Build(); - } - - static BooleanLattice initialElement() { return BooleanLattice::bottom(); } - - void transfer(const Stmt *S, BooleanLattice &L, Environment &Env) { - TransferState State(L, Env); - TransferSwitch(*S, getASTContext(), State); - } -}; - -template -void RunDataflow(llvm::StringRef Code, Matcher Expectations) { - ASSERT_THAT_ERROR( - test::checkDataflow( - Code, "fun", - [](ASTContext &C, Environment &) { return TestAnalysis(C); }, - [&Expectations]( - llvm::ArrayRef>> - Results, - ASTContext &) { EXPECT_THAT(Results, Expectations); }, - {"-fsyntax-only", "-std=c++17"}), - llvm::Succeeded()); -} - -TEST(MatchSwitchTest, JustX) { - std::string Code = R"( - void fun() { - int X = 1; - (void)X; - // [[p]] - } - )"; - RunDataflow(Code, - UnorderedElementsAre(Pair("p", Holds(BooleanLattice(true))))); -} - -TEST(MatchSwitchTest, JustFoo) { - std::string Code = R"( - void Foo(); - void fun() { - Foo(); - // [[p]] - } - )"; - RunDataflow(Code, - UnorderedElementsAre(Pair("p", Holds(BooleanLattice(false))))); -} - -TEST(MatchSwitchTest, XThenFoo) { +TEST(MatchSwitchTest, Stmts) { std::string Code = R"( void Foo(); - void fun() { + void Bar(); + void f() { int X = 1; - (void)X; - Foo(); - // [[p]] - } - )"; - RunDataflow(Code, - UnorderedElementsAre(Pair("p", Holds(BooleanLattice(false))))); -} - -TEST(MatchSwitchTest, FooThenX) { - std::string Code = R"( - void Foo(); - void fun() { Foo(); - int X = 1; - (void)X; - // [[p]] + Bar(); } )"; - RunDataflow(Code, - UnorderedElementsAre(Pair("p", Holds(BooleanLattice(true))))); + 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 MS = + ASTMatchSwitchBuilder() + .CaseOf(XMatcher, + [&XStr](const Stmt *, const MatchFinder::MatchResult &, + llvm::StringRef &State) { State = XStr; }) + .CaseOf(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(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(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(BarStr, match(BarMatcher.bind(BarStr), Ctx)); + MS(*Bar, Ctx, State); + EXPECT_EQ(State, FooStr); } -TEST(MatchSwitchTest, Neither) { +TEST(MatchSwitchTest, CtorInitializers) { std::string Code = R"( - void Bar(); - void fun(bool b) { - Bar(); - // [[p]] - } + struct f { + int i; + int j; + int z; + f(): i(1), j(1), z(1) {} + }; )"; - RunDataflow(Code, - UnorderedElementsAre(Pair("p", Holds(BooleanLattice(false))))); + 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 MS = + ASTMatchSwitchBuilder() + .CaseOf( + InitI, [&IStr](const CXXCtorInitializer *, + const MatchFinder::MatchResult &, + llvm::StringRef &State) { State = IStr; }) + .CaseOf( + 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(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(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(ZStr, match(InitZ.bind(ZStr), Ctx)); + MS(*Z, Ctx, State); + EXPECT_EQ(State, JStr); } TEST(MatchSwitchTest, ReturnNonVoid) { - using namespace ast_matchers; - auto Unit = tooling::buildASTFromCode("void f() { int x = 42; }", "input.cc", std::make_shared()); @@ -209,8 +143,8 @@ match(functionDecl(isDefinition(), hasName("f")).bind("f"), Context)) ->getBody(); - MatchSwitch> Switch = - MatchSwitchBuilder>() + ASTMatchSwitch> Switch = + ASTMatchSwitchBuilder>() .CaseOf(stmt(), [](const Stmt *, const MatchFinder::MatchResult &, const int &State) -> std::vector { @@ -221,3 +155,5 @@ std::vector Expected{1, 7, 3}; EXPECT_EQ(Actual, Expected); } + +} // namespace