1 //===- unittests/StaticAnalyzer/SvalTest.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 "CheckerRegistration.h"
10
11 #include "clang/AST/ASTContext.h"
12 #include "clang/AST/Decl.h"
13 #include "clang/AST/DeclGroup.h"
14 #include "clang/AST/RecursiveASTVisitor.h"
15 #include "clang/AST/Stmt.h"
16 #include "clang/AST/Type.h"
17 #include "clang/StaticAnalyzer/Core/Checker.h"
18 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
19 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
20 #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
21 #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
22 #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
23 #include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
24 #include "clang/Tooling/Tooling.h"
25 #include "llvm/ADT/STLExtras.h"
26 #include "llvm/ADT/StringRef.h"
27 #include "llvm/Support/Casting.h"
28 #include "llvm/Support/raw_ostream.h"
29 #include "gtest/gtest.h"
30
31 namespace clang {
32
33 // getType() tests include whole bunch of type comparisons,
34 // so when something is wrong, it's good to have gtest telling us
35 // what are those types.
operator <<(std::ostream & OS,const QualType & T)36 LLVM_ATTRIBUTE_UNUSED std::ostream &operator<<(std::ostream &OS,
37 const QualType &T) {
38 return OS << T.getAsString();
39 }
40
operator <<(std::ostream & OS,const CanQualType & T)41 LLVM_ATTRIBUTE_UNUSED std::ostream &operator<<(std::ostream &OS,
42 const CanQualType &T) {
43 return OS << QualType{T};
44 }
45
46 namespace ento {
47 namespace {
48
49 //===----------------------------------------------------------------------===//
50 // Testing framework implementation
51 //===----------------------------------------------------------------------===//
52
53 /// A simple map from variable names to symbolic values used to init them.
54 using SVals = llvm::StringMap<SVal>;
55
56 /// SValCollector is the barebone of all tests.
57 ///
58 /// It is implemented as a checker and reacts to binds, so we find
59 /// symbolic values of interest, and to end analysis, where we actually
60 /// can test whatever we gathered.
61 class SValCollector : public Checker<check::Bind, check::EndAnalysis> {
62 public:
checkBind(SVal Loc,SVal Val,const Stmt * S,CheckerContext & C) const63 void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const {
64 // Skip instantly if we finished testing.
65 // Also, we care only for binds happening in variable initializations.
66 if (Tested || !isa<DeclStmt>(S))
67 return;
68
69 if (const auto *VR = llvm::dyn_cast_or_null<VarRegion>(Loc.getAsRegion())) {
70 CollectedSVals[VR->getDescriptiveName(false)] = Val;
71 }
72 }
73
checkEndAnalysis(ExplodedGraph & G,BugReporter & B,ExprEngine & Engine) const74 void checkEndAnalysis(ExplodedGraph &G, BugReporter &B,
75 ExprEngine &Engine) const {
76 if (!Tested) {
77 test(Engine, Engine.getContext());
78 Tested = true;
79 CollectedSVals.clear();
80 }
81 }
82
83 /// Helper function for tests to access bound symbolic values.
getByName(StringRef Name) const84 SVal getByName(StringRef Name) const { return CollectedSVals[Name]; }
85
86 private:
87 /// Entry point for tests.
88 virtual void test(ExprEngine &Engine, const ASTContext &Context) const = 0;
89
90 mutable bool Tested = false;
91 mutable SVals CollectedSVals;
92 };
93
94 // SVAL_TEST is a combined way of providing a short code snippet and
95 // to test some programmatic predicates on symbolic values produced by the
96 // engine for the actual code.
97 //
98 // Each test has a NAME. One can think of it as a name for normal gtests.
99 //
100 // Each test should provide a CODE snippet. Code snippets might contain any
101 // valid C/C++, but have ONLY ONE defined function. There are no requirements
102 // about function's name or parameters. It can even be a class method. The
103 // body of the function must contain a set of variable declarations. Each
104 // variable declaration gets bound to a symbolic value, so for the following
105 // example:
106 //
107 // int x = <expr>;
108 //
109 // `x` will be bound to whatever symbolic value the engine produced for <expr>.
110 // LIVENESS and REASSIGNMENTS don't affect this binding.
111 //
112 // During the test the actual values can be accessed via `getByName` function,
113 // and, for the `x`-bound value, one must use "x" as its name.
114 //
115 // Example:
116 // SVAL_TEST(SimpleSValTest, R"(
117 // void foo() {
118 // int x = 42;
119 // })") {
120 // SVal X = getByName("x");
121 // EXPECT_TRUE(X.isConstant(42));
122 // }
123 #define SVAL_TEST(NAME, CODE) \
124 class NAME##SValCollector final : public SValCollector { \
125 public: \
126 void test(ExprEngine &Engine, const ASTContext &Context) const override; \
127 }; \
128 \
129 void add##NAME##SValCollector(AnalysisASTConsumer &AnalysisConsumer, \
130 AnalyzerOptions &AnOpts) { \
131 AnOpts.CheckersAndPackages = {{"test.##NAME##SValCollector", true}}; \
132 AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) { \
133 Registry.addChecker<NAME##SValCollector>("test.##NAME##SValCollector", \
134 "Description", ""); \
135 }); \
136 } \
137 \
138 TEST(SValTest, NAME) { runCheckerOnCode<add##NAME##SValCollector>(CODE); } \
139 void NAME##SValCollector::test(ExprEngine &Engine, \
140 const ASTContext &Context) const
141
142 //===----------------------------------------------------------------------===//
143 // Actual tests
144 //===----------------------------------------------------------------------===//
145
146 SVAL_TEST(GetConstType, R"(
147 void foo() {
148 int x = 42;
149 int *y = nullptr;
150 })") {
151 SVal X = getByName("x");
152 ASSERT_FALSE(X.getType(Context).isNull());
153 EXPECT_EQ(Context.IntTy, X.getType(Context));
154
155 SVal Y = getByName("y");
156 ASSERT_FALSE(Y.getType(Context).isNull());
157 EXPECT_EQ(Context.getUIntPtrType(), Y.getType(Context));
158 }
159
160 SVAL_TEST(GetLocAsIntType, R"(
161 void foo(int *x) {
162 long int a = (long int)x;
163 unsigned b = (long unsigned)&a;
164 int c = (long int)nullptr;
165 })") {
166 SVal A = getByName("a");
167 ASSERT_FALSE(A.getType(Context).isNull());
168 // TODO: Turn it into signed long
169 EXPECT_EQ(Context.getUIntPtrType(), A.getType(Context));
170
171 SVal B = getByName("b");
172 ASSERT_FALSE(B.getType(Context).isNull());
173 EXPECT_EQ(Context.UnsignedIntTy, B.getType(Context));
174
175 SVal C = getByName("c");
176 ASSERT_FALSE(C.getType(Context).isNull());
177 EXPECT_EQ(Context.IntTy, C.getType(Context));
178 }
179
180 SVAL_TEST(GetSymExprType, R"(
181 void foo(int a, int b) {
182 int x = a;
183 int y = a + b;
184 long z = a;
185 })") {
186 QualType Int = Context.IntTy;
187
188 SVal X = getByName("x");
189 ASSERT_FALSE(X.getType(Context).isNull());
190 EXPECT_EQ(Int, X.getType(Context));
191
192 SVal Y = getByName("y");
193 ASSERT_FALSE(Y.getType(Context).isNull());
194 EXPECT_EQ(Int, Y.getType(Context));
195
196 // TODO: Change to Long when we support symbolic casts
197 SVal Z = getByName("z");
198 ASSERT_FALSE(Z.getType(Context).isNull());
199 EXPECT_EQ(Int, Z.getType(Context));
200 }
201
202 SVAL_TEST(GetPointerType, R"(
203 int *bar();
204 int &foobar();
205 struct Z {
206 int a;
207 int *b;
208 };
209 void foo(int x, int *y, Z z) {
210 int &a = x;
211 int &b = *y;
212 int &c = *bar();
213 int &d = foobar();
214 int &e = z.a;
215 int &f = *z.b;
216 })") {
217 QualType Int = Context.IntTy;
218
219 SVal A = getByName("a");
220 ASSERT_FALSE(A.getType(Context).isNull());
221 const auto *APtrTy = dyn_cast<PointerType>(A.getType(Context));
222 ASSERT_NE(APtrTy, nullptr);
223 EXPECT_EQ(Int, APtrTy->getPointeeType());
224
225 SVal B = getByName("b");
226 ASSERT_FALSE(B.getType(Context).isNull());
227 const auto *BPtrTy = dyn_cast<PointerType>(B.getType(Context));
228 ASSERT_NE(BPtrTy, nullptr);
229 EXPECT_EQ(Int, BPtrTy->getPointeeType());
230
231 SVal C = getByName("c");
232 ASSERT_FALSE(C.getType(Context).isNull());
233 const auto *CPtrTy = dyn_cast<PointerType>(C.getType(Context));
234 ASSERT_NE(CPtrTy, nullptr);
235 EXPECT_EQ(Int, CPtrTy->getPointeeType());
236
237 SVal D = getByName("d");
238 ASSERT_FALSE(D.getType(Context).isNull());
239 const auto *DRefTy = dyn_cast<LValueReferenceType>(D.getType(Context));
240 ASSERT_NE(DRefTy, nullptr);
241 EXPECT_EQ(Int, DRefTy->getPointeeType());
242
243 SVal E = getByName("e");
244 ASSERT_FALSE(E.getType(Context).isNull());
245 const auto *EPtrTy = dyn_cast<PointerType>(E.getType(Context));
246 ASSERT_NE(EPtrTy, nullptr);
247 EXPECT_EQ(Int, EPtrTy->getPointeeType());
248
249 SVal F = getByName("f");
250 ASSERT_FALSE(F.getType(Context).isNull());
251 const auto *FPtrTy = dyn_cast<PointerType>(F.getType(Context));
252 ASSERT_NE(FPtrTy, nullptr);
253 EXPECT_EQ(Int, FPtrTy->getPointeeType());
254 }
255
256 SVAL_TEST(GetCompoundType, R"(
257 struct TestStruct {
258 int a, b;
259 };
260 union TestUnion {
261 int a;
262 float b;
263 TestStruct c;
264 };
265 void foo(int x) {
266 int a[] = {1, x, 2};
267 TestStruct b = {x, 42};
268 TestUnion c = {42};
269 TestUnion d = {.c=b};
270 }
271 )") {
272 SVal A = getByName("a");
273 ASSERT_FALSE(A.getType(Context).isNull());
274 const auto *AArrayType = dyn_cast<ArrayType>(A.getType(Context));
275 ASSERT_NE(AArrayType, nullptr);
276 EXPECT_EQ(Context.IntTy, AArrayType->getElementType());
277
278 SVal B = getByName("b");
279 ASSERT_FALSE(B.getType(Context).isNull());
280 const auto *BRecordType = dyn_cast<RecordType>(B.getType(Context));
281 ASSERT_NE(BRecordType, nullptr);
282 EXPECT_EQ("TestStruct", BRecordType->getDecl()->getName());
283
284 SVal C = getByName("c");
285 ASSERT_FALSE(C.getType(Context).isNull());
286 const auto *CRecordType = dyn_cast<RecordType>(C.getType(Context));
287 ASSERT_NE(CRecordType, nullptr);
288 EXPECT_EQ("TestUnion", CRecordType->getDecl()->getName());
289
290 auto D = getByName("d").getAs<nonloc::CompoundVal>();
291 ASSERT_TRUE(D.hasValue());
292 auto Begin = D->begin();
293 ASSERT_NE(D->end(), Begin);
294 ++Begin;
295 ASSERT_EQ(D->end(), Begin);
296 auto LD = D->begin()->getAs<nonloc::LazyCompoundVal>();
297 ASSERT_TRUE(LD.hasValue());
298 auto LDT = LD->getType(Context);
299 ASSERT_FALSE(LDT.isNull());
300 const auto *DRecordType = dyn_cast<RecordType>(LDT);
301 ASSERT_NE(DRecordType, nullptr);
302 EXPECT_EQ("TestStruct", DRecordType->getDecl()->getName());
303 }
304
305 SVAL_TEST(GetStringType, R"(
306 void foo() {
307 const char *a = "Hello, world!";
308 }
309 )") {
310 SVal A = getByName("a");
311 ASSERT_FALSE(A.getType(Context).isNull());
312 const auto *APtrTy = dyn_cast<PointerType>(A.getType(Context));
313 ASSERT_NE(APtrTy, nullptr);
314 EXPECT_EQ(Context.CharTy, APtrTy->getPointeeType());
315 }
316
317 SVAL_TEST(GetThisType, R"(
318 class TestClass {
319 void foo();
320 };
321 void TestClass::foo() {
322 const auto *a = this;
323 }
324 )") {
325 SVal A = getByName("a");
326 ASSERT_FALSE(A.getType(Context).isNull());
327 const auto *APtrTy = dyn_cast<PointerType>(A.getType(Context));
328 ASSERT_NE(APtrTy, nullptr);
329 const auto *ARecordType = dyn_cast<RecordType>(APtrTy->getPointeeType());
330 ASSERT_NE(ARecordType, nullptr);
331 EXPECT_EQ("TestClass", ARecordType->getDecl()->getName());
332 }
333
334 SVAL_TEST(GetFunctionPtrType, R"(
335 void bar();
336 void foo() {
337 auto *a = &bar;
338 }
339 )") {
340 SVal A = getByName("a");
341 ASSERT_FALSE(A.getType(Context).isNull());
342 const auto *APtrTy = dyn_cast<PointerType>(A.getType(Context));
343 ASSERT_NE(APtrTy, nullptr);
344 ASSERT_TRUE(isa<FunctionProtoType>(APtrTy->getPointeeType()));
345 }
346
347 SVAL_TEST(GetLabelType, R"(
348 void foo() {
349 entry:
350 void *a = &&entry;
351 char *b = (char *)&&entry;
352 }
353 )") {
354 SVal A = getByName("a");
355 ASSERT_FALSE(A.getType(Context).isNull());
356 EXPECT_EQ(Context.VoidPtrTy, A.getType(Context));
357
358 SVal B = getByName("a");
359 ASSERT_FALSE(B.getType(Context).isNull());
360 // TODO: Change to CharTy when we support symbolic casts
361 EXPECT_EQ(Context.VoidPtrTy, B.getType(Context));
362 }
363
364 } // namespace
365 } // namespace ento
366 } // namespace clang
367