[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)

Utkarsh Saxena via cfe-commits cfe-commits at lists.llvm.org
Fri Nov 22 03:09:47 PST 2024


https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/117122

>From 9a57223b06a8331a0ef123739a430863dee19d98 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Thu, 21 Nov 2024 07:00:56 +0000
Subject: [PATCH 1/4] [clang] Infer lifetime_capture_by for STL containers

---
 clang/include/clang/Sema/Sema.h               |  3 +
 clang/lib/Sema/SemaAttr.cpp                   | 34 ++++++++
 clang/lib/Sema/SemaDecl.cpp                   |  2 +
 clang/test/Sema/Inputs/lifetime-analysis.h    |  5 ++
 .../warn-lifetime-analysis-capture-by.cpp     | 79 +++++++++++++++++++
 5 files changed, 123 insertions(+)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 6ea6c67447b6f0..9bafcfec5d4786 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1757,6 +1757,9 @@ class Sema final : public SemaBase {
   /// Add [[clang:::lifetimebound]] attr for std:: functions and methods.
   void inferLifetimeBoundAttribute(FunctionDecl *FD);
 
+  /// Add [[clang:::lifetime_capture(this)]] to STL container methods.
+  void inferLifetimeCaptureByAttribute(FunctionDecl *FD);
+
   /// Add [[gsl::Pointer]] attributes for std:: types.
   void inferGslPointerAttribute(TypedefNameDecl *TD);
 
diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp
index 9fbad7ed67ccbe..507f7c40d58782 100644
--- a/clang/lib/Sema/SemaAttr.cpp
+++ b/clang/lib/Sema/SemaAttr.cpp
@@ -268,6 +268,40 @@ void Sema::inferLifetimeBoundAttribute(FunctionDecl *FD) {
   }
 }
 
