https://github.com/benedekaibas updated 
https://github.com/llvm/llvm-project/pull/214823

>From e52312c6db02e6fdec905517167dced615c5212b Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Fri, 7 Aug 2026 00:20:33 +0200
Subject: [PATCH 1/8] [analyzer] Add aggregate value tracking to the
 LifetimeModeling checker

---
 .../Checkers/LifetimeModeling.cpp             | 49 ++++++++++++
 .../Checkers/LifetimeModeling.h               |  4 +
 clang/test/Analysis/lifetime-bound.cpp        | 76 +++++++++++++++----
 3 files changed, 113 insertions(+), 16 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp 
b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
index 2fab20b199f01..bbaa53d9d8473 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
@@ -105,6 +105,44 @@ std::string lifetime_modeling::getRegionName(const 
MemRegion *Reg) {
   return "the region";
 }
 
+// FIXME: Nested structs are not yet handled.
+SmallVector<const MemRegion *, 4>
+lifetime_modeling::getRegionsFromAggrVal(SVal Val, CheckerContext &C) {
+  SmallVector<const MemRegion *, 4> Reg;
+
+  if (auto LCV = Val.getAs<nonloc::LazyCompoundVal>()) {
+    const TypedValueRegion *LCVRegion = LCV->getRegion();
+    QualType T = LCVRegion->getValueType();
+    MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager();
+    StoreManager &StoreMgr = C.getState()->getStateManager().getStoreManager();
+
+    if (const RecordType *RT = T->getAsStructureType()) {
+      const RecordDecl *RD = RT->getDecl()->getDefinition();
+      if (!RD)
+        return Reg;
+
+      for (const auto *I : RD->fields()) {
+        // Unnamed bitfields in a struct are not relevant for the analysis
+        // so the checker should skip them and jsut continue.
+        // CallAndMessageChecker has the same logic.
+        if (I->isUnnamedBitField())
+          continue;
+
+        const FieldRegion *FR = MemMgr.getFieldRegion(I, LCVRegion);
+        SVal V = StoreMgr.getBinding(LCV->getStore(), loc::MemRegionVal(FR));
+        if (const MemRegion *R = V.getAsRegion())
+          Reg.push_back(R);
+      }
+    }
+  } else if (auto CV = Val.getAs<nonloc::CompoundVal>()) {
+    for (SVal CVVal : *CV) {
+      if (const MemRegion *CVReg = CVVal.getAsRegion())
+        Reg.push_back(CVReg);
+    }
+  }
+  return Reg;
+}
+
 void LifetimeModeling::checkPostCall(const CallEvent &Call,
                                      CheckerContext &C) const {
   ProgramStateRef State = C.getState();
@@ -118,6 +156,12 @@ void LifetimeModeling::checkPostCall(const CallEvent &Call,
     return;
 
   SVal RetVal = Call.getReturnValue();
+  SmallVector<const MemRegion *, 4> AggrRegs =
+      lifetime_modeling::getRegionsFromAggrVal(RetVal, C);
+
+  for (const MemRegion *I : AggrRegs) {
+    State = bindSource(State, RetVal, I);
+  }
 
   for (const ParmVarDecl *PVD : FD->parameters()) {
     if (PVD->hasAttr<LifetimeBoundAttr>()) {
@@ -174,6 +218,11 @@ void LifetimeModeling::checkDeadSymbols(SymbolReaper 
&SymReaper,
         S && SymReaper.isLive(S))
       continue;
 
+    if (llvm::any_of(
+            lifetime_modeling::getRegionsFromAggrVal(Val, C),
+            [&](const MemRegion *R) { return SymReaper.isLiveRegion(R); }))
+      continue;
+
     State = State->remove<LifetimeBoundMap>(Val);
   }
 
diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h 
b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h
index 8d6c8e4882d1c..5f1950944c878 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h
@@ -22,6 +22,10 @@ bool isBoundToLifetimeSource(ProgramStateRef State, SVal 
Val);
 /// Returns the descriptive name of the memory region or a placeholder if a
 /// descriptive name cannot be constructed for it.
 std::string getRegionName(const MemRegion *Reg);
+
+/// Returns the MemRegions the fields of an aggregate value (CompoundVal,
+/// LazyCompoundVal) point to.
+SmallVector<const MemRegion *, 4> getRegionsFromAggrVal(SVal Val, 
CheckerContext &C);
 } // namespace clang::ento::lifetime_modeling
 
 #endif // LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_LIFETIMEMODELING_H
diff --git a/clang/test/Analysis/lifetime-bound.cpp 
b/clang/test/Analysis/lifetime-bound.cpp
index d29c37f639993..8a3ba8b3a6f10 100644
--- a/clang/test/Analysis/lifetime-bound.cpp
+++ b/clang/test/Analysis/lifetime-bound.cpp
@@ -155,22 +155,6 @@ void caller_nine() {
   // expected-note-re@-2    {{Origin '&SymRegion{{.*}}' bound to 'first_num', 
'second_num'}}
 }
 
