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 | |