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

>From 8f6bd5019fdc151da437f6c6c3be6c4c05e0719d Mon Sep 17 00:00:00 2001
From: Alex Wang <[email protected]>
Date: Wed, 4 Mar 2026 17:50:08 -0800
Subject: [PATCH 1/3] [LifetimeSafety] Fix false negative for GSL Owner type
 with arrow operator

---
 .../LifetimeSafety/LifetimeAnnotations.cpp    |  7 +++-
 clang/test/Sema/Inputs/lifetime-analysis.h    |  2 +
 .../Sema/warn-lifetime-analysis-nocfg.cpp     | 40 +++++++++++++++++++
 clang/test/Sema/warn-lifetime-safety.cpp      | 29 ++++++++++++++
 4 files changed, 76 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp 
b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
index 0d3da898137a6..1be35cd698669 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
@@ -152,8 +152,11 @@ bool shouldTrackImplicitObjectArg(const CXXMethodDecl 
*Callee,
     return false;
 
   if (isPointerLikeType(Callee->getReturnType())) {
-    if (!Callee->getIdentifier())
-      return false;
+    if (!Callee->getIdentifier()) {
+      return Callee->getParent()->hasAttr<OwnerAttr>() &&
+             Callee->getOverloadedOperator() ==
+                 OverloadedOperatorKind::OO_Arrow;
+    }
     return IteratorMembers.contains(Callee->getName()) ||
            InnerPointerGetters.contains(Callee->getName()) ||
            ContainerFindFns.contains(Callee->getName());
diff --git a/clang/test/Sema/Inputs/lifetime-analysis.h 
b/clang/test/Sema/Inputs/lifetime-analysis.h
index 1b07f4f13467f..85b5a5fe5e07f 100644
--- a/clang/test/Sema/Inputs/lifetime-analysis.h
+++ b/clang/test/Sema/Inputs/lifetime-analysis.h
@@ -188,6 +188,7 @@ struct unique_ptr {
   ~unique_ptr();
   T* release();
   T &operator*();
+  T *operator->();
   T *get() const;
 };
 
@@ -204,6 +205,7 @@ struct optional {
   template<typename U>
   optional(optional<U>&& __t);
 
+  T *operator->();
   T &operator*() &;
   T &&operator*() &&;
   T &value() &;
diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp 
b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
index 3305e9e270d86..534f075a6a8b9 100644
--- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
@@ -1287,3 +1287,43 @@ void test() {
     const auto ptrTSC = StringTemplateSpecC<char>().data();  // Both have 
attribute         // expected-warning {{temporary whose address is used}}
 }
 } // namespace GH175391
+
+namespace owner_arrow {
+struct TypeParamType {
+  std::string_view name() [[clang::lifetimebound]];
+};
+
+void test_optional_arrow_lifetimebound() {
+  std::string_view a;
+  a = std::optional<TypeParamType>()->name(); // expected-warning {{object 
backing the pointer 'a' will be destroyed at the end of the full-expression}} \
+                                              // cfg-warning {{object whose 
reference is captured does not live long enough}} \
+                                              // cfg-note {{destroyed here}}
+  use(a);                                     // cfg-note {{later used here}}
+}
+
+void test_optional_arrow_data() {
+  const char* p = std::optional<std::string>()->data(); // expected-warning 
{{object backing the pointer will be destroyed at the end of the 
full-expression}} \
+                                                        // cfg-warning 
{{object whose reference is captured does not live long enough}} \
+                                                        // cfg-note 
{{destroyed here}}
+  use(p);                                               // cfg-note {{later 
used here}}
+}
+
+void test_optional_arrow_non_temporary() {
+  std::optional<std::string> opt;
+  const char* p = opt->data();
+  use(p);
+}
+
+void test_unique_ptr_arrow_data() {
+  const char* p = std::unique_ptr<std::string>()->data(); // expected-warning 
{{object backing the pointer will be destroyed at the end of the 
full-expression}} \
+                                                          // cfg-warning 
{{object whose reference is captured does not live long enough}} \
+                                                          // cfg-note 
{{destroyed here}}
+  use(p);                                                 // cfg-note {{later 
used here}}
+}
+
+void test_unique_ptr_arrow_non_temporary() {
+  std::unique_ptr<std::string> up;
+  const char* p = up->data();
+  use(p);
+}
+} // namespace owner_arrow
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp 
b/clang/test/Sema/warn-lifetime-safety.cpp
index 097f3279d8e54..45cf26528d689 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -1746,3 +1746,32 @@ View test3(std::string a) {
   return b;                 // expected-note {{returned here}}
 }
 } // namespace non_trivial_views
