[clang] [LifetimeSafety] Gate lifetime checks behind per-diagnostic opts (PR #221610)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Sep 6 15:19:30 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-analysis
Author: Utkarsh Saxena (usx95)
<details>
<summary>Changes</summary>
Granular Lifetime Safety Check Gating
We split `IsLifetimeSafetyEnabled` into individual check functions in `SemaLifetimeSafety.h`.
These properties are stored in `LifetimeSafetyOpts`, which are then checked inside the `Checker` to avoid invoking expensive reporting functions mapping to diagnostics that are disabled.
`reportMisplacedLifetimebound` was found to be the most expensive (possibly because it iterates on redecls).
---
Full diff: https://github.com/llvm/llvm-project/pull/221610.diff
3 Files Affected:
- (modified) clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h (+5)
- (modified) clang/lib/Analysis/LifetimeSafety/Checker.cpp (+13-4)
- (modified) clang/lib/Sema/SemaLifetimeSafety.h (+64-34)
``````````diff
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index 101407e91aa65..68e84961010dd 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -41,6 +41,11 @@ struct LifetimeSafetyOpts {
/// Whether to suggest lifetime annotations.
bool SuggestAnnotations;
+
+ bool CheckNoescapeViolations;
+ bool CheckLifetimeboundViolations;
+ bool CheckMisplacedLifetimebound;
+ bool CheckInapplicableLifetimebound;
};
/// Enum to track functions visible across or within TU.
diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
index b136da486365a..a358215a295d1 100644
--- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp
@@ -105,10 +105,14 @@ class LifetimeChecker {
checkAnnotations(OEF);
issuePendingWarnings();
suggestAnnotations();
- reportNoescapeViolations();
- reportLifetimeboundViolations();
- reportMisplacedLifetimebound();
- reportInapplicableLifetimebound();
+ if (LSOpts.CheckNoescapeViolations)
+ reportNoescapeViolations();
+ if (LSOpts.CheckLifetimeboundViolations)
+ reportLifetimeboundViolations();
+ if (LSOpts.CheckMisplacedLifetimebound)
+ reportMisplacedLifetimebound();
+ if (LSOpts.CheckInapplicableLifetimebound)
+ reportInapplicableLifetimebound();
// Annotation inference is currently guarded by a frontend flag. In the
// future, this might be replaced by a design that differentiates between
// explicit and inferred findings with separate warning groups.
@@ -254,6 +258,7 @@ class LifetimeChecker {
}
void issuePendingWarnings() {
+ llvm::TimeTraceScope TimeTrace("IssuePendingWarnings");
if (!SemaHelper)
return;
for (const auto &[LID, Warning] : FinalWarningsMap) {
@@ -436,6 +441,7 @@ class LifetimeChecker {
}
void reportNoescapeViolations() {
+ llvm::TimeTraceScope TimeTrace("ReportNoescapeViolations");
for (auto [PVD, EscapeTarget] : NoescapeWarningsMap) {
if (const auto *E = EscapeTarget.dyn_cast<const Expr *>())
SemaHelper->reportNoescapeViolation(PVD, E);
@@ -449,6 +455,7 @@ class LifetimeChecker {
}
void reportLifetimeboundViolations() {
+ llvm::TimeTraceScope TimeTrace("ReportLifetimeboundViolations");
if (!isa<FunctionDecl>(FD))
return;
if (const auto *MD = dyn_cast<CXXMethodDecl>(FD);
@@ -471,6 +478,7 @@ class LifetimeChecker {
// Reports lifetimebound attributes that are placed on a function definition
// but not on the corresponding declaration.
void reportMisplacedLifetimebound() {
+ llvm::TimeTraceScope TimeTrace("ReportMisplacedLifetimebound");
const FunctionDecl *FDef = dyn_cast<FunctionDecl>(FD);
if (!FDef)
return;
@@ -501,6 +509,7 @@ class LifetimeChecker {
}
void reportInapplicableLifetimebound() {
+ llvm::TimeTraceScope TimeTrace("ReportInapplicableLifetimebound");
const auto *FDef = dyn_cast<FunctionDecl>(FD);
if (!FDef)
return;
diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h
index d87c9003e83cf..0fd3486bcee46 100644
--- a/clang/lib/Sema/SemaLifetimeSafety.h
+++ b/clang/lib/Sema/SemaLifetimeSafety.h
@@ -25,28 +25,7 @@
namespace clang::lifetimes {
-inline bool IsLifetimeSafetyEnabled(Sema &S, const Decl *D) {
- // TODO: Enable ObjectiveC later when we know it's stable enough.
- if (S.getLangOpts().ObjC)
- return false;
-
- // TODO: Default this flag to on in the future.
- if (!S.getLangOpts().CPlusPlus && !S.getLangOpts().EnableLifetimeSafetyInC)
- return false;
-
- // Translation-unit mode: whole-program analysis runs once on TU.
- // Individual function analysis is disabled when TU mode is enabled.
- if (S.getLangOpts().EnableLifetimeSafetyTUAnalysis)
- return isa<TranslationUnitDecl>(D);
-
- // Per-function mode: analysis runs on each function/method individually.
- // Skip TU-level calls when per-function mode is enabled.
- if (isa<TranslationUnitDecl>(D))
- return false;
-
- // Enable per-function mode via debug flag or specific diagnostics.
- if (S.getLangOpts().DebugRunLifetimeSafety)
- return true;
+inline bool ShouldCheckSafety(Sema &S, const Decl *D) {
DiagnosticsEngine &Diags = S.getDiagnostics();
constexpr unsigned DiagIDs[] = {
diag::warn_lifetime_safety_use_after_scope,
@@ -59,25 +38,40 @@ inline bool IsLifetimeSafetyEnabled(Sema &S, const Decl *D) {
diag::warn_lifetime_safety_dangling_field_moved,
diag::warn_lifetime_safety_dangling_global,
diag::warn_lifetime_safety_dangling_global_moved,
- diag::warn_lifetime_safety_noescape_escapes,
- diag::warn_lifetime_safety_lifetimebound_violation,
- diag::warn_lifetime_safety_cross_tu_misplaced_lifetimebound,
- diag::warn_lifetime_safety_intra_tu_misplaced_lifetimebound,
diag::warn_lifetime_safety_invalidated_field,
- diag::warn_lifetime_safety_invalidated_global,
- diag::warn_lifetime_safety_cross_tu_param_suggestion,
- diag::warn_lifetime_safety_intra_tu_param_suggestion,
- diag::warn_lifetime_safety_cross_tu_ctor_param_suggestion,
- diag::warn_lifetime_safety_intra_tu_ctor_param_suggestion,
- diag::warn_lifetime_safety_cross_tu_this_suggestion,
- diag::warn_lifetime_safety_intra_tu_this_suggestion,
- diag::warn_lifetime_safety_inapplicable_lifetimebound};
+ diag::warn_lifetime_safety_invalidated_global};
+ for (unsigned DiagID : DiagIDs)
+ if (!Diags.isIgnored(DiagID, D->getBeginLoc()))
+ return true;
+ return false;
+}
+
+inline bool ShouldCheckNoescapeViolations(Sema &S, const Decl *D) {
+ return !S.getDiagnostics().isIgnored(
+ diag::warn_lifetime_safety_noescape_escapes, D->getBeginLoc());
+}
+
+inline bool ShouldCheckLifetimeboundViolations(Sema &S, const Decl *D) {
+ return !S.getDiagnostics().isIgnored(
+ diag::warn_lifetime_safety_lifetimebound_violation, D->getBeginLoc());
+}
+
+inline bool ShouldCheckMisplacedLifetimebound(Sema &S, const Decl *D) {
+ DiagnosticsEngine &Diags = S.getDiagnostics();
+ constexpr unsigned DiagIDs[] = {
+ diag::warn_lifetime_safety_cross_tu_misplaced_lifetimebound,
+ diag::warn_lifetime_safety_intra_tu_misplaced_lifetimebound};
for (unsigned DiagID : DiagIDs)
if (!Diags.isIgnored(DiagID, D->getBeginLoc()))
return true;
return false;
}
+inline bool ShouldCheckInapplicableLifetimebound(Sema &S, const Decl *D) {
+ return !S.getDiagnostics().isIgnored(
+ diag::warn_lifetime_safety_inapplicable_lifetimebound, D->getBeginLoc());
+}
+
inline bool ShouldSuggestLifetimeAnnotations(Sema &S, const Decl *D) {
DiagnosticsEngine &Diags = S.getDiagnostics();
constexpr unsigned DiagIDs[] = {
@@ -93,10 +87,46 @@ inline bool ShouldSuggestLifetimeAnnotations(Sema &S, const Decl *D) {
return false;
}
+inline bool IsLifetimeSafetyEnabled(Sema &S, const Decl *D) {
+ // TODO: Enable ObjectiveC later when we know it's stable enough.
+ if (S.getLangOpts().ObjC)
+ return false;
+
+ // TODO: Default this flag to on in the future.
+ if (!S.getLangOpts().CPlusPlus && !S.getLangOpts().EnableLifetimeSafetyInC)
+ return false;
+
+ // Translation-unit mode: whole-program analysis runs once on TU.
+ // Individual function analysis is disabled when TU mode is enabled.
+ if (S.getLangOpts().EnableLifetimeSafetyTUAnalysis)
+ return isa<TranslationUnitDecl>(D);
+
+ // Per-function mode: analysis runs on each function/method individually.
+ // Skip TU-level calls when per-function mode is enabled.
+ if (isa<TranslationUnitDecl>(D))
+ return false;
+
+ // Enable per-function mode via debug flag or specific diagnostics.
+ if (S.getLangOpts().DebugRunLifetimeSafety)
+ return true;
+
+ return ShouldCheckSafety(S, D) || ShouldCheckNoescapeViolations(S, D) ||
+ ShouldCheckLifetimeboundViolations(S, D) ||
+ ShouldCheckMisplacedLifetimebound(S, D) ||
+ ShouldCheckInapplicableLifetimebound(S, D) ||
+ ShouldSuggestLifetimeAnnotations(S, D);
+}
+
inline LifetimeSafetyOpts GetLifetimeSafetyOpts(Sema &S, const Decl *D) {
LifetimeSafetyOpts LSOpts;
LSOpts.MaxCFGBlocks = S.getLangOpts().LifetimeSafetyMaxCFGBlocks;
LSOpts.SuggestAnnotations = ShouldSuggestLifetimeAnnotations(S, D);
+ LSOpts.CheckNoescapeViolations = ShouldCheckNoescapeViolations(S, D);
+ LSOpts.CheckLifetimeboundViolations =
+ ShouldCheckLifetimeboundViolations(S, D);
+ LSOpts.CheckMisplacedLifetimebound = ShouldCheckMisplacedLifetimebound(S, D);
+ LSOpts.CheckInapplicableLifetimebound =
+ ShouldCheckInapplicableLifetimebound(S, D);
return LSOpts;
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/221610
More information about the cfe-commits
mailing list