1577 lines · cpp
1//===- LifetimeSafetyTest.cpp - Lifetime Safety Tests -*---------- C++-*-===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"10#include "clang/ASTMatchers/ASTMatchFinder.h"11#include "clang/ASTMatchers/ASTMatchers.h"12#include "clang/Testing/TestAST.h"13#include "llvm/ADT/StringMap.h"14#include "gmock/gmock.h"15#include "gtest/gtest.h"16#include <optional>17#include <vector>18 19namespace clang::lifetimes::internal {20namespace {21 22using namespace ast_matchers;23using ::testing::Not;24using ::testing::SizeIs;25using ::testing::UnorderedElementsAreArray;26 27// A helper class to run the full lifetime analysis on a piece of code28// and provide an interface for querying the results.29class LifetimeTestRunner {30public:31 LifetimeTestRunner(llvm::StringRef Code) {32 std::string FullCode = R"(33 #define POINT(name) void("__lifetime_test_point_" #name)34 35 struct MyObj { ~MyObj() {} int i; };36 37 struct [[gsl::Pointer()]] View { 38 View(const MyObj&);39 View();40 };41 )";42 FullCode += Code.str();43 44 Inputs = TestInputs(FullCode);45 Inputs.Language = TestLanguage::Lang_CXX20;46 AST = std::make_unique<clang::TestAST>(Inputs);47 ASTCtx = &AST->context();48 49 // Find the target function using AST matchers.50 auto MatchResult =51 match(functionDecl(hasName("target")).bind("target"), *ASTCtx);52 auto *FD = selectFirst<FunctionDecl>("target", MatchResult);53 if (!FD) {54 ADD_FAILURE() << "Test case must have a function named 'target'";55 return;56 }57 AnalysisCtx = std::make_unique<AnalysisDeclContext>(nullptr, FD);58 CFG::BuildOptions &BuildOptions = AnalysisCtx->getCFGBuildOptions();59 BuildOptions.setAllAlwaysAdd();60 BuildOptions.AddImplicitDtors = true;61 BuildOptions.AddTemporaryDtors = true;62 BuildOptions.AddLifetime = true;63 64 // Run the main analysis.65 Analysis = std::make_unique<LifetimeSafetyAnalysis>(*AnalysisCtx, nullptr);66 Analysis->run();67 68 AnnotationToPointMap = Analysis->getFactManager().getTestPoints();69 }70 71 LifetimeSafetyAnalysis &getAnalysis() { return *Analysis; }72 ASTContext &getASTContext() { return *ASTCtx; }73 AnalysisDeclContext &getAnalysisContext() { return *AnalysisCtx; }74 75 ProgramPoint getProgramPoint(llvm::StringRef Annotation) {76 auto It = AnnotationToPointMap.find(Annotation);77 if (It == AnnotationToPointMap.end()) {78 ADD_FAILURE() << "Annotation '" << Annotation << "' not found.";79 return nullptr;80 }81 return It->second;82 }83 84private:85 TestInputs Inputs;86 std::unique_ptr<TestAST> AST;87 ASTContext *ASTCtx = nullptr;88 std::unique_ptr<AnalysisDeclContext> AnalysisCtx;89 std::unique_ptr<LifetimeSafetyAnalysis> Analysis;90 llvm::StringMap<ProgramPoint> AnnotationToPointMap;91};92 93// A convenience wrapper that uses the LifetimeSafetyAnalysis public API.94class LifetimeTestHelper {95public:96 LifetimeTestHelper(LifetimeTestRunner &Runner)97 : Runner(Runner), Analysis(Runner.getAnalysis()) {}98 99 std::optional<OriginID> getOriginForDecl(llvm::StringRef VarName) {100 auto *VD = findDecl<ValueDecl>(VarName);101 if (!VD)102 return std::nullopt;103 // This assumes the OriginManager's `get` can find an existing origin.104 // We might need a `find` method on OriginManager to avoid `getOrCreate`105 // logic in a const-query context if that becomes an issue.106 return const_cast<OriginManager &>(Analysis.getFactManager().getOriginMgr())107 .get(*VD);108 }109 110 std::vector<LoanID> getLoansForVar(llvm::StringRef VarName) {111 auto *VD = findDecl<VarDecl>(VarName);112 if (!VD) {113 ADD_FAILURE() << "Failed to find VarDecl for '" << VarName << "'";114 return {};115 }116 std::vector<LoanID> LID;117 for (const Loan &L : Analysis.getFactManager().getLoanMgr().getLoans())118 if (L.Path.D == VD)119 LID.push_back(L.ID);120 if (LID.empty()) {121 ADD_FAILURE() << "Loan for '" << VarName << "' not found.";122 return {};123 }124 return LID;125 }126 127 // Gets the set of loans that are live at the given program point. A loan is128 // considered live at point P if there is a live origin which contains this129 // loan.130 std::optional<LoanSet> getLiveLoansAtPoint(ProgramPoint P) const {131 const auto &LiveOriginsAnalysis = Runner.getAnalysis().getLiveOrigins();132 const auto &LoanPropagation = Runner.getAnalysis().getLoanPropagation();133 134 LivenessMap LiveOriginsMap = LiveOriginsAnalysis.getLiveOriginsAt(P);135 136 LoanSet::Factory F;137 LoanSet Result = F.getEmptySet();138 139 for (const auto &[OID, LI] : LiveOriginsMap) {140 LoanSet Loans = LoanPropagation.getLoans(OID, P);141 Result = clang::lifetimes::internal::utils::join(Result, Loans, F);142 }143 144 if (Result.isEmpty())145 return std::nullopt;146 147 return Result;148 }149 150 const ExpireFact *151 getExpireFactFromAllFacts(const llvm::ArrayRef<const Fact *> &FactsInBlock,152 const LoanID &loanID) {153 for (const Fact *F : FactsInBlock) {154 if (auto const *CurrentEF = F->getAs<ExpireFact>())155 if (CurrentEF->getLoanID() == loanID)156 return CurrentEF;157 }158 return nullptr;159 }160 161 std::optional<LoanSet> getLoansAtPoint(OriginID OID,162 llvm::StringRef Annotation) {163 ProgramPoint PP = Runner.getProgramPoint(Annotation);164 if (!PP)165 return std::nullopt;166 return Analysis.getLoanPropagation().getLoans(OID, PP);167 }168 169 std::optional<std::vector<std::pair<OriginID, LivenessKind>>>170 getLiveOriginsAtPoint(llvm::StringRef Annotation) {171 ProgramPoint PP = Runner.getProgramPoint(Annotation);172 if (!PP)173 return std::nullopt;174 std::vector<std::pair<OriginID, LivenessKind>> Result;175 for (auto &[OID, Info] : Analysis.getLiveOrigins().getLiveOriginsAt(PP))176 Result.push_back({OID, Info.Kind});177 return Result;178 }179 180 ProgramPoint getProgramPoint(llvm::StringRef Annotation) {181 return Runner.getProgramPoint(Annotation);182 }183 184 llvm::ArrayRef<const Fact *> getBlockContaining(ProgramPoint P) {185 return Runner.getAnalysis().getFactManager().getBlockContaining(P);186 }187 188private:189 template <typename DeclT> DeclT *findDecl(llvm::StringRef Name) {190 auto &Ctx = Runner.getASTContext();191 const auto *TargetFunc = Runner.getAnalysisContext().getDecl();192 auto Results =193 match(valueDecl(hasName(Name),194 hasAncestor(functionDecl(equalsNode(TargetFunc))))195 .bind("v"),196 Ctx);197 if (Results.empty()) {198 ADD_FAILURE() << "Declaration '" << Name << "' not found in AST.";199 return nullptr;200 }201 if (Results.size() > 1) {202 ADD_FAILURE() << "Multiple declarations found for '" << Name << "'";203 return nullptr;204 }205 return const_cast<DeclT *>(selectFirst<DeclT>("v", Results));206 }207 208 LifetimeTestRunner &Runner;209 LifetimeSafetyAnalysis &Analysis;210};211 212// ========================================================================= //213// GTest Matchers & Fixture214// ========================================================================= //215 216// A helper class to represent a set of loans, identified by variable names.217class LoanSetInfo {218public:219 LoanSetInfo(const std::vector<std::string> &Vars, LifetimeTestHelper &H)220 : LoanVars(Vars), Helper(H) {}221 std::vector<std::string> LoanVars;222 LifetimeTestHelper &Helper;223};224 225// It holds the name of the origin variable and a reference to the helper.226class OriginInfo {227public:228 OriginInfo(llvm::StringRef OriginVar, LifetimeTestHelper &Helper)229 : OriginVar(OriginVar), Helper(Helper) {}230 llvm::StringRef OriginVar;231 LifetimeTestHelper &Helper;232};233 234// A helper class to represent a set of origins, identified by variable names.235class OriginsInfo {236public:237 OriginsInfo(const std::vector<std::string> &Vars, LifetimeTestHelper &H)238 : OriginVars(Vars), Helper(H) {}239 std::vector<std::string> OriginVars;240 LifetimeTestHelper &Helper;241};242 243/// Matcher to verify the set of loans held by an origin at a specific244/// program point.245///246/// This matcher is intended to be used with an \c OriginInfo object.247///248/// \param LoanVars A vector of strings, where each string is the name of a249/// variable expected to be the source of a loan.250/// \param Annotation A string identifying the program point (created with251/// POINT()) where the check should be performed.252MATCHER_P2(HasLoansToImpl, LoanVars, Annotation, "") {253 const OriginInfo &Info = arg;254 std::optional<OriginID> OIDOpt = Info.Helper.getOriginForDecl(Info.OriginVar);255 if (!OIDOpt) {256 *result_listener << "could not find origin for '" << Info.OriginVar.str()257 << "'";258 return false;259 }260 261 std::optional<LoanSet> ActualLoansSetOpt =262 Info.Helper.getLoansAtPoint(*OIDOpt, Annotation);263 if (!ActualLoansSetOpt) {264 *result_listener << "could not get a valid loan set at point '"265 << Annotation << "'";266 return false;267 }268 std::vector<LoanID> ActualLoans(ActualLoansSetOpt->begin(),269 ActualLoansSetOpt->end());270 271 std::vector<LoanID> ExpectedLoans;272 for (const auto &LoanVar : LoanVars) {273 std::vector<LoanID> ExpectedLIDs = Info.Helper.getLoansForVar(LoanVar);274 if (ExpectedLIDs.empty()) {275 *result_listener << "could not find loan for var '" << LoanVar << "'";276 return false;277 }278 ExpectedLoans.insert(ExpectedLoans.end(), ExpectedLIDs.begin(),279 ExpectedLIDs.end());280 }281 std::sort(ExpectedLoans.begin(), ExpectedLoans.end());282 std::sort(ActualLoans.begin(), ActualLoans.end());283 if (ExpectedLoans != ActualLoans) {284 *result_listener << "Expected: {";285 for (const auto &LoanID : ExpectedLoans) {286 *result_listener << LoanID.Value << ", ";287 }288 *result_listener << "} Actual: {";289 for (const auto &LoanID : ActualLoans) {290 *result_listener << LoanID.Value << ", ";291 }292 *result_listener << "}";293 return false;294 }295 296 return ExplainMatchResult(UnorderedElementsAreArray(ExpectedLoans),297 ActualLoans, result_listener);298}299 300enum class LivenessKindFilter { Maybe, Must, All };301 302/// Matcher to verify the complete set of live origins at a program point.303MATCHER_P2(AreLiveAtImpl, Annotation, ConfFilter, "") {304 const OriginsInfo &Info = arg;305 auto &Helper = Info.Helper;306 auto ActualLiveSetOpt = Helper.getLiveOriginsAtPoint(Annotation);307 if (!ActualLiveSetOpt) {308 *result_listener << "could not get a valid live origin set at point '"309 << Annotation << "'";310 return false;311 }312 std::vector<OriginID> ActualLiveOrigins;313 for (const auto [OID, ActualConfidence] : ActualLiveSetOpt.value()) {314 if (ConfFilter == LivenessKindFilter::All)315 ActualLiveOrigins.push_back(OID);316 if (ActualConfidence == LivenessKind::Maybe &&317 ConfFilter == LivenessKindFilter::Maybe)318 ActualLiveOrigins.push_back(OID);319 if (ActualConfidence == LivenessKind::Must &&320 ConfFilter == LivenessKindFilter::Must)321 ActualLiveOrigins.push_back(OID);322 }323 324 std::vector<OriginID> ExpectedLiveOrigins;325 for (const auto &VarName : Info.OriginVars) {326 auto OriginIDOpt = Helper.getOriginForDecl(VarName);327 if (!OriginIDOpt) {328 *result_listener << "could not find an origin for variable '" << VarName329 << "'";330 return false;331 }332 ExpectedLiveOrigins.push_back(*OriginIDOpt);333 }334 std::sort(ExpectedLiveOrigins.begin(), ExpectedLiveOrigins.end());335 std::sort(ActualLiveOrigins.begin(), ActualLiveOrigins.end());336 if (ExpectedLiveOrigins != ActualLiveOrigins) {337 *result_listener << "Expected: {";338 for (const auto &OriginID : ExpectedLiveOrigins) {339 *result_listener << OriginID.Value << ", ";340 }341 *result_listener << "} Actual: {";342 for (const auto &OriginID : ActualLiveOrigins) {343 *result_listener << OriginID.Value << ", ";344 }345 *result_listener << "}";346 return false;347 }348 return true;349}350 351MATCHER_P2(HasLiveLoanAtExpiryImpl, HelperPtr, Annotation, "") {352 llvm::StringRef VarName = arg;353 LifetimeTestHelper &Helper = *HelperPtr;354 355 std::vector<LoanID> Loans = Helper.getLoansForVar(VarName);356 if (Loans.empty()) {357 *result_listener << "No loans found for variable" << VarName.str();358 return false;359 }360 361 ProgramPoint PP = Helper.getProgramPoint(Annotation);362 llvm::ArrayRef<const Fact *> AllFactsInBlock = Helper.getBlockContaining(PP);363 364 bool NoExpireFactLive = false;365 for (const LoanID CurrentLoanID : Loans) {366 const ExpireFact *EF =367 Helper.getExpireFactFromAllFacts(AllFactsInBlock, CurrentLoanID);368 if (!EF) {369 NoExpireFactLive = true;370 continue;371 }372 std::optional<LoanSet> LiveLoans = Helper.getLiveLoansAtPoint(EF);373 if (!LiveLoans.has_value()) {374 *result_listener << "No Live Loans At Expiry Location.";375 continue;376 }377 if (LiveLoans->contains({CurrentLoanID}))378 return true;379 }380 if (NoExpireFactLive) {381 *result_listener << "No Expire Fact for loan of " << VarName.str();382 return false;383 }384 *result_listener << "No loans of " << VarName.str() << " are live";385 return false;386}387 388MATCHER_P(MustBeLiveAt, Annotation, "") {389 return ExplainMatchResult(AreLiveAtImpl(Annotation, LivenessKindFilter::Must),390 arg, result_listener);391}392 393MATCHER_P(MaybeLiveAt, Annotation, "") {394 return ExplainMatchResult(395 AreLiveAtImpl(Annotation, LivenessKindFilter::Maybe), arg,396 result_listener);397}398 399MATCHER_P(AreLiveAt, Annotation, "") {400 return ExplainMatchResult(AreLiveAtImpl(Annotation, LivenessKindFilter::All),401 arg, result_listener);402}403 404// Base test fixture to manage the runner and helper.405class LifetimeAnalysisTest : public ::testing::Test {406protected:407 void SetupTest(llvm::StringRef Code) {408 Runner = std::make_unique<LifetimeTestRunner>(Code);409 Helper = std::make_unique<LifetimeTestHelper>(*Runner);410 }411 412 OriginInfo Origin(llvm::StringRef OriginVar) {413 return OriginInfo(OriginVar, *Helper);414 }415 416 /// Factory function that hides the std::vector creation.417 OriginsInfo Origins(std::initializer_list<std::string> OriginVars) {418 return OriginsInfo({OriginVars}, *Helper);419 }420 421 OriginsInfo NoOrigins() { return Origins({}); }422 423 /// Factory function that hides the std::vector creation.424 LoanSetInfo LoansTo(std::initializer_list<std::string> LoanVars) {425 return LoanSetInfo({LoanVars}, *Helper);426 }427 428 /// A convenience helper for asserting that no loans are expired.429 LoanSetInfo NoLoans() { return LoansTo({}); }430 431 // Factory function that hides the std::vector creation.432 auto HasLoansTo(std::initializer_list<std::string> LoanVars,433 const char *Annotation) {434 return HasLoansToImpl(std::vector<std::string>(LoanVars), Annotation);435 }436 437 auto HasLiveLoanAtExpiry(const char *Annotation) {438 return HasLiveLoanAtExpiryImpl(Helper.get(), Annotation);439 }440 441 std::unique_ptr<LifetimeTestRunner> Runner;442 std::unique_ptr<LifetimeTestHelper> Helper;443};444 445// ========================================================================= //446// TESTS447// ========================================================================= //448 449TEST_F(LifetimeAnalysisTest, SimpleLoanAndOrigin) {450 SetupTest(R"(451 void target() {452 int x;453 int* p = &x;454 POINT(p1);455 }456 )");457 EXPECT_THAT(Origin("p"), HasLoansTo({"x"}, "p1"));458}459 460TEST_F(LifetimeAnalysisTest, OverwriteOrigin) {461 SetupTest(R"(462 void target() {463 MyObj s1, s2;464 465 MyObj* p = &s1;466 POINT(after_s1);467 468 p = &s2;469 POINT(after_s2);470 }471 )");472 EXPECT_THAT(Origin("p"), HasLoansTo({"s1"}, "after_s1"));473 EXPECT_THAT(Origin("p"), HasLoansTo({"s2"}, "after_s2"));474}475 476TEST_F(LifetimeAnalysisTest, ConditionalLoan) {477 SetupTest(R"(478 void target(bool cond) {479 int a, b;480 int *p = nullptr;481 if (cond) {482 p = &a;483 POINT(after_then);484 } else {485 p = &b;486 POINT(after_else);487 }488 POINT(after_if);489 }490 )");491 EXPECT_THAT(Origin("p"), HasLoansTo({"a"}, "after_then"));492 EXPECT_THAT(Origin("p"), HasLoansTo({"b"}, "after_else"));493 EXPECT_THAT(Origin("p"), HasLoansTo({"a", "b"}, "after_if"));494}495 496TEST_F(LifetimeAnalysisTest, PointerChain) {497 SetupTest(R"(498 void target() {499 MyObj y;500 MyObj* ptr1 = &y;501 POINT(p1);502 503 MyObj* ptr2 = ptr1;504 POINT(p2);505 506 ptr2 = ptr1;507 POINT(p3);508 509 ptr2 = ptr2; // Self assignment510 POINT(p4);511 }512 )");513 EXPECT_THAT(Origin("ptr1"), HasLoansTo({"y"}, "p1"));514 EXPECT_THAT(Origin("ptr2"), HasLoansTo({"y"}, "p2"));515 EXPECT_THAT(Origin("ptr2"), HasLoansTo({"y"}, "p3"));516 EXPECT_THAT(Origin("ptr2"), HasLoansTo({"y"}, "p4"));517}518 519TEST_F(LifetimeAnalysisTest, ReassignToNull) {520 SetupTest(R"(521 void target() {522 MyObj s1;523 MyObj* p = &s1;524 POINT(before_null);525 p = nullptr;526 POINT(after_null);527 }528 )");529 EXPECT_THAT(Origin("p"), HasLoansTo({"s1"}, "before_null"));530 EXPECT_THAT(Origin("p"), HasLoansTo({}, "after_null"));531}532 533TEST_F(LifetimeAnalysisTest, ReassignInIf) {534 SetupTest(R"(535 void target(bool condition) {536 MyObj s1, s2;537 MyObj* p = &s1;538 POINT(before_if);539 if (condition) {540 p = &s2;541 POINT(after_reassign);542 }543 POINT(after_if);544 }545 )");546 EXPECT_THAT(Origin("p"), HasLoansTo({"s1"}, "before_if"));547 EXPECT_THAT(Origin("p"), HasLoansTo({"s2"}, "after_reassign"));548 EXPECT_THAT(Origin("p"), HasLoansTo({"s1", "s2"}, "after_if"));549}550 551TEST_F(LifetimeAnalysisTest, AssignInSwitch) {552 SetupTest(R"(553 void target(int mode) {554 MyObj s1, s2, s3;555 MyObj* p = nullptr;556 switch (mode) {557 case 1:558 p = &s1;559 POINT(case1);560 break;561 case 2:562 p = &s2;563 POINT(case2);564 break;565 default:566 p = &s3;567 POINT(case3);568 break;569 }570 POINT(after_switch);571 }572 )");573 EXPECT_THAT(Origin("p"), HasLoansTo({"s1"}, "case1"));574 EXPECT_THAT(Origin("p"), HasLoansTo({"s2"}, "case2"));575 EXPECT_THAT(Origin("p"), HasLoansTo({"s3"}, "case3"));576 EXPECT_THAT(Origin("p"), HasLoansTo({"s1", "s2", "s3"}, "after_switch"));577}578 579TEST_F(LifetimeAnalysisTest, LoopWithBreak) {580 SetupTest(R"(581 void target(int count) {582 MyObj s1;583 MyObj s2;584 MyObj* p = &s1;585 POINT(before_loop);586 for (int i = 0; i < count; ++i) {587 if (i == 5) {588 p = &s2;589 POINT(inside_if);590 break;591 }592 POINT(after_if);593 }594 POINT(after_loop);595 }596 )");597 EXPECT_THAT(Origin("p"), HasLoansTo({"s1"}, "before_loop"));598 EXPECT_THAT(Origin("p"), HasLoansTo({"s2"}, "inside_if"));599 // At the join point after if, s2 cannot make it to p without the if.600 EXPECT_THAT(Origin("p"), HasLoansTo({"s1"}, "after_if"));601 // At the join point after the loop, p could hold a loan to s1 (if the loop602 // completed normally) or to s2 (if the loop was broken).603 EXPECT_THAT(Origin("p"), HasLoansTo({"s1", "s2"}, "after_loop"));604}605 606TEST_F(LifetimeAnalysisTest, PointersInACycle) {607 SetupTest(R"(608 void target(bool condition) {609 MyObj v1, v2, v3;610 MyObj *p1 = &v1, *p2 = &v2, *p3 = &v3;611 612 POINT(before_while);613 while (condition) {614 MyObj* temp = p1;615 p1 = p2;616 p2 = p3;617 p3 = temp;618 POINT(in_loop);619 }620 POINT(after_loop);621 }622 )");623 EXPECT_THAT(Origin("p1"), HasLoansTo({"v1"}, "before_while"));624 EXPECT_THAT(Origin("p2"), HasLoansTo({"v2"}, "before_while"));625 EXPECT_THAT(Origin("p3"), HasLoansTo({"v3"}, "before_while"));626 627 // At the fixed point after the loop, all pointers could point to any of628 // the three variables.629 EXPECT_THAT(Origin("p1"), HasLoansTo({"v1", "v2", "v3"}, "after_loop"));630 EXPECT_THAT(Origin("p2"), HasLoansTo({"v1", "v2", "v3"}, "after_loop"));631 EXPECT_THAT(Origin("p3"), HasLoansTo({"v1", "v2", "v3"}, "after_loop"));632 633 EXPECT_THAT(Origin("temp"), HasLoansTo({"v1", "v2", "v3"}, "in_loop"));634 // 'temp' is a block-local origin and it's loans are not tracked outside the635 // block.636 EXPECT_THAT(Origin("temp"), HasLoansTo({}, "after_loop"));637}638 639TEST_F(LifetimeAnalysisTest, PointersAndExpirationInACycle) {640 SetupTest(R"(641 void target(bool condition) {642 MyObj v1, v2;643 MyObj *p1 = &v1, *p2 = &v2;644 645 POINT(before_while);646 while (condition) {647 POINT(in_loop_before_temp);648 MyObj temp;649 p1 = &temp;650 POINT(in_loop_after_temp);651 652 MyObj* q = p1;653 p1 = p2;654 p2 = q;655 }656 POINT(after_loop);657 }658 )");659 EXPECT_THAT(Origin("p1"), HasLoansTo({"v1"}, "before_while"));660 EXPECT_THAT(Origin("p2"), HasLoansTo({"v2"}, "before_while"));661 662 EXPECT_THAT(Origin("p1"),663 HasLoansTo({"v1", "v2", "temp"}, "in_loop_before_temp"));664 EXPECT_THAT(Origin("p2"), HasLoansTo({"v2", "temp"}, "in_loop_before_temp"));665 666 EXPECT_THAT(Origin("p1"), HasLoansTo({"temp"}, "in_loop_after_temp"));667 EXPECT_THAT(Origin("p2"), HasLoansTo({"v2", "temp"}, "in_loop_after_temp"));668 669 EXPECT_THAT(Origin("p1"), HasLoansTo({"v1", "v2", "temp"}, "after_loop"));670 EXPECT_THAT(Origin("p2"), HasLoansTo({"v2", "temp"}, "after_loop"));671}672 673TEST_F(LifetimeAnalysisTest, InfiniteLoopPrunesEdges) {674 SetupTest(R"(675 void target(MyObj out) {676 MyObj *p = &out;677 POINT(before_loop);678 679 for (;;) {680 POINT(begin);681 MyObj in;682 p = ∈683 POINT(end);684 }685 }686 )");687 EXPECT_THAT(Origin("p"), HasLoansTo({"out"}, "before_loop"));688 EXPECT_THAT(Origin("p"), HasLoansTo({"in", "out"}, "begin"));689 EXPECT_THAT(Origin("p"), HasLoansTo({"in"}, "end"));690}691 692TEST_F(LifetimeAnalysisTest, NestedScopes) {693 SetupTest(R"(694 void target() {695 MyObj* p = nullptr;696 {697 MyObj outer;698 p = &outer;699 POINT(before_inner_scope);700 {701 MyObj inner;702 p = &inner;703 POINT(inside_inner_scope);704 } // inner expires705 POINT(after_inner_scope);706 } // outer expires707 }708 )");709 EXPECT_THAT(Origin("p"), HasLoansTo({"outer"}, "before_inner_scope"));710 EXPECT_THAT(Origin("p"), HasLoansTo({"inner"}, "inside_inner_scope"));711 EXPECT_THAT(Origin("p"), HasLoansTo({"inner"}, "after_inner_scope"));712}713 714TEST_F(LifetimeAnalysisTest, NoDuplicateLoansForImplicitCastToConst) {715 SetupTest(R"(716 void target() {717 MyObj a;718 const MyObj* p = &a;719 const MyObj* q = &a;720 POINT(at_end);721 }722 )");723 EXPECT_THAT(Helper->getLoansForVar("a"), SizeIs(2));724}725 726TEST_F(LifetimeAnalysisTest, GslPointerSimpleLoan) {727 SetupTest(R"(728 void target() {729 MyObj a;730 View x = a;731 POINT(p1);732 }733 )");734 EXPECT_THAT(Origin("x"), HasLoansTo({"a"}, "p1"));735}736 737TEST_F(LifetimeAnalysisTest, GslPointerConstructFromOwner) {738 SetupTest(R"(739 void target() {740 MyObj al, bl, cl, dl, el, fl;741 View a = View(al);742 View b = View{bl};743 View c = View(View(View(cl)));744 View d = View{View(View(dl))};745 View e = View{View{View{el}}};746 View f = {fl};747 POINT(p1);748 }749 )");750 EXPECT_THAT(Origin("a"), HasLoansTo({"al"}, "p1"));751 EXPECT_THAT(Origin("b"), HasLoansTo({"bl"}, "p1"));752 EXPECT_THAT(Origin("c"), HasLoansTo({"cl"}, "p1"));753 EXPECT_THAT(Origin("d"), HasLoansTo({"dl"}, "p1"));754 EXPECT_THAT(Origin("e"), HasLoansTo({"el"}, "p1"));755 EXPECT_THAT(Origin("f"), HasLoansTo({"fl"}, "p1"));756}757 758TEST_F(LifetimeAnalysisTest, GslPointerConstructFromView) {759 SetupTest(R"(760 void target() {761 MyObj a;762 View x = View(a);763 View y = View{x};764 View z = View(View(View(y)));765 View p = View{View(View(x))};766 View q = {x};767 POINT(p1);768 }769 )");770 EXPECT_THAT(Origin("x"), HasLoansTo({"a"}, "p1"));771 EXPECT_THAT(Origin("y"), HasLoansTo({"a"}, "p1"));772 EXPECT_THAT(Origin("z"), HasLoansTo({"a"}, "p1"));773 EXPECT_THAT(Origin("p"), HasLoansTo({"a"}, "p1"));774 EXPECT_THAT(Origin("q"), HasLoansTo({"a"}, "p1"));775}776 777TEST_F(LifetimeAnalysisTest, GslPointerInConditionalOperator) {778 SetupTest(R"(779 void target(bool cond) {780 MyObj a, b;781 View v = cond ? a : b;782 POINT(p1);783 }784 )");785 EXPECT_THAT(Origin("v"), HasLoansTo({"a", "b"}, "p1"));786}787 788TEST_F(LifetimeAnalysisTest, ExtraParenthesis) {789 SetupTest(R"(790 void target() {791 MyObj a;792 View x = ((View((((a))))));793 View y = ((View{(((x)))}));794 View z = ((View(((y)))));795 View p = ((View{((x))}));796 POINT(p1);797 }798 )");799 EXPECT_THAT(Origin("x"), HasLoansTo({"a"}, "p1"));800 EXPECT_THAT(Origin("y"), HasLoansTo({"a"}, "p1"));801 EXPECT_THAT(Origin("z"), HasLoansTo({"a"}, "p1"));802 EXPECT_THAT(Origin("p"), HasLoansTo({"a"}, "p1"));803}804 805// FIXME: Handle temporaries.806TEST_F(LifetimeAnalysisTest, ViewFromTemporary) {807 SetupTest(R"(808 MyObj temporary();809 void target() {810 View v = temporary();811 POINT(p1);812 }813 )");814 EXPECT_THAT(Origin("v"), HasLoansTo({}, "p1"));815}816 817TEST_F(LifetimeAnalysisTest, GslPointerWithConstAndAuto) {818 SetupTest(R"(819 void target() {820 MyObj a;821 const View v1 = a;822 auto v2 = v1;823 const auto& v3 = v2;824 POINT(p1);825 }826 )");827 EXPECT_THAT(Origin("v1"), HasLoansTo({"a"}, "p1"));828 EXPECT_THAT(Origin("v2"), HasLoansTo({"a"}, "p1"));829 EXPECT_THAT(Origin("v3"), HasLoansTo({"a"}, "p1"));830}831 832TEST_F(LifetimeAnalysisTest, GslPointerPropagation) {833 SetupTest(R"(834 void target() {835 MyObj a;836 View x = a;837 POINT(p1);838 839 View y = x; // Propagation via copy-construction840 POINT(p2);841 842 View z;843 z = x; // Propagation via copy-assignment844 POINT(p3);845 }846 )");847 848 EXPECT_THAT(Origin("x"), HasLoansTo({"a"}, "p1"));849 EXPECT_THAT(Origin("y"), HasLoansTo({"a"}, "p2"));850 EXPECT_THAT(Origin("z"), HasLoansTo({"a"}, "p3"));851}852 853TEST_F(LifetimeAnalysisTest, GslPointerReassignment) {854 SetupTest(R"(855 void target() {856 MyObj safe;857 View v;858 v = safe;859 POINT(p1);860 {861 MyObj unsafe;862 v = unsafe;863 POINT(p2);864 } // `unsafe` expires here.865 POINT(p3);866 }867 )");868 869 EXPECT_THAT(Origin("v"), HasLoansTo({"safe"}, "p1"));870 EXPECT_THAT(Origin("v"), HasLoansTo({"unsafe"}, "p2"));871 EXPECT_THAT(Origin("v"), HasLoansTo({"unsafe"}, "p3"));872}873 874TEST_F(LifetimeAnalysisTest, GslPointerConversionOperator) {875 SetupTest(R"(876 struct String;877 878 struct [[gsl::Pointer()]] StringView {879 StringView() = default;880 };881 882 struct String {883 ~String() {}884 operator StringView() const;885 };886 887 void target() {888 String xl, yl;889 StringView x = xl;890 StringView y;891 y = yl;892 POINT(p1);893 }894 )");895 EXPECT_THAT(Origin("x"), HasLoansTo({"xl"}, "p1"));896 EXPECT_THAT(Origin("y"), HasLoansTo({"yl"}, "p1"));897}898 899TEST_F(LifetimeAnalysisTest, LifetimeboundSimple) {900 SetupTest(R"(901 View Identity(View v [[clang::lifetimebound]]);902 void target() {903 MyObj a, b;904 View v1 = a;905 POINT(p1);906 907 View v2 = Identity(v1);908 View v3 = Identity(b);909 POINT(p2);910 }911 )");912 EXPECT_THAT(Origin("v1"), HasLoansTo({"a"}, "p1"));913 // The origin of v2 should now contain the loan to 'o' from v1.914 EXPECT_THAT(Origin("v2"), HasLoansTo({"a"}, "p2"));915 EXPECT_THAT(Origin("v3"), HasLoansTo({"b"}, "p2"));916}917 918TEST_F(LifetimeAnalysisTest, LifetimeboundMemberFunction) {919 SetupTest(R"(920 struct [[gsl::Pointer()]] MyView {921 MyView(const MyObj& o) {}922 MyView pass() [[clang::lifetimebound]] { return *this; }923 };924 void target() {925 MyObj o;926 MyView v1 = o;927 POINT(p1);928 MyView v2 = v1.pass();929 POINT(p2);930 }931 )");932 EXPECT_THAT(Origin("v1"), HasLoansTo({"o"}, "p1"));933 // The call v1.pass() is bound to 'v1'. The origin of v2 should get the loans934 // from v1.935 EXPECT_THAT(Origin("v2"), HasLoansTo({"o"}, "p2"));936}937 938TEST_F(LifetimeAnalysisTest, LifetimeboundMultipleArgs) {939 SetupTest(R"(940 View Choose(bool cond, View a [[clang::lifetimebound]], View b [[clang::lifetimebound]]);941 void target() {942 MyObj o1, o2;943 View v1 = o1;944 View v2 = o2;945 POINT(p1);946 947 View v3 = Choose(true, v1, v2);948 POINT(p2);949 }950 )");951 EXPECT_THAT(Origin("v1"), HasLoansTo({"o1"}, "p1"));952 EXPECT_THAT(Origin("v2"), HasLoansTo({"o2"}, "p2"));953 // v3 should have loans from both v1 and v2, demonstrating the union of954 // loans.955 EXPECT_THAT(Origin("v3"), HasLoansTo({"o1", "o2"}, "p2"));956}957 958TEST_F(LifetimeAnalysisTest, LifetimeboundMixedArgs) {959 SetupTest(R"(960 View Choose(bool cond, View a [[clang::lifetimebound]], View b);961 void target() {962 MyObj o1, o2;963 View v1 = o1;964 View v2 = o2;965 POINT(p1);966 967 View v3 = Choose(true, v1, v2);968 POINT(p2);969 }970 )");971 EXPECT_THAT(Origin("v1"), HasLoansTo({"o1"}, "p1"));972 EXPECT_THAT(Origin("v2"), HasLoansTo({"o2"}, "p1"));973 // v3 should only have loans from v1, as v2 is not lifetimebound.974 EXPECT_THAT(Origin("v3"), HasLoansTo({"o1"}, "p2"));975}976 977TEST_F(LifetimeAnalysisTest, LifetimeboundChainOfViews) {978 SetupTest(R"(979 View Identity(View v [[clang::lifetimebound]]);980 View DoubleIdentity(View v [[clang::lifetimebound]]);981 982 void target() {983 MyObj obj;984 View v1 = obj;985 POINT(p1);986 View v2 = DoubleIdentity(Identity(v1));987 POINT(p2);988 }989 )");990 EXPECT_THAT(Origin("v1"), HasLoansTo({"obj"}, "p1"));991 // v2 should inherit the loan from v1 through the chain of calls.992 EXPECT_THAT(Origin("v2"), HasLoansTo({"obj"}, "p2"));993}994 995TEST_F(LifetimeAnalysisTest, LifetimeboundRawPointerParameter) {996 SetupTest(R"(997 View ViewFromPtr(const MyObj* p [[clang::lifetimebound]]);998 MyObj* PtrFromPtr(const MyObj* p [[clang::lifetimebound]]);999 MyObj* PtrFromView(View v [[clang::lifetimebound]]);1000 1001 void target() {1002 MyObj a;1003 View v = ViewFromPtr(&a);1004 POINT(p1);1005 1006 MyObj b;1007 MyObj* ptr1 = PtrFromPtr(&b);1008 MyObj* ptr2 = PtrFromPtr(PtrFromPtr(PtrFromPtr(ptr1)));1009 POINT(p2);1010 1011 MyObj c;1012 View v2 = ViewFromPtr(PtrFromView(c));1013 POINT(p3);1014 }1015 )");1016 EXPECT_THAT(Origin("v"), HasLoansTo({"a"}, "p1"));1017 EXPECT_THAT(Origin("ptr1"), HasLoansTo({"b"}, "p2"));1018 EXPECT_THAT(Origin("ptr2"), HasLoansTo({"b"}, "p2"));1019 EXPECT_THAT(Origin("v2"), HasLoansTo({"c"}, "p3"));1020}1021 1022// FIXME: This can be controversial and may be revisited in the future.1023TEST_F(LifetimeAnalysisTest, LifetimeboundConstRefViewParameter) {1024 SetupTest(R"(1025 View Identity(const View& v [[clang::lifetimebound]]);1026 void target() {1027 MyObj o;1028 View v1 = o;1029 View v2 = Identity(v1);1030 POINT(p1);1031 }1032 )");1033 EXPECT_THAT(Origin("v2"), HasLoansTo({"o"}, "p1"));1034}1035 1036TEST_F(LifetimeAnalysisTest, LifetimeboundConstRefObjParam) {1037 SetupTest(R"(1038 View Identity(const MyObj& o [[clang::lifetimebound]]);1039 void target() {1040 MyObj a;1041 View v1 = Identity(a);1042 POINT(p1);1043 }1044 )");1045 EXPECT_THAT(Origin("v1"), HasLoansTo({"a"}, "p1"));1046}1047 1048TEST_F(LifetimeAnalysisTest, LifetimeboundReturnReference) {1049 SetupTest(R"(1050 const MyObj& Identity(View v [[clang::lifetimebound]]);1051 void target() {1052 MyObj a;1053 View v1 = a;1054 POINT(p1);1055 1056 View v2 = Identity(v1);1057 1058 const MyObj& b = Identity(v1);1059 View v3 = Identity(b);1060 POINT(p2);1061 1062 MyObj c;1063 View v4 = Identity(c);1064 POINT(p3);1065 }1066 )");1067 EXPECT_THAT(Origin("v1"), HasLoansTo({"a"}, "p1"));1068 EXPECT_THAT(Origin("v2"), HasLoansTo({"a"}, "p2"));1069 1070 // FIXME: Handle reference types. 'v3' should have loan to 'a' instead of 'b'.1071 EXPECT_THAT(Origin("v3"), HasLoansTo({"b"}, "p2"));1072 1073 EXPECT_THAT(Origin("v4"), HasLoansTo({"c"}, "p3"));1074}1075 1076TEST_F(LifetimeAnalysisTest, LifetimeboundTemplateFunction) {1077 SetupTest(R"(1078 template <typename T>1079 const T& Identity(T&& v [[clang::lifetimebound]]);1080 void target() {1081 MyObj a;1082 View v1 = Identity(a);1083 POINT(p1);1084 1085 View v2 = Identity(v1);1086 const View& v3 = Identity(v1);1087 POINT(p2);1088 }1089 )");1090 EXPECT_THAT(Origin("v1"), HasLoansTo({"a"}, "p1"));1091 EXPECT_THAT(Origin("v2"), HasLoansTo({"a"}, "p2"));1092 EXPECT_THAT(Origin("v3"), HasLoansTo({"a"}, "p2"));1093}1094 1095TEST_F(LifetimeAnalysisTest, LifetimeboundTemplateClass) {1096 SetupTest(R"(1097 template<typename T>1098 struct [[gsl::Pointer()]] MyTemplateView {1099 MyTemplateView(const T& o) {}1100 MyTemplateView pass() [[clang::lifetimebound]] { return *this; }1101 };1102 void target() {1103 MyObj o;1104 MyTemplateView<MyObj> v1 = o;1105 POINT(p1);1106 MyTemplateView<MyObj> v2 = v1.pass();1107 POINT(p2);1108 }1109 )");1110 EXPECT_THAT(Origin("v1"), HasLoansTo({"o"}, "p1"));1111 EXPECT_THAT(Origin("v2"), HasLoansTo({"o"}, "p2"));1112}1113 1114TEST_F(LifetimeAnalysisTest, LifetimeboundConversionOperator) {1115 SetupTest(R"(1116 struct MyOwner {1117 MyObj o;1118 operator View() const [[clang::lifetimebound]];1119 };1120 1121 void target() {1122 MyOwner owner;1123 View v = owner;1124 POINT(p1);1125 }1126 )");1127 EXPECT_THAT(Origin("v"), HasLoansTo({"owner"}, "p1"));1128}1129 1130TEST_F(LifetimeAnalysisTest, LivenessDeadPointer) {1131 SetupTest(R"(1132 void target() {1133 POINT(p1);1134 MyObj s;1135 MyObj* p = &s;1136 POINT(p2);1137 }1138 )");1139 EXPECT_THAT(NoOrigins(), AreLiveAt("p2"));1140 EXPECT_THAT(NoOrigins(), AreLiveAt("p1"));1141}1142 1143TEST_F(LifetimeAnalysisTest, LivenessSimpleReturn) {1144 SetupTest(R"(1145 MyObj* target() {1146 MyObj s;1147 MyObj* p = &s;1148 POINT(p1);1149 return p;1150 }1151 )");1152 EXPECT_THAT(Origins({"p"}), MustBeLiveAt("p1"));1153}1154 1155TEST_F(LifetimeAnalysisTest, LivenessKilledByReassignment) {1156 SetupTest(R"(1157 MyObj* target() {1158 MyObj s1, s2;1159 MyObj* p = &s1;1160 POINT(p1);1161 p = &s2;1162 POINT(p2);1163 return p;1164 }1165 )");1166 EXPECT_THAT(Origins({"p"}), MustBeLiveAt("p2"));1167 EXPECT_THAT(NoOrigins(), AreLiveAt("p1"));1168}1169 1170TEST_F(LifetimeAnalysisTest, LivenessAcrossBranches) {1171 SetupTest(R"(1172 MyObj* target(bool c) {1173 MyObj x, y;1174 MyObj* p = nullptr;1175 POINT(p1);1176 if (c) {1177 p = &x;1178 POINT(p2);1179 } else {1180 p = &y;1181 POINT(p3);1182 }1183 return p;1184 }1185 )");1186 EXPECT_THAT(Origins({"p"}), MustBeLiveAt("p2"));1187 EXPECT_THAT(Origins({"p"}), MustBeLiveAt("p3"));1188 // Before the `if`, the value of `p` (`nullptr`) is always overwritten before.1189 EXPECT_THAT(NoOrigins(), AreLiveAt("p1"));1190}1191 1192TEST_F(LifetimeAnalysisTest, LivenessInLoop) {1193 SetupTest(R"(1194 MyObj* target(bool c) {1195 MyObj s1, s2;1196 MyObj* p = &s1;1197 MyObj* q = &s2;1198 POINT(p1);1199 while(c) {1200 POINT(p2);1201 1202 p = q;1203 POINT(p3);1204 }1205 POINT(p4);1206 return p;1207 }1208 )");1209 1210 EXPECT_THAT(Origins({"p"}), MustBeLiveAt("p4"));1211 EXPECT_THAT(NoOrigins(), MaybeLiveAt("p4"));1212 1213 EXPECT_THAT(Origins({"p", "q"}), MaybeLiveAt("p3"));1214 1215 EXPECT_THAT(Origins({"q"}), MustBeLiveAt("p2"));1216 EXPECT_THAT(NoOrigins(), MaybeLiveAt("p2"));1217 1218 EXPECT_THAT(Origins({"p", "q"}), MaybeLiveAt("p1"));1219}1220 1221TEST_F(LifetimeAnalysisTest, LivenessInLoopAndIf) {1222 // See https://github.com/llvm/llvm-project/issues/156959.1223 SetupTest(R"(1224 void target(bool cond) {1225 MyObj b;1226 while (cond) {1227 POINT(p1);1228 1229 MyObj a;1230 View p = b;1231 1232 POINT(p2);1233 1234 if (cond) {1235 POINT(p3);1236 p = a;1237 }1238 POINT(p4);1239 (void)p;1240 POINT(p5);1241 }1242 }1243 )");1244 EXPECT_THAT(NoOrigins(), AreLiveAt("p5"));1245 EXPECT_THAT(Origins({"p"}), MustBeLiveAt("p4"));1246 EXPECT_THAT(NoOrigins(), AreLiveAt("p3"));1247 EXPECT_THAT(Origins({"p"}), MaybeLiveAt("p2"));1248 EXPECT_THAT(NoOrigins(), AreLiveAt("p1"));1249}1250 1251TEST_F(LifetimeAnalysisTest, LivenessInLoopAndIf2) {1252 SetupTest(R"(1253 void target(MyObj safe, bool condition) {1254 MyObj* p = &safe;1255 MyObj* q = &safe;1256 POINT(p1);1257 1258 while (condition) {1259 POINT(p2);1260 MyObj x;1261 p = &x;1262 1263 POINT(p3);1264 1265 if (condition) {1266 q = p;1267 POINT(p4);1268 }1269 1270 POINT(p5);1271 (void)*p;1272 (void)*q;1273 POINT(p6);1274 }1275 }1276 )");1277 EXPECT_THAT(Origins({"q"}), MaybeLiveAt("p6"));1278 EXPECT_THAT(NoOrigins(), MustBeLiveAt("p6"));1279 1280 EXPECT_THAT(Origins({"p", "q"}), MustBeLiveAt("p5"));1281 1282 EXPECT_THAT(Origins({"p", "q"}), MustBeLiveAt("p4"));1283 1284 EXPECT_THAT(Origins({"p"}), MustBeLiveAt("p3"));1285 EXPECT_THAT(Origins({"q"}), MaybeLiveAt("p3"));1286 1287 EXPECT_THAT(Origins({"q"}), MaybeLiveAt("p2"));1288 EXPECT_THAT(NoOrigins(), MustBeLiveAt("p2"));1289 1290 EXPECT_THAT(Origins({"q"}), MaybeLiveAt("p1"));1291 EXPECT_THAT(NoOrigins(), MustBeLiveAt("p1"));1292}1293 1294TEST_F(LifetimeAnalysisTest, LivenessOutsideLoop) {1295 SetupTest(R"(1296 void target(MyObj safe) {1297 MyObj* p = &safe;1298 for (int i = 0; i < 1; ++i) {1299 MyObj s;1300 p = &s;1301 POINT(p1);1302 }1303 POINT(p2);1304 (void)*p;1305 }1306 )");1307 EXPECT_THAT(Origins({"p"}), MustBeLiveAt("p2"));1308 EXPECT_THAT(Origins({"p"}), MaybeLiveAt("p1"));1309}1310 1311TEST_F(LifetimeAnalysisTest, TrivialDestructorsUAF) {1312 SetupTest(R"(1313 void target() {1314 int *ptr;1315 {1316 int s = 1;1317 ptr = &s;1318 }1319 POINT(p1); 1320 (void)*ptr;1321 }1322 )");1323 EXPECT_THAT(Origin("ptr"), HasLoansTo({"s"}, "p1"));1324 EXPECT_THAT(Origins({"ptr"}), MustBeLiveAt("p1"));1325}1326 1327TEST_F(LifetimeAnalysisTest, TrivialClassDestructorsUAF) {1328 SetupTest(R"(1329 class S {1330 View a, b;1331 };1332 1333 void target() {1334 S* ptr;1335 {1336 S s;1337 ptr = &s;1338 }1339 POINT(p1);1340 (void)ptr;1341 }1342 )");1343 EXPECT_THAT(Origin("ptr"), HasLoansTo({"s"}, "p1"));1344 EXPECT_THAT(Origins({"ptr"}), MustBeLiveAt("p1"));1345}1346 1347TEST_F(LifetimeAnalysisTest, SimpleReturnStackAddress) {1348 SetupTest(R"(1349 MyObj* target() {1350 MyObj s;1351 MyObj* p = &s;1352 POINT(p1);1353 return p;1354 }1355 )");1356 EXPECT_THAT("s", HasLiveLoanAtExpiry("p1"));1357}1358 1359TEST_F(LifetimeAnalysisTest, DirectReturn) {1360 SetupTest(R"(1361 MyObj* target() {1362 MyObj s;1363 POINT(P);1364 return &s;1365 }1366 )");1367 EXPECT_THAT("s", HasLiveLoanAtExpiry("P"));1368}1369 1370TEST_F(LifetimeAnalysisTest, ConditionalAssignUnconditionalReturn) {1371 SetupTest(R"(1372 MyObj* target(bool c) {1373 MyObj s1;1374 MyObj* p = nullptr;1375 if (c) {1376 p = &s1;1377 }1378 POINT(P);1379 return p;1380 }1381 )");1382 EXPECT_THAT("s1", HasLiveLoanAtExpiry("P"));1383}1384 1385TEST_F(LifetimeAnalysisTest, MultipleAssignments) {1386 SetupTest(R"(1387 MyObj* target() {1388 MyObj s;1389 MyObj* p1 = &s;1390 MyObj* p2 = &s;1391 POINT(P);1392 return p2;1393 }1394 )");1395 // Test if atleast one loan to "s" is live;1396 EXPECT_THAT("s", HasLiveLoanAtExpiry("P"));1397}1398 1399TEST_F(LifetimeAnalysisTest, ConditionalAssignBothBranches) {1400 SetupTest(R"(1401 MyObj* target(bool c) {1402 MyObj s1;1403 static MyObj s2;1404 MyObj* p = nullptr;1405 if (c) {1406 p = &s1;1407 } else {1408 p = &s2;1409 }1410 POINT(P);1411 return p;1412 }1413 )");1414 EXPECT_THAT("s1", HasLiveLoanAtExpiry("P"));1415}1416 1417TEST_F(LifetimeAnalysisTest, ReassignFromSafeToLocalThenReturn) {1418 SetupTest(R"(1419 MyObj* target() {1420 static MyObj safe_obj;1421 MyObj local_obj;1422 MyObj* p = &safe_obj;1423 1424 p = &local_obj;1425 POINT(P); 1426 return p;1427 }1428 )");1429 EXPECT_THAT("local_obj", HasLiveLoanAtExpiry("P"));1430}1431 1432TEST_F(LifetimeAnalysisTest, PointerChainToLocal) {1433 SetupTest(R"(1434 MyObj* target() {1435 MyObj local_obj;1436 MyObj* p1 = &local_obj;1437 MyObj* p2 = p1;1438 POINT(P);1439 return p2;1440 }1441 )");1442 EXPECT_THAT("local_obj", HasLiveLoanAtExpiry("P"));1443}1444 1445TEST_F(LifetimeAnalysisTest, MultipleAssignmentMultipleReturn) {1446 SetupTest(R"(1447 MyObj* target(bool c1, bool c2) {1448 static MyObj global_obj;1449 MyObj local_obj1;1450 MyObj local_obj2;1451 MyObj* p = nullptr;1452 if(c1){1453 p = &local_obj1;1454 POINT(C1);1455 return p;1456 }1457 else if(c2){1458 p = &local_obj2;1459 POINT(C2);1460 return p;1461 }1462 p = &global_obj;1463 POINT(C3);1464 return p;1465 }1466 )");1467 1468 EXPECT_THAT("local_obj1", HasLiveLoanAtExpiry("C1"));1469 EXPECT_THAT("local_obj2", HasLiveLoanAtExpiry("C2"));1470 1471 EXPECT_THAT("local_obj1", Not(HasLiveLoanAtExpiry("C3")));1472 EXPECT_THAT("local_obj2", Not(HasLiveLoanAtExpiry("C3")));1473}1474 1475TEST_F(LifetimeAnalysisTest, MultipleAssignmentsSingleReturn) {1476 SetupTest(R"(1477 MyObj* target(bool c1, bool c2) {1478 static MyObj global_obj;1479 MyObj local_obj1;1480 MyObj local_obj2;1481 MyObj* p = nullptr;1482 if(c1){1483 p = &local_obj1;1484 }1485 else if(c2){1486 p = &local_obj2;1487 }1488 else{1489 p = &global_obj;1490 }1491 POINT(P);1492 return p;1493 }1494 )");1495 EXPECT_THAT("local_obj1", HasLiveLoanAtExpiry("P"));1496 EXPECT_THAT("local_obj2", HasLiveLoanAtExpiry("P"));1497}1498 1499TEST_F(LifetimeAnalysisTest, UseAfterScopeThenReturn) {1500 SetupTest(R"(1501 MyObj* target() {1502 MyObj* p;1503 {1504 MyObj local_obj;1505 p = &local_obj;1506 POINT(p1);1507 }1508 POINT(p2);1509 return p;1510 }1511 )");1512 EXPECT_THAT(Origin("p"), HasLoansTo({"local_obj"}, "p2"));1513 EXPECT_THAT(Origins({"p"}), MustBeLiveAt("p2"));1514 1515 EXPECT_THAT(Origin("p"), HasLoansTo({"local_obj"}, "p1"));1516 EXPECT_THAT(Origins({"p"}), MustBeLiveAt("p1"));1517 1518 EXPECT_THAT("local_obj", HasLiveLoanAtExpiry("p2"));1519}1520 1521TEST_F(LifetimeAnalysisTest, ReturnBeforeUseAfterScope) {1522 SetupTest(R"(1523 MyObj* target(bool c) {1524 MyObj* p;1525 static MyObj global_obj;1526 {1527 MyObj local_obj;1528 p = &local_obj;1529 if(c){1530 POINT(p1);1531 return p;1532 }1533 }1534 POINT(p2);1535 return &global_obj;1536 }1537 )");1538 EXPECT_THAT("local_obj", HasLiveLoanAtExpiry("p1"));1539 1540 EXPECT_THAT(NoOrigins(), AreLiveAt("p2"));1541 1542 EXPECT_THAT(Origin("p"), HasLoansTo({"local_obj"}, "p1"));1543 EXPECT_THAT(Origins({"p"}), MustBeLiveAt("p1"));1544}1545 1546TEST_F(LifetimeAnalysisTest, TrivialDestructorsUAR) {1547 SetupTest(R"(1548 int* target() {1549 int s = 10;1550 int* p = &s;1551 POINT(p1);1552 return p;1553 }1554 )");1555 EXPECT_THAT("s", HasLiveLoanAtExpiry("p1"));1556}1557 1558TEST_F(LifetimeAnalysisTest, TrivialClassDestructorsUAR) {1559 SetupTest(R"(1560 class S {1561 View a, b;1562 };1563 1564 S* target() {1565 S *ptr;1566 S s;1567 ptr = &s;1568 POINT(p1);1569 return ptr;1570 }1571 )");1572 EXPECT_THAT("s", HasLiveLoanAtExpiry("p1"));1573}1574 1575} // anonymous namespace1576} // namespace clang::lifetimes::internal1577