[clang] [clang] Emit -Wdangling diagnoses for cases where a gsl-pointer is construct from a gsl-owner object in a container. (PR #104556)
Haojian Wu via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 4 05:15:44 PDT 2024
https://github.com/hokein updated https://github.com/llvm/llvm-project/pull/104556
>From aa8dda3e3ccd24f12e50509f79ade63b90cf97b5 Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Mon, 19 Aug 2024 13:46:16 +0200
Subject: [PATCH] [clang] Diagnose dangling issues for cases where a
gsl-pointer is construct from a gsl-owner object in a container.
---
clang/docs/ReleaseNotes.rst | 2 ++
clang/lib/Sema/CheckExprLifetime.cpp | 26 +++++++++--------
.../Sema/warn-lifetime-analysis-nocfg.cpp | 28 +++++++++++++++++++
3 files changed, 44 insertions(+), 12 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index bcdbc5b702765e..e1b174ab43696f 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -273,6 +273,8 @@ Improvements to Clang's diagnostics
- The lifetimebound and GSL analysis in clang are coherent, allowing clang to
detect more use-after-free bugs. (#GH100549).
+- Clang now diagnoses dangling cases where a gsl-pointer is constructed from a gsl-owner object inside a container (#GH100384).
+
Improvements to Clang's time-trace
----------------------------------
diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp
index 1482711cc2839e..6b8611a11afa45 100644
--- a/clang/lib/Sema/CheckExprLifetime.cpp
+++ b/clang/lib/Sema/CheckExprLifetime.cpp
@@ -401,13 +401,17 @@ static void visitFunctionCallArguments(IndirectLocalPath &Path, Expr *Call,
visitLocalsRetainedByInitializer(Path, Arg, Visit, true);
Path.pop_back();
};
- auto VisitGSLPointerArg = [&](const Decl *D, Expr *Arg, bool Value) {
+ auto VisitGSLPointerArg = [&](const FunctionDecl *Callee, Expr *Arg) {
// We are not interested in the temporary base objects of gsl Pointers:
// Temp().ptr; // Here ptr might not dangle.
if (isa<MemberExpr>(Arg->IgnoreImpCasts()))
return;
- // Once we initialized a value with a reference, it can no longer dangle.
- if (!Value) {
+ auto ReturnType = Callee->getReturnType();
+
+ // Once we initialized a value with a non gsl-owner reference, it can no
+ // longer dangle.
+ if (ReturnType->isReferenceType() &&
+ !isRecordWithAttr<OwnerAttr>(ReturnType->getPointeeType())) {
for (const IndirectLocalPathEntry &PE : llvm::reverse(Path)) {
if (PE.Kind == IndirectLocalPathEntry::GslReferenceInit ||
PE.Kind == IndirectLocalPathEntry::LifetimeBoundCall)
@@ -418,9 +422,10 @@ static void visitFunctionCallArguments(IndirectLocalPath &Path, Expr *Call,
break;
}
}
- Path.push_back({Value ? IndirectLocalPathEntry::GslPointerInit
- : IndirectLocalPathEntry::GslReferenceInit,
- Arg, D});
+ Path.push_back({ReturnType->isReferenceType()
+ ? IndirectLocalPathEntry::GslReferenceInit
+ : IndirectLocalPathEntry::GslPointerInit,
+ Arg, Callee});
if (Arg->isGLValue())
visitLocalsRetainedByReferenceBinding(Path, Arg, RK_ReferenceBinding,
Visit);
@@ -451,8 +456,7 @@ static void visitFunctionCallArguments(IndirectLocalPath &Path, Expr *Call,
else if (EnableGSLAnalysis) {
if (auto *CME = dyn_cast<CXXMethodDecl>(Callee);
CME && shouldTrackImplicitObjectArg(CME))
- VisitGSLPointerArg(Callee, ObjectArg,
- !Callee->getReturnType()->isReferenceType());
+ VisitGSLPointerArg(Callee, ObjectArg);
}
}
@@ -463,13 +467,11 @@ static void visitFunctionCallArguments(IndirectLocalPath &Path, Expr *Call,
VisitLifetimeBoundArg(Callee->getParamDecl(I), Args[I]);
else if (EnableGSLAnalysis && I == 0) {
if (shouldTrackFirstArgument(Callee)) {
- VisitGSLPointerArg(Callee, Args[0],
- !Callee->getReturnType()->isReferenceType());
+ VisitGSLPointerArg(Callee, Args[0]);
} else if (auto *CCE = dyn_cast<CXXConstructExpr>(Call);
CCE &&
CCE->getConstructor()->getParent()->hasAttr<PointerAttr>()) {
- VisitGSLPointerArg(CCE->getConstructor()->getParamDecl(0), Args[0],
- true);
+ VisitGSLPointerArg(CCE->getConstructor(), Args[0]);
}
}
}
diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
index 67d1ceaa02d039..59357d0730a7d9 100644
--- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
@@ -275,6 +275,34 @@ int &danglingRawPtrFromLocal3() {
return *o; // expected-warning {{reference to stack memory associated with local variable 'o' returned}}
}
+// GH100384
+std::string_view containerWithAnnotatedElements() {
+ std::string_view c1 = std::vector<std::string>().at(0); // expected-warning {{object backing the pointer will be destroyed at the end of the full-expression}}
+ c1 = std::vector<std::string>().at(0); // expected-warning {{object backing the pointer}}
+
+ // no warning on constructing from gsl-pointer
+ std::string_view c2 = std::vector<std::string_view>().at(0);
+
+ std::vector<std::string> local;
+ return local.at(0); // expected-warning {{address of stack memory associated with local variable}}
+}
+
+std::string_view localUniquePtr(int i) {
+ std::unique_ptr<std::string> c1;
+ if (i)
+ return *c1; // expected-warning {{address of stack memory associated with local variable}}
+ std::unique_ptr<std::string_view> c2;
+ return *c2; // expect no-warning.
+}
+
+std::string_view localOptional(int i) {
+ std::optional<std::string> o;
+ if (i)
+ return o.value(); // expected-warning {{address of stack memory associated with local variable}}
+ std::optional<std::string_view> abc;
+ return abc.value(); // expect no warning
+}
+
const char *danglingRawPtrFromTemp() {
return std::basic_string<char>().c_str(); // expected-warning {{returning address of local temporary object}}
}
More information about the cfe-commits
mailing list