[clang] [LifetimeSafety] Diagnose invalidated-field and invalidated-global (PR #196680)
https://github.com/zeyi2 updated
https://github.com/llvm/llvm-project/pull/196680
>From 678ef78a052179af827329ef8e794a5dacf87e15 Mon Sep 17 00:00:00 2001
From: Zeyi Xu
Date: Sat, 9 May 2026 10:39:13 +0800
Subject: [PATCH 1/2] [LifetimeSafety] Diagnose invalidated-field and
invalidated-global
---
.../Analyses/LifetimeSafety/LifetimeSafety.h | 6 +
.../clang/Basic/DiagnosticSemaKinds.td| 8 +
clang/lib/Analysis/LifetimeSafety/Checker.cpp | 20 +-
.../LifetimeSafety/FactsGenerator.cpp | 7 +-
clang/lib/Sema/SemaLifetimeSafety.h | 35 +++
.../warn-lifetime-safety-invalidations.cpp| 215 --
6 files changed, 266 insertions(+), 25 deletions(-)
diff --git
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index d20ac87a7c8d9..2332ce36646ed 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -88,6 +88,12 @@ class LifetimeSafetySemaHelper {
virtual void reportUseAfterInvalidation(const ParmVarDecl *PVD,
const Expr *UseExpr,
const Expr *InvalidationExpr) {}
+ virtual void reportInvalidatedField(const Expr *IssueExpr,
+ const FieldDecl *Field,
+ const Expr *InvalidationExpr) {}
+ virtual void reportInvalidatedGlobal(const Expr *IssueExpr,
+ const VarDecl *DanglingGlobal,
+ const Expr *InvalidationExpr) {}
using EscapingTarget =
llvm::PointerUnion;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index c69b2ce3648f8..07846cd761ee3 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10985,6 +10985,14 @@ def warn_lifetime_safety_invalidation
: Warning<"%select{object whose reference is captured|parameter}0 is later
invalidated">,
InGroup,
DefaultIgnore;
+def warn_lifetime_safety_invalidated_field
+: Warning<"object whose reference is stored in a field is later
invalidated">,
+ InGroup,
+ DefaultIgnore;
+def warn_lifetime_safety_invalidated_global
+: Warning<"object whose reference is stored in global or static storage is
later invalidated">,
+ InGroup,
+ DefaultIgnore;
def warn_lifetime_safety_dangling_field
: Warning<"address of stack memory escapes to a field">,
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 4ae90cf751ec3..fb292caf5c5de 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -259,7 +259,25 @@ class LifetimeChecker {
MovedExpr, ExpiryLoc);
} else if (const auto *OEF =
CausingFact.dyn_cast()) {
-if (const auto *RetEscape = dyn_cast(OEF))
+if (Warning.InvalidatedByExpr) {
+ if (const auto *FieldEscape = dyn_cast(OEF))
+// Field escape later invalidated.
+SemaHelper->reportInvalidatedField(IssueExpr,
+ FieldEscape->getFieldDecl(),
+ Warning.InvalidatedByExpr);
+ else if (const auto *GlobalEscape = dyn_cast(OEF))
+// Global escape later invalidated.
+SemaHelper->reportInvalidatedGlobal(IssueExpr,
+GlobalEscape->getGlobal(),
+Warning.InvalidatedByExpr);
+ else if (isa(OEF))
+// Return escape.
+SemaHelper->reportUseAfterReturn(
+IssueExpr, cast(OEF)->getReturnExpr(),
+MovedExpr, ExpiryLoc);
+ else
+llvm_unreachable("Unhandled OriginEscapesFact type");
+} else if (const auto *RetEscape = dyn_cast(OEF))
// Return stack address.
SemaHelper->reportUseAfterReturn(
IssueExpr, RetEscape->getReturnExpr(), MovedExpr, ExpiryLoc);
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 0a06548d881d1..0e6b032914b05 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -810,9 +810,10 @@ void FactsGenerator::handleInvalidatingCall(const Expr
*Call,
if (!isInvalidationMethod(*MD))
return;
- // Heuristics to turn-down false positives.
- auto *DRE = dyn_cast(Args[0]);
- if (!DRE || DRE->getDecl()->getType()->isReferenceType())
+ // Heuristics to turn-down false positives. Skip membe
[clang] [LifetimeSafety] Diagnose invalidated-field and invalidated-global (PR #196680)
@@ -810,9 +810,10 @@ void FactsGenerator::handleInvalidatingCall(const Expr *Call, if (!isInvalidationMethod(*MD)) return; - // Heuristics to turn-down false positives. - auto *DRE = dyn_cast(Args[0]); - if (!DRE || DRE->getDecl()->getType()->isReferenceType()) + // Heuristics to turn-down false positives. Skip member field expressions for + // now. This is not a perfect filter and will still surface some false + // positives (e.g. `auto& r = s.v`). + if (!isa(Args[0]->IgnoreParenImpCasts())) zeyi2 wrote: This is from the reverted PR: https://github.com/llvm/llvm-project/pull/195231 These are technically two separate changes. But to support the new field/global diagnostics, we need to make that heuristic slightly less conservative first, so I merged these two PRs. https://github.com/llvm/llvm-project/pull/196680 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [LifetimeSafety] Diagnose invalidated-field and invalidated-global (PR #196680)
llvmorg-github-actions[bot] wrote:
@llvm/pr-subscribers-clang-temporal-safety
@llvm/pr-subscribers-clang
Author: Zeyi Xu (zeyi2)
Changes
Teach lifetime safety invalidation diagnostics to handle origins that escape
through fields or global/static storage before the referenced object is
invalidated. Previously they were skipped.
Closes https://github.com/llvm/llvm-project/issues/195706
Closes https://github.com/llvm/llvm-project/issues/193044
---
Full diff: https://github.com/llvm/llvm-project/pull/196680.diff
6 Files Affected:
- (modified)
clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h (+6)
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+8)
- (modified) clang/lib/Analysis/LifetimeSafety/Checker.cpp (+19-1)
- (modified) clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp (+4-3)
- (modified) clang/lib/Sema/SemaLifetimeSafety.h (+35)
- (modified) clang/test/Sema/warn-lifetime-safety-invalidations.cpp (+204-21)
``diff
diff --git
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index d20ac87a7c8d9..2332ce36646ed 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -88,6 +88,12 @@ class LifetimeSafetySemaHelper {
virtual void reportUseAfterInvalidation(const ParmVarDecl *PVD,
const Expr *UseExpr,
const Expr *InvalidationExpr) {}
+ virtual void reportInvalidatedField(const Expr *IssueExpr,
+ const FieldDecl *Field,
+ const Expr *InvalidationExpr) {}
+ virtual void reportInvalidatedGlobal(const Expr *IssueExpr,
+ const VarDecl *DanglingGlobal,
+ const Expr *InvalidationExpr) {}
using EscapingTarget =
llvm::PointerUnion;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index c69b2ce3648f8..07846cd761ee3 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10985,6 +10985,14 @@ def warn_lifetime_safety_invalidation
: Warning<"%select{object whose reference is captured|parameter}0 is later
invalidated">,
InGroup,
DefaultIgnore;
+def warn_lifetime_safety_invalidated_field
+: Warning<"object whose reference is stored in a field is later
invalidated">,
+ InGroup,
+ DefaultIgnore;
+def warn_lifetime_safety_invalidated_global
+: Warning<"object whose reference is stored in global or static storage is
later invalidated">,
+ InGroup,
+ DefaultIgnore;
def warn_lifetime_safety_dangling_field
: Warning<"address of stack memory escapes to a field">,
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 4ae90cf751ec3..fb292caf5c5de 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -259,7 +259,25 @@ class LifetimeChecker {
MovedExpr, ExpiryLoc);
} else if (const auto *OEF =
CausingFact.dyn_cast()) {
-if (const auto *RetEscape = dyn_cast(OEF))
+if (Warning.InvalidatedByExpr) {
+ if (const auto *FieldEscape = dyn_cast(OEF))
+// Field escape later invalidated.
+SemaHelper->reportInvalidatedField(IssueExpr,
+ FieldEscape->getFieldDecl(),
+ Warning.InvalidatedByExpr);
+ else if (const auto *GlobalEscape = dyn_cast(OEF))
+// Global escape later invalidated.
+SemaHelper->reportInvalidatedGlobal(IssueExpr,
+GlobalEscape->getGlobal(),
+Warning.InvalidatedByExpr);
+ else if (isa(OEF))
+// Return escape.
+SemaHelper->reportUseAfterReturn(
+IssueExpr, cast(OEF)->getReturnExpr(),
+MovedExpr, ExpiryLoc);
+ else
+llvm_unreachable("Unhandled OriginEscapesFact type");
+} else if (const auto *RetEscape = dyn_cast(OEF))
// Return stack address.
SemaHelper->reportUseAfterReturn(
IssueExpr, RetEscape->getReturnExpr(), MovedExpr, ExpiryLoc);
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 0a06548d881d1..0e6b032914b05 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -810,9 +810,10 @@ void FactsGenerator:
[clang] [LifetimeSafety] Diagnose invalidated-field and invalidated-global (PR #196680)
https://github.com/zeyi2 ready_for_review https://github.com/llvm/llvm-project/pull/196680 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [LifetimeSafety] Diagnose invalidated-field and invalidated-global (PR #196680)
https://github.com/zeyi2 edited https://github.com/llvm/llvm-project/pull/196680 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [LifetimeSafety] Diagnose invalidated-field and invalidated-global (PR #196680)
https://github.com/zeyi2 edited https://github.com/llvm/llvm-project/pull/196680 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [LifetimeSafety] Diagnose invalidated-field and invalidated-global (PR #196680)
https://github.com/zeyi2 created
https://github.com/llvm/llvm-project/pull/196680
None
>From 678ef78a052179af827329ef8e794a5dacf87e15 Mon Sep 17 00:00:00 2001
From: Zeyi Xu
Date: Sat, 9 May 2026 10:39:13 +0800
Subject: [PATCH] [LifetimeSafety] Diagnose invalidated-field and
invalidated-global
---
.../Analyses/LifetimeSafety/LifetimeSafety.h | 6 +
.../clang/Basic/DiagnosticSemaKinds.td| 8 +
clang/lib/Analysis/LifetimeSafety/Checker.cpp | 20 +-
.../LifetimeSafety/FactsGenerator.cpp | 7 +-
clang/lib/Sema/SemaLifetimeSafety.h | 35 +++
.../warn-lifetime-safety-invalidations.cpp| 215 --
6 files changed, 266 insertions(+), 25 deletions(-)
diff --git
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index d20ac87a7c8d9..2332ce36646ed 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -88,6 +88,12 @@ class LifetimeSafetySemaHelper {
virtual void reportUseAfterInvalidation(const ParmVarDecl *PVD,
const Expr *UseExpr,
const Expr *InvalidationExpr) {}
+ virtual void reportInvalidatedField(const Expr *IssueExpr,
+ const FieldDecl *Field,
+ const Expr *InvalidationExpr) {}
+ virtual void reportInvalidatedGlobal(const Expr *IssueExpr,
+ const VarDecl *DanglingGlobal,
+ const Expr *InvalidationExpr) {}
using EscapingTarget =
llvm::PointerUnion;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index c69b2ce3648f8..07846cd761ee3 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10985,6 +10985,14 @@ def warn_lifetime_safety_invalidation
: Warning<"%select{object whose reference is captured|parameter}0 is later
invalidated">,
InGroup,
DefaultIgnore;
+def warn_lifetime_safety_invalidated_field
+: Warning<"object whose reference is stored in a field is later
invalidated">,
+ InGroup,
+ DefaultIgnore;
+def warn_lifetime_safety_invalidated_global
+: Warning<"object whose reference is stored in global or static storage is
later invalidated">,
+ InGroup,
+ DefaultIgnore;
def warn_lifetime_safety_dangling_field
: Warning<"address of stack memory escapes to a field">,
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index 4ae90cf751ec3..fb292caf5c5de 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -259,7 +259,25 @@ class LifetimeChecker {
MovedExpr, ExpiryLoc);
} else if (const auto *OEF =
CausingFact.dyn_cast()) {
-if (const auto *RetEscape = dyn_cast(OEF))
+if (Warning.InvalidatedByExpr) {
+ if (const auto *FieldEscape = dyn_cast(OEF))
+// Field escape later invalidated.
+SemaHelper->reportInvalidatedField(IssueExpr,
+ FieldEscape->getFieldDecl(),
+ Warning.InvalidatedByExpr);
+ else if (const auto *GlobalEscape = dyn_cast(OEF))
+// Global escape later invalidated.
+SemaHelper->reportInvalidatedGlobal(IssueExpr,
+GlobalEscape->getGlobal(),
+Warning.InvalidatedByExpr);
+ else if (isa(OEF))
+// Return escape.
+SemaHelper->reportUseAfterReturn(
+IssueExpr, cast(OEF)->getReturnExpr(),
+MovedExpr, ExpiryLoc);
+ else
+llvm_unreachable("Unhandled OriginEscapesFact type");
+} else if (const auto *RetEscape = dyn_cast(OEF))
// Return stack address.
SemaHelper->reportUseAfterReturn(
IssueExpr, RetEscape->getReturnExpr(), MovedExpr, ExpiryLoc);
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 0a06548d881d1..0e6b032914b05 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -810,9 +810,10 @@ void FactsGenerator::handleInvalidatingCall(const Expr
*Call,
if (!isInvalidationMethod(*MD))
return;
- // Heuristics to turn-down false positives.
- auto *DRE = dyn_cast(Args[0]);
- if (!DRE || DRE->getDecl()->getType()->isReferenceType())
+ // Heuristics to turn-down false positives. Skip mem
[clang] [LifetimeSafety] Diagnose invalidated-field and invalidated-global (PR #196680)
https://github.com/zeyi2 edited https://github.com/llvm/llvm-project/pull/196680 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