+
+namespace OwnerArrowOperator {
+void test_optional_arrow() {
+  const char* p;
+  {
+    std::optional<std::string> opt;
+    p = opt->data();  // expected-warning {{object whose reference is captured 
does not live long enough}}
+  }                   // expected-note {{destroyed here}}
+  (void)*p;           // expected-note {{later used here}}
+}
+
+void test_optional_arrow_lifetimebound() {
+  View v;
+  {
+    std::optional<MyObj> opt;
+    v = opt->getView();  // expected-warning {{object whose reference is 
captured does not live long enough}}
+  }                      // expected-note {{destroyed here}}
+  v.use();               // expected-note {{later used here}}
+}
+
+void test_unique_ptr_arrow() {
+  const char* p;
+  {
+    std::unique_ptr<std::string> up;
+    p = up->data();  // expected-warning {{object whose reference is captured 
does not live long enough}}
+  }                  // expected-note {{destroyed here}}
+  (void)*p;          // expected-note {{later used here}}
+}
+} // namespace OwnerArrowOperator

>From 8951733d6f23cdc206c6a6f677f21ef305b21738 Mon Sep 17 00:00:00 2001
From: Alex Wang <[email protected]>
Date: Thu, 5 Mar 2026 10:29:57 -0800
Subject: [PATCH 2/3] fix false positive for sema analysis

---
 .../LifetimeSafety/LifetimeAnnotations.cpp       | 14 +++++++++++---
 clang/test/Sema/warn-lifetime-analysis-nocfg.cpp | 16 ++++++++++++++++
 clang/test/Sema/warn-lifetime-safety.cpp         |  9 +++++++++
 3 files changed, 36 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp 
b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
index 1be35cd698669..2eb22f768cad9 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
@@ -153,9 +153,17 @@ bool shouldTrackImplicitObjectArg(const CXXMethodDecl 
*Callee,
 
   if (isPointerLikeType(Callee->getReturnType())) {
     if (!Callee->getIdentifier()) {
-      return Callee->getParent()->hasAttr<OwnerAttr>() &&
-             Callee->getOverloadedOperator() ==
-                 OverloadedOperatorKind::OO_Arrow;
+      // e.g., std::optional<T>::operator->() returns T*.
+      if (Callee->getParent()->hasAttr<OwnerAttr>() &&
+          Callee->getOverloadedOperator() == OverloadedOperatorKind::OO_Arrow) 
{
+        if (RunningUnderLifetimeSafety)
+          return true;
+        // For Sema analysis, don't track operator-> when the pointee is a GSL
+        // Pointer (e.g., optional<string_view>), as Sema can't distinguish the
+        // Pointer object's lifetime from the data it observes.
+        return !isGslPointerType(Callee->getReturnType()->getPointeeType());
+      }
+      return false;
     }
     return IteratorMembers.contains(Callee->getName()) ||
            InnerPointerGetters.contains(Callee->getName()) ||
diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp 
b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
index 534f075a6a8b9..315c7ce0964d3 100644
--- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
@@ -1326,4 +1326,20 @@ void test_unique_ptr_arrow_non_temporary() {
   const char* p = up->data();
   use(p);
 }
+
+void test_optional_view_arrow_data() {
+  const char* p;
+  p = std::optional<std::string_view>()->data();
+  (void)*p;
+}
+
+void test_optional_view_arrow() {
+  // FIXME: Sema analysis doesn't warn here because we suppress operator->
+  // tracking when the pointee is a GSL Pointer to avoid false positives (see
+  // test_optional_view_arrow_data). The CFG-based lifetime safety analysis
+  // handles this correctly.
+  std::string_view* p = std::optional<std::string_view>().operator->(); // 
cfg-warning {{object whose reference is captured does not live long enough}} \
+                                                                        // 
cfg-note {{destroyed here}}
+  (void)*p;                                                             // 
cfg-note {{later used here}}
+}
 } // namespace owner_arrow
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp 
b/clang/test/Sema/warn-lifetime-safety.cpp
index 45cf26528d689..a75c70aa3674a 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -1774,4 +1774,13 @@ void test_unique_ptr_arrow() {
   }                  // expected-note {{destroyed here}}
   (void)*p;          // expected-note {{later used here}}
 }
+
+void test_optional_view_arrow() {
+    const char* p;
+    {
+        std::optional<std::string_view> opt;
+        p = opt->data();
+    }
+    (void)*p;
+}
 } // namespace OwnerArrowOperator

>From bb4bbcdcd9d32f91c0afc6be7477736920e3a7be Mon Sep 17 00:00:00 2001
From: Alex Wang <[email protected]>
Date: Thu, 5 Mar 2026 12:04:07 -0800
Subject: [PATCH 3/3] remove handling for sema analysis

