[clang] [LifetimeSafety] Propagate origins through explicit cast expressions (PR #192180)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Apr 14 21:47:44 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-analysis
Author: Zhijie Wang (aeft)
<details>
<summary>Changes</summary>
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
---
Full diff: https://github.com/llvm/llvm-project/pull/192180.diff
3 Files Affected:
- (modified) clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h (+1-1)
- (modified) clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp (+7-8)
- (modified) clang/test/Sema/warn-lifetime-safety.cpp (+27)
``````````diff
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 cae56ddd3d7c3..d27eb1be22429 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -270,14 +270,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())
@@ -288,11 +288,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:
@@ -517,8 +517,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 f87b5cbdd0230..9f4456465281a 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}}
``````````
</details>
https://github.com/llvm/llvm-project/pull/192180
More information about the cfe-commits
mailing list