https://github.com/aeft updated https://github.com/llvm/llvm-project/pull/187917

>From d8fc000985092f57732f5cf0aa9fa7dfd26665ee Mon Sep 17 00:00:00 2001
From: Alex Wang <[email protected]>
Date: Sat, 21 Mar 2026 23:53:45 -0700
Subject: [PATCH] [LifetimeSafety] Track origins for lifetimebound calls
 returning record types

---
 .../Analysis/Analyses/LifetimeSafety/Facts.h  |   2 +-
 .../Analyses/LifetimeSafety/FactsGenerator.h  |   3 +
 .../Analyses/LifetimeSafety/Origins.h         |  22 +-
 .../LifetimeSafety/FactsGenerator.cpp         |  41 ++-
 .../LifetimeSafety/LifetimeSafety.cpp         |   3 +
 clang/lib/Analysis/LifetimeSafety/Origins.cpp |  88 ++++++-
 .../Sema/warn-lifetime-analysis-nocfg.cpp     |  20 +-
 clang/test/Sema/warn-lifetime-safety.cpp      | 249 +++++++++++++++++-
 8 files changed, 390 insertions(+), 38 deletions(-)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index fdcf317c69cbf..93bfae4079e47 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -313,7 +313,7 @@ class TestPointFact : public Fact {
 class FactManager {
 public:
   FactManager(const AnalysisDeclContext &AC, const CFG &Cfg)
-      : OriginMgr(AC.getASTContext(), AC.getDecl()) {
+      : OriginMgr(AC.getASTContext()) {
     BlockToFacts.resize(Cfg.getNumBlockIDs());
   }
 
diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index dfcbdc7d73007..775b51afa0237 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -57,6 +57,9 @@ class FactsGenerator : public 
ConstStmtVisitor<FactsGenerator> {
   OriginList *getOriginsList(const ValueDecl &D);
   OriginList *getOriginsList(const Expr &E);
 
+  bool hasOrigins(QualType QT) const;
+  bool hasOrigins(const Expr *E) const;
+
   void flow(OriginList *Dst, OriginList *Src, bool Kill);
 
   void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr);
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
index 8c638bdcace3f..12148d9b29c9c 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
@@ -20,6 +20,7 @@
 #include "clang/AST/TypeBase.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LifetimeStats.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/Utils.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
 #include "llvm/Support/raw_ostream.h"
 
 namespace clang::lifetimes::internal {
@@ -117,15 +118,17 @@ class OriginList {
   OriginList *InnerList = nullptr;
 };
 
-bool hasOrigins(QualType QT);
-bool hasOrigins(const Expr *E);
 bool doesDeclHaveStorage(const ValueDecl *D);
 
 /// Manages the creation, storage, and retrieval of origins for pointer-like
 /// variables and expressions.
 class OriginManager {
 public:
-  explicit OriginManager(ASTContext &AST, const Decl *D);
+  explicit OriginManager(ASTContext &AST);
+
+  /// Must be called after collectLifetimeboundOriginTypes() to ensure
+  /// ThisOrigins reflects the complete set of tracked types.
+  void initializeThisOrigins(const Decl *D);
 
   /// Gets or creates the OriginList for a given ValueDecl.
   ///
@@ -155,11 +158,20 @@ class OriginManager {
 
   unsigned getNumOrigins() const { return NextOriginID.Value; }
 
+  bool hasOrigins(QualType QT) const;
+  bool hasOrigins(const Expr *E) const;
+
   void dump(OriginID OID, llvm::raw_ostream &OS) const;
 
   /// Collects statistics about expressions that lack associated origins.
   void collectMissingOrigins(Stmt &FunctionBody, LifetimeSafetyStats &LSStats);
 
+  /// Pre-scans the function body (and constructor init lists) to discover
+  /// return types of [[clang::lifetimebound]] calls, registering them for
+  /// origin tracking.
+  void collectLifetimeboundOriginTypes(AnalysisDeclContext &AC);
+  void registerLifetimeboundOriginType(QualType QT);
+
 private:
   OriginID getNextOriginID() { return NextOriginID++; }
 
@@ -178,6 +190,10 @@ class OriginManager {
   llvm::DenseMap<const clang::ValueDecl *, OriginList *> DeclToList;
   llvm::DenseMap<const clang::Expr *, OriginList *> ExprToList;
   std::optional<OriginList *> ThisOrigins;
+  /// Types that are not inherently pointer-like but require origin tracking
+  /// because they are returned from functions with [[clang::lifetimebound]]
+  /// parameters.
+  llvm::DenseSet<const Type *> LifetimeboundOriginTypes;
 };
 } // namespace clang::lifetimes::internal
 
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp 
b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 3259505584c9f..890f1d2ba3a7b 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -36,6 +36,14 @@ OriginList *FactsGenerator::getOriginsList(const Expr &E) {
   return FactMgr.getOriginMgr().getOrCreateList(&E);
 }
 
+bool FactsGenerator::hasOrigins(QualType QT) const {
+  return FactMgr.getOriginMgr().hasOrigins(QT);
+}
+
+bool FactsGenerator::hasOrigins(const Expr *E) const {
+  return FactMgr.getOriginMgr().hasOrigins(E);
+}
+
 /// Propagates origin information from Src to Dst through all levels of
 /// indirection, creating OriginFlowFacts at each level.
 ///
@@ -182,14 +190,13 @@ void FactsGenerator::VisitCXXConstructExpr(const 
CXXConstructExpr *CCE) {
     handleGSLPointerConstruction(CCE);
     return;
   }
-  // Implicit copy/move constructors of lambda closures lack
-  // [[clang::lifetimebound]], so `handleFunctionCall` cannot propagate 
origins.
-  // Handle them directly to keep the origin chain intact (e.g., `return
-  // lambda;` copies the closure).
-  if (const auto *RD = CCE->getType()->getAsCXXRecordDecl();
-      RD && RD->isLambda() &&
-      CCE->getConstructor()->isCopyOrMoveConstructor() &&
-      CCE->getNumArgs() == 1) {
+  // For defaulted (implicit or `= default`) copy/move constructors, propagate
+  // origins directly. User-defined copy/move constructors have opaque 
semantics
+  // and fall through to `handleFunctionCall`, where [[clang::lifetimebound]] 
is
+  // needed to propagate origins.
+  if (CCE->getConstructor()->isCopyOrMoveConstructor() &&
+      CCE->getConstructor()->isDefaulted() && CCE->getNumArgs() == 1 &&
+      hasOrigins(CCE->getType())) {
     const Expr *Arg = CCE->getArg(0);
     if (OriginList *ArgList = getRValueOrigins(Arg, getOriginsList(*Arg))) {
       flow(getOriginsList(*CCE), ArgList, /*Kill=*/true);
@@ -398,8 +405,20 @@ void FactsGenerator::VisitCXXOperatorCallExpr(const 
CXXOperatorCallExpr *OCE) {
   // and are handled separately.
   if (OCE->getOperator() == OO_Equal && OCE->getNumArgs() == 2 &&
       hasOrigins(OCE->getArg(0)->getType())) {
-    handleAssignment(OCE->getArg(0), OCE->getArg(1));
-    return;
+    // Pointer-like types: assignment inherently propagates origins.
+    QualType LHSTy = OCE->getArg(0)->getType();
+    if (LHSTy->isPointerOrReferenceType() || isGslPointerType(LHSTy)) {
+      handleAssignment(OCE->getArg(0), OCE->getArg(1));
+      return;
+    }
+    // Other tracked types: only defaulted operator= propagates origins.
+    // User-defined operator= has opaque semantics, so don't handle them now.
+    if (const auto *MD =
+            dyn_cast_or_null<CXXMethodDecl>(OCE->getDirectCallee());
+        MD && MD->isDefaulted()) {
+      handleAssignment(OCE->getArg(0), OCE->getArg(1));
+      return;
+    }
   }
   VisitCallExpr(OCE);
 }
@@ -662,7 +681,7 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
   auto IsArgLifetimeBound = [FD](unsigned I) -> bool {
     const ParmVarDecl *PVD = nullptr;
     if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
-        Method && Method->isInstance()) {
+        Method && Method->isInstance() && !isa<CXXConstructorDecl>(FD)) {
       if (I == 0)
         // For the 'this' argument, the attribute is on the method itself.
         return implicitObjectParamIsLifetimeBound(Method) ||
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp 
b/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp
index 714f979fa5ee7..56a187202d8fa 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp
@@ -71,6 +71,9 @@ void LifetimeSafetyAnalysis::run() {
 
   FactMgr = std::make_unique<FactManager>(AC, Cfg);
 
+  FactMgr->getOriginMgr().collectLifetimeboundOriginTypes(AC);
+  FactMgr->getOriginMgr().initializeThisOrigins(AC.getDecl());
+
   FactsGenerator FactGen(*FactMgr, AC);
   FactGen.run();
 
diff --git a/clang/lib/Analysis/LifetimeSafety/Origins.cpp 
b/clang/lib/Analysis/LifetimeSafety/Origins.cpp
index 0122f7a734541..2655835317d9b 100644
--- a/clang/lib/Analysis/LifetimeSafety/Origins.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Origins.cpp
@@ -18,6 +18,7 @@
 #include "clang/AST/TypeBase.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
 #include "clang/Analysis/Analyses/LifetimeSafety/LifetimeStats.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
 #include "llvm/ADT/StringMap.h"
 
 namespace clang::lifetimes::internal {
@@ -29,10 +30,10 @@ class MissingOriginCollector
 public:
   MissingOriginCollector(
       const llvm::DenseMap<const clang::Expr *, OriginList *> 
&ExprToOriginList,
-      LifetimeSafetyStats &LSStats)
-      : ExprToOriginList(ExprToOriginList), LSStats(LSStats) {}
+      const OriginManager &OM, LifetimeSafetyStats &LSStats)
+      : ExprToOriginList(ExprToOriginList), OM(OM), LSStats(LSStats) {}
   bool VisitExpr(Expr *E) {
-    if (!hasOrigins(E))
+    if (!OM.hasOrigins(E))
       return true;
     // Check if we have an origin for this expression.
     if (!ExprToOriginList.contains(E)) {
@@ -46,13 +47,60 @@ class MissingOriginCollector
 
 private:
   const llvm::DenseMap<const clang::Expr *, OriginList *> &ExprToOriginList;
+  const OriginManager &OM;
   LifetimeSafetyStats &LSStats;
 };
+
+class LifetimeboundOriginTypeCollector
+    : public RecursiveASTVisitor<LifetimeboundOriginTypeCollector> {
+public:
+  LifetimeboundOriginTypeCollector(OriginManager &OM) : OM(OM) {}
+
+  bool VisitCallExpr(const CallExpr *CE) {
+    if (const auto *FD = CE->getDirectCallee())
+      collect(FD, FD->getReturnType());
+    return true;
+  }
+
+  bool VisitCXXConstructExpr(const CXXConstructExpr *CCE) {
+    collect(CCE->getConstructor(), CCE->getType());
+    return true;
+  }
+
+  bool shouldVisitLambdaBody() const { return false; }
+
+private:
+  OriginManager &OM;
+
+  void collect(const FunctionDecl *FD, QualType RetType) {
+    if (!FD)
+      return;
+    FD = getDeclWithMergedLifetimeBoundAttrs(FD);
+
+    if (const auto *MD = dyn_cast<CXXMethodDecl>(FD);
+        MD && MD->isInstance() && !isa<CXXConstructorDecl>(MD) &&
+        implicitObjectParamIsLifetimeBound(MD)) {
+      OM.registerLifetimeboundOriginType(RetType);
+      return;
+    }
+
+    for (const auto *Param : FD->parameters()) {
+      if (Param->hasAttr<LifetimeBoundAttr>()) {
+        OM.registerLifetimeboundOriginType(RetType);
+        return;
+      }
+    }
+  }
+};
+
 } // namespace
 
-bool hasOrigins(QualType QT) {
+bool OriginManager::hasOrigins(QualType QT) const {
   if (QT->isPointerOrReferenceType() || isGslPointerType(QT))
     return true;
+  if (LifetimeboundOriginTypes.contains(
+          QT->getCanonicalTypeUnqualified().getTypePtr()))
+    return true;
   const auto *RD = QT->getAsCXXRecordDecl();
   if (!RD)
     return false;
@@ -70,7 +118,9 @@ bool hasOrigins(QualType QT) {
 ///
 /// An expression has origins if:
 /// - It's a glvalue (has addressable storage), OR
-/// - Its type is pointer-like (pointer, reference, or gsl::Pointer)
+/// - Its type is pointer-like (pointer, reference, or gsl::Pointer), OR
+/// - Its type is registered for origin tracking (e.g., return type of a
+/// [[clang::lifetimebound]] function)
 ///
 /// Examples:
 /// - `int x; x` : has origin (glvalue)
@@ -78,7 +128,7 @@ bool hasOrigins(QualType QT) {
 /// - `std::string_view{}` : has 1 origin (prvalue of pointer type)
 /// - `42` : no origin (prvalue of non-pointer type)
 /// - `x + y` : (where x, y are int) → no origin (prvalue of non-pointer type)
-bool hasOrigins(const Expr *E) {
+bool OriginManager::hasOrigins(const Expr *E) const {
   return E->isGLValue() || hasOrigins(E->getType());
 }
 
@@ -99,8 +149,9 @@ bool doesDeclHaveStorage(const ValueDecl *D) {
   return !D->getType()->isReferenceType();
 }
 
-OriginManager::OriginManager(ASTContext &AST, const Decl *D) : AST(AST) {
-  // Create OriginList for 'this' expr.
+OriginManager::OriginManager(ASTContext &AST) : AST(AST) {}
+
+void OriginManager::initializeThisOrigins(const Decl *D) {
   const auto *MD = llvm::dyn_cast_or_null<CXXMethodDecl>(D);
   if (!MD || !MD->isInstance())
     return;
@@ -232,8 +283,27 @@ const Origin &OriginManager::getOrigin(OriginID ID) const {
 
 void OriginManager::collectMissingOrigins(Stmt &FunctionBody,
                                           LifetimeSafetyStats &LSStats) {
-  MissingOriginCollector Collector(this->ExprToList, LSStats);
+  MissingOriginCollector Collector(this->ExprToList, *this, LSStats);
   Collector.TraverseStmt(const_cast<Stmt *>(&FunctionBody));
 }
 
+void OriginManager::collectLifetimeboundOriginTypes(AnalysisDeclContext &AC) {
+  LifetimeboundOriginTypeCollector Collector(*this);
+  if (Stmt *Body = AC.getBody())
+    Collector.TraverseStmt(Body);
+  if (const auto *CD = dyn_cast<CXXConstructorDecl>(AC.getDecl()))
+    for (const auto *Init : CD->inits())
+      Collector.TraverseStmt(Init->getInit());
+}
+
+void OriginManager::registerLifetimeboundOriginType(QualType QT) {
+  // TODO: Support [[gsl::Owner]] return types. For now, skip them because they
+  // change owner origin-list shape and can break GSL construction flow.
+  if (!QT->getAsCXXRecordDecl() || isGslOwnerType(QT) || hasOrigins(QT))
+    return;
+
+  LifetimeboundOriginTypes.insert(
+      QT->getCanonicalTypeUnqualified().getTypePtr());
+}
+
 } // namespace clang::lifetimes::internal
diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp 
b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
index a725119444e2f..d58f23e4b554c 100644
--- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
@@ -576,12 +576,13 @@ struct FooView {
   FooView(const Foo& foo [[clang::lifetimebound]]);
 };
 FooView test3(int i, std::optional<Foo> a) {
-  // FIXME: Detect this using the CFG-based lifetime analysis.
-  //        Origin tracking for non-pointers type retured from lifetimebound 
fn is missing.
-  //        https://github.com/llvm/llvm-project/issues/163600
   if (i)
-    return *a; // expected-warning {{address of stack memory}}
-  return a.value(); // expected-warning {{address of stack memory}}
+    return *a; // expected-warning {{address of stack memory}} \
+               // cfg-warning {{address of stack memory is returned later}} \
+               // cfg-note {{returned here}}
+  return a.value(); // expected-warning {{address of stack memory}} \
+                    // cfg-warning {{address of stack memory is returned 
later}} \
+                    // cfg-note {{returned here}}
 }
 } // namespace GH93386
 
@@ -591,11 +592,10 @@ struct UrlAnalyzed {
 };
 std::string StrCat(std::string_view, std::string_view);
 void test1() {
-  // FIXME: Detect this using the CFG-based lifetime analysis.
-  //        Origin tracking for non-pointers type retured from lifetimebound 
fn is missing.
-  //        https://github.com/llvm/llvm-project/issues/163600
-  UrlAnalyzed url(StrCat("abc", "bcd")); // expected-warning {{object backing 
the pointer will be destroyed}}
-  use(url);
+  UrlAnalyzed url(StrCat("abc", "bcd")); // expected-warning {{object backing 
the pointer will be destroyed}} \
+                                         // cfg-warning {{object whose 
reference is captured does not live long enough}} \
+                                         // cfg-note {{destroyed here}}
+  use(url);                              // cfg-note {{later used here}}
 }
 
 std::string_view ReturnStringView(std::string_view abc 
[[clang::lifetimebound]]);
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp 
b/clang/test/Sema/warn-lifetime-safety.cpp
index bd09bb70e9a11..a8c0efeb875c0 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -909,7 +909,6 @@ void lifetimebound_return_reference() {
   (void)*ptr;             // expected-note {{later used here}}
 }
 
-// FIXME: No warning for non gsl::Pointer types. Origin tracking is only 
supported for pointer types.
 struct LifetimeBoundCtor {
   LifetimeBoundCtor();
   LifetimeBoundCtor(const MyObj& obj [[clang::lifetimebound]]);
@@ -919,9 +918,9 @@ void lifetimebound_ctor() {
   LifetimeBoundCtor v;
   {
     MyObj obj;
-    v = obj;
-  }
-  (void)v;
+    v = obj; // expected-warning {{object whose reference is captured does not 
live long enough}}
+  }          // expected-note {{destroyed here}}
+  (void)v;   // expected-note {{later used here}}
 }
 
 View lifetimebound_return_of_local() {
@@ -2102,3 +2101,245 @@ void pointer_in_array_use_after_scope() {
 }
 
 } // namespace array
+
+namespace track_origins_for_lifetimebound_record_type {
+
+template <class T> void use(T);
+
+struct S {
+  S();
+  S(const std::string &s [[clang::lifetimebound]]);
+
+  S return_self_after_registration() const;
+};
+
+S getS(const std::string &s [[clang::lifetimebound]]);
+
+void from_free_function() {
+  S s = getS(std::string("temp")); // expected-warning {{object whose 
reference is captured does not live long enough}} \
+                                   // expected-note {{destroyed here}}
+  use(s);                          // expected-note {{later used here}}
+}
+
+void from_constructor() {
+  S s(std::string("temp")); // expected-warning {{object whose reference is 
captured does not live long enough}} \
+                              // expected-note {{destroyed here}}
+  use(s); // expected-note {{later used here}}
+}
+
+struct Factory {
+  S make(const std::string &s [[clang::lifetimebound]]);
+  static S create(const std::string &s [[clang::lifetimebound]]);
+  S makeThis() const [[clang::lifetimebound]];
+};
+
+void from_method() {
+  Factory f;
+  S s = f.make(std::string("temp")); // expected-warning {{object whose 
reference is captured does not live long enough}} \
+                                     // expected-note {{destroyed here}}
+  use(s);                            // expected-note {{later used here}}
+}
+
+void from_static_method() {
+  S s = Factory::create(std::string("temp")); // expected-warning {{object 
whose reference is captured does not live long enough}} \
+                                              // expected-note {{destroyed 
here}}
+  use(s);                                     // expected-note {{later used 
here}}
+}
+
+void from_lifetimebound_this_method() {
+  S value;
+  {
+    Factory f;
+    value = f.makeThis(); // expected-warning {{object whose reference is 
captured does not live long enough}}
+  } // expected-note {{destroyed here}}
+  use(value); // expected-note {{later used here}}
+}
+
+void across_scope() {
+  S s{};
+  {
+    std::string str{"abc"};
+    s = getS(str); // expected-warning {{object whose reference is captured 
does not live long enough}}
+  }                // expected-note {{destroyed here}}
+  use(s); // expected-note {{later used here}}
+}
+
+void same_scope() {
+  std::string str{"abc"};
+  S s = getS(str);
+  use(s);
+}
+
+S copy_propagation() {
+  std::string str{"abc"};
+  S a = getS(str); // expected-warning {{address of stack memory is returned 
later}}
+  S b = a;
+  return b; // expected-note {{returned here}}
+}
+
+void assignment_propagation() {
+  S a, b;
+  {
+    std::string str{"abc"};
+    a = getS(str); // expected-warning {{object whose reference is captured 
does not live long enough}}
+    b = a;
+  }                // expected-note {{destroyed here}}
+  use(b);          // expected-note {{later used here}}
+}
+
+S getSNoAnnotation(const std::string &s);
+
+void no_annotation() {
+  S s = getSNoAnnotation(std::string("temp"));
+  use(s);
+}
+
+void mix_annotated_and_not() {
+  S s1 = getS(std::string("temp")); // expected-warning {{object whose 
reference is captured does not live long enough}} \
+                                    // expected-note {{destroyed here}}
+  S s2 = getSNoAnnotation(std::string("temp"));
+  use(s1); // expected-note {{later used here}}
+  use(s2);
+}
+
+S getS2(const std::string &a [[clang::lifetimebound]], const std::string &b 
[[clang::lifetimebound]]);
+
+S multiple_lifetimebound_params() {
+  std::string str{"abc"};
+  S s = getS2(str, std::string("temp")); // expected-warning {{address of 
stack memory is returned later}} \
+                                         // expected-warning {{object whose 
reference is captured does not live long enough}} \
+                                         // expected-note {{destroyed here}}
+  return s;                              // expected-note {{returned here}} \
+                                         // expected-note {{later used here}}
+}
+
+int getInt(const std::string &s [[clang::lifetimebound]]);
+
+void primitive_return() {
+  int i = getInt(std::string("temp"));
+  use(i);
+}
+
+template <class T>
+T make(const std::string &s [[clang::lifetimebound]]);
+
+void from_template_instantiation() {
+  S s = make<S>(std::string("temp")); // expected-warning {{object whose 
reference is captured does not live long enough}} \
+                                      // expected-note {{destroyed here}}
+  use(s);                             // expected-note {{later used here}}
+}
+
+struct FieldInitFromLifetimebound {
+  S value; // function-note {{this field dangles}}
+  FieldInitFromLifetimebound() : value(getS(std::string("temp"))) {} // 
function-warning {{address of stack memory escapes to a field}}
+};
+
+S S::return_self_after_registration() const {
+  std::string s{"abc"};
+  getS(s);
+  return *this;
+}
+
+struct SWithUserDefinedCopyLikeOps {
+  SWithUserDefinedCopyLikeOps();
+  SWithUserDefinedCopyLikeOps(const std::string &s [[clang::lifetimebound]]) : 
owned(s), data(s) {}
+
+  SWithUserDefinedCopyLikeOps(const SWithUserDefinedCopyLikeOps &other) : 
owned("copy"), data(owned) {}
+
+  SWithUserDefinedCopyLikeOps &operator=(const SWithUserDefinedCopyLikeOps &) {
+    owned = "copy";
+    data = owned;
+    return *this;
+  }
+
+  std::string owned;
+  std::string_view data;
+};
+
+SWithUserDefinedCopyLikeOps getSWithUserDefinedCopyLikeOps(const std::string 
&s [[clang::lifetimebound]]);
+
+SWithUserDefinedCopyLikeOps 
user_defined_copy_ctor_should_not_assume_origin_propagation() {
+  std::string str{"abc"};
+  SWithUserDefinedCopyLikeOps s = getSWithUserDefinedCopyLikeOps(str);
+  SWithUserDefinedCopyLikeOps copy = s; // Copy is rescued by user-defined 
copy constructor, so should not warn.
+  return copy;
+}
+
+void user_defined_assignment_should_not_assume_origin_propagation() {
+  SWithUserDefinedCopyLikeOps dst;
+  {
+    std::string str{"abc"};
+    SWithUserDefinedCopyLikeOps src = getSWithUserDefinedCopyLikeOps(str);
+    dst = src;
+  }
+  use(dst);
+}
+
+struct SWithOriginPropagatingCopy {
+  SWithOriginPropagatingCopy();
+  SWithOriginPropagatingCopy(const std::string &s [[clang::lifetimebound]]) : 
data(s) {}
+  SWithOriginPropagatingCopy(const SWithOriginPropagatingCopy &other) : 
data(other.data) {}
+  std::string_view data;
+};
+
+SWithOriginPropagatingCopy getSWithOriginPropagatingCopy(const std::string &s 
[[clang::lifetimebound]]);
+
+// FIXME: False negative. User-defined copy ctor may propagate origins.
+SWithOriginPropagatingCopy user_defined_copy_with_origin_propagation() {
+  std::string str{"abc"};
+  SWithOriginPropagatingCopy s = getSWithOriginPropagatingCopy(str);
+  SWithOriginPropagatingCopy copy = s;
+  return copy; // Should warn.
+}
+
+struct DefaultedOuter {
+  DefaultedOuter();
+  DefaultedOuter(const std::string &s [[clang::lifetimebound]]) : inner(s) {}
+  SWithUserDefinedCopyLikeOps inner;
+};
+
+DefaultedOuter getDefaultedOuter(const std::string &s 
[[clang::lifetimebound]]);
+
+// FIXME: False positive. The defaulted outer copy ctor invokes
+// SWithUserDefinedCopyLikeOps's user-defined copy ctor, so `copy` should be
+// semantically safe.
+DefaultedOuter nested_defaulted_outer_with_user_defined_inner() {
+  std::string str{"abc"};
+  DefaultedOuter o = getDefaultedOuter(str); // expected-warning {{address of 
stack memory is returned later}}
+  DefaultedOuter copy = o;
+  return copy; // expected-note {{returned here}}
+}
+
+std::string_view getSV(S s [[clang::lifetimebound]]);
+
+// FIXME: False negative. Non-pointer/ref/gsl::Pointer parameter types marked
+// [[clang::lifetimebound]] are not registered for origin tracking.
+void dangling_view_from_non_pointer_param() {
+  std::string_view sv;
+  {
+    S s;
+    sv = getSV(s);
+  }
+  use(sv); // Should warn.
+}
+
+const S &getRef(const std::string &s [[clang::lifetimebound]]);
+
+// FIXME: False negative. The analysis tracks the returned reference,
+// but loses that information when it is copied into a new `S` object.
+S from_ref() {
+  std::string str{"abc"};
+  S s = getRef(str);
+  return s; // Should warn.
+}
+
+MyObj getMyObj(const MyObj &obj [[clang::lifetimebound]]);
+
+void gsl_owner_return_does_not_crash() {
+  MyObj obj;
+  View v = obj;
+  getMyObj(obj);
+  use(v);
+}
+
+} // namespace track_origins_for_lifetimebound_record_type

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to