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/AST/ExprCXX.h"
13 #include "clang/AST/Stmt.h"
14 #include "clang/ASTMatchers/ASTMatchFinder.h"
15 #include "clang/ASTMatchers/ASTMatchers.h"
16 #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
17 #include "clang/Analysis/FlowSensitive/StorageLocation.h"
18 #include "clang/Analysis/FlowSensitive/Value.h"
19 #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
20 #include "clang/Tooling/Tooling.h"
21 #include "gmock/gmock.h"
22 #include "gtest/gtest.h"
28 using namespace clang
;
29 using namespace dataflow
;
30 using ::clang::dataflow::test::findValueDecl
;
31 using ::clang::dataflow::test::getFieldValue
;
32 using ::testing::Contains
;
33 using ::testing::IsNull
;
34 using ::testing::NotNull
;
36 class EnvironmentTest
: public ::testing::Test
{
38 EnvironmentTest() : DAContext(std::make_unique
<WatchedLiteralsSolver
>()) {}
40 DataflowAnalysisContext DAContext
;
43 TEST_F(EnvironmentTest
, FlowCondition
) {
44 Environment
Env(DAContext
);
45 auto &A
= Env
.arena();
47 EXPECT_TRUE(Env
.proves(A
.makeLiteral(true)));
48 EXPECT_TRUE(Env
.allows(A
.makeLiteral(true)));
49 EXPECT_FALSE(Env
.proves(A
.makeLiteral(false)));
50 EXPECT_FALSE(Env
.allows(A
.makeLiteral(false)));
52 auto &X
= A
.makeAtomRef(A
.makeAtom());
53 EXPECT_FALSE(Env
.proves(X
));
54 EXPECT_TRUE(Env
.allows(X
));
57 EXPECT_TRUE(Env
.proves(X
));
58 EXPECT_TRUE(Env
.allows(X
));
60 auto &NotX
= A
.makeNot(X
);
61 EXPECT_FALSE(Env
.proves(NotX
));
62 EXPECT_FALSE(Env
.allows(NotX
));
65 TEST_F(EnvironmentTest
, SetAndGetValueOnCfgOmittedNodes
) {
66 // Check that we can set a value on an expression that is omitted from the CFG
67 // (see `ignoreCFGOmittedNodes()`), then retrieve that same value from the
68 // expression. This is a regression test; `setValue()` and `getValue()`
69 // previously did not use `ignoreCFGOmittedNodes()` consistently.
71 using namespace ast_matchers
;
73 std::string Code
= R
"cc(
78 // Method call on a temporary produces an `ExprWithCleanups`.
85 tooling::buildASTFromCodeWithArgs(Code
, {"-fsyntax-only", "-std=c++17"});
86 auto &Context
= Unit
->getASTContext();
88 ASSERT_EQ(Context
.getDiagnostics().getClient()->getNumErrors(), 0U);
90 const ExprWithCleanups
*WithCleanups
= selectFirst
<ExprWithCleanups
>(
92 match(exprWithCleanups(hasType(isInteger())).bind("cleanups"), Context
));
93 ASSERT_NE(WithCleanups
, nullptr);
95 const ParenExpr
*Paren
= selectFirst
<ParenExpr
>(
96 "paren", match(parenExpr(hasType(isInteger())).bind("paren"), Context
));
97 ASSERT_NE(Paren
, nullptr);
99 Environment
Env(DAContext
);
101 cast
<IntegerValue
>(Env
.createValue(Unit
->getASTContext().IntTy
));
102 Env
.setValue(*WithCleanups
, *Val1
);
103 EXPECT_EQ(Env
.getValue(*WithCleanups
), Val1
);
106 cast
<IntegerValue
>(Env
.createValue(Unit
->getASTContext().IntTy
));
107 Env
.setValue(*Paren
, *Val2
);
108 EXPECT_EQ(Env
.getValue(*Paren
), Val2
);
111 TEST_F(EnvironmentTest
, CreateValueRecursiveType
) {
112 using namespace ast_matchers
;
114 std::string Code
= R
"cc(
119 // Use both fields to force them to be created with `createValue`.
120 void Usage(Recursive R) { (void)R.X; (void)R.R; }
124 tooling::buildASTFromCodeWithArgs(Code
, {"-fsyntax-only", "-std=c++11"});
125 auto &Context
= Unit
->getASTContext();
127 ASSERT_EQ(Context
.getDiagnostics().getClient()->getNumErrors(), 0U);
130 match(qualType(hasDeclaration(recordDecl(
131 hasName("Recursive"),
132 has(fieldDecl(hasName("R")).bind("field-r")))))
135 const QualType
*TyPtr
= selectFirst
<QualType
>("target", Results
);
136 ASSERT_THAT(TyPtr
, NotNull());
137 QualType Ty
= *TyPtr
;
138 ASSERT_FALSE(Ty
.isNull());
140 const FieldDecl
*R
= selectFirst
<FieldDecl
>("field-r", Results
);
141 ASSERT_THAT(R
, NotNull());
143 Results
= match(functionDecl(hasName("Usage")).bind("fun"), Context
);
144 const auto *Fun
= selectFirst
<FunctionDecl
>("fun", Results
);
145 ASSERT_THAT(Fun
, NotNull());
147 // Verify that the struct and the field (`R`) with first appearance of the
148 // type is created successfully.
149 Environment
Env(DAContext
, *Fun
);
151 auto &SLoc
= cast
<RecordStorageLocation
>(Env
.createObject(Ty
));
152 PointerValue
*PV
= cast_or_null
<PointerValue
>(getFieldValue(&SLoc
, *R
, Env
));
153 EXPECT_THAT(PV
, NotNull());
156 TEST_F(EnvironmentTest
, DifferentReferenceLocInJoin
) {
157 // This tests the case where the storage location for a reference-type
158 // variable is different for two states being joined. We used to believe this
159 // could not happen and therefore had an assertion disallowing this; this test
160 // exists to demonstrate that we can handle this condition without a failing
161 // assertion. See also the discussion here:
162 // https://discourse.llvm.org/t/70086/6
164 using namespace ast_matchers
;
166 std::string Code
= R
"cc(
171 tooling::buildASTFromCodeWithArgs(Code
, {"-fsyntax-only", "-std=c++11"});
172 auto &Context
= Unit
->getASTContext();
174 ASSERT_EQ(Context
.getDiagnostics().getClient()->getNumErrors(), 0U);
176 const ValueDecl
*Ref
= findValueDecl(Context
, "ref");
178 Environment
Env1(DAContext
);
179 StorageLocation
&Loc1
= Env1
.createStorageLocation(Context
.IntTy
);
180 Env1
.setStorageLocation(*Ref
, Loc1
);
182 Environment
Env2(DAContext
);
183 StorageLocation
&Loc2
= Env2
.createStorageLocation(Context
.IntTy
);
184 Env2
.setStorageLocation(*Ref
, Loc2
);
186 EXPECT_NE(&Loc1
, &Loc2
);
188 Environment::ValueModel Model
;
189 Environment EnvJoined
=
190 Environment::join(Env1
, Env2
, Model
, Environment::DiscardExprState
);
192 // Joining environments with different storage locations for the same
193 // declaration results in the declaration being removed from the joined
195 EXPECT_EQ(EnvJoined
.getStorageLocation(*Ref
), nullptr);
198 TEST_F(EnvironmentTest
, InitGlobalVarsFun
) {
199 using namespace ast_matchers
;
201 std::string Code
= R
"cc(
203 int Target () { return Global; }
207 tooling::buildASTFromCodeWithArgs(Code
, {"-fsyntax-only", "-std=c++11"});
208 auto &Context
= Unit
->getASTContext();
210 ASSERT_EQ(Context
.getDiagnostics().getClient()->getNumErrors(), 0U);
213 match(decl(anyOf(varDecl(hasName("Global")).bind("global"),
214 functionDecl(hasName("Target")).bind("target"))),
216 const auto *Fun
= selectFirst
<FunctionDecl
>("target", Results
);
217 const auto *Var
= selectFirst
<VarDecl
>("global", Results
);
218 ASSERT_THAT(Fun
, NotNull());
219 ASSERT_THAT(Var
, NotNull());
221 // Verify the global variable is populated when we analyze `Target`.
222 Environment
Env(DAContext
, *Fun
);
224 EXPECT_THAT(Env
.getValue(*Var
), NotNull());
227 // Tests that fields mentioned only in default member initializers are included
228 // in the set of tracked fields.
229 TEST_F(EnvironmentTest
, IncludeFieldsFromDefaultInitializers
) {
230 using namespace ast_matchers
;
232 std::string Code
= R
"cc(
242 tooling::buildASTFromCodeWithArgs(Code
, {"-fsyntax-only", "-std=c++11"});
243 auto &Context
= Unit
->getASTContext();
245 ASSERT_EQ(Context
.getDiagnostics().getClient()->getNumErrors(), 0U);
247 auto Results
= match(
248 qualType(hasDeclaration(
249 cxxRecordDecl(hasName("S"),
250 hasMethod(cxxConstructorDecl().bind("target")))
254 const auto *Constructor
= selectFirst
<FunctionDecl
>("target", Results
);
255 const auto *Rec
= selectFirst
<RecordDecl
>("struct", Results
);
256 const auto QTy
= *selectFirst
<QualType
>("ty", Results
);
257 ASSERT_THAT(Constructor
, NotNull());
258 ASSERT_THAT(Rec
, NotNull());
259 ASSERT_FALSE(QTy
.isNull());
261 auto Fields
= Rec
->fields();
262 FieldDecl
*XDecl
= nullptr;
263 for (FieldDecl
*Field
: Fields
) {
264 if (Field
->getNameAsString() == "X") {
269 ASSERT_THAT(XDecl
, NotNull());
271 // Verify that the `X` field of `S` is populated when analyzing the
272 // constructor, even though it is not referenced directly in the constructor.
273 Environment
Env(DAContext
, *Constructor
);
275 auto &Loc
= cast
<RecordStorageLocation
>(Env
.createObject(QTy
));
276 EXPECT_THAT(getFieldValue(&Loc
, *XDecl
, Env
), NotNull());
279 TEST_F(EnvironmentTest
, InitGlobalVarsFieldFun
) {
280 using namespace ast_matchers
;
282 std::string Code
= R
"cc(
283 struct S { int Bar; };
285 int Target () { return Global.Bar; }
289 tooling::buildASTFromCodeWithArgs(Code
, {"-fsyntax-only", "-std=c++11"});
290 auto &Context
= Unit
->getASTContext();
292 ASSERT_EQ(Context
.getDiagnostics().getClient()->getNumErrors(), 0U);
295 match(decl(anyOf(varDecl(hasName("Global")).bind("global"),
296 functionDecl(hasName("Target")).bind("target"))),
298 const auto *Fun
= selectFirst
<FunctionDecl
>("target", Results
);
299 const auto *GlobalDecl
= selectFirst
<VarDecl
>("global", Results
);
300 ASSERT_THAT(Fun
, NotNull());
301 ASSERT_THAT(GlobalDecl
, NotNull());
303 ASSERT_TRUE(GlobalDecl
->getType()->isStructureType());
304 auto GlobalFields
= GlobalDecl
->getType()->getAsRecordDecl()->fields();
306 FieldDecl
*BarDecl
= nullptr;
307 for (FieldDecl
*Field
: GlobalFields
) {
308 if (Field
->getNameAsString() == "Bar") {
312 FAIL() << "Unexpected field: " << Field
->getNameAsString();
314 ASSERT_THAT(BarDecl
, NotNull());
316 // Verify the global variable is populated when we analyze `Target`.
317 Environment
Env(DAContext
, *Fun
);
319 const auto *GlobalLoc
=
320 cast
<RecordStorageLocation
>(Env
.getStorageLocation(*GlobalDecl
));
321 auto *BarVal
= getFieldValue(GlobalLoc
, *BarDecl
, Env
);
322 EXPECT_TRUE(isa
<IntegerValue
>(BarVal
));
325 TEST_F(EnvironmentTest
, InitGlobalVarsConstructor
) {
326 using namespace ast_matchers
;
328 std::string Code
= R
"cc(
331 Target() : Field(Global) {}
337 tooling::buildASTFromCodeWithArgs(Code
, {"-fsyntax-only", "-std=c++11"});
338 auto &Context
= Unit
->getASTContext();
340 ASSERT_EQ(Context
.getDiagnostics().getClient()->getNumErrors(), 0U);
344 varDecl(hasName("Global")).bind("global"),
345 cxxConstructorDecl(ofClass(hasName("Target"))).bind("target"))),
347 const auto *Ctor
= selectFirst
<CXXConstructorDecl
>("target", Results
);
348 const auto *Var
= selectFirst
<VarDecl
>("global", Results
);
349 ASSERT_TRUE(Ctor
!= nullptr);
350 ASSERT_THAT(Var
, NotNull());
352 // Verify the global variable is populated when we analyze `Target`.
353 Environment
Env(DAContext
, *Ctor
);
355 EXPECT_THAT(Env
.getValue(*Var
), NotNull());
358 // Pointers to Members are a tricky case of accessor calls, complicated further
359 // when using templates where the pointer to the member is a template argument.
360 // This is a repro of a failure case seen in the wild.
361 TEST_F(EnvironmentTest
,
362 ModelMemberForAccessorUsingMethodPointerThroughTemplate
) {
363 using namespace ast_matchers
;
365 std::string Code
= R
"cc(
367 int accessor() {return member;}
372 template <auto method>
374 return (S->*method)();
377 // We want to analyze the instantiation of Target for the accessor.
378 int Instantiator () {S S; return Target<&S::accessor>(&S); }
382 // C++17 for the simplifying use of auto in the template declaration.
383 tooling::buildASTFromCodeWithArgs(Code
, {"-fsyntax-only", "-std=c++17"});
384 auto &Context
= Unit
->getASTContext();
386 ASSERT_EQ(Context
.getDiagnostics().getClient()->getNumErrors(), 0U);
388 auto Results
= match(
389 decl(anyOf(functionDecl(hasName("Target"), isTemplateInstantiation())
391 fieldDecl(hasName("member")).bind("member"),
392 recordDecl(hasName("S")).bind("struct"))),
394 const auto *Fun
= selectFirst
<FunctionDecl
>("target", Results
);
395 const auto *Struct
= selectFirst
<RecordDecl
>("struct", Results
);
396 const auto *Member
= selectFirst
<FieldDecl
>("member", Results
);
397 ASSERT_THAT(Fun
, NotNull());
398 ASSERT_THAT(Struct
, NotNull());
399 ASSERT_THAT(Member
, NotNull());
401 // Verify that `member` is modeled for `S` when we analyze
402 // `Target<&S::accessor>`.
403 Environment
Env(DAContext
, *Fun
);
405 EXPECT_THAT(DAContext
.getModeledFields(QualType(Struct
->getTypeForDecl(), 0)),
409 // This is a repro of a failure case seen in the wild.
410 TEST_F(EnvironmentTest
, CXXDefaultInitExprResultObjIsWrappedExprResultObj
) {
411 using namespace ast_matchers
;
413 std::string Code
= R
"cc(
424 tooling::buildASTFromCodeWithArgs(Code
, {"-fsyntax-only", "-std=c++11"});
425 auto &Context
= Unit
->getASTContext();
427 ASSERT_EQ(Context
.getDiagnostics().getClient()->getNumErrors(), 0U);
430 match(cxxConstructorDecl(
431 hasAnyConstructorInitializer(cxxCtorInitializer(
432 withInitializer(expr().bind("default_init_expr")))))
435 const auto *Constructor
= selectFirst
<CXXConstructorDecl
>("ctor", Results
);
436 const auto *DefaultInit
=
437 selectFirst
<CXXDefaultInitExpr
>("default_init_expr", Results
);
439 Environment
Env(DAContext
, *Constructor
);
441 EXPECT_EQ(&Env
.getResultObjectLocation(*DefaultInit
),
442 &Env
.getResultObjectLocation(*DefaultInit
->getExpr()));
445 // This test verifies the behavior of `getResultObjectLocation()` in
446 // scenarios involving inherited constructors.
447 // Since the specific AST node of interest `CXXConstructorDecl` is implicitly
448 // generated, we cannot annotate any statements inside of it as we do in tests
449 // within TransferTest. Thus, the only way to get the right `Environment` is by
450 // explicitly initializing it as we do in tests within EnvironmentTest.
451 // This is why this test is not inside TransferTest, where most of the tests for
452 // `getResultObjectLocation()` are located.
453 TEST_F(EnvironmentTest
, ResultObjectLocationForInheritedCtorInitExpr
) {
454 using namespace ast_matchers
;
456 std::string Code
= R
"(
460 struct Derived : Base {
464 Derived d = Derived(0);
468 tooling::buildASTFromCodeWithArgs(Code
, {"-fsyntax-only", "-std=c++20"});
469 auto &Context
= Unit
->getASTContext();
471 ASSERT_EQ(Context
.getDiagnostics().getClient()->getNumErrors(), 0U);
474 match(cxxConstructorDecl(
475 hasAnyConstructorInitializer(cxxCtorInitializer(
476 withInitializer(expr().bind("inherited_ctor_init_expr")))))
479 const auto *Constructor
= selectFirst
<CXXConstructorDecl
>("ctor", Results
);
480 const auto *InheritedCtorInit
= selectFirst
<CXXInheritedCtorInitExpr
>(
481 "inherited_ctor_init_expr", Results
);
483 EXPECT_EQ(InheritedCtorInit
->child_begin(), InheritedCtorInit
->child_end());
485 Environment
Env(DAContext
, *Constructor
);
488 RecordStorageLocation
&Loc
= Env
.getResultObjectLocation(*InheritedCtorInit
);
489 EXPECT_NE(&Loc
, nullptr);
491 EXPECT_EQ(&Loc
, Env
.getThisPointeeStorageLocation());
494 TEST_F(EnvironmentTest
, Stmt
) {
495 using namespace ast_matchers
;
497 std::string Code
= R
"cc(
504 tooling::buildASTFromCodeWithArgs(Code
, {"-fsyntax-only", "-std=c++11"});
505 auto &Context
= Unit
->getASTContext();
507 ASSERT_EQ(Context
.getDiagnostics().getClient()->getNumErrors(), 0U);
509 auto *DeclStatement
= const_cast<DeclStmt
*>(selectFirst
<DeclStmt
>(
510 "d", match(declStmt(hasSingleDecl(varDecl(hasName("AnS")))).bind("d"),
512 ASSERT_THAT(DeclStatement
, NotNull());
513 auto *Init
= (cast
<VarDecl
>(*DeclStatement
->decl_begin()))->getInit();
514 ASSERT_THAT(Init
, NotNull());
516 // Verify that we can retrieve the result object location for the initializer
517 // expression when we analyze the DeclStmt for `AnS`.
518 Environment
Env(DAContext
, *DeclStatement
);
519 // Don't crash when initializing.
521 // And don't crash when retrieving the result object location.
522 Env
.getResultObjectLocation(*Init
);
525 // This is a crash repro.
526 TEST_F(EnvironmentTest
, LambdaCapturingThisInFieldInitializer
) {
527 using namespace ast_matchers
;
528 std::string Code
= R
"cc(
530 int f{[this]() { return 1; }()};
535 tooling::buildASTFromCodeWithArgs(Code
, {"-fsyntax-only", "-std=c++11"});
536 auto &Context
= Unit
->getASTContext();
538 ASSERT_EQ(Context
.getDiagnostics().getClient()->getNumErrors(), 0U);
540 auto *LambdaCallOperator
= selectFirst
<CXXMethodDecl
>(
541 "method", match(cxxMethodDecl(hasName("operator()"),
542 ofClass(cxxRecordDecl(isLambda())))
546 Environment
Env(DAContext
, *LambdaCallOperator
);
547 // Don't crash when initializing.
549 // And initialize the captured `this` pointee.
550 ASSERT_NE(nullptr, Env
.getThisPointeeStorageLocation());