[clang] [clang] Fix the post-filtering heuristic for GSLPointer. (PR #114044)

Haojian Wu via cfe-commits cfe-commits at lists.llvm.org
Wed Dec 11 05:25:46 PST 2024


https://github.com/hokein updated https://github.com/llvm/llvm-project/pull/114044

>From 75a8bd038d6ba68eb70136c0be9ad66fac0c181f Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Fri, 1 Nov 2024 16:51:03 +0100
Subject: [PATCH 1/6] [clang] Fix the post-filtering heuristics for GSLPointer
 case.

---
 clang/docs/ReleaseNotes.rst                   |   2 +
 clang/lib/Sema/CheckExprLifetime.cpp          | 113 ++++++++++++++----
 .../Sema/warn-lifetime-analysis-nocfg.cpp     |  48 +++++++-
 3 files changed, 139 insertions(+), 24 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index a541d399d1e749..25de0cfd72eaaf 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -612,6 +612,8 @@ Improvements to Clang's diagnostics
 
 - Clang now diagnoses ``[[deprecated]]`` attribute usage on local variables (#GH90073).
 
+- Fix false positives when `[[gsl::Owner/Pointer]]` and `[[clang::lifetimebound]]` are used together.
+
 - Improved diagnostic message for ``__builtin_bit_cast`` size mismatch (#GH115870).
 
 - Clang now omits shadow warnings for enum constants in separate class scopes (#GH62588).
diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp
index 868081292bc32c..07610f243f9169 100644
--- a/clang/lib/Sema/CheckExprLifetime.cpp
+++ b/clang/lib/Sema/CheckExprLifetime.cpp
@@ -1152,6 +1152,87 @@ static bool pathOnlyHandlesGslPointer(const IndirectLocalPath &Path) {
   }
   return false;
 }
+// Result of analyzing the Path for GSLPointer.
+enum AnalysisResult {
+  // Path does not correspond to a GSLPointer.
+  NotGSLPointer,
+
+  // A relevant case was identified.
+  Report,
+  // Stop the entire traversal.
+  Abandon,
+  // Skip this step and continue traversing inner AST nodes.
+  Skip,
+};
+// Analyze cases where a GSLPointer is initialized or assigned from a
+// temporary owner object.
+static AnalysisResult analyzePathForGSLPointer(const IndirectLocalPath &Path,
+                                               Local L) {
+  if (!pathOnlyHandlesGslPointer(Path))
+    return NotGSLPointer;
+
+  // At this point, Path represents a series of operations involving a
+  // GSLPointer, either in the process of initialization or assignment.
+
+  // Note: A LifetimeBoundCall can appear interleaved in this sequence.
+  // For example:
+  //    const std::string& Ref(const std::string& a [[clang::lifetimebound]]);
+  //    string_view abc = Ref(std::string());
+  // The "Path" is [GSLPointerInit, LifetimeboundCall], where "L" is the
+  // temporary "std::string()" object. We need to check if the function with the
+  // lifetimebound attribute returns a "owner" type.
+  if (Path.back().Kind == IndirectLocalPathEntry::LifetimeBoundCall) {
+    // The lifetimebound applies to the implicit object parameter of a method.
+    if (const auto *Method = llvm::dyn_cast<CXXMethodDecl>(Path.back().D)) {
+      if (Method->getReturnType()->isReferenceType() &&
+          isRecordWithAttr<OwnerAttr>(
+              Method->getReturnType()->getPointeeType()))
+        return Report;
+      return Abandon;
+    }
+    // The lifetimebound applies to a function parameter.
+    const auto *PD = llvm::dyn_cast<ParmVarDecl>(Path.back().D);
+    if (const auto *FD = llvm::dyn_cast<FunctionDecl>(PD->getDeclContext())) {
+      if (isa<CXXConstructorDecl>(FD)) {
+        // Constructor case: the parameter is annotated with lifetimebound
+        //   e.g., GSLPointer(const S& s [[clang::lifetimebound]])
+        // We still respect this case even the type S is not an owner.
+        return Report;
+      }
+      // For regular functions, check if the return type has an Owner attribute.
+      //   e.g., const GSLOwner& func(const Foo& foo [[clang::lifetimebound]])
+      if (FD->getReturnType()->isReferenceType() &&
+          isRecordWithAttr<OwnerAttr>(FD->getReturnType()->getPointeeType()))
+        return Report;
+    }
+    return Abandon;
+  }
+
+  if (isa<DeclRefExpr>(L)) {
+    // We do not want to follow the references when returning a pointer
+    // originating from a local owner to avoid the following false positive:
+    //   int &p = *localUniquePtr;
+    //   someContainer.add(std::move(localUniquePtr));
+    //   return p;
+    if (!pathContainsInit(Path) && isRecordWithAttr<OwnerAttr>(L->getType()))
+      return Report;
+    return Abandon;
+  }
+
+  // The GSLPointer is from a temporary object.
+  auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L);
+
+  bool IsGslPtrValueFromGslTempOwner =
+      MTE && !MTE->getExtendingDecl() &&
+      isRecordWithAttr<OwnerAttr>(MTE->getType());
+  // Skipping a chain of initializing gsl::Pointer annotated objects.
+  // We are looking only for the final source to find out if it was
+  // a local or temporary owner or the address of a local
+  // variable/param.
+  if (!IsGslPtrValueFromGslTempOwner)
+    return Skip;
+  return Report;
+}
 
 static bool isAssignmentOperatorLifetimeBound(CXXMethodDecl *CMD) {
   return CMD && isNormalAssignmentOperator(CMD) && CMD->param_size() == 1 &&
@@ -1189,27 +1270,17 @@ checkExprLifetimeImpl(Sema &SemaRef, const InitializedEntity *InitEntity,
 
     auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L);
 
-    bool IsGslPtrValueFromGslTempOwner = false;
-    if (pathOnlyHandlesGslPointer(Path)) {
-      if (isa<DeclRefExpr>(L)) {
-        // We do not want to follow the references when returning a pointer
-        // originating from a local owner to avoid the following false positive:
-        //   int &p = *localUniquePtr;
-        //   someContainer.add(std::move(localUniquePtr));
-        //   return p;
-        if (pathContainsInit(Path) ||
-            !isRecordWithAttr<OwnerAttr>(L->getType()))
-          return false;
-      } else {
-        IsGslPtrValueFromGslTempOwner =
-            MTE && !MTE->getExtendingDecl() &&
-            isRecordWithAttr<OwnerAttr>(MTE->getType());
-        // Skipping a chain of initializing gsl::Pointer annotated objects.
-        // We are looking only for the final source to find out if it was
-        // a local or temporary owner or the address of a local variable/param.
-        if (!IsGslPtrValueFromGslTempOwner)
-          return true;
-      }
+    bool IsGslPtrValueFromGslTempOwner = true;
+    switch (analyzePathForGSLPointer(Path, L)) {
+    case Abandon:
+       return false;
+    case Skip:
+       return true;
+    case NotGSLPointer:
+      IsGslPtrValueFromGslTempOwner = false;
+      LLVM_FALLTHROUGH;
+    case Report:
+      break;
     }
 
     switch (LK) {
diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
index fc876926ba2e63..624ed8d64c79f0 100644
--- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
@@ -604,8 +604,9 @@ struct [[gsl::Pointer]] Span {
 
 // Pointer from Owner<Pointer>
 std::string_view test5() {
-  std::string_view a = StatusOr<std::string_view>().valueLB(); // expected-warning {{object backing the pointer will be dest}}
-  return StatusOr<std::string_view>().valueLB(); // expected-warning {{returning address of local temporary}}
+  // The Owner<Pointer> doesn't own the object which its inner pointer points to.
+  std::string_view a = StatusOr<std::string_view>().valueLB(); // OK
+  return StatusOr<std::string_view>().valueLB(); // OK
 
   // No dangling diagnostics on non-lifetimebound methods.
   std::string_view b = StatusOr<std::string_view>().valueNoLB();
@@ -652,7 +653,7 @@ Span<std::string> test10(StatusOr<std::vector<std::string>> aa) {
 
 // Pointer<Owner>> from Owner<Pointer<Owner>>
 Span<std::string> test11(StatusOr<Span<std::string>> aa) {
-  return aa.valueLB(); // expected-warning {{address of stack memory}}
+  return aa.valueLB(); // OK
   return aa.valueNoLB(); // OK.
 }
 
@@ -693,3 +694,44 @@ void test() {
   auto y = std::set<int>{}.begin(); // expected-warning {{object backing the pointer}}
 }
 } // namespace GH118064
+
+namespace LifetimeboundInterleave {
+
+const std::string& Ref(const std::string& abc [[clang::lifetimebound]]);
+std::string_view test1() {
+  std::string_view t1 = Ref(std::string()); // expected-warning {{object backing}}
+  t1 = Ref(std::string()); // expected-warning {{object backing}}
+  return Ref(std::string()); // expected-warning {{returning address}}
+}
+
+template <typename T>
+struct Foo {
+  const T& get() const [[clang::lifetimebound]];
+  const T& getNoLB() const;
+};
+std::string_view test2(Foo<std::string> r1, Foo<std::string_view> r2) {
+  std::string_view t1 = Foo<std::string>().get(); // expected-warning {{object backing}}
+  t1 = Foo<std::string>().get(); // expected-warning {{object backing}}
+  return r1.get(); // expected-warning {{address of stack}}
+  
+  std::string_view t2 = Foo<std::string_view>().get();
+  t2 = Foo<std::string_view>().get();
+  return r2.get();
+
+  // no warning on no-LB-annotated method.
+  std::string_view t3 = Foo<std::string>().getNoLB(); 
+  t3 = Foo<std::string>().getNoLB(); 
+  return r1.getNoLB(); 
+}
+
+struct Bar {};
+struct [[gsl::Pointer]] Pointer {
+  Pointer(const Bar & bar [[clang::lifetimebound]]);
+};
+Pointer test3(Bar bar) {
+  Pointer p = Pointer(Bar()); // expected-warning {{temporary}}
+  p = Pointer(Bar()); // expected-warning {{object backing}}
+  return bar; // expected-warning {{address of stack}}
+}
+
+} // namespace LifetimeboundInterleave

>From de28aeff4dded46fd08548810bb3084d1fc3e429 Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Mon, 4 Nov 2024 16:36:48 +0100
Subject: [PATCH 2/6] Address review comments

---
 clang/lib/Sema/CheckExprLifetime.cpp | 39 ++++++++++++----------------
 1 file changed, 17 insertions(+), 22 deletions(-)

diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp
index 07610f243f9169..fbe87c3df69158 100644
--- a/clang/lib/Sema/CheckExprLifetime.cpp
+++ b/clang/lib/Sema/CheckExprLifetime.cpp
@@ -1183,28 +1183,23 @@ static AnalysisResult analyzePathForGSLPointer(const IndirectLocalPath &Path,
   // lifetimebound attribute returns a "owner" type.
   if (Path.back().Kind == IndirectLocalPathEntry::LifetimeBoundCall) {
     // The lifetimebound applies to the implicit object parameter of a method.
-    if (const auto *Method = llvm::dyn_cast<CXXMethodDecl>(Path.back().D)) {
-      if (Method->getReturnType()->isReferenceType() &&
-          isRecordWithAttr<OwnerAttr>(
-              Method->getReturnType()->getPointeeType()))
-        return Report;
-      return Abandon;
-    }
+    const FunctionDecl* FD = llvm::dyn_cast_or_null<FunctionDecl>(Path.back().D);
     // The lifetimebound applies to a function parameter.
-    const auto *PD = llvm::dyn_cast<ParmVarDecl>(Path.back().D);
-    if (const auto *FD = llvm::dyn_cast<FunctionDecl>(PD->getDeclContext())) {
-      if (isa<CXXConstructorDecl>(FD)) {
-        // Constructor case: the parameter is annotated with lifetimebound
-        //   e.g., GSLPointer(const S& s [[clang::lifetimebound]])
-        // We still respect this case even the type S is not an owner.
-        return Report;
-      }
-      // For regular functions, check if the return type has an Owner attribute.
-      //   e.g., const GSLOwner& func(const Foo& foo [[clang::lifetimebound]])
-      if (FD->getReturnType()->isReferenceType() &&
-          isRecordWithAttr<OwnerAttr>(FD->getReturnType()->getPointeeType()))
-        return Report;
+    if (const auto *PD = llvm::dyn_cast<ParmVarDecl>(Path.back().D))
+      FD = llvm::dyn_cast<FunctionDecl>(PD->getDeclContext());
+
+    if (isa_and_present<CXXConstructorDecl>(FD)) {
+      // Constructor case: the parameter is annotated with lifetimebound
+      //   e.g., GSLPointer(const S& s [[clang::lifetimebound]])
+      // We still respect this case even the type S is not an owner.
+      return Report;
     }
+    // Check if the return type has an Owner attribute.
+    //   e.g., const GSLOwner& func(const Foo& foo [[clang::lifetimebound]])
+    if (FD && FD->getReturnType()->isReferenceType() &&
+          isRecordWithAttr<OwnerAttr>(FD->getReturnType()->getPointeeType()))
+      return Report;
+
     return Abandon;
   }
 
@@ -1273,9 +1268,9 @@ checkExprLifetimeImpl(Sema &SemaRef, const InitializedEntity *InitEntity,
     bool IsGslPtrValueFromGslTempOwner = true;
     switch (analyzePathForGSLPointer(Path, L)) {
     case Abandon:
-       return false;
+      return false;
     case Skip:
-       return true;
+      return true;
     case NotGSLPointer:
       IsGslPtrValueFromGslTempOwner = false;
       LLVM_FALLTHROUGH;

>From 739e09d7cb51a9eb453c33c249278783cbd87b6d Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Tue, 5 Nov 2024 13:10:39 +0100
Subject: [PATCH 3/6] clang-format

---
 clang/lib/Sema/CheckExprLifetime.cpp | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp
index fbe87c3df69158..f52e69e9ac1e67 100644
--- a/clang/lib/Sema/CheckExprLifetime.cpp
+++ b/clang/lib/Sema/CheckExprLifetime.cpp
@@ -1183,7 +1183,8 @@ static AnalysisResult analyzePathForGSLPointer(const IndirectLocalPath &Path,
   // lifetimebound attribute returns a "owner" type.
   if (Path.back().Kind == IndirectLocalPathEntry::LifetimeBoundCall) {
     // The lifetimebound applies to the implicit object parameter of a method.
-    const FunctionDecl* FD = llvm::dyn_cast_or_null<FunctionDecl>(Path.back().D);
+    const FunctionDecl *FD =
+        llvm::dyn_cast_or_null<FunctionDecl>(Path.back().D);
     // The lifetimebound applies to a function parameter.
     if (const auto *PD = llvm::dyn_cast<ParmVarDecl>(Path.back().D))
       FD = llvm::dyn_cast<FunctionDecl>(PD->getDeclContext());
@@ -1197,7 +1198,7 @@ static AnalysisResult analyzePathForGSLPointer(const IndirectLocalPath &Path,
     // Check if the return type has an Owner attribute.
     //   e.g., const GSLOwner& func(const Foo& foo [[clang::lifetimebound]])
     if (FD && FD->getReturnType()->isReferenceType() &&
-          isRecordWithAttr<OwnerAttr>(FD->getReturnType()->getPointeeType()))
+        isRecordWithAttr<OwnerAttr>(FD->getReturnType()->getPointeeType()))
       return Report;
 
     return Abandon;

>From b01656a10eee077c6f4faa35248f7a12d43c8903 Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Tue, 5 Nov 2024 17:18:32 +0100
Subject: [PATCH 4/6] Add more tests, and a minor fix for a function returning
 gsl pointer.

---
 clang/lib/Sema/CheckExprLifetime.cpp           | 15 +++++++++------
 .../test/Sema/warn-lifetime-analysis-nocfg.cpp | 18 ++++++++++++++++++
 2 files changed, 27 insertions(+), 6 deletions(-)

diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp
index f52e69e9ac1e67..9f07b8e7343eb4 100644
--- a/clang/lib/Sema/CheckExprLifetime.cpp
+++ b/clang/lib/Sema/CheckExprLifetime.cpp
@@ -1179,8 +1179,8 @@ static AnalysisResult analyzePathForGSLPointer(const IndirectLocalPath &Path,
   //    const std::string& Ref(const std::string& a [[clang::lifetimebound]]);
   //    string_view abc = Ref(std::string());
   // The "Path" is [GSLPointerInit, LifetimeboundCall], where "L" is the
-  // temporary "std::string()" object. We need to check if the function with the
-  // lifetimebound attribute returns a "owner" type.
+  // temporary "std::string()" object. We need to check the return type of the
+  // function with the lifetimebound attribute.
   if (Path.back().Kind == IndirectLocalPathEntry::LifetimeBoundCall) {
     // The lifetimebound applies to the implicit object parameter of a method.
     const FunctionDecl *FD =
@@ -1195,10 +1195,13 @@ static AnalysisResult analyzePathForGSLPointer(const IndirectLocalPath &Path,
       // We still respect this case even the type S is not an owner.
       return Report;
     }
-    // Check if the return type has an Owner attribute.
-    //   e.g., const GSLOwner& func(const Foo& foo [[clang::lifetimebound]])
-    if (FD && FD->getReturnType()->isReferenceType() &&
-        isRecordWithAttr<OwnerAttr>(FD->getReturnType()->getPointeeType()))
+    // Check the return type, e.g.
+    //   const GSLOwner& func(const Foo& foo [[clang::lifetimebound]])
+    //   GSLPointer func(const Foo& foo [[clang::lifetimebound]])
+    if (FD &&
+        ((FD->getReturnType()->isReferenceType() &&
+          isRecordWithAttr<OwnerAttr>(FD->getReturnType()->getPointeeType())) ||
+         isRecordWithAttr<PointerAttr>(FD->getReturnType())))
       return Report;
 
     return Abandon;
diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
index 624ed8d64c79f0..3845cb32b37f6e 100644
--- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
@@ -698,10 +698,28 @@ void test() {
 namespace LifetimeboundInterleave {
 
 const std::string& Ref(const std::string& abc [[clang::lifetimebound]]);
+
+std::string_view TakeSv(std::string_view abc [[clang::lifetimebound]]);
+std::string_view TakeStrRef(const std::string& abc [[clang::lifetimebound]]);
+std::string_view TakeStr(std::string abc [[clang::lifetimebound]]);
+
 std::string_view test1() {
   std::string_view t1 = Ref(std::string()); // expected-warning {{object backing}}
   t1 = Ref(std::string()); // expected-warning {{object backing}}
   return Ref(std::string()); // expected-warning {{returning address}}
+  
+  std::string_view t2 = TakeSv(std::string()); // expected-warning {{object backing}}
+  t2 = TakeSv(std::string()); // expected-warning {{object backing}}
+  return TakeSv(std::string()); // expected-warning {{returning address}}
+
+  std::string_view t3 = TakeStrRef(std::string()); // expected-warning {{temporary}}
+  t3 = TakeStrRef(std::string()); // expected-warning {{object backing}}
+  return TakeStrRef(std::string()); // expected-warning {{returning address}}
+
+
+  std::string_view t4 = TakeStr(std::string());
+  t4 = TakeStr(std::string());
+  return TakeStr(std::string());
 }
 
 template <typename T>

>From 919b7238e10d140e31209c9e2f478fa318cd3744 Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Tue, 19 Nov 2024 11:37:52 +0100
Subject: [PATCH 5/6] Fix a newly-found false positive.

---
 clang/lib/Sema/CheckExprLifetime.cpp          |  2 ++
 clang/test/Sema/Inputs/lifetime-analysis.h    |  5 ++++
 .../Sema/warn-lifetime-analysis-nocfg.cpp     | 24 +++++++++++++++++++
 3 files changed, 31 insertions(+)

diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp
index 9f07b8e7343eb4..894a6d4949d243 100644
--- a/clang/lib/Sema/CheckExprLifetime.cpp
+++ b/clang/lib/Sema/CheckExprLifetime.cpp
@@ -367,6 +367,8 @@ static bool shouldTrackImplicitObjectArg(const CXXMethodDecl *Callee) {
   if (Callee->getReturnType()->isReferenceType()) {
     if (!Callee->getIdentifier()) {
       auto OO = Callee->getOverloadedOperator();
+      if (!Callee->getParent()->hasAttr<OwnerAttr>())
+        return false;
       return OO == OverloadedOperatorKind::OO_Subscript ||
              OO == OverloadedOperatorKind::OO_Star;
     }
diff --git a/clang/test/Sema/Inputs/lifetime-analysis.h b/clang/test/Sema/Inputs/lifetime-analysis.h
index 5c151385b1fe5a..f888e6ab94bb6a 100644
--- a/clang/test/Sema/Inputs/lifetime-analysis.h
+++ b/clang/test/Sema/Inputs/lifetime-analysis.h
@@ -128,6 +128,11 @@ struct reference_wrapper {
 template<typename T>
 reference_wrapper<T> ref(T& t) noexcept;
 
+template <typename T>
+struct [[gsl::Pointer]] iterator {
+  T& operator*() const;
+};
+
 struct false_type {
     static constexpr bool value = false;
     constexpr operator bool() const noexcept { return value; }
diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
index 3845cb32b37f6e..45b4dc838f44ed 100644
--- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
@@ -752,4 +752,28 @@ Pointer test3(Bar bar) {
   return bar; // expected-warning {{address of stack}}
 }
 
+template<typename T>
+struct MySpan {
+  MySpan(const std::vector<T>& v);
+  using iterator = std::iterator<T>;
+  iterator begin() const [[clang::lifetimebound]];
+};
+template <typename T>
+typename MySpan<T>::iterator ReturnFirstIt(const MySpan<T>& v [[clang::lifetimebound]]);
+
+void test4() {
+  std::vector<int> v{1};
+  // MySpan<T> doesn't own any underlying T objects, the pointee object of
+  // the MySpan iterator is still alive when the whole span is destroyed, thus
+  // no diagnostic.
+  const int& t1 = *MySpan<int>(v).begin();
+  const int& t2 = *ReturnFirstIt(MySpan<int>(v));
+  // Ideally, we would diagnose the following case, but due to implementation
+  // constraints, we do not.
+  const int& t4 = *MySpan<int>(std::vector<int>{}).begin();
+  
+  auto it1 = MySpan<int>(v).begin(); // expected-warning {{temporary whose address is use}}
+  auto it2 = ReturnFirstIt(MySpan<int>(v)); // expected-warning {{temporary whose address is used}}
+}
+
 } // namespace LifetimeboundInterleave

>From 38a0f8dabbff33519389e4884fdad1510a884a03 Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Wed, 11 Dec 2024 14:25:05 +0100
Subject: [PATCH 6/6] Fix some broken capture-by tests.

---
 clang/lib/Sema/CheckExprLifetime.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp
index 894a6d4949d243..843fdb4a65cd73 100644
--- a/clang/lib/Sema/CheckExprLifetime.cpp
+++ b/clang/lib/Sema/CheckExprLifetime.cpp
@@ -1203,7 +1203,7 @@ static AnalysisResult analyzePathForGSLPointer(const IndirectLocalPath &Path,
     if (FD &&
         ((FD->getReturnType()->isReferenceType() &&
           isRecordWithAttr<OwnerAttr>(FD->getReturnType()->getPointeeType())) ||
-         isRecordWithAttr<PointerAttr>(FD->getReturnType())))
+         isPointerLikeType(FD->getReturnType())))
       return Report;
 
     return Abandon;



More information about the cfe-commits mailing list