[clang] [LifetimeSafety] Handle escape through function call (PR #186126)
Abhinav Pradeep via cfe-commits
cfe-commits at lists.llvm.org
Mon Jun 8 00:58:37 PDT 2026
https://github.com/AbhinavPradeep updated https://github.com/llvm/llvm-project/pull/186126
>From d269ace78b5eb433121a3524a0f772c0b0917103 Mon Sep 17 00:00:00 2001
From: Abhinav Pradeep <abhinav.pradeep at oracle.com>
Date: Fri, 13 Mar 2026 00:21:23 +1000
Subject: [PATCH 1/3] Added new CallEscapeFact.
---
.../Analysis/Analyses/LifetimeSafety/Facts.h | 37 +++++++++++++++++++
clang/lib/Analysis/LifetimeSafety/Facts.cpp | 7 ++++
2 files changed, 44 insertions(+)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index 88b509e1b94df..8a5b24eae9b90 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -15,11 +15,13 @@
#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_FACTS_H
#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
#include "clang/Analysis/Analyses/LifetimeSafety/Loans.h"
#include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
#include "clang/Analysis/Analyses/LifetimeSafety/Utils.h"
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Analysis/CFG.h"
+#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Debug.h"
@@ -165,6 +167,7 @@ class OriginEscapesFact : public Fact {
Return, /// Escapes via return statement.
Field, /// Escapes via assignment to a field.
Global, /// Escapes via assignment to global storage.
+ Call, /// Escapes as argument to a function call.
} EscKind;
static bool classof(const Fact *F) {
@@ -234,6 +237,40 @@ class GlobalEscapeFact : public OriginEscapesFact {
const OriginManager &OM) const override;
};
+/// Represents escape of an origin through a function call.
+/// Example:
+/// void f(int *i);
+/// void g(int *j[[clang::noescape]]) {f(j)};
+/// This fact enables us to catch that the noescape parameter j escapes through
+/// the call to function f
+class CallEscapeFact : public OriginEscapesFact {
+ // Currently the analysis handles the following call-like expressions:
+ // - VisitCXXOperatorCallExpr to handle CXXOperatorCallExpr, a sub-class of
+ // CallExpr.
+ // - VisitCXXMemberCallExpr to handle CXXMemberCallExpr, a sub-class of
+ // CallExpr.
+ // - VisitCXXConstructExpr and handleGSLPointerConstruction deal with
+ // CXXConstructExpr. Whilst call like, it is not a sub-class of CallExpr.
+ // Therefore, this type is taken to be the union of CallExpr * and
+ // CXXConstructExpr *:
+ using CallLikeExprPtr = llvm::PointerUnion<CallExpr *, CXXConstructExpr *>;
+ const CallLikeExprPtr Call;
+ const unsigned ArgumentIndex;
+
+public:
+ CallEscapeFact(OriginID OID, const CallLikeExprPtr Call, const unsigned Index)
+ : OriginEscapesFact(OID, EscapeKind::Call), Call(Call),
+ ArgumentIndex(Index) {}
+ static bool classof(const Fact *F) {
+ return F->getKind() == Kind::OriginEscapes &&
+ static_cast<const OriginEscapesFact *>(F)->getEscapeKind() ==
+ EscapeKind::Call;
+ }
+ const CallLikeExprPtr getCall() const { return Call; };
+ void dump(llvm::raw_ostream &OS, const LoanManager &,
+ const OriginManager &OM) const override;
+};
+
class UseFact : public Fact {
const Expr *UseExpr;
const OriginList *OList;
diff --git a/clang/lib/Analysis/LifetimeSafety/Facts.cpp b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
index 3d7fbcdacc830..158abb5868186 100644
--- a/clang/lib/Analysis/LifetimeSafety/Facts.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Facts.cpp
@@ -77,6 +77,13 @@ void GlobalEscapeFact::dump(llvm::raw_ostream &OS, const LoanManager &,
OS << ", via Global)\n";
}
+void CallEscapeFact::dump(llvm::raw_ostream &OS, const LoanManager &,
+ const OriginManager &OM) const {
+ OS << "CallEscapes (";
+ OM.dump(getEscapedOriginID(), OS);
+ OS << ", via Call)\n";
+}
+
void UseFact::dump(llvm::raw_ostream &OS, const LoanManager &,
const OriginManager &OM) const {
OS << "Use (";
>From 021cc16b0cdcf8c8252a14116e95aa05d2765370 Mon Sep 17 00:00:00 2001
From: Abhinav Pradeep <abhinav.pradeep at oracle.com>
Date: Sat, 21 Mar 2026 22:52:08 +1000
Subject: [PATCH 2/3] Fixed up CallEscapeFact and ensured that it does not
participate in liveness analysis. Added no-escape checking which uses a
placeholder error message.
---
.../Analysis/Analyses/LifetimeSafety/Facts.h | 22 ++++------
clang/lib/Analysis/LifetimeSafety/Checker.cpp | 4 ++
.../LifetimeSafety/FactsGenerator.cpp | 40 +++++++++++++++++--
.../Analysis/LifetimeSafety/LiveOrigins.cpp | 6 +++
.../Sema/warn-lifetime-safety-noescape.cpp | 9 +++--
5 files changed, 60 insertions(+), 21 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
index 8a5b24eae9b90..5bed7cf3a70c0 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -244,29 +244,23 @@ class GlobalEscapeFact : public OriginEscapesFact {
/// This fact enables us to catch that the noescape parameter j escapes through
/// the call to function f
class CallEscapeFact : public OriginEscapesFact {
- // Currently the analysis handles the following call-like expressions:
- // - VisitCXXOperatorCallExpr to handle CXXOperatorCallExpr, a sub-class of
- // CallExpr.
- // - VisitCXXMemberCallExpr to handle CXXMemberCallExpr, a sub-class of
- // CallExpr.
- // - VisitCXXConstructExpr and handleGSLPointerConstruction deal with
- // CXXConstructExpr. Whilst call like, it is not a sub-class of CallExpr.
- // Therefore, this type is taken to be the union of CallExpr * and
- // CXXConstructExpr *:
- using CallLikeExprPtr = llvm::PointerUnion<CallExpr *, CXXConstructExpr *>;
- const CallLikeExprPtr Call;
+ const Expr *Call;
+ const Expr *Argument;
const unsigned ArgumentIndex;
public:
- CallEscapeFact(OriginID OID, const CallLikeExprPtr Call, const unsigned Index)
+ CallEscapeFact(OriginID OID, const Expr *Call, const unsigned Index,
+ const Expr *Argument)
: OriginEscapesFact(OID, EscapeKind::Call), Call(Call),
- ArgumentIndex(Index) {}
+ Argument(Argument), ArgumentIndex(Index) {}
static bool classof(const Fact *F) {
return F->getKind() == Kind::OriginEscapes &&
static_cast<const OriginEscapesFact *>(F)->getEscapeKind() ==
EscapeKind::Call;
}
- const CallLikeExprPtr getCall() const { return Call; };
+ const Expr *getCall() const { return Call; };
+ unsigned getArgumentIndex() const { return ArgumentIndex; };
+ const Expr *getArgument() const { return Argument; };
void dump(llvm::raw_ostream &OS, const LoanManager &,
const OriginManager &OM) const override;
};
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 2a71b5eb9a934..7209a237d4273 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -126,6 +126,10 @@ class LifetimeChecker {
NoescapeWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
if (auto *GlobalEsc = dyn_cast<GlobalEscapeFact>(OEF))
NoescapeWarningsMap.try_emplace(PVD, GlobalEsc->getGlobal());
+ if (auto *CallEsc = dyn_cast<CallEscapeFact>(OEF))
+ // Currently this triggers the wrong reporting. Will fix with next
+ // commit!
+ NoescapeWarningsMap.try_emplace(PVD, CallEsc->getArgument());
return;
}
// Skip annotation suggestion for moved loans, as ownership transfer
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 633f9ae57930b..0893b6e8ab3f2 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -14,6 +14,7 @@
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/OperationKinds.h"
+#include "clang/AST/Stmt.h"
#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
#include "clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h"
#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
@@ -21,6 +22,7 @@
#include "clang/Analysis/Analyses/PostOrderCFGView.h"
#include "clang/Analysis/CFG.h"
#include "clang/Basic/OperatorKinds.h"
+#include "clang/Basic/SourceManager.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Casting.h"
@@ -943,13 +945,45 @@ void FactsGenerator::handleFunctionCall(const Expr *Call,
ArrayRef<const Expr *> Args,
bool IsGslConstruction) {
OriginList *CallList = getOriginsList(*Call);
+ SourceManager &SM = AC.getASTContext().getSourceManager();
+ // To avoid over-reporting, we assume the following are noescape:
+ // - All parameters to functions declared in the system headers
+ // - The implicit `this` parameter for member functions
+ auto IsArgNoEscape = [FD, &SM](unsigned I) -> bool {
+ const ParmVarDecl *PVD = nullptr;
+ if (const auto *Method = dyn_cast<CXXMethodDecl>(FD);
+ Method && Method->isInstance()) {
+ // There is currently no way to declare 'this' is noescape for member
+ // functions We therefore return true as the user cannot do anything via
+ // annotation, so we make the conservative approximation
+ if (I == 0) {
+ return true;
+ }
+ if ((I - 1) < Method->getNumParams()) {
+ PVD = Method->getParamDecl(I - 1);
+ }
+ } else if (I < FD->getNumParams()) {
+ PVD = FD->getParamDecl(I);
+ }
+ if (PVD && !SM.isInSystemHeader(PVD->getLocation()))
+ return PVD->hasAttr<clang::NoEscapeAttr>();
+ return true;
+ };
+ // All arguments to a function are a use of the corresponding expressions.
+ for (unsigned I = 0; I < Args.size(); ++I) {
+ handleUse(Args[I]);
+ OriginList *ArgList = getOriginsList(*Args[I]);
+ if (!IsArgNoEscape(I)) {
+ for (OriginList *L = ArgList; L; L = L->peelOuterOrigin()) {
+ EscapesInCurrentBlock.push_back(FactMgr.createFact<CallEscapeFact>(
+ L->getOuterOriginID(), Call, I, Args[I]));
+ }
+ }
+ }
// Ignore functions returning values with no origin.
FD = getDeclWithMergedLifetimeBoundAttrs(FD);
if (!FD)
return;
- // All arguments to a function are a use of the corresponding expressions.
- for (const Expr *Arg : Args)
- handleUse(Arg);
handleInvalidatingCall(Call, FD, Args);
handleDestructiveCall(Call, FD, Args);
handleMovedArgsInCall(FD, Args);
diff --git a/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp b/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp
index cfbcacf04b1b0..87640dbdca925 100644
--- a/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp
@@ -9,6 +9,7 @@
#include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"
#include "Dataflow.h"
#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
+#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
namespace clang::lifetimes::internal {
@@ -64,6 +65,8 @@ static SourceLocation GetFactLoc(CausingFactType F) {
return FieldEsc->getFieldDecl()->getLocation();
if (auto *GlobalEsc = dyn_cast<GlobalEscapeFact>(OEF))
return GlobalEsc->getGlobal()->getLocation();
+ if (auto *CallEsc = dyn_cast<CallEscapeFact>(OEF))
+ return CallEsc->getArgument()->getExprLoc();
}
llvm_unreachable("unhandled causing fact in PointerUnion");
}
@@ -148,6 +151,9 @@ class AnalysisImpl
/// An escaping origin (e.g., via return) makes the origin live with definite
/// confidence, as it dominates this program point.
Lattice transfer(Lattice In, const OriginEscapesFact &OEF) {
+ // CallEscapeFact should not affect liveness
+ if (isa<CallEscapeFact>(&OEF))
+ return In;
OriginID OID = OEF.getEscapedOriginID();
return Lattice(Factory.add(In.LiveOrigins, OID,
LivenessInfo(&OEF, LivenessKind::Must)));
diff --git a/clang/test/Sema/warn-lifetime-safety-noescape.cpp b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
index 4bb57e6b9df95..7b944136ff1cb 100644
--- a/clang/test/Sema/warn-lifetime-safety-noescape.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
@@ -8,9 +8,10 @@ struct [[gsl::Owner]] MyObj {
};
struct [[gsl::Pointer()]] View {
- View(const MyObj&); // Borrows from MyObj
+ View(const MyObj& obj [[clang::noescape]]); // Borrows from MyObj
View();
void use() const;
+ void let_parameter_escape(const MyObj& obj) const;
};
View return_noescape_directly(const MyObj& in [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes}}
@@ -150,9 +151,9 @@ struct ObjConsumer {
View member_view; // expected-note {{escapes to this field}}
};
-// FIXME: Escaping through another param is not detected.
-void escape_through_param(const MyObj& in, std::vector<View> &v) {
- v.push_back(in);
+void escape_through_param(const MyObj& in [[clang::noescape]], std::vector<View> &v) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes}}
+ // Has wrong reporting by virtue of how the reportNoescapeViolations is written. Will fix in the next commit!
+ v.push_back(in); // expected-note {{returned here}}
}
View reassign_to_second(
>From 54d5a633f6358e3d1f21336b3f1a9b49819e5919 Mon Sep 17 00:00:00 2001
From: Abhinav Pradeep <abhinav.pradeep at oracle.com>
Date: Mon, 8 Jun 2026 17:55:20 +1000
Subject: [PATCH 3/3] Rebased and fixed up reporting.
---
.../Analyses/LifetimeSafety/LifetimeSafety.h | 5 +++
.../clang/Basic/DiagnosticSemaKinds.td | 1 +
clang/lib/Analysis/LifetimeSafety/Checker.cpp | 32 ++++++++-----------
clang/lib/Sema/SemaLifetimeSafety.h | 11 +++++++
.../Sema/warn-lifetime-safety-noescape.cpp | 7 ++--
5 files changed, 33 insertions(+), 23 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index 398cce1395854..9a082f236180f 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -118,6 +118,11 @@ class LifetimeSafetySemaHelper {
// assignment to a global variable
virtual void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape,
const VarDecl *EscapeGlobal) {}
+ // Reports misuse of [[clang::noescape]] when parameter escapes through
+ // a function call.
+ virtual void
+ reportNoescapeViolationThroughCall(const ParmVarDecl *ParmWithNoescape,
+ const Expr *EscapeCall) {}
// Reports misuse of [[clang::lifetimebound]] when parameter doesn't escape
// through return.
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 4e3585b7b8191..099c584162a4e 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11051,6 +11051,7 @@ def note_lifetime_safety_escapes_to_field_here: Note<"escapes to this field">;
def note_lifetime_safety_escapes_to_global_here: Note<"escapes to this global storage">;
def note_lifetime_safety_escapes_to_static_storage_here: Note<"escapes to this static storage">;
def note_lifetime_safety_lifetimebound_here: Note<"'lifetimebound' attribute appears here on the definition">;
+def note_lifetime_safety_escapes_through_call_here: Note<"escapes through this call">;
def warn_lifetime_safety_intra_tu_param_suggestion
: Warning<"parameter in intra-TU function should be marked "
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 7209a237d4273..dbad69548393f 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -59,7 +59,8 @@ class LifetimeChecker {
private:
llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;
llvm::DenseMap<AnnotationTarget, EscapingTarget> AnnotationWarningsMap;
- llvm::DenseMap<const ParmVarDecl *, EscapingTarget> NoescapeWarningsMap;
+ llvm::DenseMap<const ParmVarDecl *, const OriginEscapesFact *>
+ NoescapeWarningsMap;
llvm::DenseSet<const Decl *> VerifiedLiftimeboundEscapes;
const LoanPropagationAnalysis &LoanPropagation;
const MovedLoansAnalysis &MovedLoans;
@@ -120,16 +121,7 @@ class LifetimeChecker {
auto CheckParam = [&](const ParmVarDecl *PVD, bool IsMoved) {
// NoEscape param should not escape.
if (PVD->hasAttr<NoEscapeAttr>()) {
- if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
- NoescapeWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr());
- if (auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
- NoescapeWarningsMap.try_emplace(PVD, FieldEsc->getFieldDecl());
- if (auto *GlobalEsc = dyn_cast<GlobalEscapeFact>(OEF))
- NoescapeWarningsMap.try_emplace(PVD, GlobalEsc->getGlobal());
- if (auto *CallEsc = dyn_cast<CallEscapeFact>(OEF))
- // Currently this triggers the wrong reporting. Will fix with next
- // commit!
- NoescapeWarningsMap.try_emplace(PVD, CallEsc->getArgument());
+ NoescapeWarningsMap.try_emplace(PVD, OEF);
return;
}
// Skip annotation suggestion for moved loans, as ownership transfer
@@ -410,15 +402,17 @@ class LifetimeChecker {
}
void reportNoescapeViolations() {
- for (auto [PVD, EscapeTarget] : NoescapeWarningsMap) {
- if (const auto *E = EscapeTarget.dyn_cast<const Expr *>())
- SemaHelper->reportNoescapeViolation(PVD, E);
- else if (const auto *FD = EscapeTarget.dyn_cast<const FieldDecl *>())
- SemaHelper->reportNoescapeViolation(PVD, FD);
- else if (const auto *G = EscapeTarget.dyn_cast<const VarDecl *>())
- SemaHelper->reportNoescapeViolation(PVD, G);
+ for (auto [PVD, OEF] : NoescapeWarningsMap) {
+ if (const auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
+ SemaHelper->reportNoescapeViolation(PVD, ReturnEsc->getReturnExpr());
+ else if (const auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
+ SemaHelper->reportNoescapeViolation(PVD, FieldEsc->getFieldDecl());
+ else if (const auto *GlobalEsc = dyn_cast<GlobalEscapeFact>(OEF))
+ SemaHelper->reportNoescapeViolation(PVD, GlobalEsc->getGlobal());
+ else if (const auto *CallEsc = dyn_cast<CallEscapeFact>(OEF))
+ SemaHelper->reportNoescapeViolationThroughCall(PVD, CallEsc->getCall());
else
- llvm_unreachable("Unhandled EscapingTarget type");
+ llvm_unreachable("Unhandled escape fact kind");
}
}
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index 6da4953dea56d..d4e219006118b 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -398,6 +398,17 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper {
<< EscapeGlobal->getEndLoc();
}
+ void reportNoescapeViolationThroughCall(const ParmVarDecl *ParmWithNoescape,
+ const Expr *EscapeCall) override {
+ S.Diag(ParmWithNoescape->getBeginLoc(),
+ diag::warn_lifetime_safety_noescape_escapes)
+ << ParmWithNoescape->getSourceRange();
+
+ S.Diag(EscapeCall->getBeginLoc(),
+ diag::note_lifetime_safety_escapes_through_call_here)
+ << EscapeCall->getSourceRange();
+ }
+
void addLifetimeBoundToImplicitThis(const CXXMethodDecl *MD) override {
S.addLifetimeBoundToImplicitThis(const_cast<CXXMethodDecl *>(MD));
}
diff --git a/clang/test/Sema/warn-lifetime-safety-noescape.cpp b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
index 7b944136ff1cb..fcf58d4a54740 100644
--- a/clang/test/Sema/warn-lifetime-safety-noescape.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-noescape.cpp
@@ -105,13 +105,13 @@ View identity_lifetimebound(View v [[clang::lifetimebound]]) { return v; }
View escape_through_lifetimebound_call(
const MyObj& in [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes}}
- return identity_lifetimebound(in); // expected-note {{returned here}}
+ return identity_lifetimebound(in); // expected-note {{escapes through this call}}
}
View no_annotation_identity(View v) { return v; }
View escape_through_unannotated_call(const MyObj& in [[clang::noescape]]) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes}}
- return no_annotation_identity(in); // expected-note {{returned here}}
+ return no_annotation_identity(in); // expected-note {{escapes through this call}}
}
View global_view; // expected-note {{escapes to this global storage}}
@@ -152,8 +152,7 @@ struct ObjConsumer {
};
void escape_through_param(const MyObj& in [[clang::noescape]], std::vector<View> &v) { // expected-warning {{parameter is marked [[clang::noescape]] but escapes}}
- // Has wrong reporting by virtue of how the reportNoescapeViolations is written. Will fix in the next commit!
- v.push_back(in); // expected-note {{returned here}}
+ v.push_back(in); // expected-note {{escapes through this call}}
}
View reassign_to_second(
More information about the cfe-commits
mailing list