[clang] 1802123 - [LifetimeSafety] Propagate origins through explicit cast expressions (#192180)

via cfe-commits cfe-commits at lists.llvm.org
Wed Apr 15 06:58:59 PDT 2026


Author: Zhijie Wang
Date: 2026-04-15T19:28:54+05:30
New Revision: 18021237534da5efd9ae6bd9f1b79243d608b58f

URL: https://github.com/llvm/llvm-project/commit/18021237534da5efd9ae6bd9f1b79243d608b58f
DIFF: https://github.com/llvm/llvm-project/commit/18021237534da5efd9ae6bd9f1b79243d608b58f.diff

LOG: [LifetimeSafety] Propagate origins through explicit cast expressions (#192180)

Before this PR, `FactsGenerator` handled cast nodes with
`VisitImplicitCastExpr` (`CastKind` switch case) and
`VisitCXXFunctionalCastExpr` (handle`gsl::Pointer` types). Other
explicit casts (`CStyleCastExpr`, `CXXStaticCastExpr`, ...) had no
handler, so origin was silently dropped. This is the root cause of
#190912: the dangle in `a = StringView(s);` is missed even though the
equivalent `StringView tmp(s); a = tmp;` is reported.

The policy for "does this cast propagate origin?" is a function of
`CastKind`, independent of whether the cast is implicit or explicit.
This PR replaces `VisitImplicitCastExpr` with a generic `VisitCastExpr`.
`VisitCXXFunctionalCastExpr` is retained only to preserve the
`handleTestPoint` logic, then delegates to `VisitCastExpr`.

This mirrors `clang/lib/AST/ExprConstant.cpp`, where each evaluator
implements only `VisitCastExpr` and switches on `CastKind`; the few
ExprClass-specific overrides (e.g., `VisitCXXDynamicCastExpr`) exist
solely to attach constexpr-validity diagnostics before delegating back.

Scope: the set of `CastKind`s that propagate origin is unchanged.

Fixes: #190912

Added: 
    

Modified: 
    clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
    clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
    clang/test/Sema/warn-lifetime-safety.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index 3cf5971973c2a..a86f22a181a70 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -41,7 +41,7 @@ class FactsGenerator : public ConstStmtVisitor<FactsGenerator> {
   void VisitMemberExpr(const MemberExpr *ME);
   void VisitCallExpr(const CallExpr *CE);
   void VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *N);
-  void VisitImplicitCastExpr(const ImplicitCastExpr *ICE);
+  void VisitCastExpr(const CastExpr *CE);
   void VisitUnaryOperator(const UnaryOperator *UO);
   void VisitReturnStmt(const ReturnStmt *RS);
   void VisitBinaryOperator(const BinaryOperator *BO);

diff  --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index fcc6035c3c1c8..5adf842e5f6d0 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -279,14 +279,14 @@ void FactsGenerator::VisitCXXNullPtrLiteralExpr(
   getOriginsList(*N);
 }
 
-void FactsGenerator::VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
-  OriginList *Dest = getOriginsList(*ICE);
+void FactsGenerator::VisitCastExpr(const CastExpr *CE) {
+  OriginList *Dest = getOriginsList(*CE);
   if (!Dest)
     return;
-  const Expr *SubExpr = ICE->getSubExpr();
+  const Expr *SubExpr = CE->getSubExpr();
   OriginList *Src = getOriginsList(*SubExpr);
 
-  switch (ICE->getCastKind()) {
+  switch (CE->getCastKind()) {
   case CK_LValueToRValue:
     // TODO: Decide what to do for x-values here.
     if (!SubExpr->isLValue())
@@ -297,11 +297,11 @@ void FactsGenerator::VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
     // `int *p, *q; p = q;`) should propagate the inner origin (what the pointer
     // points to), not the outer origin (the pointer's storage location). Strip
     // the outer lvalue origin.
-    flow(getOriginsList(*ICE), getRValueOrigins(SubExpr, Src),
+    flow(getOriginsList(*CE), getRValueOrigins(SubExpr, Src),
          /*Kill=*/true);
     return;
   case CK_NullToPointer:
-    getOriginsList(*ICE);
+    getOriginsList(*CE);
     // TODO: Flow into them a null origin.
     return;
   case CK_NoOp:
@@ -542,8 +542,7 @@ void FactsGenerator::VisitCXXFunctionalCastExpr(
   // expression.
   if (handleTestPoint(FCE))
     return;
-  if (isGslPointerType(FCE->getType()))
-    killAndFlowOrigin(*FCE, *FCE->getSubExpr());
+  VisitCastExpr(FCE);
 }
 
 void FactsGenerator::VisitInitListExpr(const InitListExpr *ILE) {

diff  --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp
index c083c30f5855d..082db5d5cbf15 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -936,6 +936,33 @@ void lifetimebound_ctor() {
   (void)v;   // expected-note {{later used here}}
 }
 
+void lifetimebound_ctor_functional_cast() {
+  LifetimeBoundCtor v;
+  {
+    MyObj obj;
+    v = LifetimeBoundCtor(obj); // expected-warning {{object whose reference is captured does not live long enough}}
+  }                             // expected-note {{destroyed here}}
+  (void)v;                      // expected-note {{later used here}}
+}
+
+void lifetimebound_ctor_c_style_cast() {
+  LifetimeBoundCtor v;
+  {
+    MyObj obj;
+    v = (LifetimeBoundCtor)(obj); // expected-warning {{object whose reference is captured does not live long enough}}
+  }                               // expected-note {{destroyed here}}
+  (void)v;                        // expected-note {{later used here}}
+}
+
+void lifetimebound_ctor_static_cast() {
+  LifetimeBoundCtor v;
+  {
+    MyObj obj;
+    v = static_cast<LifetimeBoundCtor>(obj); // expected-warning {{object whose reference is captured does not live long enough}}
+  }                                          // expected-note {{destroyed here}}
+  (void)v;                                   // expected-note {{later used here}}
+}
+
 View lifetimebound_return_of_local() {
   MyObj stack;
   return Identity(stack); // expected-warning {{address of stack memory is returned later}}


        


More information about the cfe-commits mailing list