| 1 | //===- unittests/StaticAnalyzer/StoreTest.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 "Reusables.h" |
| 10 | |
| 11 | #include "clang/Tooling/Tooling.h" |
| 12 | #include "gtest/gtest.h" |
| 13 | |
| 14 | namespace clang { |
| 15 | namespace ento { |
| 16 | namespace { |
| 17 | |
| 18 | class StoreTestConsumer : public ExprEngineConsumer { |
| 19 | public: |
| 20 | StoreTestConsumer(CompilerInstance &C) : ExprEngineConsumer(C) {} |
| 21 | |
| 22 | bool HandleTopLevelDecl(DeclGroupRef DG) override { |
| 23 | for (const auto *D : DG) |
| 24 | performTest(D); |
| 25 | return true; |
| 26 | } |
| 27 | |
| 28 | private: |
| 29 | virtual void performTest(const Decl *D) = 0; |
| 30 | }; |
| 31 | |
| 32 | template <class ConsumerTy> class TestAction : public ASTFrontendAction { |
| 33 | public: |
| 34 | std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler, |
| 35 | StringRef File) override { |
| 36 | return std::make_unique<ConsumerTy>(Compiler); |
| 37 | } |
| 38 | }; |
| 39 | |
| 40 | // Test that we can put a value into an int-type variable and load it |
| 41 | // back from that variable. Test what happens if default bindings are used. |
| 42 | class VariableBindConsumer : public StoreTestConsumer { |
| 43 | void performTest(const Decl *D) override { |
| 44 | StoreManager &SManager = Eng.getStoreManager(); |
| 45 | SValBuilder &Builder = Eng.getSValBuilder(); |
| 46 | MemRegionManager &MRManager = SManager.getRegionManager(); |
| 47 | const ASTContext &ASTCtxt = Eng.getContext(); |
| 48 | |
| 49 | const auto *VDX0 = findDeclByName<VarDecl>(Where: D, Name: "x0" ); |
| 50 | const auto *VDY0 = findDeclByName<VarDecl>(Where: D, Name: "y0" ); |
| 51 | const auto *VDZ0 = findDeclByName<VarDecl>(Where: D, Name: "z0" ); |
| 52 | const auto *VDX1 = findDeclByName<VarDecl>(Where: D, Name: "x1" ); |
| 53 | const auto *VDY1 = findDeclByName<VarDecl>(Where: D, Name: "y1" ); |
| 54 | |
| 55 | ASSERT_TRUE(VDX0 && VDY0 && VDZ0 && VDX1 && VDY1); |
| 56 | |
| 57 | const StackFrameContext *SFC = |
| 58 | Eng.getAnalysisDeclContextManager().getStackFrame(D); |
| 59 | |
| 60 | Loc LX0 = loc::MemRegionVal(MRManager.getVarRegion(VD: VDX0, LC: SFC)); |
| 61 | Loc LY0 = loc::MemRegionVal(MRManager.getVarRegion(VD: VDY0, LC: SFC)); |
| 62 | Loc LZ0 = loc::MemRegionVal(MRManager.getVarRegion(VD: VDZ0, LC: SFC)); |
| 63 | Loc LX1 = loc::MemRegionVal(MRManager.getVarRegion(VD: VDX1, LC: SFC)); |
| 64 | Loc LY1 = loc::MemRegionVal(MRManager.getVarRegion(VD: VDY1, LC: SFC)); |
| 65 | |
| 66 | Store StInit = SManager.getInitialStore(InitLoc: SFC).getStore(); |
| 67 | SVal Zero = Builder.makeZeroVal(type: ASTCtxt.IntTy); |
| 68 | SVal One = Builder.makeIntVal(1, ASTCtxt.IntTy); |
| 69 | SVal NarrowZero = Builder.makeZeroVal(type: ASTCtxt.CharTy); |
| 70 | |
| 71 | // Bind(Zero) |
| 72 | Store StX0 = SManager.Bind(store: StInit, loc: LX0, val: Zero).ResultingStore.getStore(); |
| 73 | EXPECT_EQ(Zero, SManager.getBinding(StX0, LX0, ASTCtxt.IntTy)); |
| 74 | |
| 75 | // BindDefaultInitial(Zero) |
| 76 | Store StY0 = SManager.BindDefaultInitial(store: StInit, R: LY0.getAsRegion(), V: Zero) |
| 77 | .ResultingStore.getStore(); |
| 78 | EXPECT_EQ(Zero, SManager.getBinding(StY0, LY0, ASTCtxt.IntTy)); |
| 79 | EXPECT_EQ(Zero, *SManager.getDefaultBinding(StY0, LY0.getAsRegion())); |
| 80 | |
| 81 | // BindDefaultZero() |
| 82 | Store StZ0 = SManager.BindDefaultZero(store: StInit, R: LZ0.getAsRegion()) |
| 83 | .ResultingStore.getStore(); |
| 84 | // BindDefaultZero wipes the region with '0 S8b', not with out Zero. |
| 85 | // Direct load, however, does give us back the object of the type |
| 86 | // that we specify for loading. |
| 87 | EXPECT_EQ(Zero, SManager.getBinding(StZ0, LZ0, ASTCtxt.IntTy)); |
| 88 | EXPECT_EQ(NarrowZero, *SManager.getDefaultBinding(StZ0, LZ0.getAsRegion())); |
| 89 | |
| 90 | // Bind(One) |
| 91 | Store StX1 = SManager.Bind(store: StInit, loc: LX1, val: One).ResultingStore.getStore(); |
| 92 | EXPECT_EQ(One, SManager.getBinding(StX1, LX1, ASTCtxt.IntTy)); |
| 93 | |
| 94 | // BindDefaultInitial(One) |
| 95 | Store StY1 = SManager.BindDefaultInitial(store: StInit, R: LY1.getAsRegion(), V: One) |
| 96 | .ResultingStore.getStore(); |
| 97 | EXPECT_EQ(One, SManager.getBinding(StY1, LY1, ASTCtxt.IntTy)); |
| 98 | EXPECT_EQ(One, *SManager.getDefaultBinding(StY1, LY1.getAsRegion())); |
| 99 | } |
| 100 | |
| 101 | public: |
| 102 | using StoreTestConsumer::StoreTestConsumer; |
| 103 | }; |
| 104 | |
| 105 | TEST(Store, VariableBind) { |
| 106 | EXPECT_TRUE(tooling::runToolOnCode( |
| 107 | std::make_unique<TestAction<VariableBindConsumer>>(), |
| 108 | "void foo() { int x0, y0, z0, x1, y1; }" )); |
| 109 | } |
| 110 | |
| 111 | class LiteralCompoundConsumer : public StoreTestConsumer { |
| 112 | void performTest(const Decl *D) override { |
| 113 | StoreManager &SManager = Eng.getStoreManager(); |
| 114 | SValBuilder &Builder = Eng.getSValBuilder(); |
| 115 | MemRegionManager &MRManager = SManager.getRegionManager(); |
| 116 | ASTContext &ASTCtxt = Eng.getContext(); |
| 117 | |
| 118 | using namespace ast_matchers; |
| 119 | |
| 120 | const auto *CL = findNode<CompoundLiteralExpr>(Where: D, What: compoundLiteralExpr()); |
| 121 | |
| 122 | const StackFrameContext *SFC = |
| 123 | Eng.getAnalysisDeclContextManager().getStackFrame(D); |
| 124 | |
| 125 | QualType Int = ASTCtxt.IntTy; |
| 126 | |
| 127 | // Get region for 'test' |
| 128 | const SubRegion *CLRegion = MRManager.getCompoundLiteralRegion(CL, LC: SFC); |
| 129 | |
| 130 | // Get value for 'test[0]' |
| 131 | NonLoc Zero = Builder.makeIntVal(integer: 0, isUnsigned: false); |
| 132 | loc::MemRegionVal ZeroElement( |
| 133 | MRManager.getElementRegion(elementType: ASTCtxt.IntTy, Idx: Zero, superRegion: CLRegion, Ctx: ASTCtxt)); |
| 134 | |
| 135 | Store StInit = SManager.getInitialStore(InitLoc: SFC).getStore(); |
| 136 | // Let's bind constant 1 to 'test[0]' |
| 137 | SVal One = Builder.makeIntVal(integer: 1, type: Int); |
| 138 | Store StX = |
| 139 | SManager.Bind(store: StInit, loc: ZeroElement, val: One).ResultingStore.getStore(); |
| 140 | |
| 141 | // And make sure that we can read this binding back as it was |
| 142 | EXPECT_EQ(One, SManager.getBinding(StX, ZeroElement, Int)); |
| 143 | } |
| 144 | |
| 145 | public: |
| 146 | using StoreTestConsumer::StoreTestConsumer; |
| 147 | }; |
| 148 | |
| 149 | TEST(Store, LiteralCompound) { |
| 150 | EXPECT_TRUE(tooling::runToolOnCode( |
| 151 | std::make_unique<TestAction<LiteralCompoundConsumer>>(), |
| 152 | "void foo() { int *test = (int[]){ 1, 2, 3 }; }" , "input.c" )); |
| 153 | } |
| 154 | |
| 155 | } // namespace |
| 156 | } // namespace ento |
| 157 | } // namespace clang |
| 158 | |