-struct View {
-  int *p;
-};
-View makeView(int &x [[clang::lifetimebound]]);
-
-void clang_analyzer_dumpLifetimeOriginsOf(View);
-
-void caller_view() {
-  int v = 42;
-  View w = makeView(v);
-  // FIXME: Currently none of the maps cover LazyCompoundVal.
-  clang_analyzer_dumpLifetimeOriginsOf(w); // no-warning
-}
-
-
-
 // These are the test cases for testing the correctness of the emitted warning 
from the UseAfterLifetimeEnd checker.
 
 // Return value bound to annotated param cases.
@@ -410,3 +394,63 @@ void no_dangling_by_value_argument() {
   // The returned reference does not dangle.
   takes_by_value(BoundToSelf());
 }
+
+struct F {
+  int *p;
+};
+
+F makeView(int &x [[clang::lifetimebound]]) { return F{&x}; }
+
+F whole_struct_return_lazycompoundval() {
+  int x = 5; // expected-note {{'x' initialized here}}
+  return makeView(x);
+  // expected-warning@-1 {{Returning value bound to 'x' that will go out of 
scope}}
+  // expected-note@-2    {{Returning value bound to 'x' that will go out of 
scope}}
+  // expected-warning@-3 {{Address of stack memory associated with local 
variable 'x' returned to caller}}
+  // expected-note@-4    {{Address of stack memory associated with local 
variable 'x' returned to caller}}
+  // expected-warning@-5 {{address of stack memory associated with local 
variable 'x' returned}}
+}
+
+struct PtrPair {
+  int *p;
+  int *q;
+};
+
+int global_v = 4;
+
+PtrPair makePair(int &x [[clang::lifetimebound]]) {
+  return PtrPair{&x, &global_v};
+}
+
+PtrPair return_pair_by_value() {
+  int local = 5; // expected-note {{'local' initialized here}}
+  return makePair(local);
+  // expected-warning@-1 {{Returning value bound to 'local' that will go out 
of scope}}
+  // expected-note@-2    {{Returning value bound to 'local' that will go out 
of scope}}
+  // expected-warning@-3 {{Address of stack memory associated with local 
variable 'local' returned to caller}}
+  // expected-note@-4    {{Address of stack memory associated with local 
variable 'local' returned to caller}}
+  // expected-warning@-5 {{address of stack memory associated with local 
variable 'local' returned}}
+}
+
+struct InnerS {
+  int *p;
+};
+
+struct OuterS {
+  InnerS inner;
+  int *q;
+};
+
+OuterS makeNested(int &x [[clang::lifetimebound]]) {
+  return OuterS{InnerS{&x}};
+}
+
+// FIXME: Nested structs are not yet handled by getRegionsFromAggrVal,
+// that is why this dangling pointer is not yet detected.
+OuterS nested_struct_return_not_yet_detected() {
+  int y = 5;
+  return makeNested(y);
+  // expected-warning@-1 {{Address of stack memory associated with local 
variable 'y' returned to caller}}
+  // expected-note@-2    {{Address of stack memory associated with local 
variable 'y' returned to caller}}
+  // expected-warning@-3 {{address of stack memory associated with local 
variable 'y' returned}}
+}

>From edfc842c3169724de4ecb6376c51d04f0d42af14 Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Fri, 7 Aug 2026 01:01:05 +0200
Subject: [PATCH 2/8] More descriptive FIXME.

---
 clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp 
