[clang] [LifetimeSafety] Add warning for member functions returning field origins (PR #229784)
Utkarsh Saxena via cfe-commits
cfe-commits at lists.llvm.org
Mon Oct 12 05:07:36 PDT 2026
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/229784
>From 7600c66ebf57a0a24991891ceed3469314d7a919 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Wed, 7 Oct 2026 14:10:53 +0000
Subject: [PATCH] field-origin-escape
---
.../Analyses/LifetimeSafety/LifetimeSafety.h | 6 +
.../Analysis/Analyses/LifetimeSafety/Loans.h | 43 ++++---
clang/include/clang/Basic/DiagnosticGroups.td | 7 ++
.../clang/Basic/DiagnosticSemaKinds.td | 8 ++
clang/lib/Analysis/LifetimeSafety/Checker.cpp | 29 +++++
.../LifetimeSafety/FactsGenerator.cpp | 15 ++-
clang/lib/Analysis/LifetimeSafety/Loans.cpp | 16 +++
clang/lib/Sema/SemaLifetimeSafety.h | 22 ++++
.../LifetimeSafety/annotation-suggestions.cpp | 105 +++++++++++++++++-
9 files changed, 232 insertions(+), 19 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index 6854902059e50..e6fbc2a5caee7 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -46,6 +46,7 @@ struct LifetimeSafetyOpts {
bool CheckLifetimeboundViolations;
bool CheckMisplacedLifetimebound;
bool CheckInapplicableLifetimebound;
+ bool CheckFieldOriginEscape;
};
/// Enum to track functions visible across or within TU.
@@ -164,6 +165,11 @@ class LifetimeSafetySemaHelper {
const CXXMethodDecl *MD,
const Expr *EscapeExpr) {}
+ // Reports when a member function returns an origin of a field.
+ virtual void reportFieldOriginEscape(const CXXMethodDecl *MD,
+ const FieldDecl *FD,
+ const Expr *EscapeExpr) {}
+
// Adds inferred lifetime bound attribute for implicit this to its
// TypeSourceInfo.
virtual void addLifetimeBoundToImplicitThis(const CXXMethodDecl *MD) {}
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h
index 00c931e4f9f66..cf70c132fc18a 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h
@@ -70,32 +70,42 @@ class PathElement {
const FieldDecl *FD;
};
-/// Represents the base of a placeholder access path, which is either a
-/// function parameter or the implicit 'this' object of an instance method.
-/// Placeholder paths never expire within the function scope, as they represent
-/// storage from the caller's scope.
+/// Represents the base of a placeholder access path, which is a function
+/// parameter, the implicit 'this' object of an instance method, or a field of
+/// the enclosing class. Placeholder paths never expire within the function
+/// scope, as they represent storage from the caller's or object's scope.
class PlaceholderBase : public llvm::FoldingSetNode {
- llvm::PointerUnion<const ParmVarDecl *, const CXXMethodDecl *> ParamOrMethod;
+ llvm::PointerUnion<const ParmVarDecl *, const CXXMethodDecl *,
+ const FieldDecl *>
+ ParamMethodOrField;
public:
- PlaceholderBase(const ParmVarDecl *PVD) : ParamOrMethod(PVD) {}
- PlaceholderBase(const CXXMethodDecl *MD) : ParamOrMethod(MD) {}
+ PlaceholderBase(const ParmVarDecl *PVD) : ParamMethodOrField(PVD) {}
+ PlaceholderBase(const CXXMethodDecl *MD) : ParamMethodOrField(MD) {}
+ PlaceholderBase(const FieldDecl *FD) : ParamMethodOrField(FD) {}
const ParmVarDecl *getParmVarDecl() const {
- return ParamOrMethod.dyn_cast<const ParmVarDecl *>();
+ return ParamMethodOrField.dyn_cast<const ParmVarDecl *>();
}
const CXXMethodDecl *getImplicitThisParent() const {
- return ParamOrMethod.dyn_cast<const CXXMethodDecl *>();
+ return ParamMethodOrField.dyn_cast<const CXXMethodDecl *>();
}
- using KeyTy = llvm::PointerUnion<const ParmVarDecl *, const CXXMethodDecl *>;
+ const FieldDecl *getFieldDecl() const {
+ return ParamMethodOrField.dyn_cast<const FieldDecl *>();
+ }
+
+ using KeyTy = llvm::PointerUnion<const ParmVarDecl *, const CXXMethodDecl *,
+ const FieldDecl *>;
- static void Profile(llvm::FoldingSetNodeID &ID, KeyTy ParamOrMethod) {
- ID.AddPointer(ParamOrMethod.getOpaqueValue());
+ static void Profile(llvm::FoldingSetNodeID &ID, KeyTy ParamMethodOrField) {
+ ID.AddPointer(ParamMethodOrField.getOpaqueValue());
}
- void Profile(llvm::FoldingSetNodeID &ID) const { Profile(ID, ParamOrMethod); }
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ Profile(ID, ParamMethodOrField);
+ }
};
/// Represents the storage location being borrowed, e.g., a specific stack
@@ -229,6 +239,9 @@ class LoanManager {
Loan *createPlaceholderLoan(const CXXMethodDecl *MD) {
return createLoan(AccessPath(getOrCreatePlaceholderBase(MD)));
}
+ Loan *createPlaceholderLoan(const FieldDecl *FD) {
+ return createLoan(AccessPath(getOrCreatePlaceholderBase(FD)));
+ }
const Loan *getLoan(LoanID ID) const {
assert(ID.Value < AllLoans.size());
@@ -240,9 +253,11 @@ class LoanManager {
private:
LoanID getNextLoanID() { return NextLoanID++; }
- /// Gets or creates a placeholder base for a given parameter or method.
+ /// Gets or creates a placeholder base for a given parameter, method, or
+ /// field.
const PlaceholderBase *getOrCreatePlaceholderBase(const ParmVarDecl *PVD);
const PlaceholderBase *getOrCreatePlaceholderBase(const CXXMethodDecl *MD);
+ const PlaceholderBase *getOrCreatePlaceholderBase(const FieldDecl *FD);
LoanID NextLoanID{0};
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 1da7698944b24..5ad1136d66fea 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -744,6 +744,13 @@ Validates correct usage and adherence to lifetime safety annotations like [[clan
}];
}
+def LifetimeSafetyFieldOriginEscape
+ : DiagGroup<"lifetime-safety-field-origin-escape"> {
+ code Documentation = [{
+Detects member functions returning an origin of a field (such as a pointer, reference, or view field).
+ }];
+}
+
def LifetimeSafetyAll : DiagGroup<"lifetime-safety-all",
[LifetimeSafety,
LifetimeSafetySuggestions,
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 9208aba1445d7..70e0c7ce62fed 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11143,6 +11143,14 @@ def warn_lifetime_safety_noescape_escapes
InGroup<LifetimeSafetyNoescape>,
DefaultIgnore;
+def warn_lifetime_safety_field_origin_escape
+ : Warning<"return value depends on the lifetime of the object referenced by %0, which cannot be expressed with [[clang::lifetimebound]]">,
+ InGroup<LifetimeSafetyFieldOriginEscape>,
+ DefaultIgnore;
+
+def note_lifetime_safety_owned_field_suggestion
+ : Note<"if %0 is owned by the class, mark the method [[clang::lifetimebound]]">;
+
// For non-floating point, expressions of the form x == x or x != x
// should result in a warning, since these always evaluate to a constant.
// Array comparisons have similar warnings
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index fbf345460b6d8..8ada82a64483c 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -60,6 +60,7 @@ class LifetimeChecker {
llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;
llvm::DenseMap<AnnotationTarget, EscapingTarget> AnnotationWarningsMap;
llvm::DenseMap<const ParmVarDecl *, EscapingTarget> NoescapeWarningsMap;
+ llvm::DenseMap<const FieldDecl *, const Expr *> FieldOriginEscapesMap;
llvm::DenseSet<const Decl *> VerifiedLiftimeboundEscapes;
const LoanPropagationAnalysis &LoanPropagation;
const MovedLoansAnalysis &MovedLoans;
@@ -105,6 +106,8 @@ class LifetimeChecker {
checkAnnotations(OEF);
issuePendingWarnings();
suggestAnnotations();
+ if (LSOpts.CheckFieldOriginEscape)
+ reportFieldOriginEscapes();
if (LSOpts.CheckNoescapeViolations)
reportNoescapeViolations();
if (LSOpts.CheckLifetimeboundViolations)
@@ -171,6 +174,19 @@ class LifetimeChecker {
AnnotationWarningsMap.try_emplace(MD, ReturnEsc->getReturnExpr());
}
};
+ auto CheckField = [&](const FieldDecl *Field) {
+ if (!LSOpts.CheckFieldOriginEscape)
+ return;
+ const auto *MD = dyn_cast<CXXMethodDecl>(FD);
+ if (MD && implicitObjectParamIsLifetimeBound(MD))
+ return;
+ if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF)) {
+ if (OriginList *RetList = FactMgr.getOriginMgr().getOrCreateList(
+ ReturnEsc->getReturnExpr());
+ RetList && RetList->getOuterOriginID() == EscapedOID)
+ FieldOriginEscapesMap.try_emplace(Field, ReturnEsc->getReturnExpr());
+ }
+ };
auto MovedAtEscape = MovedLoans.getMovedLoans(OEF);
for (LoanID LID : EscapedLoans) {
const Loan *L = FactMgr.getLoanMgr().getLoan(LID);
@@ -181,6 +197,8 @@ class LifetimeChecker {
CheckParam(PVD, /*IsMoved=*/MovedAtEscape.lookup(LID));
else if (const auto *MD = PB->getImplicitThisParent())
CheckImplicitThis(MD);
+ else if (const auto *Field = PB->getFieldDecl())
+ CheckField(Field);
}
}
@@ -446,6 +464,17 @@ class LifetimeChecker {
}
}
+ void reportFieldOriginEscapes() {
+ if (!SemaHelper)
+ return;
+ const auto *MD = dyn_cast<CXXMethodDecl>(FD);
+ if (!MD)
+ return;
+ llvm::TimeTraceScope TimeTrace("ReportFieldOriginEscapes");
+ for (auto [Field, EscapeExpr] : FieldOriginEscapesMap)
+ SemaHelper->reportFieldOriginEscape(MD, Field, EscapeExpr);
+ }
+
void reportNoescapeViolations() {
llvm::TimeTraceScope TimeTrace("ReportNoescapeViolations");
for (auto [PVD, EscapeTarget] : NoescapeWarningsMap) {
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index d1113783ab882..ba3106517e88d 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -1214,10 +1214,21 @@ llvm::SmallVector<Fact *> FactsGenerator::issuePlaceholderLoans() {
llvm::SmallVector<Fact *> PlaceholderLoanFacts;
if (auto ThisOrigins = FactMgr.getOriginMgr().getThisOrigins()) {
OriginList *List = *ThisOrigins;
- const Loan *L =
- FactMgr.getLoanMgr().createPlaceholderLoan(cast<CXXMethodDecl>(FD));
+ const auto *MD = cast<CXXMethodDecl>(FD);
+ const Loan *L = FactMgr.getLoanMgr().createPlaceholderLoan(MD);
PlaceholderLoanFacts.push_back(
FactMgr.createFact<IssueFact>(L->getID(), List->getOuterOriginID()));
+ if (!isa<CXXConstructorDecl>(MD))
+ if (const CXXRecordDecl *ClassDecl = MD->getParent()->getDefinition())
+ for (const FieldDecl *Field : ClassDecl->fields()) {
+ OriginList *FieldList = getOriginsList(*Field);
+ if (!FieldList)
+ continue;
+ const Loan *FieldLoan =
+ FactMgr.getLoanMgr().createPlaceholderLoan(Field);
+ PlaceholderLoanFacts.push_back(FactMgr.createFact<IssueFact>(
+ FieldLoan->getID(), FieldList->getOuterOriginID()));
+ }
}
for (const ParmVarDecl *PVD : FD->parameters()) {
OriginList *List = getOriginsList(*PVD);
diff --git a/clang/lib/Analysis/LifetimeSafety/Loans.cpp b/clang/lib/Analysis/LifetimeSafety/Loans.cpp
index e6cbd922d20c7..c15f9b499b35b 100644
--- a/clang/lib/Analysis/LifetimeSafety/Loans.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Loans.cpp
@@ -21,6 +21,8 @@ void AccessPath::dump(llvm::raw_ostream &OS) const {
OS << "$" << PVD->getNameAsString();
else if (PB->getImplicitThisParent())
OS << "$this";
+ else if (const auto *FD = PB->getFieldDecl())
+ OS << "$this." << FD->getNameAsString();
} else if (const auto *E = getAsNewAllocation())
OS << "NewAllocation at " << E;
else
@@ -62,4 +64,18 @@ LoanManager::getOrCreatePlaceholderBase(const CXXMethodDecl *MD) {
PlaceholderBases.insert(NewPB, InsertToken);
return NewPB;
}
+
+const PlaceholderBase *
+LoanManager::getOrCreatePlaceholderBase(const FieldDecl *FD) {
+ llvm::FoldingSetNodeID ID;
+ PlaceholderBase::Profile(ID, FD);
+ llvm::FoldingSetInsertToken InsertToken;
+ if (PlaceholderBase *Existing = PlaceholderBases.lookup(ID, InsertToken))
+ return Existing;
+
+ void *Mem = LoanAllocator.Allocate<PlaceholderBase>();
+ PlaceholderBase *NewPB = new (Mem) PlaceholderBase(FD);
+ PlaceholderBases.insert(NewPB, InsertToken);
+ return NewPB;
+}
} // namespace clang::lifetimes::internal
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index 52ee8c0037b8d..18694229889b1 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -72,6 +72,11 @@ inline bool ShouldCheckInapplicableLifetimebound(Sema &S, const Decl *D) {
diag::warn_lifetime_safety_inapplicable_lifetimebound, D->getBeginLoc());
}
+inline bool ShouldCheckFieldOriginEscape(Sema &S, const Decl *D) {
+ return !S.getDiagnostics().isIgnored(
+ diag::warn_lifetime_safety_field_origin_escape, D->getBeginLoc());
+}
+
inline bool ShouldSuggestLifetimeAnnotations(Sema &S, const Decl *D) {
DiagnosticsEngine &Diags = S.getDiagnostics();
constexpr unsigned DiagIDs[] = {
@@ -114,6 +119,7 @@ inline bool IsLifetimeSafetyEnabled(Sema &S, const Decl *D) {
ShouldCheckLifetimeboundViolations(S, D) ||
ShouldCheckMisplacedLifetimebound(S, D) ||
ShouldCheckInapplicableLifetimebound(S, D) ||
+ ShouldCheckFieldOriginEscape(S, D) ||
ShouldSuggestLifetimeAnnotations(S, D);
}
@@ -127,6 +133,7 @@ inline LifetimeSafetyOpts GetLifetimeSafetyOpts(Sema &S, const Decl *D) {
LSOpts.CheckMisplacedLifetimebound = ShouldCheckMisplacedLifetimebound(S, D);
LSOpts.CheckInapplicableLifetimebound =
ShouldCheckInapplicableLifetimebound(S, D);
+ LSOpts.CheckFieldOriginEscape = ShouldCheckFieldOriginEscape(S, D);
return LSOpts;
}
@@ -458,6 +465,21 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
<< EscapeExpr->getSourceRange();
}
+ void reportFieldOriginEscape(const CXXMethodDecl *MD, const FieldDecl *FD,
+ const Expr *EscapeExpr) override {
+ std::string FieldDesc = getDiagSubjectDescription(FD);
+ S.Diag(MD->getLocation(), diag::warn_lifetime_safety_field_origin_escape)
+ << FieldDesc << MD->getNameInfo().getSourceRange();
+
+ S.Diag(EscapeExpr->getBeginLoc(), diag::note_lifetime_safety_returned_here)
+ << EscapeExpr->getSourceRange();
+
+ if (FD->getType()->isPointerType())
+ S.Diag(FD->getLocation(),
+ diag::note_lifetime_safety_owned_field_suggestion)
+ << FieldDesc << FD->getSourceRange();
+ }
+
void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
const Expr *EscapeExpr) override {
S.Diag(ParmWithNoescape->getBeginLoc(),
diff --git a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
index 4deb6d1f8688d..acb4907ec863b 100644
--- a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
+++ b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp
@@ -1,8 +1,8 @@
// RUN: rm -rf %t
// RUN: split-file %s %t
-// RUN: %clang_cc1 -fsyntax-only -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety-suggestions -Wlifetime-safety -Wlifetime-safety-annotation-placement -Wno-dangling -I%t -I%S -verify %t/test_source.cpp
-// RUN: %clang_cc1 -fsyntax-only -std=c++23 -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety-suggestions -Wlifetime-safety -Wno-dangling -I%t -I%S -verify %t/test_source.cpp
-// RUN: %clang_cc1 -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety-suggestions -Wlifetime-safety -Wno-dangling -I%t -I%S -fixit %t/test_source.cpp
+// RUN: %clang_cc1 -fsyntax-only -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety-suggestions -Wlifetime-safety -Wlifetime-safety-annotation-placement -Wlifetime-safety-field-origin-escape -Wno-dangling -I%t -I%S -verify %t/test_source.cpp
+// RUN: %clang_cc1 -fsyntax-only -std=c++23 -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety-suggestions -Wlifetime-safety -Wlifetime-safety-field-origin-escape -Wno-dangling -I%t -I%S -verify %t/test_source.cpp
+// RUN: %clang_cc1 -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety-suggestions -Wlifetime-safety -Wlifetime-safety-field-origin-escape -Wno-dangling -I%t -I%S -fixit %t/test_source.cpp
// RUN: %clang_cc1 -fsyntax-only -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety-suggestions -Wno-dangling -I%t -I%S -Werror=lifetime-safety-suggestions %t/test_source.cpp
View definition_before_header(View a);
@@ -700,3 +700,102 @@ S foo() {
return S(create_up());
}
} // namespace GH193747
+
+namespace field_origin_escape {
+
+class Context {
+public:
+ // This method should be annotated with lifetimebound.
+ std::string_view method() const { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}}
+ return method_; // expected-note {{param returned here}}
+ }
+
+private:
+ std::string method_ = "42";
+};
+
+class ContextAnnotated {
+public:
+ std::string_view method() const [[clang::lifetimebound]] {
+ return method_;
+ }
+
+private:
+ std::string method_ = "42";
+};
+
+class ContextWrapper {
+public:
+ explicit ContextWrapper(Context &inner, // expected-warning {{parameter in intra-TU constructor should be marked [[clang::lifetimebound]]}}
+ ContextAnnotated &annot) // expected-warning {{parameter in intra-TU constructor should be marked [[clang::lifetimebound]]}}
+ : inner_(inner), annot_(annot) {}
+
+ Context &inner() const { // expected-warning {{return value depends on the lifetime of the object referenced by field 'inner_', which cannot be expressed with [[clang::lifetimebound]]}}
+ return inner_; // expected-note {{returned here}}
+ }
+
+ // This method should NOT be annotated with lifetimebound.
+ std::string_view method() const { // expected-warning {{return value depends on the lifetime of the object referenced by field 'inner_', which cannot be expressed with [[clang::lifetimebound]]}}
+ return inner_.method(); // expected-note {{returned here}}
+ }
+
+ std::string_view annot_method() const { // expected-warning {{return value depends on the lifetime of the object referenced by field 'annot_', which cannot be expressed with [[clang::lifetimebound]]}}
+ return annot_.method(); // expected-note {{returned here}}
+ }
+
+private:
+ Context &inner_; // expected-note {{escapes to this field}}
+ ContextAnnotated &annot_; // expected-note {{escapes to this field}}
+};
+
+struct FieldTypes {
+ int val_;
+ int *ptr_; // expected-note 2 {{if field 'ptr_' is owned by the class, mark the method [[clang::lifetimebound]]}}
+ int &ref_;
+ std::string str_;
+ std::string_view view_;
+
+ // Loans to fields (depend on enclosing object, not field origin).
+ int &get_val_ref() { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}}
+ return val_; // expected-note {{param returned here}}
+ }
+ int *get_val_ptr() { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}}
+ return &val_; // expected-note {{param returned here}}
+ }
+ std::string_view get_str_view() const { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}}
+ return str_; // expected-note {{param returned here}}
+ }
+ const char *get_str_data() const { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}}
+ return str_.data(); // expected-note {{param returned here}}
+ }
+ int *&get_ptr_ref() { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}}
+ return ptr_; // expected-note {{param returned here}}
+ }
+
+ // Owned pointer field annotated with [[clang::lifetimebound]] -> no warning.
+ int *get_owned_ptr() const [[clang::lifetimebound]] {
+ return ptr_;
+ }
+
+ // Origins of fields -> field-origin-escape warning, no lifetimebound suggestion on 'this'.
+ int *get_ptr() const { // expected-warning {{return value depends on the lifetime of the object referenced by field 'ptr_', which cannot be expressed with [[clang::lifetimebound]]}}
+ return ptr_; // expected-note {{returned here}}
+ }
+ int &get_ptr_deref() const { // expected-warning {{return value depends on the lifetime of the object referenced by field 'ptr_', which cannot be expressed with [[clang::lifetimebound]]}}
+ return *ptr_; // expected-note {{returned here}}
+ }
+ int &get_ref() const { // expected-warning {{return value depends on the lifetime of the object referenced by field 'ref_', which cannot be expressed with [[clang::lifetimebound]]}}
+ return ref_; // expected-note {{returned here}}
+ }
+ int *get_ref_addr() const { // expected-warning {{return value depends on the lifetime of the object referenced by field 'ref_', which cannot be expressed with [[clang::lifetimebound]]}}
+ return &ref_; // expected-note {{returned here}}
+ }
+ std::string_view get_view() const { // expected-warning {{return value depends on the lifetime of the object referenced by field 'view_', which cannot be expressed with [[clang::lifetimebound]]}}
+ return view_; // expected-note {{returned here}}
+ }
+ const char *get_view_data() const { // expected-warning {{return value depends on the lifetime of the object referenced by field 'view_', which cannot be expressed with [[clang::lifetimebound]]}}
+ return view_.data(); // expected-note {{returned here}}
+ }
+};
+
+} // namespace field_origin_escape
More information about the cfe-commits
mailing list