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).getStore(); |
73 | EXPECT_EQ(Zero, SManager.getBinding(StX0, LX0, ASTCtxt.IntTy)); |
74 | |
75 | // BindDefaultInitial(Zero) |
76 | Store StY0 = |
77 | SManager.BindDefaultInitial(store: StInit, R: LY0.getAsRegion(), V: Zero).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()).getStore(); |
83 | // BindDefaultZero wipes the region with '0 S8b', not with out Zero. |
84 | // Direct load, however, does give us back the object of the type |
85 | // that we specify for loading. |
86 | EXPECT_EQ(Zero, SManager.getBinding(StZ0, LZ0, ASTCtxt.IntTy)); |
87 | EXPECT_EQ(NarrowZero, *SManager.getDefaultBinding(StZ0, LZ0.getAsRegion())); |
88 | |
89 | // Bind(One) |
90 | Store StX1 = SManager.Bind(store: StInit, loc: LX1, val: One).getStore(); |
91 | EXPECT_EQ(One, SManager.getBinding(StX1, LX1, ASTCtxt.IntTy)); |
92 | |
93 | // BindDefaultInitial(One) |
94 | Store StY1 = |
95 | SManager.BindDefaultInitial(store: StInit, R: LY1.getAsRegion(), V: One).getStore(); |
96 | EXPECT_EQ(One, SManager.getBinding(StY1, LY1, ASTCtxt.IntTy)); |
97 | EXPECT_EQ(One, *SManager.getDefaultBinding(StY1, LY1.getAsRegion())); |
98 | } |
99 | |
100 | public: |
101 | using StoreTestConsumer::StoreTestConsumer; |
102 | }; |
103 | |
104 | TEST(Store, VariableBind) { |
105 | EXPECT_TRUE(tooling::runToolOnCode( |
106 | std::make_unique<TestAction<VariableBindConsumer>>(), |
107 | "void foo() { int x0, y0, z0, x1, y1; }" )); |
108 | } |
109 | |
110 | class LiteralCompoundConsumer : public StoreTestConsumer { |
111 | void performTest(const Decl *D) override { |
112 | StoreManager &SManager = Eng.getStoreManager(); |
113 | SValBuilder &Builder = Eng.getSValBuilder(); |
114 | MemRegionManager &MRManager = SManager.getRegionManager(); |
115 | ASTContext &ASTCtxt = Eng.getContext(); |
116 | |
117 | using namespace ast_matchers; |
118 | |
119 | const auto *CL = findNode<CompoundLiteralExpr>(Where: D, What: compoundLiteralExpr()); |
120 | |
121 | const StackFrameContext *SFC = |
122 | Eng.getAnalysisDeclContextManager().getStackFrame(D); |
123 | |
124 | QualType Int = ASTCtxt.IntTy; |
125 | |
126 | // Get region for 'test' |
127 | const SubRegion *CLRegion = MRManager.getCompoundLiteralRegion(CL, LC: SFC); |
128 | |
129 | // Get value for 'test[0]' |
130 | NonLoc Zero = Builder.makeIntVal(integer: 0, isUnsigned: false); |
131 | loc::MemRegionVal ZeroElement( |
132 | MRManager.getElementRegion(elementType: ASTCtxt.IntTy, Idx: Zero, superRegion: CLRegion, Ctx&: ASTCtxt)); |
133 | |
134 | Store StInit = SManager.getInitialStore(InitLoc: SFC).getStore(); |
135 | // Let's bind constant 1 to 'test[0]' |
136 | SVal One = Builder.makeIntVal(integer: 1, type: Int); |
137 | Store StX = SManager.Bind(store: StInit, loc: ZeroElement, val: One).getStore(); |
138 | |
139 | // And make sure that we can read this binding back as it was |
140 | EXPECT_EQ(One, SManager.getBinding(StX, ZeroElement, Int)); |
141 | } |
142 | |
143 | public: |
144 | using StoreTestConsumer::StoreTestConsumer; |
145 | }; |
146 | |
147 | TEST(Store, LiteralCompound) { |
148 | EXPECT_TRUE(tooling::runToolOnCode( |
149 | std::make_unique<TestAction<LiteralCompoundConsumer>>(), |
150 | "void foo() { int *test = (int[]){ 1, 2, 3 }; }" , "input.c" )); |
151 | } |
152 | |
153 | } // namespace |
154 | } // namespace ento |
155 | } // namespace clang |
156 | |