---
 .../LifetimeSafety/LifetimeAnnotations.cpp    | 15 ++---
 .../Sema/warn-lifetime-analysis-nocfg.cpp     | 58 +------------------
 2 files changed, 6 insertions(+), 67 deletions(-)

diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp 
b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
index 2eb22f768cad9..6cbb03720ed06 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
@@ -154,16 +154,11 @@ bool shouldTrackImplicitObjectArg(const CXXMethodDecl 
*Callee,
   if (isPointerLikeType(Callee->getReturnType())) {
     if (!Callee->getIdentifier()) {
       // e.g., std::optional<T>::operator->() returns T*.
-      if (Callee->getParent()->hasAttr<OwnerAttr>() &&
-          Callee->getOverloadedOperator() == OverloadedOperatorKind::OO_Arrow) 
{
-        if (RunningUnderLifetimeSafety)
-          return true;
-        // For Sema analysis, don't track operator-> when the pointee is a GSL
-        // Pointer (e.g., optional<string_view>), as Sema can't distinguish the
-        // Pointer object's lifetime from the data it observes.
-        return !isGslPointerType(Callee->getReturnType()->getPointeeType());
-      }
-      return false;
+      return RunningUnderLifetimeSafety
+                 ? Callee->getParent()->hasAttr<OwnerAttr>() &&
+                       Callee->getOverloadedOperator() ==
+                           OverloadedOperatorKind::OO_Arrow
+                 : false;
     }
     return IteratorMembers.contains(Callee->getName()) ||
            InnerPointerGetters.contains(Callee->getName()) ||
diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp 
b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
index 315c7ce0964d3..65d323b27c977 100644
--- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
@@ -1286,60 +1286,4 @@ void test() {
     const auto ptrTSB = StringTemplateSpecB<char>().data();  // 
Definition-only attribute   // expected-warning {{temporary whose address is 
used}}
     const auto ptrTSC = StringTemplateSpecC<char>().data();  // Both have 
attribute         // expected-warning {{temporary whose address is used}}
 }
-} // namespace GH175391
-
-namespace owner_arrow {
-struct TypeParamType {
-  std::string_view name() [[clang::lifetimebound]];
-};
-
-void test_optional_arrow_lifetimebound() {
-  std::string_view a;
-  a = std::optional<TypeParamType>()->name(); // expected-warning {{object 
backing the pointer 'a' will be destroyed at the end of the full-expression}} \
-                                              // cfg-warning {{object whose 
reference is captured does not live long enough}} \
-                                              // cfg-note {{destroyed here}}
-  use(a);                                     // cfg-note {{later used here}}
-}
-
-void test_optional_arrow_data() {
-  const char* p = std::optional<std::string>()->data(); // expected-warning 
{{object backing the pointer will be destroyed at the end of the 
full-expression}} \
-                                                        // cfg-warning 
{{object whose reference is captured does not live long enough}} \
-                                                        // cfg-note 
{{destroyed here}}
-  use(p);                                               // cfg-note {{later 
used here}}
-}
-
-void test_optional_arrow_non_temporary() {
-  std::optional<std::string> opt;
-  const char* p = opt->data();
-  use(p);
-}
-
-void test_unique_ptr_arrow_data() {
-  const char* p = std::unique_ptr<std::string>()->data(); // expected-warning 
{{object backing the pointer will be destroyed at the end of the 
full-expression}} \
-                                                          // cfg-warning 
{{object whose reference is captured does not live long enough}} \
-                                                          // cfg-note 
{{destroyed here}}
-  use(p);                                                 // cfg-note {{later 
used here}}
-}
-
-void test_unique_ptr_arrow_non_temporary() {
-  std::unique_ptr<std::string> up;
-  const char* p = up->data();
-  use(p);
-}
-
-void test_optional_view_arrow_data() {
-  const char* p;
-  p = std::optional<std::string_view>()->data();
-  (void)*p;
-}
-
-void test_optional_view_arrow() {
-  // FIXME: Sema analysis doesn't warn here because we suppress operator->
-  // tracking when the pointee is a GSL Pointer to avoid false positives (see
-  // test_optional_view_arrow_data). The CFG-based lifetime safety analysis
-  // handles this correctly.
-  std::string_view* p = std::optional<std::string_view>().operator->(); // 
cfg-warning {{object whose reference is captured does not live long enough}} \
-                                                                        // 
cfg-note {{destroyed here}}
-  (void)*p;                                                             // 
cfg-note {{later used here}}
-}
-} // namespace owner_arrow
+} // namespace GH175391
\ No newline at end of file

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

Reply via email to