[clang] [clang] Improve the lifetime_capture_by diagnostic on the constructor. (PR #117792)

Haojian Wu via cfe-commits cfe-commits at lists.llvm.org
Tue Nov 26 13:16:33 PST 2024


https://github.com/hokein created https://github.com/llvm/llvm-project/pull/117792

With this change, the lifetime_capture_by code path will not handle the constructor decl to avoid bogus diagnostics (see the testcase).

Instead, we reuse the lifetimebound code as the lifetime_capture_by(this) has the same semantic as lifetimebound in constructor. The downside is that the lifetimebound diagnostic is reused  for the capture case (I think it is not a big issue).


Fixes #117680

>From edd8e7354c4ff96446d32830f4cd5e6c3c333a84 Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Tue, 26 Nov 2024 21:42:45 +0100
Subject: [PATCH] [clang] Improve the lifetime_capture_by diagnostic on the
 constructor.

---
 clang/lib/Sema/CheckExprLifetime.cpp          | 11 +++++++++++
 clang/lib/Sema/SemaChecking.cpp               |  6 ++++++
 .../warn-lifetime-analysis-capture-by.cpp     | 19 +++++++++++++++++++
 3 files changed, 36 insertions(+)

diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp
index 6cdd4dc629e50a..c4fa73127410b5 100644
--- a/clang/lib/Sema/CheckExprLifetime.cpp
+++ b/clang/lib/Sema/CheckExprLifetime.cpp
@@ -535,6 +535,9 @@ static void visitFunctionCallArguments(IndirectLocalPath &Path, Expr *Call,
 
   bool EnableGSLAnalysis = !Callee->getASTContext().getDiagnostics().isIgnored(
       diag::warn_dangling_lifetime_pointer, SourceLocation());
+  bool EnableDanglingCapture =
+      !Callee->getASTContext().getDiagnostics().isIgnored(
+          diag::warn_dangling_reference_captured, SourceLocation());
   Expr *ObjectArg = nullptr;
   if (isa<CXXOperatorCallExpr>(Call) && Callee->isCXXInstanceMember()) {
     ObjectArg = Args[0];
@@ -623,6 +626,14 @@ static void visitFunctionCallArguments(IndirectLocalPath &Path, Expr *Call,
     }
     if (CheckCoroCall || Callee->getParamDecl(I)->hasAttr<LifetimeBoundAttr>())
       VisitLifetimeBoundArg(Callee->getParamDecl(I), Arg);
+    else if (const auto *CaptureAttr =
+                 Callee->getParamDecl(I)->getAttr<LifetimeCaptureByAttr>();
+             EnableDanglingCapture && CaptureAttr &&
+             isa<CXXConstructorDecl>(Callee) &&
+             llvm::any_of(CaptureAttr->params(), [](int ArgIdx) {
+               return ArgIdx == LifetimeCaptureByAttr::THIS;
+             }))
+      VisitLifetimeBoundArg(Callee->getParamDecl(I), Arg);
     else if (EnableGSLAnalysis && I == 0) {
       // Perform GSL analysis for the first argument
       if (shouldTrackFirstArgument(Callee)) {
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index a49605e4867651..1605523097a6b1 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -3240,8 +3240,14 @@ void Sema::checkLifetimeCaptureBy(FunctionDecl *FD, bool IsMemberFunction,
                                  unsigned ArgIdx) {
     if (!Attr)
       return;
+
     Expr *Captured = const_cast<Expr *>(GetArgAt(ArgIdx));
     for (int CapturingParamIdx : Attr->params()) {
+      // lifetime_capture_by(this) case is handled in the lifetimebound expr
+      // initialization codepath.
+      if (CapturingParamIdx == LifetimeCaptureByAttr::THIS &&
+          isa<CXXConstructorDecl>(FD))
+        continue;
       Expr *Capturing = const_cast<Expr *>(GetArgAt(CapturingParamIdx));
       CapturingEntity CE{Capturing};
       // Ensure that 'Captured' outlives the 'Capturing' entity.
diff --git a/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp b/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp
index 4d562bac1e305b..77523210e50203 100644
--- a/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp
@@ -411,3 +411,22 @@ void use() {
 }
 } // namespace with_span
 } // namespace inferred_capture_by
+
+namespace on_constructor {
+struct T {
+  T(const int& t [[clang::lifetime_capture_by(this)]]);
+};
+struct T2 {
+  T2(const int& t [[clang::lifetime_capture_by(x)]], int& x);
+};
+int foo(const T& t);
+
+void test() {
+  auto x = foo(T(1)); // OK. no diagnosic
+  T(1); // OK. no diagnostic
+  T t(1); // expected-warning {{temporary whose address is used}}
+    
+  int a;
+  T2(1, a); // expected-warning {{object whose reference is captured by}}
+}
+} // namespace on_constructor



More information about the cfe-commits mailing list