+static bool IsPointerLikeType(QualType QT) {
+  QT = QT.getNonReferenceType();
+  if (QT->isPointerType())
+    return true;
+  auto *RD = QT->getAsCXXRecordDecl();
+  if (!RD)
+    return false;
+  RD = RD->getCanonicalDecl();
+  if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
+    RD = CTSD->getSpecializedTemplate()->getTemplatedDecl();
+  return RD->hasAttr<PointerAttr>();
+}
+
+void Sema::inferLifetimeCaptureByAttribute(FunctionDecl *FD) {
+  if (!FD)
+    return;
+  auto *MD = dyn_cast<CXXMethodDecl>(FD);
+  if (!MD || !MD->getIdentifier() || !MD->getParent()->isInStdNamespace())
+    return;
+  static const llvm::StringSet<> CapturingMethods{"insert", "push",
+                                                  "push_front", "push_back"};
+  if (!CapturingMethods.contains(MD->getName()))
+    return;
+  for (ParmVarDecl *PVD : MD->parameters()) {
+    if (PVD->hasAttr<LifetimeCaptureByAttr>())
+      return;
+    if (IsPointerLikeType(PVD->getType())) {
+      int CaptureByThis[] = {LifetimeCaptureByAttr::THIS};
+      PVD->addAttr(
+          LifetimeCaptureByAttr::CreateImplicit(Context, CaptureByThis, 1));
+    }
+  }
+}
+
 void Sema::inferNullableClassAttribute(CXXRecordDecl *CRD) {
   static const llvm::StringSet<> Nullable{
       "auto_ptr",         "shared_ptr", "unique_ptr",         "exception_ptr",
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index be570f3a1829d0..5b30d0f2c22d16 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -11913,6 +11913,7 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
   NamedDecl *OldDecl = nullptr;
   bool MayNeedOverloadableChecks = false;
 
+  inferLifetimeCaptureByAttribute(NewFD);
   // Merge or overload the declaration with an existing declaration of
   // the same name, if appropriate.
   if (!Previous.empty()) {
@@ -16716,6 +16717,7 @@ void Sema::AddKnownFunctionAttributes(FunctionDecl *FD) {
 
   LazyProcessLifetimeCaptureByParams(FD);
   inferLifetimeBoundAttribute(FD);
+  inferLifetimeCaptureByAttribute(FD);
   AddKnownFunctionAttributesForReplaceableGlobalAllocationFunction(FD);
 
   // If C++ exceptions are enabled but we are told extern "C" functions cannot
diff --git a/clang/test/Sema/Inputs/lifetime-analysis.h b/clang/test/Sema/Inputs/lifetime-analysis.h
index 41d1e2f074cc83..5c151385b1fe5a 100644
--- a/clang/test/Sema/Inputs/lifetime-analysis.h
+++ b/clang/test/Sema/Inputs/lifetime-analysis.h
@@ -49,6 +49,11 @@ struct vector {
 	vector(InputIterator first, InputIterator __last);
 
   T &at(int n);
+
+  void push_back(const T&);
+  void push_back(T&&);
+  
+  void insert(iterator, T&&);
 };
 
 template<typename T>
diff --git a/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp b/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp
index b3fde386b8616c..462cb2d3f3fd6e 100644
--- a/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp
@@ -366,3 +366,82 @@ void use() {
   capture3(std::string(), x3); // expected-warning {{object whose reference is captured by 'x3' will be destroyed at the end of the full-expression}}
 }
 } // namespace temporary_views
+
+// ****************************************************************************
+// Inferring annotation for STL containers
+// ****************************************************************************
+namespace inferred_capture_by {
+const std::string* getLifetimeBoundPointer(const std::string &s [[clang::lifetimebound]]);
+const std::string* getNotLifetimeBoundPointer(const std::string &s);
+
+namespace with_string_views {
+std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);
+std::string_view getNotLifetimeBoundView(const std::string& s);
+void use() {
+  std::string local;
+  std::vector<std::string_view> views;
+  views.push_back(std::string()); // expected-warning {{object whose reference is captured by 'views' will be destroyed at the end of the full-expression}}
+  views.insert(views.begin(), 
+            std::string()); // expected-warning {{object whose reference is captured by 'views' will be destroyed at the end of the full-expression}}
+  views.push_back(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured by 'views' will be destroyed at the end of the full-expression}}
+  views.push_back(getNotLifetimeBoundView(std::string()));
+  views.push_back(local);
+  views.insert(views.end(), local);
+
+  std::vector<std::string> strings;
+  strings.push_back(std::string());
+  strings.insert(strings.begin(), std::string());
+}
+} // namespace with_string_views
+
+namespace with_pointers {
+const std::string* getLifetimeBoundPointer(const std::string &s [[clang::lifetimebound]]);
+const std::string* getLifetimeBoundPointer(std::string_view s [[clang::lifetimebound]]);
+const std::string* getNotLifetimeBoundPointer(const std::string &s);
+std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);
+
+void use() {
+  std::string local;
+  std::vector<const std::string*> pointers;
+  pointers.push_back(getLifetimeBoundPointer(std::string())); // expected-warning {{object whose reference is captured by 'pointers' will be destroyed at the end of the full-expression}}
+  pointers.push_back(getLifetimeBoundPointer(*getLifetimeBoundPointer(std::string()))); // expected-warning {{object whose reference is captured by 'pointers' will be destroyed at the end of the full-expression}}
+  pointers.push_back(getLifetimeBoundPointer(getLifetimeBoundView(std::string()))); // expected-warning {{object whose reference is captured by 'pointers' will be destroyed at the end of the full-expression}}
+  pointers.push_back(getLifetimeBoundPointer(local));
+
+  pointers.push_back(getLifetimeBoundPointer(*getNotLifetimeBoundPointer(std::string())));
+  pointers.push_back(getNotLifetimeBoundPointer(std::string()));
+}
+} // namespace with_pointers
+
+namespace with_optional {
+class [[gsl::Pointer()]] my_view : public std::string_view {};
+class non_pointer_view : public std::string_view {};
+
+std::optional<std::string> getOptionalString();
+std::optional<std::string_view> getOptionalView();
+std::optional<std::string_view> getOptionalViewLifetimebound(const std::string& s [[clang::lifetimebound]]);
+std::optional<my_view> getOptionalMyView();
+std::optional<non_pointer_view> getOptionalNonPointerView();
+my_view getMyView();
+non_pointer_view getNonPointerView();
+
+void use() {
+  std::string local;
+  std::vector<std::string_view> views;
+  
+  std::optional<std::string_view> optional;
+  views.push_back(optional.value());
+  views.push_back(getOptionalString().value()); // expected-warning {{object whose reference is captured by 'views' will be destroyed at the end of the full-expression}}
+  views.push_back(getOptionalView().value());
+  views.push_back(getOptionalViewLifetimebound(std::string()).value()); // FIXME: Diagnose it.
+  views.push_back(getOptionalMyView().value());
+
+  views.push_back(getOptionalNonPointerView().value());
+  views.push_back(getMyView());
+  views.push_back(getNonPointerView());
+  views.push_back(std::string_view{});
+  views.push_back(my_view{});
+  views.push_back(non_pointer_view{});
+}
+} // namespace with_optional
+} // namespace inferred_capture_by