b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
index bbaa53d9d8473..f447f93207199 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
@@ -105,7 +105,7 @@ std::string lifetime_modeling::getRegionName(const 
MemRegion *Reg) {
   return "the region";
 }
 
-// FIXME: Nested structs are not yet handled.
+// FIXME: Retrieving the MemRegions of nested struct fields is not yet 
supported.
 SmallVector<const MemRegion *, 4>
 lifetime_modeling::getRegionsFromAggrVal(SVal Val, CheckerContext &C) {
   SmallVector<const MemRegion *, 4> Reg;

>From 813b7d8a6a8f741d5f8d68a59230ce354c3a4bc3 Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Fri, 7 Aug 2026 12:08:45 +0200
Subject: [PATCH 3/8] Correct expected-notes.

---
 clang/test/Analysis/lifetime-bound.cpp | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)

diff --git a/clang/test/Analysis/lifetime-bound.cpp 
b/clang/test/Analysis/lifetime-bound.cpp
index 8a3ba8b3a6f10..4624bce2ec204 100644
--- a/clang/test/Analysis/lifetime-bound.cpp
+++ b/clang/test/Analysis/lifetime-bound.cpp
@@ -402,13 +402,14 @@ struct F {
 F makeView(int &x [[clang::lifetimebound]]) { return F{&x}; }
 
 F whole_struct_return_lazycompoundval() {
-  int x = 5; // expected-note {{'x' initialized here}}
+  int x = 5;// expected-note {{'x' initialized here}}
   return makeView(x);
   // expected-warning@-1 {{Returning value bound to 'x' that will go out of 
scope}}
-  // expected-note@-2    {{Returning value bound to 'x' that will go out of 
scope}}
-  // expected-warning@-3 {{Address of stack memory associated with local 
variable 'x' returned to caller}}
-  // expected-note@-4    {{Address of stack memory associated with local 
variable 'x' returned to caller}}
-  // expected-warning@-5 {{address of stack memory associated with local 
variable 'x' returned}}
+  // expected-note@-2    {{Lifetime of 'x' ended here}}
+  // expected-note@-3    {{Value's lifetime bound to the lifetime of 'x' here}}
+  // expected-warning@-4 {{Address of stack memory associated with local 
variable 'x' returned to caller}}
+  // expected-note@-5    {{Address of stack memory associated with local 
variable 'x' returned to caller}}
+  // expected-warning@-6 {{address of stack memory associated with local 
variable 'x' returned}}
 }
 
 struct PtrPair {
@@ -426,10 +427,11 @@ PtrPair return_pair_by_value() {
   int local = 5; // expected-note {{'local' initialized here}}
   return makePair(local);
   // expected-warning@-1 {{Returning value bound to 'local' that will go out 
of scope}}
-  // expected-note@-2    {{Returning value bound to 'local' that will go out 
of scope}}
-  // expected-warning@-3 {{Address of stack memory associated with local 
variable 'local' returned to caller}}
-  // expected-note@-4    {{Address of stack memory associated with local 
variable 'local' returned to caller}}
-  // expected-warning@-5 {{address of stack memory associated with local 
variable 'local' returned}}
+  // expected-note@-2    {{Lifetime of 'local' ended here}}
+  // expected-note@-3    {{Value's lifetime bound to the lifetime of 'local' 
here}}
+  // expected-warning@-4 {{Address of stack memory associated with local 
variable 'local' returned to caller}}
+  // expected-note@-5    {{Address of stack memory associated with local 
variable 'local' returned to caller}}
+  // expected-warning@-6 {{address of stack memory associated with local 
variable 'local' returned}}
 }
 
 struct InnerS {

>From 49dd875fe6d0a0ace1f34313d69fd72ca20558ab Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Fri, 7 Aug 2026 12:12:36 +0200
Subject: [PATCH 4/8] Add space before expected-note.

---
 clang/test/Analysis/lifetime-bound.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/Analysis/lifetime-bound.cpp 
b/clang/test/Analysis/lifetime-bound.cpp
index 4624bce2ec204..c1f7f9f2ffda5 100644
--- a/clang/test/Analysis/lifetime-bound.cpp
+++ b/clang/test/Analysis/lifetime-bound.cpp
@@ -402,7 +402,7 @@ struct F {
 F makeView(int &x [[clang::lifetimebound]]) { return F{&x}; }
 
 F whole_struct_return_lazycompoundval() {
-  int x = 5;// expected-note {{'x' initialized here}}
+  int x = 5; // expected-note {{'x' initialized here}}
   return makeView(x);
   // expected-warning@-1 {{Returning value bound to 'x' that will go out of 
scope}}
   // expected-note@-2    {{Lifetime of 'x' ended here}}

>From a4f634dbb67c05dcd3b17cc8ea93293f23e1c3e1 Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Fri, 7 Aug 2026 12:37:24 +0200
Subject: [PATCH 5/8] Fix formatting issue.

---
 clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h 
b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h
index 5f1950944c878..d6cacfd73a4ef 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h
@@ -25,7 +25,8 @@ std::string getRegionName(const MemRegion *Reg);
 
 /// Returns the MemRegions the fields of an aggregate value (CompoundVal,
 /// LazyCompoundVal) point to.
-SmallVector<const MemRegion *, 4> getRegionsFromAggrVal(SVal Val, 
CheckerContext &C);
+SmallVector<const MemRegion *, 4> getRegionsFromAggrVal(SVal Val,
+                                                        CheckerContext &C);
 } // namespace clang::ento::lifetime_modeling
 
 #endif // LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_LIFETIMEMODELING_H

