1 //===- unittests/Analysis/FlowSensitive/TransferTest.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 "NoopAnalysis.h" 10 #include "TestingSupport.h" 11 #include "clang/AST/ASTContext.h" 12 #include "clang/AST/Decl.h" 13 #include "clang/ASTMatchers/ASTMatchFinder.h" 14 #include "clang/ASTMatchers/ASTMatchers.h" 15 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" 16 #include "clang/Analysis/FlowSensitive/StorageLocation.h" 17 #include "clang/Analysis/FlowSensitive/Value.h" 18 #include "llvm/ADT/ArrayRef.h" 19 #include "llvm/ADT/StringRef.h" 20 #include "llvm/Support/Casting.h" 21 #include "gmock/gmock.h" 22 #include "gtest/gtest.h" 23 #include <cassert> 24 #include <string> 25 #include <utility> 26 27 namespace { 28 29 using namespace clang; 30 using namespace dataflow; 31 using ::testing::_; 32 using ::testing::ElementsAre; 33 using ::testing::IsNull; 34 using ::testing::NotNull; 35 using ::testing::Pair; 36 37 class TransferTest : public ::testing::Test { 38 protected: 39 template <typename Matcher> 40 void runDataflow(llvm::StringRef Code, Matcher Match) { 41 test::checkDataflow<NoopAnalysis>( 42 Code, "target", 43 [](ASTContext &C, Environment &) { return NoopAnalysis(C); }, 44 [&Match](llvm::ArrayRef< 45 std::pair<std::string, DataflowAnalysisState<NoopLattice>>> 46 Results, 47 ASTContext &ASTCtx) { Match(Results, ASTCtx); }, 48 {"-fsyntax-only", "-std=c++17"}); 49 } 50 }; 51 52 /// Returns the `ValueDecl` for the given identifier. 53 /// 54 /// Requirements: 55 /// 56 /// `Name` must be unique in `ASTCtx`. 57 static const ValueDecl *findValueDecl(ASTContext &ASTCtx, 58 llvm::StringRef Name) { 59 auto TargetNodes = ast_matchers::match( 60 ast_matchers::valueDecl(ast_matchers::hasName(Name)).bind("v"), ASTCtx); 61 assert(TargetNodes.size() == 1 && "Name must be unique"); 62 auto *const Result = ast_matchers::selectFirst<ValueDecl>("v", TargetNodes); 63 assert(Result != nullptr); 64 return Result; 65 } 66 67 TEST_F(TransferTest, IntVarDecl) { 68 std::string Code = R"( 69 void target() { 70 int foo; 71 // [[p]] 72 } 73 )"; 74 runDataflow( 75 Code, [](llvm::ArrayRef< 76 std::pair<std::string, DataflowAnalysisState<NoopLattice>>> 77 Results, 78 ASTContext &ASTCtx) { 79 ASSERT_THAT(Results, ElementsAre(Pair("p", _))); 80 const Environment &Env = Results[0].second.Env; 81 82 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo"); 83 ASSERT_THAT(FooDecl, NotNull()); 84 85 const StorageLocation *FooLoc = Env.getStorageLocation(*FooDecl); 86 ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc)); 87 88 const Value *FooVal = Env.getValue(*FooLoc); 89 ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal)); 90 }); 91 } 92 93 TEST_F(TransferTest, StructVarDecl) { 94 std::string Code = R"( 95 struct Foo { 96 int Bar; 97 }; 98 99 void target() { 100 Foo foo; 101 // [[p]] 102 } 103 )"; 104 runDataflow( 105 Code, [](llvm::ArrayRef< 106 std::pair<std::string, DataflowAnalysisState<NoopLattice>>> 107 Results, 108 ASTContext &ASTCtx) { 109 ASSERT_THAT(Results, ElementsAre(Pair("p", _))); 110 const Environment &Env = Results[0].second.Env; 111 112 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo"); 113 ASSERT_THAT(FooDecl, NotNull()); 114 115 ASSERT_TRUE(FooDecl->getType()->isStructureType()); 116 auto FooFields = FooDecl->getType()->getAsRecordDecl()->fields(); 117 118 FieldDecl *BarDecl = nullptr; 119 for (FieldDecl *Field : FooFields) { 120 if (Field->getNameAsString() == "Bar") { 121 BarDecl = Field; 122 } else { 123 FAIL() << "Unexpected field: " << Field->getNameAsString(); 124 } 125 } 126 ASSERT_THAT(BarDecl, NotNull()); 127 128 const auto *FooLoc = 129 cast<AggregateStorageLocation>(Env.getStorageLocation(*FooDecl)); 130 const auto *BarLoc = 131 cast<ScalarStorageLocation>(&FooLoc->getChild(*BarDecl)); 132 133 const auto *FooVal = cast<StructValue>(Env.getValue(*FooLoc)); 134 const auto *BarVal = cast<IntegerValue>(&FooVal->getChild(*BarDecl)); 135 ASSERT_EQ(Env.getValue(*BarLoc), BarVal); 136 }); 137 } 138 139 TEST_F(TransferTest, ClassVarDecl) { 140 std::string Code = R"( 141 class Foo { 142 int Bar; 143 }; 144 145 void target() { 146 Foo foo; 147 // [[p]] 148 } 149 )"; 150 runDataflow( 151 Code, [](llvm::ArrayRef< 152 std::pair<std::string, DataflowAnalysisState<NoopLattice>>> 153 Results, 154 ASTContext &ASTCtx) { 155 ASSERT_THAT(Results, ElementsAre(Pair("p", _))); 156 const Environment &Env = Results[0].second.Env; 157 158 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo"); 159 ASSERT_THAT(FooDecl, NotNull()); 160 161 ASSERT_TRUE(FooDecl->getType()->isClassType()); 162 auto FooFields = FooDecl->getType()->getAsRecordDecl()->fields(); 163 164 FieldDecl *BarDecl = nullptr; 165 for (FieldDecl *Field : FooFields) { 166 if (Field->getNameAsString() == "Bar") { 167 BarDecl = Field; 168 } else { 169 FAIL() << "Unexpected field: " << Field->getNameAsString(); 170 } 171 } 172 ASSERT_THAT(BarDecl, NotNull()); 173 174 const auto *FooLoc = 175 cast<AggregateStorageLocation>(Env.getStorageLocation(*FooDecl)); 176 const auto *BarLoc = 177 cast<ScalarStorageLocation>(&FooLoc->getChild(*BarDecl)); 178 179 const auto *FooVal = cast<StructValue>(Env.getValue(*FooLoc)); 180 const auto *BarVal = cast<IntegerValue>(&FooVal->getChild(*BarDecl)); 181 ASSERT_EQ(Env.getValue(*BarLoc), BarVal); 182 }); 183 } 184 185 TEST_F(TransferTest, ReferenceVarDecl) { 186 std::string Code = R"( 187 struct Foo {}; 188 189 Foo& getFoo(); 190 191 void target() { 192 Foo& foo = getFoo(); 193 // [[p]] 194 } 195 )"; 196 runDataflow( 197 Code, [](llvm::ArrayRef< 198 std::pair<std::string, DataflowAnalysisState<NoopLattice>>> 199 Results, 200 ASTContext &ASTCtx) { 201 ASSERT_THAT(Results, ElementsAre(Pair("p", _))); 202 const Environment &Env = Results[0].second.Env; 203 204 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo"); 205 ASSERT_THAT(FooDecl, NotNull()); 206 207 const StorageLocation *FooLoc = Env.getStorageLocation(*FooDecl); 208 ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc)); 209 210 const ReferenceValue *FooVal = 211 cast<ReferenceValue>(Env.getValue(*FooLoc)); 212 const StorageLocation &FooPointeeLoc = FooVal->getPointeeLoc(); 213 ASSERT_TRUE(isa<AggregateStorageLocation>(&FooPointeeLoc)); 214 215 const Value *FooPointeeVal = Env.getValue(FooPointeeLoc); 216 ASSERT_TRUE(isa_and_nonnull<StructValue>(FooPointeeVal)); 217 }); 218 } 219 220 TEST_F(TransferTest, SelfReferentialReferenceVarDecl) { 221 std::string Code = R"( 222 struct Foo; 223 224 struct Baz {}; 225 226 struct Bar { 227 Foo& FooRef; 228 Foo* FooPtr; 229 Baz& BazRef; 230 Baz* BazPtr; 231 }; 232 233 struct Foo { 234 Bar& Bar; 235 }; 236 237 Foo& getFoo(); 238 239 void target() { 240 Foo& foo = getFoo(); 241 // [[p]] 242 } 243 )"; 244 runDataflow(Code, [](llvm::ArrayRef<std::pair< 245 std::string, DataflowAnalysisState<NoopLattice>>> 246 Results, 247 ASTContext &ASTCtx) { 248 ASSERT_THAT(Results, ElementsAre(Pair("p", _))); 249 const Environment &Env = Results[0].second.Env; 250 251 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo"); 252 ASSERT_THAT(FooDecl, NotNull()); 253 254 ASSERT_TRUE(FooDecl->getType()->isReferenceType()); 255 ASSERT_TRUE(FooDecl->getType().getNonReferenceType()->isStructureType()); 256 const auto FooFields = 257 FooDecl->getType().getNonReferenceType()->getAsRecordDecl()->fields(); 258 259 FieldDecl *BarDecl = nullptr; 260 for (FieldDecl *Field : FooFields) { 261 if (Field->getNameAsString() == "Bar") { 262 BarDecl = Field; 263 } else { 264 FAIL() << "Unexpected field: " << Field->getNameAsString(); 265 } 266 } 267 ASSERT_THAT(BarDecl, NotNull()); 268 269 ASSERT_TRUE(BarDecl->getType()->isReferenceType()); 270 ASSERT_TRUE(BarDecl->getType().getNonReferenceType()->isStructureType()); 271 const auto BarFields = 272 BarDecl->getType().getNonReferenceType()->getAsRecordDecl()->fields(); 273 274 FieldDecl *FooRefDecl = nullptr; 275 FieldDecl *FooPtrDecl = nullptr; 276 FieldDecl *BazRefDecl = nullptr; 277 FieldDecl *BazPtrDecl = nullptr; 278 for (FieldDecl *Field : BarFields) { 279 if (Field->getNameAsString() == "FooRef") { 280 FooRefDecl = Field; 281 } else if (Field->getNameAsString() == "FooPtr") { 282 FooPtrDecl = Field; 283 } else if (Field->getNameAsString() == "BazRef") { 284 BazRefDecl = Field; 285 } else if (Field->getNameAsString() == "BazPtr") { 286 BazPtrDecl = Field; 287 } else { 288 FAIL() << "Unexpected field: " << Field->getNameAsString(); 289 } 290 } 291 ASSERT_THAT(FooRefDecl, NotNull()); 292 ASSERT_THAT(FooPtrDecl, NotNull()); 293 ASSERT_THAT(BazRefDecl, NotNull()); 294 ASSERT_THAT(BazPtrDecl, NotNull()); 295 296 const auto *FooLoc = 297 cast<ScalarStorageLocation>(Env.getStorageLocation(*FooDecl)); 298 const auto *FooVal = cast<ReferenceValue>(Env.getValue(*FooLoc)); 299 const auto *FooPointeeVal = 300 cast<StructValue>(Env.getValue(FooVal->getPointeeLoc())); 301 302 const auto *BarVal = 303 cast<ReferenceValue>(&FooPointeeVal->getChild(*BarDecl)); 304 const auto *BarPointeeVal = 305 cast<StructValue>(Env.getValue(BarVal->getPointeeLoc())); 306 307 const auto *FooRefVal = 308 cast<ReferenceValue>(&BarPointeeVal->getChild(*FooRefDecl)); 309 const StorageLocation &FooRefPointeeLoc = FooRefVal->getPointeeLoc(); 310 ASSERT_THAT(Env.getValue(FooRefPointeeLoc), IsNull()); 311 312 const auto *FooPtrVal = 313 cast<PointerValue>(&BarPointeeVal->getChild(*FooPtrDecl)); 314 const StorageLocation &FooPtrPointeeLoc = FooPtrVal->getPointeeLoc(); 315 ASSERT_THAT(Env.getValue(FooPtrPointeeLoc), IsNull()); 316 317 const auto *BazRefVal = 318 cast<ReferenceValue>(&BarPointeeVal->getChild(*BazRefDecl)); 319 const StorageLocation &BazRefPointeeLoc = BazRefVal->getPointeeLoc(); 320 ASSERT_THAT(Env.getValue(BazRefPointeeLoc), NotNull()); 321 322 const auto *BazPtrVal = 323 cast<PointerValue>(&BarPointeeVal->getChild(*BazPtrDecl)); 324 const StorageLocation &BazPtrPointeeLoc = BazPtrVal->getPointeeLoc(); 325 ASSERT_THAT(Env.getValue(BazPtrPointeeLoc), NotNull()); 326 }); 327 } 328 329 TEST_F(TransferTest, PointerVarDecl) { 330 std::string Code = R"( 331 struct Foo {}; 332 333 Foo* getFoo(); 334 335 void target() { 336 Foo* foo = getFoo(); 337 // [[p]] 338 } 339 )"; 340 runDataflow( 341 Code, [](llvm::ArrayRef< 342 std::pair<std::string, DataflowAnalysisState<NoopLattice>>> 343 Results, 344 ASTContext &ASTCtx) { 345 ASSERT_THAT(Results, ElementsAre(Pair("p", _))); 346 const Environment &Env = Results[0].second.Env; 347 348 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo"); 349 ASSERT_THAT(FooDecl, NotNull()); 350 351 const StorageLocation *FooLoc = Env.getStorageLocation(*FooDecl); 352 ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc)); 353 354 const PointerValue *FooVal = cast<PointerValue>(Env.getValue(*FooLoc)); 355 const StorageLocation &FooPointeeLoc = FooVal->getPointeeLoc(); 356 ASSERT_TRUE(isa<AggregateStorageLocation>(&FooPointeeLoc)); 357 358 const Value *FooPointeeVal = Env.getValue(FooPointeeLoc); 359 ASSERT_TRUE(isa_and_nonnull<StructValue>(FooPointeeVal)); 360 }); 361 } 362 363 TEST_F(TransferTest, SelfReferentialPointerVarDecl) { 364 std::string Code = R"( 365 struct Foo; 366 367 struct Baz {}; 368 369 struct Bar { 370 Foo& FooRef; 371 Foo* FooPtr; 372 Baz& BazRef; 373 Baz* BazPtr; 374 }; 375 376 struct Foo { 377 Bar* Bar; 378 }; 379 380 Foo* getFoo(); 381 382 void target() { 383 Foo* foo = getFoo(); 384 // [[p]] 385 } 386 )"; 387 runDataflow( 388 Code, [](llvm::ArrayRef< 389 std::pair<std::string, DataflowAnalysisState<NoopLattice>>> 390 Results, 391 ASTContext &ASTCtx) { 392 ASSERT_THAT(Results, ElementsAre(Pair("p", _))); 393 const Environment &Env = Results[0].second.Env; 394 395 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo"); 396 ASSERT_THAT(FooDecl, NotNull()); 397 398 ASSERT_TRUE(FooDecl->getType()->isPointerType()); 399 ASSERT_TRUE(FooDecl->getType() 400 ->getAs<PointerType>() 401 ->getPointeeType() 402 ->isStructureType()); 403 const auto FooFields = FooDecl->getType() 404 ->getAs<PointerType>() 405 ->getPointeeType() 406 ->getAsRecordDecl() 407 ->fields(); 408 409 FieldDecl *BarDecl = nullptr; 410 for (FieldDecl *Field : FooFields) { 411 if (Field->getNameAsString() == "Bar") { 412 BarDecl = Field; 413 } else { 414 FAIL() << "Unexpected field: " << Field->getNameAsString(); 415 } 416 } 417 ASSERT_THAT(BarDecl, NotNull()); 418 419 ASSERT_TRUE(BarDecl->getType()->isPointerType()); 420 ASSERT_TRUE(BarDecl->getType() 421 ->getAs<PointerType>() 422 ->getPointeeType() 423 ->isStructureType()); 424 const auto BarFields = BarDecl->getType() 425 ->getAs<PointerType>() 426 ->getPointeeType() 427 ->getAsRecordDecl() 428 ->fields(); 429 430 FieldDecl *FooRefDecl = nullptr; 431 FieldDecl *FooPtrDecl = nullptr; 432 FieldDecl *BazRefDecl = nullptr; 433 FieldDecl *BazPtrDecl = nullptr; 434 for (FieldDecl *Field : BarFields) { 435 if (Field->getNameAsString() == "FooRef") { 436 FooRefDecl = Field; 437 } else if (Field->getNameAsString() == "FooPtr") { 438 FooPtrDecl = Field; 439 } else if (Field->getNameAsString() == "BazRef") { 440 BazRefDecl = Field; 441 } else if (Field->getNameAsString() == "BazPtr") { 442 BazPtrDecl = Field; 443 } else { 444 FAIL() << "Unexpected field: " << Field->getNameAsString(); 445 } 446 } 447 ASSERT_THAT(FooRefDecl, NotNull()); 448 ASSERT_THAT(FooPtrDecl, NotNull()); 449 ASSERT_THAT(BazRefDecl, NotNull()); 450 ASSERT_THAT(BazPtrDecl, NotNull()); 451 452 const auto *FooLoc = 453 cast<ScalarStorageLocation>(Env.getStorageLocation(*FooDecl)); 454 const auto *FooVal = cast<PointerValue>(Env.getValue(*FooLoc)); 455 const auto *FooPointeeVal = 456 cast<StructValue>(Env.getValue(FooVal->getPointeeLoc())); 457 458 const auto *BarVal = 459 cast<PointerValue>(&FooPointeeVal->getChild(*BarDecl)); 460 const auto *BarPointeeVal = 461 cast<StructValue>(Env.getValue(BarVal->getPointeeLoc())); 462 463 const auto *FooRefVal = 464 cast<ReferenceValue>(&BarPointeeVal->getChild(*FooRefDecl)); 465 const StorageLocation &FooRefPointeeLoc = FooRefVal->getPointeeLoc(); 466 ASSERT_THAT(Env.getValue(FooRefPointeeLoc), IsNull()); 467 468 const auto *FooPtrVal = 469 cast<PointerValue>(&BarPointeeVal->getChild(*FooPtrDecl)); 470 const StorageLocation &FooPtrPointeeLoc = FooPtrVal->getPointeeLoc(); 471 ASSERT_THAT(Env.getValue(FooPtrPointeeLoc), IsNull()); 472 473 const auto *BazRefVal = 474 cast<ReferenceValue>(&BarPointeeVal->getChild(*BazRefDecl)); 475 const StorageLocation &BazRefPointeeLoc = BazRefVal->getPointeeLoc(); 476 ASSERT_THAT(Env.getValue(BazRefPointeeLoc), NotNull()); 477 478 const auto *BazPtrVal = 479 cast<PointerValue>(&BarPointeeVal->getChild(*BazPtrDecl)); 480 const StorageLocation &BazPtrPointeeLoc = BazPtrVal->getPointeeLoc(); 481 ASSERT_THAT(Env.getValue(BazPtrPointeeLoc), NotNull()); 482 }); 483 } 484 485 TEST_F(TransferTest, JoinVarDecl) { 486 std::string Code = R"( 487 void target(bool b) { 488 int foo; 489 // [[p1]] 490 if (b) { 491 int bar; 492 // [[p2]] 493 } else { 494 int baz; 495 // [[p3]] 496 } 497 (void)0; 498 // [[p4]] 499 } 500 )"; 501 runDataflow( 502 Code, [](llvm::ArrayRef< 503 std::pair<std::string, DataflowAnalysisState<NoopLattice>>> 504 Results, 505 ASTContext &ASTCtx) { 506 ASSERT_THAT(Results, ElementsAre(Pair("p4", _), Pair("p3", _), 507 Pair("p2", _), Pair("p1", _))); 508 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo"); 509 ASSERT_THAT(FooDecl, NotNull()); 510 511 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar"); 512 ASSERT_THAT(BarDecl, NotNull()); 513 514 const ValueDecl *BazDecl = findValueDecl(ASTCtx, "baz"); 515 ASSERT_THAT(BazDecl, NotNull()); 516 517 const Environment &Env1 = Results[3].second.Env; 518 const StorageLocation *FooLoc = Env1.getStorageLocation(*FooDecl); 519 ASSERT_THAT(FooLoc, NotNull()); 520 ASSERT_THAT(Env1.getStorageLocation(*BarDecl), IsNull()); 521 ASSERT_THAT(Env1.getStorageLocation(*BazDecl), IsNull()); 522 523 const Environment &Env2 = Results[2].second.Env; 524 ASSERT_EQ(Env2.getStorageLocation(*FooDecl), FooLoc); 525 ASSERT_THAT(Env2.getStorageLocation(*BarDecl), NotNull()); 526 ASSERT_THAT(Env2.getStorageLocation(*BazDecl), IsNull()); 527 528 const Environment &Env3 = Results[1].second.Env; 529 ASSERT_EQ(Env3.getStorageLocation(*FooDecl), FooLoc); 530 ASSERT_THAT(Env3.getStorageLocation(*BarDecl), IsNull()); 531 ASSERT_THAT(Env3.getStorageLocation(*BazDecl), NotNull()); 532 533 const Environment &Env4 = Results[0].second.Env; 534 ASSERT_EQ(Env4.getStorageLocation(*FooDecl), FooLoc); 535 ASSERT_THAT(Env4.getStorageLocation(*BarDecl), IsNull()); 536 ASSERT_THAT(Env4.getStorageLocation(*BazDecl), IsNull()); 537 }); 538 } 539 540 } // namespace 541