>From 2c8f6700adffeb4c82d1c7a32b13ead911910735 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Fri, 22 Nov 2024 08:32:35 +0000
Subject: [PATCH 2/4] add ast tests

---
 clang/include/clang/Sema/Sema.h               |   2 +-
 clang/lib/Sema/SemaAttr.cpp                   |  13 ++-
 clang/test/AST/attr-lifetime-capture-by.cpp   | 103 ++++++++++++++++++
 .../warn-lifetime-analysis-capture-by.cpp     |  58 ++--------
 4 files changed, 125 insertions(+), 51 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 9bafcfec5d4786..5fe23e0d0efd3b 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1757,7 +1757,7 @@ class Sema final : public SemaBase {
   /// Add [[clang:::lifetimebound]] attr for std:: functions and methods.
   void inferLifetimeBoundAttribute(FunctionDecl *FD);
 
-  /// Add [[clang:::lifetime_capture(this)]] to STL container methods.
+  /// Add [[clang:::lifetime_capture_by(this)]] to STL container methods.
   void inferLifetimeCaptureByAttribute(FunctionDecl *FD);
 
   /// Add [[gsl::Pointer]] attributes for std:: types.
diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp
index 507f7c40d58782..716d8ed1fae4f8 100644
--- a/clang/lib/Sema/SemaAttr.cpp
+++ b/clang/lib/Sema/SemaAttr.cpp
@@ -11,6 +11,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "CheckExprLifetime.h"
 #include "clang/AST/ASTConsumer.h"
 #include "clang/AST/Attr.h"
 #include "clang/AST/Expr.h"
@@ -268,14 +269,13 @@ void Sema::inferLifetimeBoundAttribute(FunctionDecl *FD) {
   }
 }
 
