1 //===- unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp -------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
10 #include "TestingSupport.h"
11 #include "clang/AST/DeclCXX.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/ASTMatchers/ASTMatchers.h"
14 #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
15 #include "clang/Analysis/FlowSensitive/StorageLocation.h"
16 #include "clang/Analysis/FlowSensitive/Value.h"
17 #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
18 #include "clang/Tooling/Tooling.h"
19 #include "gmock/gmock.h"
20 #include "gtest/gtest.h"
25 using namespace clang
;
26 using namespace dataflow
;
27 using ::clang::dataflow::test::getFieldValue
;
28 using ::testing::IsNull
;
29 using ::testing::NotNull
;
31 class EnvironmentTest
: public ::testing::Test
{
33 EnvironmentTest() : DAContext(std::make_unique
<WatchedLiteralsSolver
>()) {}
35 DataflowAnalysisContext DAContext
;
38 TEST_F(EnvironmentTest
, FlowCondition
) {
39 Environment
Env(DAContext
);
40 auto &A
= Env
.arena();
42 EXPECT_TRUE(Env
.proves(A
.makeLiteral(true)));
43 EXPECT_TRUE(Env
.allows(A
.makeLiteral(true)));
44 EXPECT_FALSE(Env
.proves(A
.makeLiteral(false)));
45 EXPECT_FALSE(Env
.allows(A
.makeLiteral(false)));
47 auto &X
= A
.makeAtomRef(A
.makeAtom());
48 EXPECT_FALSE(Env
.proves(X
));
49 EXPECT_TRUE(Env
.allows(X
));
52 EXPECT_TRUE(Env
.proves(X
));
53 EXPECT_TRUE(Env
.allows(X
));
55 auto &NotX
= A
.makeNot(X
);
56 EXPECT_FALSE(Env
.proves(NotX
));
57 EXPECT_FALSE(Env
.allows(NotX
));
60 TEST_F(EnvironmentTest
, CreateValueRecursiveType
) {
61 using namespace ast_matchers
;
63 std::string Code
= R
"cc(
68 // Use both fields to force them to be created with `createValue`.
69 void Usage(Recursive R) { (void)R.X; (void)R.R; }
73 tooling::buildASTFromCodeWithArgs(Code
, {"-fsyntax-only", "-std=c++11"});
74 auto &Context
= Unit
->getASTContext();
76 ASSERT_EQ(Context
.getDiagnostics().getClient()->getNumErrors(), 0U);
79 match(qualType(hasDeclaration(recordDecl(
81 has(fieldDecl(hasName("R")).bind("field-r")))))
84 const QualType
*TyPtr
= selectFirst
<QualType
>("target", Results
);
85 ASSERT_THAT(TyPtr
, NotNull());
87 ASSERT_FALSE(Ty
.isNull());
89 const FieldDecl
*R
= selectFirst
<FieldDecl
>("field-r", Results
);
90 ASSERT_THAT(R
, NotNull());
92 Results
= match(functionDecl(hasName("Usage")).bind("fun"), Context
);
93 const auto *Fun
= selectFirst
<FunctionDecl
>("fun", Results
);
94 ASSERT_THAT(Fun
, NotNull());
96 // Verify that the struct and the field (`R`) with first appearance of the
97 // type is created successfully.
98 Environment
Env(DAContext
, *Fun
);
99 auto &SLoc
= cast
<RecordStorageLocation
>(Env
.createObject(Ty
));
100 PointerValue
*PV
= cast_or_null
<PointerValue
>(getFieldValue(&SLoc
, *R
, Env
));
101 EXPECT_THAT(PV
, NotNull());
104 TEST_F(EnvironmentTest
, JoinRecords
) {
105 using namespace ast_matchers
;
107 std::string Code
= R
"cc(
109 // Need to use the type somewhere so that the `QualType` gets created;
114 tooling::buildASTFromCodeWithArgs(Code
, {"-fsyntax-only", "-std=c++11"});
115 auto &Context
= Unit
->getASTContext();
117 ASSERT_EQ(Context
.getDiagnostics().getClient()->getNumErrors(), 0U);
120 match(qualType(hasDeclaration(recordDecl(hasName("S")))).bind("SType"),
122 const QualType
*TyPtr
= selectFirst
<QualType
>("SType", Results
);
123 ASSERT_THAT(TyPtr
, NotNull());
124 QualType Ty
= *TyPtr
;
125 ASSERT_FALSE(Ty
.isNull());
127 auto *ConstructExpr
= CXXConstructExpr::CreateEmpty(Context
, 0);
128 ConstructExpr
->setType(Ty
);
129 ConstructExpr
->setValueKind(VK_PRValue
);
131 // Two different `RecordValue`s with the same location are joined into a
132 // third `RecordValue` with that same location.
134 Environment
Env1(DAContext
);
135 auto &Val1
= *cast
<RecordValue
>(Env1
.createValue(Ty
));
136 RecordStorageLocation
&Loc
= Val1
.getLoc();
137 Env1
.setValue(*ConstructExpr
, Val1
);
139 Environment
Env2(DAContext
);
140 auto &Val2
= Env2
.create
<RecordValue
>(Loc
);
141 Env2
.setValue(Loc
, Val2
);
142 Env2
.setValue(*ConstructExpr
, Val2
);
144 Environment::ValueModel Model
;
145 Environment EnvJoined
= Environment::join(Env1
, Env2
, Model
);
146 auto *JoinedVal
= cast
<RecordValue
>(EnvJoined
.getValue(*ConstructExpr
));
147 EXPECT_NE(JoinedVal
, &Val1
);
148 EXPECT_NE(JoinedVal
, &Val2
);
149 EXPECT_EQ(&JoinedVal
->getLoc(), &Loc
);
152 // Two different `RecordValue`s with different locations are joined into a
153 // third `RecordValue` with a location different from the other two.
155 Environment
Env1(DAContext
);
156 auto &Val1
= *cast
<RecordValue
>(Env1
.createValue(Ty
));
157 Env1
.setValue(*ConstructExpr
, Val1
);
159 Environment
Env2(DAContext
);
160 auto &Val2
= *cast
<RecordValue
>(Env2
.createValue(Ty
));
161 Env2
.setValue(*ConstructExpr
, Val2
);
163 Environment::ValueModel Model
;
164 Environment EnvJoined
= Environment::join(Env1
, Env2
, Model
);
165 auto *JoinedVal
= cast
<RecordValue
>(EnvJoined
.getValue(*ConstructExpr
));
166 EXPECT_NE(JoinedVal
, &Val1
);
167 EXPECT_NE(JoinedVal
, &Val2
);
168 EXPECT_NE(&JoinedVal
->getLoc(), &Val1
.getLoc());
169 EXPECT_NE(&JoinedVal
->getLoc(), &Val2
.getLoc());
173 TEST_F(EnvironmentTest
, InitGlobalVarsFun
) {
174 using namespace ast_matchers
;
176 std::string Code
= R
"cc(
178 int Target () { return Global; }
182 tooling::buildASTFromCodeWithArgs(Code
, {"-fsyntax-only", "-std=c++11"});
183 auto &Context
= Unit
->getASTContext();
185 ASSERT_EQ(Context
.getDiagnostics().getClient()->getNumErrors(), 0U);
188 match(decl(anyOf(varDecl(hasName("Global")).bind("global"),
189 functionDecl(hasName("Target")).bind("target"))),
191 const auto *Fun
= selectFirst
<FunctionDecl
>("target", Results
);
192 const auto *Var
= selectFirst
<VarDecl
>("global", Results
);
193 ASSERT_THAT(Fun
, NotNull());
194 ASSERT_THAT(Var
, NotNull());
196 // Verify the global variable is populated when we analyze `Target`.
197 Environment
Env(DAContext
, *Fun
);
198 EXPECT_THAT(Env
.getValue(*Var
), NotNull());
201 // Tests that fields mentioned only in default member initializers are included
202 // in the set of tracked fields.
203 TEST_F(EnvironmentTest
, IncludeFieldsFromDefaultInitializers
) {
204 using namespace ast_matchers
;
206 std::string Code
= R
"cc(
216 tooling::buildASTFromCodeWithArgs(Code
, {"-fsyntax-only", "-std=c++11"});
217 auto &Context
= Unit
->getASTContext();
219 ASSERT_EQ(Context
.getDiagnostics().getClient()->getNumErrors(), 0U);
221 auto Results
= match(
222 qualType(hasDeclaration(
223 cxxRecordDecl(hasName("S"),
224 hasMethod(cxxConstructorDecl().bind("target")))
228 const auto *Constructor
= selectFirst
<FunctionDecl
>("target", Results
);
229 const auto *Rec
= selectFirst
<RecordDecl
>("struct", Results
);
230 const auto QTy
= *selectFirst
<QualType
>("ty", Results
);
231 ASSERT_THAT(Constructor
, NotNull());
232 ASSERT_THAT(Rec
, NotNull());
233 ASSERT_FALSE(QTy
.isNull());
235 auto Fields
= Rec
->fields();
236 FieldDecl
*XDecl
= nullptr;
237 for (FieldDecl
*Field
: Fields
) {
238 if (Field
->getNameAsString() == "X") {
243 ASSERT_THAT(XDecl
, NotNull());
245 // Verify that the `X` field of `S` is populated when analyzing the
246 // constructor, even though it is not referenced directly in the constructor.
247 Environment
Env(DAContext
, *Constructor
);
248 auto &Loc
= cast
<RecordStorageLocation
>(Env
.createObject(QTy
));
249 EXPECT_THAT(getFieldValue(&Loc
, *XDecl
, Env
), NotNull());
252 TEST_F(EnvironmentTest
, InitGlobalVarsFieldFun
) {
253 using namespace ast_matchers
;
255 std::string Code
= R
"cc(
256 struct S { int Bar; };
258 int Target () { return Global.Bar; }
262 tooling::buildASTFromCodeWithArgs(Code
, {"-fsyntax-only", "-std=c++11"});
263 auto &Context
= Unit
->getASTContext();
265 ASSERT_EQ(Context
.getDiagnostics().getClient()->getNumErrors(), 0U);
268 match(decl(anyOf(varDecl(hasName("Global")).bind("global"),
269 functionDecl(hasName("Target")).bind("target"))),
271 const auto *Fun
= selectFirst
<FunctionDecl
>("target", Results
);
272 const auto *GlobalDecl
= selectFirst
<VarDecl
>("global", Results
);
273 ASSERT_THAT(Fun
, NotNull());
274 ASSERT_THAT(GlobalDecl
, NotNull());
276 ASSERT_TRUE(GlobalDecl
->getType()->isStructureType());
277 auto GlobalFields
= GlobalDecl
->getType()->getAsRecordDecl()->fields();
279 FieldDecl
*BarDecl
= nullptr;
280 for (FieldDecl
*Field
: GlobalFields
) {
281 if (Field
->getNameAsString() == "Bar") {
285 FAIL() << "Unexpected field: " << Field
->getNameAsString();
287 ASSERT_THAT(BarDecl
, NotNull());
289 // Verify the global variable is populated when we analyze `Target`.
290 Environment
Env(DAContext
, *Fun
);
291 const auto *GlobalLoc
=
292 cast
<RecordStorageLocation
>(Env
.getStorageLocation(*GlobalDecl
));
293 auto *BarVal
= getFieldValue(GlobalLoc
, *BarDecl
, Env
);
294 EXPECT_TRUE(isa
<IntegerValue
>(BarVal
));
297 TEST_F(EnvironmentTest
, InitGlobalVarsConstructor
) {
298 using namespace ast_matchers
;
300 std::string Code
= R
"cc(
303 Target() : Field(Global) {}
309 tooling::buildASTFromCodeWithArgs(Code
, {"-fsyntax-only", "-std=c++11"});
310 auto &Context
= Unit
->getASTContext();
312 ASSERT_EQ(Context
.getDiagnostics().getClient()->getNumErrors(), 0U);
316 varDecl(hasName("Global")).bind("global"),
317 cxxConstructorDecl(ofClass(hasName("Target"))).bind("target"))),
319 const auto *Ctor
= selectFirst
<CXXConstructorDecl
>("target", Results
);
320 const auto *Var
= selectFirst
<VarDecl
>("global", Results
);
321 ASSERT_TRUE(Ctor
!= nullptr);
322 ASSERT_THAT(Var
, NotNull());
324 // Verify the global variable is populated when we analyze `Target`.
325 Environment
Env(DAContext
, *Ctor
);
326 EXPECT_THAT(Env
.getValue(*Var
), NotNull());
329 TEST_F(EnvironmentTest
, RefreshRecordValue
) {
330 using namespace ast_matchers
;
332 std::string Code
= R
"cc(
341 tooling::buildASTFromCodeWithArgs(Code
, {"-fsyntax-only", "-std=c++11"});
342 auto &Context
= Unit
->getASTContext();
344 ASSERT_EQ(Context
.getDiagnostics().getClient()->getNumErrors(), 0U);
346 auto Results
= match(functionDecl(hasName("target")).bind("target"), Context
);
347 const auto *Target
= selectFirst
<FunctionDecl
>("target", Results
);
348 ASSERT_THAT(Target
, NotNull());
350 Results
= match(declRefExpr(to(varDecl(hasName("s")))).bind("s"), Context
);
351 const auto *DRE
= selectFirst
<DeclRefExpr
>("s", Results
);
352 ASSERT_THAT(DRE
, NotNull());
354 Environment
Env(DAContext
, *Target
);
355 EXPECT_THAT(Env
.getStorageLocation(*DRE
), IsNull());
356 refreshRecordValue(*DRE
, Env
);
357 EXPECT_THAT(Env
.getStorageLocation(*DRE
), NotNull());