>From d9a357bc481223cf1369025656373e28646fc71b Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Fri, 7 Aug 2026 13:00:36 +0200
Subject: [PATCH 6/8] Re-run clang format on the comment as well in
 LifetimeModeling.

---
 clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp 
b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
index f447f93207199..ebbccc2c799c0 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
@@ -105,7 +105,8 @@ std::string lifetime_modeling::getRegionName(const 
MemRegion *Reg) {
   return "the region";
 }
 
-// FIXME: Retrieving the MemRegions of nested struct fields is not yet 
supported.
+// FIXME: Retrieving the MemRegions of nested struct fields is not yet
+// supported.
 SmallVector<const MemRegion *, 4>
 lifetime_modeling::getRegionsFromAggrVal(SVal Val, CheckerContext &C) {
   SmallVector<const MemRegion *, 4> Reg;

>From 62d4f3ce17203c28ffff3357483a9b2883325ffb Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Sat, 8 Aug 2026 13:49:22 +0200
Subject: [PATCH 7/8] Address review comments.

---
 .../Checkers/LifetimeModeling.cpp             | 90 ++++++++++++-------
 .../Checkers/LifetimeModeling.h               |  4 +-
 clang/test/Analysis/lifetime-bound.cpp        | 39 +++++---
 3 files changed, 86 insertions(+), 47 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp 
b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
index ebbccc2c799c0..882c90d10d72a 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
@@ -105,45 +105,68 @@ std::string lifetime_modeling::getRegionName(const 
MemRegion *Reg) {
   return "the region";
 }
 
-// FIXME: Retrieving the MemRegions of nested struct fields is not yet
-// supported.
-SmallVector<const MemRegion *, 4>
-lifetime_modeling::getRegionsFromAggrVal(SVal Val, CheckerContext &C) {
+namespace clang::ento::lifetime_modeling {
+
+static SmallVector<const MemRegion *, 4>
+getRegionsFromAggrVal(nonloc::LazyCompoundVal LCV, CheckerContext &C) {
   SmallVector<const MemRegion *, 4> Reg;
 
-  if (auto LCV = Val.getAs<nonloc::LazyCompoundVal>()) {
-    const TypedValueRegion *LCVRegion = LCV->getRegion();
-    QualType T = LCVRegion->getValueType();
-    MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager();
-    StoreManager &StoreMgr = C.getState()->getStateManager().getStoreManager();
-
-    if (const RecordType *RT = T->getAsStructureType()) {
-      const RecordDecl *RD = RT->getDecl()->getDefinition();
-      if (!RD)
-        return Reg;
-
-      for (const auto *I : RD->fields()) {
-        // Unnamed bitfields in a struct are not relevant for the analysis
-        // so the checker should skip them and jsut continue.
-        // CallAndMessageChecker has the same logic.
-        if (I->isUnnamedBitField())
-          continue;
-
-        const FieldRegion *FR = MemMgr.getFieldRegion(I, LCVRegion);
-        SVal V = StoreMgr.getBinding(LCV->getStore(), loc::MemRegionVal(FR));
-        if (const MemRegion *R = V.getAsRegion())
-          Reg.push_back(R);
-      }
-    }
-  } else if (auto CV = Val.getAs<nonloc::CompoundVal>()) {
-    for (SVal CVVal : *CV) {
-      if (const MemRegion *CVReg = CVVal.getAsRegion())
-        Reg.push_back(CVReg);
+  const TypedValueRegion *LCVRegion = LCV.getRegion();
+  QualType T = LCVRegion->getValueType();
+  MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager();
+  StoreManager &StoreMgr = C.getState()->getStateManager().getStoreManager();
+
+  // FIXME: getAsRecordDecl() also includes unions which need different
+  // handling. Reading a binding for every member of a union may produce 
regions
+  // that are not actually live.
+  if (const RecordDecl *RD = T->getAsRecordDecl()) {
+    RD = RD->getDefinition();
+    if (!RD)
+      return Reg;
+
+    for (const auto *FD : RD->fields()) {
+      // Unnamed bitfields in a record are not relevant for the analysis
+      // so the checker should skip them and just continue.
+      // CallAndMessageChecker has the same logic.
+      if (FD->isUnnamedBitField())
+        continue;
+
+      const FieldRegion *FR = MemMgr.getFieldRegion(FD, LCVRegion);
+      SVal V = StoreMgr.getBinding(LCV.getStore(), loc::MemRegionVal(FR));
+      if (const MemRegion *R = V.getAsRegion())
+        Reg.push_back(R);
     }
   }
   return Reg;
 }
 
+static SmallVector<const MemRegion *, 4>
+getRegionsFromAggrVal(nonloc::CompoundVal CV, CheckerContext &C) {
+  SmallVector<const MemRegion *, 4> Reg;
+  for (SVal CVVal : CV) {
+    if (const MemRegion *CVReg = CVVal.getAsRegion())
+      Reg.push_back(CVReg);
+  }
+  return Reg;
+}
+
+} // namespace clang::ento::lifetime_modeling
+
+// FIXME: Retrieving the MemRegions of nested struct fields, base subobjects 
are
+// not yet supported. If a field of the aggregate is an aggregate (nested
+// structs) then no region is extracted for it. Handling it can be done by
+// recursing with non-aggregate fields as the base case.
+SmallVector<const MemRegion *, 4>
+lifetime_modeling::getRegionsFromAggrVal(SVal Val, CheckerContext &C) {
+  if (auto LCV = Val.getAs<nonloc::LazyCompoundVal>())
+    return getRegionsFromAggrVal(*LCV, C);
+
+  if (auto CV = Val.getAs<nonloc::CompoundVal>())
+    return getRegionsFromAggrVal(*CV, C);
+
+  return {};
+}
+
 void LifetimeModeling::checkPostCall(const CallEvent &Call,
                                      CheckerContext &C) const {
   ProgramStateRef State = C.getState();
@@ -221,8 +244,9 @@ void LifetimeModeling::checkDeadSymbols(SymbolReaper 
&SymReaper,
 
     if (llvm::any_of(
             lifetime_modeling::getRegionsFromAggrVal(Val, C),
-            [&](const MemRegion *R) { return SymReaper.isLiveRegion(R); }))
+            [&](const MemRegion *R) { return SymReaper.isLiveRegion(R); })) {
       continue;
+    }
 
     State = State->remove<LifetimeBoundMap>(Val);
   }
diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h 
b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h
index d6cacfd73a4ef..3be3e829f40da 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h
@@ -23,8 +23,8 @@ bool isBoundToLifetimeSource(ProgramStateRef State, SVal Val);
 /// descriptive name cannot be constructed for it.
 std::string getRegionName(const MemRegion *Reg);
 
-/// Returns the MemRegions the fields of an aggregate value (CompoundVal,
-/// LazyCompoundVal) point to.
+/// Returns the MemRegions the fields of an aggregate value
+/// (CompoundVal, LazyCompoundVal) point to.
 SmallVector<const MemRegion *, 4> getRegionsFromAggrVal(SVal Val,
                                                         CheckerContext &C);
 } // namespace clang::ento::lifetime_modeling
diff --git a/clang/test/Analysis/lifetime-bound.cpp 
b/clang/test/Analysis/lifetime-bound.cpp
index c1f7f9f2ffda5..749f264fe53eb 100644
--- a/clang/test/Analysis/lifetime-bound.cpp
+++ b/clang/test/Analysis/lifetime-bound.cpp
@@ -395,13 +395,13 @@ void no_dangling_by_value_argument() {
   takes_by_value(BoundToSelf());
 }
 
-struct F {
+struct IntPtr {
   int *p;
 };
 
-F makeView(int &x [[clang::lifetimebound]]) { return F{&x}; }
+IntPtr makeView(int &x [[clang::lifetimebound]]) { return IntPtr{&x}; }
 
-F whole_struct_return_lazycompoundval() {
+IntPtr whole_struct_return_lazycompoundval() {
   int x = 5; // expected-note {{'x' initialized here}}
   return makeView(x);
   // expected-warning@-1 {{Returning value bound to 'x' that will go out of 
scope}}
@@ -434,25 +434,40 @@ PtrPair return_pair_by_value() {
   // expected-warning@-6 {{address of stack memory associated with local 
variable 'local' returned}}
 }
 
-struct InnerS {
-  int *p;
-};
-
-struct OuterS {
-  InnerS inner;
+struct NestedIntPtr {
+  IntPtr inner;
   int *q;
 };
 
-OuterS makeNested(int &x [[clang::lifetimebound]]) {
-  return OuterS{InnerS{&x}};
+NestedIntPtr makeNested(int &x [[clang::lifetimebound]]) {
+  return NestedIntPtr{IntPtr{&x}};
 }
 
 // FIXME: Nested structs are not yet handled by getRegionsFromAggrVal,
 // that is why this dangling pointer is not yet detected.
-OuterS nested_struct_return_not_yet_detected() {
+NestedIntPtr nested_struct_return_not_yet_detected() {
   int y = 5;
   return makeNested(y);
   // expected-warning@-1 {{Address of stack memory associated with local 
variable 'y' returned to caller}}
   // expected-note@-2    {{Address of stack memory associated with local 
variable 'y' returned to caller}}
   // expected-warning@-3 {{address of stack memory associated with local 
variable 'y' returned}}
 }