-static bool IsPointerLikeType(QualType QT) {
+static bool isPointerLikeType(QualType QT) {
   QT = QT.getNonReferenceType();
   if (QT->isPointerType())
     return true;
   auto *RD = QT->getAsCXXRecordDecl();
   if (!RD)
     return false;
-  RD = RD->getCanonicalDecl();
   if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
     RD = CTSD->getSpecializedTemplate()->getTemplatedDecl();
   return RD->hasAttr<PointerAttr>();
@@ -287,14 +287,19 @@ void Sema::inferLifetimeCaptureByAttribute(FunctionDecl *FD) {
   auto *MD = dyn_cast<CXXMethodDecl>(FD);
   if (!MD || !MD->getIdentifier() || !MD->getParent()->isInStdNamespace())
     return;
+  // FIXME: Infer for operator[] for map-like containers. For example:
+  //    std::map<string_view, ...> m;
+  //    m[ReturnString(..)] = ...;
   static const llvm::StringSet<> CapturingMethods{"insert", "push",
                                                   "push_front", "push_back"};
   if (!CapturingMethods.contains(MD->getName()))
     return;
-  for (ParmVarDecl *PVD : MD->parameters()) {
+  // Do not infer if any parameter is explicitly annotated.
+  for (ParmVarDecl *PVD : MD->parameters())
     if (PVD->hasAttr<LifetimeCaptureByAttr>())
       return;
-    if (IsPointerLikeType(PVD->getType())) {
+  for (ParmVarDecl *PVD : MD->parameters()) {
+    if (isPointerLikeType(PVD->getType())) {
       int CaptureByThis[] = {LifetimeCaptureByAttr::THIS};
       PVD->addAttr(
           LifetimeCaptureByAttr::CreateImplicit(Context, CaptureByThis, 1));
diff --git a/clang/test/AST/attr-lifetime-capture-by.cpp b/clang/test/AST/attr-lifetime-capture-by.cpp
index da2eb0cf3d592e..11133c6047efb2 100644
--- a/clang/test/AST/attr-lifetime-capture-by.cpp
+++ b/clang/test/AST/attr-lifetime-capture-by.cpp
@@ -7,3 +7,106 @@ struct S {
 };
 
 // CHECK: CXXMethodDecl {{.*}}clang::lifetime_capture_by(a, b, global)
+
+// ****************************************************************************
+// Infer annotation for STL container methods.
+// ****************************************************************************
+namespace __gnu_cxx {
+template <typename T>
+struct basic_iterator {};
+}
+
+namespace std {
+template<typename T> class allocator {};
+template <typename T, typename Alloc = allocator<T>>
+struct vector {
+  typedef __gnu_cxx::basic_iterator<T> iterator;
+  iterator begin();
+
+  vector();
+
+  void push_back(const T&);
+  void push_back(T&&);
+
+  void insert(iterator, T&&);
+};
+} // namespace std
+struct [[gsl::Pointer()]] View {};
+std::vector<View> views;
+// CHECK:   ClassTemplateSpecializationDecl {{.*}} struct vector definition implicit_instantiation
+// CHECK:       TemplateArgument type 'View'
+// CHECK-NOT:   LifetimeCaptureByAttr
+
+// CHECK:       CXXMethodDecl {{.*}} push_back 'void (const View &)'
+// CHECK:           ParmVarDecl {{.*}} 'const View &'
+// CHECK:               LifetimeCaptureByAttr {{.*}} Implicit
+// CHECK-NOT:   LifetimeCaptureByAttr
+
+// CHECK:       CXXMethodDecl {{.*}} push_back 'void (View &&)'
+// CHECK:           ParmVarDecl {{.*}} 'View &&'
+// CHECK:               LifetimeCaptureByAttr {{.*}} Implicit
+
+// CHECK:       CXXMethodDecl {{.*}} insert 'void (iterator, View &&)'
+// CHECK:           ParmVarDecl {{.*}} 'iterator'
+// CHECK:               LifetimeCaptureByAttr {{.*}} Implicit
+// CHECK:           ParmVarDecl {{.*}} 'View &&'
+// CHECK:               LifetimeCaptureByAttr {{.*}} Implicit
+// CHECK-NOT:   LifetimeCaptureByAttr
+
+template <class T> struct [[gsl::Pointer()]] ViewTemplate {};
+std::vector<ViewTemplate<int>> templated_views;
+// CHECK:   ClassTemplateSpecializationDecl {{.*}} struct vector definition implicit_instantiation
+// CHECK:       TemplateArgument type 'ViewTemplate<int>'
+// CHECK-NOT:   LifetimeCaptureByAttr
+
+// CHECK:       CXXMethodDecl {{.*}} push_back 'void (const ViewTemplate<int> &)'
+// CHECK:           ParmVarDecl {{.*}} 'const ViewTemplate<int> &'
+// CHECK:               LifetimeCaptureByAttr {{.*}} Implicit
+// CHECK-NOT:   LifetimeCaptureByAttr
+
+// CHECK:       CXXMethodDecl {{.*}} push_back 'void (ViewTemplate<int> &&)'
+// CHECK:           ParmVarDecl {{.*}} 'ViewTemplate<int> &&'
+// CHECK:               LifetimeCaptureByAttr {{.*}} Implicit
+
+// CHECK:       CXXMethodDecl {{.*}} insert 'void (iterator, ViewTemplate<int> &&)'
+// CHECK:           ParmVarDecl {{.*}} 'iterator'
+// CHECK:               LifetimeCaptureByAttr {{.*}} Implicit
+// CHECK:           ParmVarDecl {{.*}} 'ViewTemplate<int> &&'
+// CHECK:               LifetimeCaptureByAttr {{.*}} Implicit
+// CHECK-NOT:   LifetimeCaptureByAttr
+
+std::vector<int*> pointers;
+// CHECK:   ClassTemplateSpecializationDecl {{.*}} struct vector definition implicit_instantiation
+// CHECK:       TemplateArgument type 'int *'
+// CHECK-NOT:   LifetimeCaptureByAttr
+
+// CHECK:       CXXMethodDecl {{.*}} push_back 'void (int *const &)'
+// CHECK:           ParmVarDecl {{.*}} 'int *const &'
+// CHECK:               LifetimeCaptureByAttr {{.*}} Implicit
+// CHECK-NOT:   LifetimeCaptureByAttr
+
+// CHECK:       CXXMethodDecl {{.*}} push_back 'void (int *&&)'
+// CHECK:           ParmVarDecl {{.*}} 'int *&&'
+// CHECK:               LifetimeCaptureByAttr {{.*}} Implicit
+
+// CHECK:       CXXMethodDecl {{.*}} insert 'void (iterator, int *&&)'
+// CHECK:           ParmVarDecl {{.*}} 'iterator'
+// CHECK:               LifetimeCaptureByAttr {{.*}} Implicit
+// CHECK:           ParmVarDecl {{.*}} 'int *&&'
+// CHECK:               LifetimeCaptureByAttr {{.*}} Implicit
+// CHECK-NOT:   LifetimeCaptureByAttr
+
+std::vector<int> ints;
+// CHECK:   ClassTemplateSpecializationDecl {{.*}} struct vector definition implicit_instantiation
+// CHECK:       TemplateArgument type 'int'
+
+// CHECK:       CXXMethodDecl {{.*}} push_back 'void (const int &)'
+// CHECK-NOT:   LifetimeCaptureByAttr
+
+// CHECK:       CXXMethodDecl {{.*}} push_back 'void (int &&)'
+// CHECK-NOT:   LifetimeCaptureByAttr
+
+// CHECK:       CXXMethodDecl {{.*}} insert 'void (iterator, int &&)'
+// CHECK:           ParmVarDecl {{.*}} 'iterator'
+// CHECK:               LifetimeCaptureByAttr {{.*}} Implicit
+// CHECK-NOT:   LifetimeCaptureByAttr
\ No newline at end of file
diff --git a/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp b/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp
index 462cb2d3f3fd6e..4d562bac1e305b 100644
--- a/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp
@@ -374,7 +374,6 @@ namespace inferred_capture_by {
 const std::string* getLifetimeBoundPointer(const std::string &s [[clang::lifetimebound]]);
 const std::string* getNotLifetimeBoundPointer(const std::string &s);
 
-namespace with_string_views {
 std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);
 std::string_view getNotLifetimeBoundView(const std::string& s);
 void use() {
@@ -391,57 +390,24 @@ void use() {
   std::vector<std::string> strings;
   strings.push_back(std::string());
   strings.insert(strings.begin(), std::string());
-}
-} // namespace with_string_views
 
-namespace with_pointers {
-const std::string* getLifetimeBoundPointer(const std::string &s [[clang::lifetimebound]]);
-const std::string* getLifetimeBoundPointer(std::string_view s [[clang::lifetimebound]]);
-const std::string* getNotLifetimeBoundPointer(const std::string &s);
-std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);
-
-void use() {
-  std::string local;
   std::vector<const std::string*> pointers;
   pointers.push_back(getLifetimeBoundPointer(std::string())); // expected-warning {{object whose reference is captured by 'pointers' will be destroyed at the end of the full-expression}}
-  pointers.push_back(getLifetimeBoundPointer(*getLifetimeBoundPointer(std::string()))); // expected-warning {{object whose reference is captured by 'pointers' will be destroyed at the end of the full-expression}}
-  pointers.push_back(getLifetimeBoundPointer(getLifetimeBoundView(std::string()))); // expected-warning {{object whose reference is captured by 'pointers' will be destroyed at the end of the full-expression}}
-  pointers.push_back(getLifetimeBoundPointer(local));
-
-  pointers.push_back(getLifetimeBoundPointer(*getNotLifetimeBoundPointer(std::string())));
-  pointers.push_back(getNotLifetimeBoundPointer(std::string()));
+  pointers.push_back(&local);
 }
-} // namespace with_pointers
-
-namespace with_optional {
-class [[gsl::Pointer()]] my_view : public std::string_view {};
-class non_pointer_view : public std::string_view {};
 
-std::optional<std::string> getOptionalString();
-std::optional<std::string_view> getOptionalView();
-std::optional<std::string_view> getOptionalViewLifetimebound(const std::string& s [[clang::lifetimebound]]);
-std::optional<my_view> getOptionalMyView();
-std::optional<non_pointer_view> getOptionalNonPointerView();
-my_view getMyView();
-non_pointer_view getNonPointerView();
+namespace with_span {
+// Templated view types.
+template<typename T>
+struct [[gsl::Pointer]] Span {
+  Span(const std::vector<T> &V);
+};
 
 void use() {
-  std::string local;
-  std::vector<std::string_view> views;
-  
-  std::optional<std::string_view> optional;
-  views.push_back(optional.value());
-  views.push_back(getOptionalString().value()); // expected-warning {{object whose reference is captured by 'views' will be destroyed at the end of the full-expression}}
-  views.push_back(getOptionalView().value());
-  views.push_back(getOptionalViewLifetimebound(std::string()).value()); // FIXME: Diagnose it.
-  views.push_back(getOptionalMyView().value());
-
-  views.push_back(getOptionalNonPointerView().value());
-  views.push_back(getMyView());
-  views.push_back(getNonPointerView());
-  views.push_back(std::string_view{});
-  views.push_back(my_view{});
-  views.push_back(non_pointer_view{});
+  std::vector<Span<int>> spans;
+  spans.push_back(std::vector<int>{1, 2, 3}); // expected-warning {{object whose reference is captured by 'spans' will be destroyed at the end of the full-expression}}
+  std::vector<int> local;
+  spans.push_back(local);
 }
-} // namespace with_optional
+} // namespace with_span
 } // namespace inferred_capture_by

>From 3ea45b1af62893aad9ad8055202584c3fd90aba7 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Fri, 22 Nov 2024 11:05:27 +0000
Subject: [PATCH 3/4] new line

---
 clang/test/AST/attr-lifetime-capture-by.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/AST/attr-lifetime-capture-by.cpp b/clang/test/AST/attr-lifetime-capture-by.cpp
index 11133c6047efb2..bff494dbcc730c 100644
--- a/clang/test/AST/attr-lifetime-capture-by.cpp
+++ b/clang/test/AST/attr-lifetime-capture-by.cpp
@@ -109,4 +109,4 @@ std::vector<int> ints;
 // CHECK:       CXXMethodDecl {{.*}} insert 'void (iterator, int &&)'
 // CHECK:           ParmVarDecl {{.*}} 'iterator'
 // CHECK:               LifetimeCaptureByAttr {{.*}} Implicit
-// CHECK-NOT:   LifetimeCaptureByAttr
\ No newline at end of file
+// CHECK-NOT:   LifetimeCaptureByAttr

>From ab9a998225d604069e2bfefc0a647e9cdd0f231f Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Fri, 22 Nov 2024 11:09:33 +0000
Subject: [PATCH 4/4] add // CHECK-NOT:   LifetimeCaptureByAttr

---
 clang/test/AST/attr-lifetime-capture-by.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/clang/test/AST/attr-lifetime-capture-by.cpp b/clang/test/AST/attr-lifetime-capture-by.cpp
index bff494dbcc730c..c3afe267301ad7 100644
--- a/clang/test/AST/attr-lifetime-capture-by.cpp
+++ b/clang/test/AST/attr-lifetime-capture-by.cpp
@@ -31,6 +31,9 @@ struct vector {
   void insert(iterator, T&&);
 };
 } // namespace std
+
+// CHECK-NOT:   LifetimeCaptureByAttr
+
 struct [[gsl::Pointer()]] View {};
 std::vector<View> views;
 // CHECK:   ClassTemplateSpecializationDecl {{.*}} struct vector definition implicit_instantiation



More information about the cfe-commits mailing list