//===- unittests/StaticAnalyzer/StoreTest.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 "Reusables.h" #include "clang/Tooling/Tooling.h" #include "gtest/gtest.h" namespace clang { namespace ento { namespace { class StoreTestConsumer : public ExprEngineConsumer { public: StoreTestConsumer(CompilerInstance &C) : ExprEngineConsumer(C) {} bool HandleTopLevelDecl(DeclGroupRef DG) override { for (const auto *D : DG) performTest(D); return true; } private: virtual void performTest(const Decl *D) = 0; }; template class TestAction : public ASTFrontendAction { public: std::unique_ptr CreateASTConsumer(CompilerInstance &Compiler, StringRef File) override { return std::make_unique(Compiler); } }; // Test that we can put a value into an int-type variable and load it // back from that variable. Test what happens if default bindings are used. class VariableBindConsumer : public StoreTestConsumer { void performTest(const Decl *D) override { StoreManager &SManager = Eng.getStoreManager(); SValBuilder &Builder = Eng.getSValBuilder(); MemRegionManager &MRManager = SManager.getRegionManager(); const ASTContext &ASTCtxt = Eng.getContext(); const auto *VDX0 = findDeclByName(D, "x0"); const auto *VDY0 = findDeclByName(D, "y0"); const auto *VDZ0 = findDeclByName(D, "z0"); const auto *VDX1 = findDeclByName(D, "x1"); const auto *VDY1 = findDeclByName(D, "y1"); ASSERT_TRUE(VDX0 && VDY0 && VDZ0 && VDX1 && VDY1); const StackFrameContext *SFC = Eng.getAnalysisDeclContextManager().getStackFrame(D); Loc LX0 = loc::MemRegionVal(MRManager.getVarRegion(VDX0, SFC)); Loc LY0 = loc::MemRegionVal(MRManager.getVarRegion(VDY0, SFC)); Loc LZ0 = loc::MemRegionVal(MRManager.getVarRegion(VDZ0, SFC)); Loc LX1 = loc::MemRegionVal(MRManager.getVarRegion(VDX1, SFC)); Loc LY1 = loc::MemRegionVal(MRManager.getVarRegion(VDY1, SFC)); Store StInit = SManager.getInitialStore(SFC).getStore(); SVal Zero = Builder.makeZeroVal(ASTCtxt.IntTy); SVal One = Builder.makeIntVal(1, ASTCtxt.IntTy); SVal NarrowZero = Builder.makeZeroVal(ASTCtxt.CharTy); // Bind(Zero) Store StX0 = SManager.Bind(StInit, LX0, Zero).getStore(); EXPECT_EQ(Zero, SManager.getBinding(StX0, LX0, ASTCtxt.IntTy)); // BindDefaultInitial(Zero) Store StY0 = SManager.BindDefaultInitial(StInit, LY0.getAsRegion(), Zero).getStore(); EXPECT_EQ(Zero, SManager.getBinding(StY0, LY0, ASTCtxt.IntTy)); EXPECT_EQ(Zero, *SManager.getDefaultBinding(StY0, LY0.getAsRegion())); // BindDefaultZero() Store StZ0 = SManager.BindDefaultZero(StInit, LZ0.getAsRegion()).getStore(); // BindDefaultZero wipes the region with '0 S8b', not with out Zero. // Direct load, however, does give us back the object of the type // that we specify for loading. EXPECT_EQ(Zero, SManager.getBinding(StZ0, LZ0, ASTCtxt.IntTy)); EXPECT_EQ(NarrowZero, *SManager.getDefaultBinding(StZ0, LZ0.getAsRegion())); // Bind(One) Store StX1 = SManager.Bind(StInit, LX1, One).getStore(); EXPECT_EQ(One, SManager.getBinding(StX1, LX1, ASTCtxt.IntTy)); // BindDefaultInitial(One) Store StY1 = SManager.BindDefaultInitial(StInit, LY1.getAsRegion(), One).getStore(); EXPECT_EQ(One, SManager.getBinding(StY1, LY1, ASTCtxt.IntTy)); EXPECT_EQ(One, *SManager.getDefaultBinding(StY1, LY1.getAsRegion())); } public: using StoreTestConsumer::StoreTestConsumer; }; TEST(Store, VariableBind) { EXPECT_TRUE(tooling::runToolOnCode( std::make_unique>(), "void foo() { int x0, y0, z0, x1, y1; }")); } class LiteralCompoundConsumer : public StoreTestConsumer { void performTest(const Decl *D) override { StoreManager &SManager = Eng.getStoreManager(); SValBuilder &Builder = Eng.getSValBuilder(); MemRegionManager &MRManager = SManager.getRegionManager(); ASTContext &ASTCtxt = Eng.getContext(); using namespace ast_matchers; const auto *CL = findNode(D, compoundLiteralExpr()); const StackFrameContext *SFC = Eng.getAnalysisDeclContextManager().getStackFrame(D); QualType Int = ASTCtxt.IntTy; // Get region for 'test' const SubRegion *CLRegion = MRManager.getCompoundLiteralRegion(CL, SFC); // Get value for 'test[0]' NonLoc Zero = Builder.makeIntVal(0, false); loc::MemRegionVal ZeroElement( MRManager.getElementRegion(ASTCtxt.IntTy, Zero, CLRegion, ASTCtxt)); Store StInit = SManager.getInitialStore(SFC).getStore(); // Let's bind constant 1 to 'test[0]' SVal One = Builder.makeIntVal(1, Int); Store StX = SManager.Bind(StInit, ZeroElement, One).getStore(); // And make sure that we can read this binding back as it was EXPECT_EQ(One, SManager.getBinding(StX, ZeroElement, Int)); } public: using StoreTestConsumer::StoreTestConsumer; }; TEST(Store, LiteralCompound) { EXPECT_TRUE(tooling::runToolOnCode( std::make_unique>(), "void foo() { int *test = (int[]){ 1, 2, 3 }; }", "input.c")); } } // namespace } // namespace ento } // namespace clang