+
+struct IntPtrArr {
+  int *arr[4];
+};
+
+IntPtrArr makeIntPtrArr(int &x [[clang::lifetimebound]]) {
+  return IntPtrArr{{&x, &global_v}};
+}
+
+// FIXME: Array fields are not split into their individual elements by
+// getRegionsFromAggrVal, that is why this dangling pointer is not yet
+// detected.
+IntPtrArr return_array_field_not_yet_detected() {
+  int z = 5;
+  return makeIntPtrArr(z);
+  // expected-warning@-1 {{Address of stack memory associated with local 
variable 'z' returned to caller}}
+  // expected-note@-2    {{Address of stack memory associated with local 
variable 'z' returned to caller}}
+  // expected-warning@-3 {{address of stack memory associated with local 
variable 'z' returned}}
+}

>From 24bf85a76ad73ec791534259fa4d7ebb5b0cc9bb Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Sun, 9 Aug 2026 18:35:07 +0200
Subject: [PATCH 8/8] Add FIXME for array aggregates.

---
 clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp 
b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
index 882c90d10d72a..c2995deb3a6e1 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
@@ -117,8 +117,9 @@ getRegionsFromAggrVal(nonloc::LazyCompoundVal LCV, 
CheckerContext &C) {
   StoreManager &StoreMgr = C.getState()->getStateManager().getStoreManager();
 
   // FIXME: getAsRecordDecl() also includes unions which need different
-  // handling. Reading a binding for every member of a union may produce 
regions
-  // that are not actually live.
+  // handling while also returning null on array aggregate types. Reading a
+  // binding for every member of a union may produce regions that are not
+  // actually live.
   if (const RecordDecl *RD = T->getAsRecordDecl()) {
     RD = RD->getDefinition();
     if (!RD)

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

Reply via email to