//===- 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 // //===----------------------------------------------------------------------===// // // This file defines a simplistic version of Constant Propagation as an example // of a forward, monotonic dataflow analysis. The analysis tracks all // variables in the scope, but lacks escape analysis. // //===----------------------------------------------------------------------===// #include "clang/Analysis/FlowSensitive/MatchSwitch.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.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/Analysis/FlowSensitive/TestingSupport.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 #include #include #include using namespace clang; using namespace dataflow; 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 *, 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); } }; class MatchSwitchTest : public ::testing::Test { protected: 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_F(MatchSwitchTest, JustX) { std::string Code = R"( void fun() { int X = 1; (void)X; // [[p]] } )"; RunDataflow(Code, UnorderedElementsAre(Pair("p", Holds(BooleanLattice(true))))); } TEST_F(MatchSwitchTest, JustFoo) { std::string Code = R"( void Foo(); void fun() { Foo(); // [[p]] } )"; RunDataflow(Code, UnorderedElementsAre(Pair("p", Holds(BooleanLattice(false))))); } TEST_F(MatchSwitchTest, XThenFoo) { std::string Code = R"( void Foo(); void fun() { int X = 1; (void)X; Foo(); // [[p]] } )"; RunDataflow(Code, UnorderedElementsAre(Pair("p", Holds(BooleanLattice(false))))); } TEST_F(MatchSwitchTest, FooThenX) { std::string Code = R"( void Foo(); void fun() { Foo(); int X = 1; (void)X; // [[p]] } )"; RunDataflow(Code, UnorderedElementsAre(Pair("p", Holds(BooleanLattice(true))))); } TEST_F(MatchSwitchTest, Neither) { std::string Code = R"( void Bar(); void fun(bool b) { Bar(); // [[p]] } )"; RunDataflow(Code, UnorderedElementsAre(Pair("p", Holds(BooleanLattice(false))))); }