brintos

brintos / llvm-project-archived public Read only

0
0
Text · 41.7 KiB · a895475 Raw
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 = &in;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