[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