1 //===- unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.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 "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" 23 #include <memory> 24 #include <string> 25 26 namespace { 27 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; 35 36 class EnvironmentTest : public ::testing::Test { 37 protected: 38 EnvironmentTest() : DAContext(std::make_unique<WatchedLiteralsSolver>()) {} 39 40 DataflowAnalysisContext DAContext; 41 }; 42 43 TEST_F(EnvironmentTest, FlowCondition) { 44 Environment Env(DAContext); 45 auto &A = Env.arena(); 46 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))); 51 52 auto &X = A.makeAtomRef(A.makeAtom()); 53 EXPECT_FALSE(Env.proves(X)); 54 EXPECT_TRUE(Env.allows(X)); 55 56 Env.assume(X); 57 EXPECT_TRUE(Env.proves(X)); 58 EXPECT_TRUE(Env.allows(X)); 59 60 auto &NotX = A.makeNot(X); 61 EXPECT_FALSE(Env.proves(NotX)); 62 EXPECT_FALSE(Env.allows(NotX)); 63 } 64 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. 70 71 using namespace ast_matchers; 72 73 std::string Code = R"cc( 74 struct S { 75 int f(); 76 }; 77 void target() { 78 // Method call on a temporary produces an `ExprWithCleanups`. 79 S().f(); 80 (1); 81 } 82 )cc"; 83 84 auto Unit = 85 tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"}); 86 auto &Context = Unit->getASTContext(); 87 88 ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U); 89 90 const ExprWithCleanups *WithCleanups = selectFirst<ExprWithCleanups>( 91 "cleanups", 92 match(exprWithCleanups(hasType(isInteger())).bind("cleanups"), Context)); 93 ASSERT_NE(WithCleanups, nullptr); 94 95 const ParenExpr *Paren = selectFirst<ParenExpr>( 96 "paren", match(parenExpr(hasType(isInteger())).bind("paren"), Context)); 97 ASSERT_NE(Paren, nullptr); 98 99 Environment Env(DAContext); 100 IntegerValue *Val1 = 101 cast<IntegerValue>(Env.createValue(Unit->getASTContext().IntTy)); 102 Env.setValue(*WithCleanups, *Val1); 103 EXPECT_EQ(Env.getValue(*WithCleanups), Val1); 104 105 IntegerValue *Val2 = 106 cast<IntegerValue>(Env.createValue(Unit->getASTContext().IntTy)); 107 Env.setValue(*Paren, *Val2); 108 EXPECT_EQ(Env.getValue(*Paren), Val2); 109 } 110 111 TEST_F(EnvironmentTest, CreateValueRecursiveType) { 112 using namespace ast_matchers; 113 114 std::string Code = R"cc( 115 struct Recursive { 116 bool X; 117 Recursive *R; 118 }; 119 // Use both fields to force them to be created with `createValue`. 120 void Usage(Recursive R) { (void)R.X; (void)R.R; } 121 )cc"; 122 123 auto Unit = 124 tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"}); 125 auto &Context = Unit->getASTContext(); 126 127 ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U); 128 129 auto Results = 130 match(qualType(hasDeclaration(recordDecl( 131 hasName("Recursive"), 132 has(fieldDecl(hasName("R")).bind("field-r"))))) 133 .bind("target"), 134 Context); 135 const QualType *TyPtr = selectFirst<QualType>("target", Results); 136 ASSERT_THAT(TyPtr, NotNull()); 137 QualType Ty = *TyPtr; 138 ASSERT_FALSE(Ty.isNull()); 139 140 const FieldDecl *R = selectFirst<FieldDecl>("field-r", Results); 141 ASSERT_THAT(R, NotNull()); 142 143 Results = match(functionDecl(hasName("Usage")).bind("fun"), Context); 144 const auto *Fun = selectFirst<FunctionDecl>("fun", Results); 145 ASSERT_THAT(Fun, NotNull()); 146 147 // Verify that the struct and the field (`R`) with first appearance of the 148 // type is created successfully. 149 Environment Env(DAContext, *Fun); 150 Env.initialize(); 151 auto &SLoc = cast<RecordStorageLocation>(Env.createObject(Ty)); 152 PointerValue *PV = cast_or_null<PointerValue>(getFieldValue(&SLoc, *R, Env)); 153 EXPECT_THAT(PV, NotNull()); 154 } 155 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 163 164 using namespace ast_matchers; 165 166 std::string Code = R"cc( 167 void f(int &ref) {} 168 )cc"; 169 170 auto Unit = 171 tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"}); 172 auto &Context = Unit->getASTContext(); 173 174 ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U); 175 176 const ValueDecl *Ref = findValueDecl(Context, "ref"); 177 178 Environment Env1(DAContext); 179 StorageLocation &Loc1 = Env1.createStorageLocation(Context.IntTy); 180 Env1.setStorageLocation(*Ref, Loc1); 181 182 Environment Env2(DAContext); 183 StorageLocation &Loc2 = Env2.createStorageLocation(Context.IntTy); 184 Env2.setStorageLocation(*Ref, Loc2); 185 186 EXPECT_NE(&Loc1, &Loc2); 187 188 Environment::ValueModel Model; 189 Environment EnvJoined = 190 Environment::join(Env1, Env2, Model, Environment::DiscardExprState); 191 192 // Joining environments with different storage locations for the same 193 // declaration results in the declaration being removed from the joined 194 // environment. 195 EXPECT_EQ(EnvJoined.getStorageLocation(*Ref), nullptr); 196 } 197 198 TEST_F(EnvironmentTest, InitGlobalVarsFun) { 199 using namespace ast_matchers; 200 201 std::string Code = R"cc( 202 int Global = 0; 203 int Target () { return Global; } 204 )cc"; 205 206 auto Unit = 207 tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"}); 208 auto &Context = Unit->getASTContext(); 209 210 ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U); 211 212 auto Results = 213 match(decl(anyOf(varDecl(hasName("Global")).bind("global"), 214 functionDecl(hasName("Target")).bind("target"))), 215 Context); 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()); 220 221 // Verify the global variable is populated when we analyze `Target`. 222 Environment Env(DAContext, *Fun); 223 Env.initialize(); 224 EXPECT_THAT(Env.getValue(*Var), NotNull()); 225 } 226 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; 231 232 std::string Code = R"cc( 233 struct S { 234 S() {} 235 int X = 3; 236 int Y = X; 237 }; 238 S foo(); 239 )cc"; 240 241 auto Unit = 242 tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"}); 243 auto &Context = Unit->getASTContext(); 244 245 ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U); 246 247 auto Results = match( 248 qualType(hasDeclaration( 249 cxxRecordDecl(hasName("S"), 250 hasMethod(cxxConstructorDecl().bind("target"))) 251 .bind("struct"))) 252 .bind("ty"), 253 Context); 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()); 260 261 auto Fields = Rec->fields(); 262 FieldDecl *XDecl = nullptr; 263 for (FieldDecl *Field : Fields) { 264 if (Field->getNameAsString() == "X") { 265 XDecl = Field; 266 break; 267 } 268 } 269 ASSERT_THAT(XDecl, NotNull()); 270 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); 274 Env.initialize(); 275 auto &Loc = cast<RecordStorageLocation>(Env.createObject(QTy)); 276 EXPECT_THAT(getFieldValue(&Loc, *XDecl, Env), NotNull()); 277 } 278 279 TEST_F(EnvironmentTest, InitGlobalVarsFieldFun) { 280 using namespace ast_matchers; 281 282 std::string Code = R"cc( 283 struct S { int Bar; }; 284 S Global = {0}; 285 int Target () { return Global.Bar; } 286 )cc"; 287 288 auto Unit = 289 tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"}); 290 auto &Context = Unit->getASTContext(); 291 292 ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U); 293 294 auto Results = 295 match(decl(anyOf(varDecl(hasName("Global")).bind("global"), 296 functionDecl(hasName("Target")).bind("target"))), 297 Context); 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()); 302 303 ASSERT_TRUE(GlobalDecl->getType()->isStructureType()); 304 auto GlobalFields = GlobalDecl->getType()->getAsRecordDecl()->fields(); 305 306 FieldDecl *BarDecl = nullptr; 307 for (FieldDecl *Field : GlobalFields) { 308 if (Field->getNameAsString() == "Bar") { 309 BarDecl = Field; 310 break; 311 } 312 FAIL() << "Unexpected field: " << Field->getNameAsString(); 313 } 314 ASSERT_THAT(BarDecl, NotNull()); 315 316 // Verify the global variable is populated when we analyze `Target`. 317 Environment Env(DAContext, *Fun); 318 Env.initialize(); 319 const auto *GlobalLoc = 320 cast<RecordStorageLocation>(Env.getStorageLocation(*GlobalDecl)); 321 auto *BarVal = getFieldValue(GlobalLoc, *BarDecl, Env); 322 EXPECT_TRUE(isa<IntegerValue>(BarVal)); 323 } 324 325 TEST_F(EnvironmentTest, InitGlobalVarsConstructor) { 326 using namespace ast_matchers; 327 328 std::string Code = R"cc( 329 int Global = 0; 330 struct Target { 331 Target() : Field(Global) {} 332 int Field; 333 }; 334 )cc"; 335 336 auto Unit = 337 tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"}); 338 auto &Context = Unit->getASTContext(); 339 340 ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U); 341 342 auto Results = 343 match(decl(anyOf( 344 varDecl(hasName("Global")).bind("global"), 345 cxxConstructorDecl(ofClass(hasName("Target"))).bind("target"))), 346 Context); 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()); 351 352 // Verify the global variable is populated when we analyze `Target`. 353 Environment Env(DAContext, *Ctor); 354 Env.initialize(); 355 EXPECT_THAT(Env.getValue(*Var), NotNull()); 356 } 357 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; 364 365 std::string Code = R"cc( 366 struct S { 367 int accessor() {return member;} 368 369 int member = 0; 370 }; 371 372 template <auto method> 373 int Target(S* S) { 374 return (S->*method)(); 375 } 376 377 // We want to analyze the instantiation of Target for the accessor. 378 int Instantiator () {S S; return Target<&S::accessor>(&S); } 379 )cc"; 380 381 auto Unit = 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(); 385 386 ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U); 387 388 auto Results = match( 389 decl(anyOf(functionDecl(hasName("Target"), isTemplateInstantiation()) 390 .bind("target"), 391 fieldDecl(hasName("member")).bind("member"), 392 recordDecl(hasName("S")).bind("struct"))), 393 Context); 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()); 400 401 // Verify that `member` is modeled for `S` when we analyze 402 // `Target<&S::accessor>`. 403 Environment Env(DAContext, *Fun); 404 Env.initialize(); 405 EXPECT_THAT(DAContext.getModeledFields(QualType(Struct->getTypeForDecl(), 0)), 406 Contains(Member)); 407 } 408 409 // This is a repro of a failure case seen in the wild. 410 TEST_F(EnvironmentTest, CXXDefaultInitExprResultObjIsWrappedExprResultObj) { 411 using namespace ast_matchers; 412 413 std::string Code = R"cc( 414 struct Inner {}; 415 416 struct S { 417 S() {} 418 419 Inner i = {}; 420 }; 421 )cc"; 422 423 auto Unit = 424 tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"}); 425 auto &Context = Unit->getASTContext(); 426 427 ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U); 428 429 auto Results = 430 match(cxxConstructorDecl( 431 hasAnyConstructorInitializer(cxxCtorInitializer( 432 withInitializer(expr().bind("default_init_expr"))))) 433 .bind("ctor"), 434 Context); 435 const auto *Constructor = selectFirst<CXXConstructorDecl>("ctor", Results); 436 const auto *DefaultInit = 437 selectFirst<CXXDefaultInitExpr>("default_init_expr", Results); 438 439 Environment Env(DAContext, *Constructor); 440 Env.initialize(); 441 EXPECT_EQ(&Env.getResultObjectLocation(*DefaultInit), 442 &Env.getResultObjectLocation(*DefaultInit->getExpr())); 443 } 444 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; 455 456 std::string Code = R"( 457 struct Base { 458 Base(int b) {} 459 }; 460 struct Derived : Base { 461 using Base::Base; 462 }; 463 464 Derived d = Derived(0); 465 )"; 466 467 auto Unit = 468 tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++20"}); 469 auto &Context = Unit->getASTContext(); 470 471 ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U); 472 473 auto Results = 474 match(cxxConstructorDecl( 475 hasAnyConstructorInitializer(cxxCtorInitializer( 476 withInitializer(expr().bind("inherited_ctor_init_expr"))))) 477 .bind("ctor"), 478 Context); 479 const auto *Constructor = selectFirst<CXXConstructorDecl>("ctor", Results); 480 const auto *InheritedCtorInit = selectFirst<CXXInheritedCtorInitExpr>( 481 "inherited_ctor_init_expr", Results); 482 483 EXPECT_EQ(InheritedCtorInit->child_begin(), InheritedCtorInit->child_end()); 484 485 Environment Env(DAContext, *Constructor); 486 Env.initialize(); 487 488 RecordStorageLocation &Loc = Env.getResultObjectLocation(*InheritedCtorInit); 489 EXPECT_NE(&Loc, nullptr); 490 491 EXPECT_EQ(&Loc, Env.getThisPointeeStorageLocation()); 492 } 493 494 TEST_F(EnvironmentTest, Stmt) { 495 using namespace ast_matchers; 496 497 std::string Code = R"cc( 498 struct S { int i; }; 499 void foo() { 500 S AnS = S{1}; 501 } 502 )cc"; 503 auto Unit = 504 tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"}); 505 auto &Context = Unit->getASTContext(); 506 507 ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U); 508 509 auto *DeclStatement = const_cast<DeclStmt *>(selectFirst<DeclStmt>( 510 "d", match(declStmt(hasSingleDecl(varDecl(hasName("AnS")))).bind("d"), 511 Context))); 512 ASSERT_THAT(DeclStatement, NotNull()); 513 auto *Init = (cast<VarDecl>(*DeclStatement->decl_begin()))->getInit(); 514 ASSERT_THAT(Init, NotNull()); 515 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. 520 Env.initialize(); 521 // And don't crash when retrieving the result object location. 522 Env.getResultObjectLocation(*Init); 523 } 524 525 // This is a crash repro. 526 TEST_F(EnvironmentTest, LambdaCapturingThisInFieldInitializer) { 527 using namespace ast_matchers; 528 std::string Code = R"cc( 529 struct S { 530 int f{[this]() { return 1; }()}; 531 }; 532 )cc"; 533 534 auto Unit = 535 tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"}); 536 auto &Context = Unit->getASTContext(); 537 538 ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U); 539 540 auto *LambdaCallOperator = selectFirst<CXXMethodDecl>( 541 "method", match(cxxMethodDecl(hasName("operator()"), 542 ofClass(cxxRecordDecl(isLambda()))) 543 .bind("method"), 544 Context)); 545 546 Environment Env(DAContext, *LambdaCallOperator); 547 // Don't crash when initializing. 548 Env.initialize(); 549 // And initialize the captured `this` pointee. 550 ASSERT_NE(nullptr, Env.getThisPointeeStorageLocation()); 551 } 552 553 } // namespace 554