[clang] [clang] Detect dangling assignment for "Container<Pointer>" case. (PR #108205)
Haojian Wu via cfe-commits
cfe-commits at lists.llvm.org
Wed Oct 2 07:34:29 PDT 2024
https://github.com/hokein updated https://github.com/llvm/llvm-project/pull/108205
>From ff2f955965fd5f8f17ae76a11e495217aa4f02fd Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Wed, 2 Oct 2024 13:26:44 +0200
Subject: [PATCH] [clang] Detect dangling assignment for "Container<Pointer>"
case.
---
clang/lib/Sema/CheckExprLifetime.cpp | 18 ++++++++++++++--
.../Sema/warn-lifetime-analysis-nocfg.cpp | 21 ++++++++++++++++---
2 files changed, 34 insertions(+), 5 deletions(-)
diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp
index 009b8d000e6b0e..26f49bc559dc55 100644
--- a/clang/lib/Sema/CheckExprLifetime.cpp
+++ b/clang/lib/Sema/CheckExprLifetime.cpp
@@ -1096,7 +1096,8 @@ static bool shouldRunGSLAssignmentAnalysis(const Sema &SemaRef,
diag::warn_dangling_lifetime_pointer_assignment, SourceLocation());
return (EnableGSLAssignmentWarnings &&
(isRecordWithAttr<PointerAttr>(Entity.LHS->getType()) ||
- isAssignmentOperatorLifetimeBound(Entity.AssignmentOperator)));
+ isAssignmentOperatorLifetimeBound(Entity.AssignmentOperator) ||
+ isContainerOfPointer(Entity.LHS->getType()->getAsRecordDecl())));
}
static void checkExprLifetimeImpl(Sema &SemaRef,
@@ -1391,8 +1392,21 @@ static void checkExprLifetimeImpl(Sema &SemaRef,
};
llvm::SmallVector<IndirectLocalPathEntry, 8> Path;
- if (LK == LK_Assignment && shouldRunGSLAssignmentAnalysis(SemaRef, *AEntity))
+ if (LK == LK_Assignment &&
+ shouldRunGSLAssignmentAnalysis(SemaRef, *AEntity)) {
+ if (isContainerOfPointer(AEntity->LHS->getType()->getAsRecordDecl())) {
+ // Bail out for user-defined assignment operators, as their contract is
+ // unknown.
+ if (!AEntity->AssignmentOperator->isDefaulted())
+ return;
+ // Skip the top MaterializeTemoraryExpr because it is temporary object of
+ // the containerOfPointer itself.
+ if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(Init))
+ Init = MTE->getSubExpr();
+ }
+
Path.push_back({IndirectLocalPathEntry::GslPointerAssignment, Init});
+ }
if (Init->isGLValue())
visitLocalsRetainedByReferenceBinding(Path, Init, RK_ReferenceBinding,
diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
index 731639ab16a735..db53a434789812 100644
--- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
@@ -634,18 +634,27 @@ void test() {
std::optional<std::string_view> o3 = std::optional<std::string>(s); // expected-warning {{object backing the pointer}}
std::optional<std::string_view> o4 = std::optional<std::string_view>(s);
- // FIXME: should work for assignment cases
- v1 = {std::string()};
- o1 = std::string();
+ v1 = {std::string()}; // expected-warning {{object backing the pointer}}
+ o1 = std::string(); // expected-warning {{object backing the pointer}}
// no warning on copying pointers.
std::vector<std::string_view> n1 = {std::string_view()};
+ n1 = {std::string_view()};
std::optional<std::string_view> n2 = {std::string_view()};
+ n2 = {std::string_view()};
std::optional<std::string_view> n3 = std::string_view();
+ n3 = std::string_view();
std::optional<std::string_view> n4 = std::make_optional(std::string_view());
+ n4 = std::make_optional(std::string_view());
const char* b = "";
std::optional<std::string_view> n5 = std::make_optional(b);
+ n5 = std::make_optional(b);
std::optional<std::string_view> n6 = std::make_optional("test");
+ n6 = std::make_optional("test");
+ // Deeper nested containers are not supported; only first-level nesting is
+ // supported.
+ std::vector<std::vector<std::string_view>> n7 = {{std::string()}};
+ n7 = {{std::string()}};
}
std::vector<std::string_view> test2(int i) {
@@ -683,7 +692,9 @@ std::optional<std::string_view> test3(int i) {
return s; // expected-warning {{address of stack memory associated}}
return sv; // fine
Container2<std::string_view> c1 = Container1<Foo>(); // no diagnostic as Foo is not an Owner.
+ c1 = Container1<Foo>();
Container2<std::string_view> c2 = Container1<FooOwner>(); // expected-warning {{object backing the pointer will be destroyed}}
+ c2 = Container1<FooOwner>(); // expected-warning {{object backing the pointer}}
return GetFoo(); // fine, we don't know Foo is owner or not, be conservative.
return GetFooOwner(); // expected-warning {{returning address of local temporary object}}
}
@@ -713,10 +724,12 @@ struct [[gsl::Pointer]] Span {
// Pointer from Owner<Pointer>
std::string_view test5() {
std::string_view a = StatusOr<std::string_view>().valueLB(); // expected-warning {{object backing the pointer will be dest}}
+ a = StatusOr<std::string_view>().valueLB(); // expected-warning {{object backing the pointer}}
return StatusOr<std::string_view>().valueLB(); // expected-warning {{returning address of local temporary}}
// No dangling diagnostics on non-lifetimebound methods.
std::string_view b = StatusOr<std::string_view>().valueNoLB();
+ b = StatusOr<std::string_view>().valueNoLB();
return StatusOr<std::string_view>().valueNoLB();
}
@@ -773,8 +786,10 @@ const int& test12(Span<int> a) {
void test13() {
// FIXME: RHS is Owner<Pointer>, we skip this case to avoid false positives.
std::optional<Span<int*>> abc = std::vector<int*>{};
+ abc = std::vector<int*> {};
std::optional<Span<int>> t = std::vector<int> {}; // expected-warning {{object backing the pointer will be destroyed}}
+ t = std::vector<int> {}; // expected-warning {{object backing the pointer}}
}
} // namespace GH100526
More information about the cfe-commits
mailing list