[clang] [LifetimeSafety] Add missing origins stats for lifetime analysis (PR #166568)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Nov 25 21:33:46 PST 2025
https://github.com/DEBADRIBASAK updated https://github.com/llvm/llvm-project/pull/166568
>From fd8693c51af7d9f91be0926f4150e77c39e2dba4 Mon Sep 17 00:00:00 2001
From: Debadri Basak <debadri1010 at gmail.com>
Date: Wed, 5 Nov 2025 14:07:42 +0000
Subject: [PATCH 1/4] Adding the lifetime stats collection logic to
AnalysisBasedWarnings
---
.../Analyses/LifetimeSafety/LifetimeSafety.h | 15 +++++++---
.../Analyses/LifetimeSafety/Origins.h | 6 ++++
.../clang/Sema/AnalysisBasedWarnings.h | 9 ++++++
.../LifetimeSafety/LifetimeSafety.cpp | 13 +++++---
clang/lib/Analysis/LifetimeSafety/Origins.cpp | 18 +++++++++++
clang/lib/Sema/AnalysisBasedWarnings.cpp | 30 +++++++++++++++++--
6 files changed, 81 insertions(+), 10 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index 91ffbb169f947..eb532bc8be3a7 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -23,7 +23,11 @@
#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
#include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"
#include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
#include "clang/Analysis/AnalysisDeclContext.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/raw_ostream.h"
+#include <string>
namespace clang::lifetimes {
@@ -44,10 +48,6 @@ class LifetimeSafetyReporter {
Confidence Confidence) {}
};
-/// The main entry point for the analysis.
-void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC,
- LifetimeSafetyReporter *Reporter);
-
namespace internal {
/// An object to hold the factories for immutable collections, ensuring
/// that all created states share the same underlying memory management.
@@ -60,6 +60,7 @@ struct LifetimeFactory {
/// Running the lifetime safety analysis and querying its results. It
/// encapsulates the various dataflow analyses.
class LifetimeSafetyAnalysis {
+
public:
LifetimeSafetyAnalysis(AnalysisDeclContext &AC,
LifetimeSafetyReporter *Reporter);
@@ -82,6 +83,12 @@ class LifetimeSafetyAnalysis {
std::unique_ptr<LoanPropagationAnalysis> LoanPropagation;
};
} // namespace internal
+
+/// The main entry point for the analysis.
+std::unique_ptr<internal::LifetimeSafetyAnalysis>
+runLifetimeSafetyAnalysis(AnalysisDeclContext &AC,
+ LifetimeSafetyReporter *Reporter);
+
} // namespace clang::lifetimes
#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_H
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
index ba138b078b379..3f8c8a4d7ce9b 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
@@ -16,7 +16,10 @@
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
+#include "clang/AST/TypeBase.h"
#include "clang/Analysis/Analyses/LifetimeSafety/Utils.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/raw_ostream.h"
namespace clang::lifetimes::internal {
@@ -76,6 +79,8 @@ class OriginManager {
void dump(OriginID OID, llvm::raw_ostream &OS) const;
+ const llvm::StringMap<int> getMissingOrigins() const;
+
private:
OriginID getNextOriginID() { return NextOriginID++; }
@@ -85,6 +90,7 @@ class OriginManager {
llvm::SmallVector<Origin> AllOrigins;
llvm::DenseMap<const clang::ValueDecl *, OriginID> DeclToOriginID;
llvm::DenseMap<const clang::Expr *, OriginID> ExprToOriginID;
+ llvm::StringMap<unsigned> ExprTypeToMissingOriginCount;
};
} // namespace clang::lifetimes::internal
diff --git a/clang/include/clang/Sema/AnalysisBasedWarnings.h b/clang/include/clang/Sema/AnalysisBasedWarnings.h
index 4103c3f006a8f..604039ef61cb7 100644
--- a/clang/include/clang/Sema/AnalysisBasedWarnings.h
+++ b/clang/include/clang/Sema/AnalysisBasedWarnings.h
@@ -14,7 +14,10 @@
#define LLVM_CLANG_SEMA_ANALYSISBASEDWARNINGS_H
#include "clang/AST/Decl.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringMap.h"
#include <memory>
namespace clang {
@@ -95,6 +98,9 @@ class AnalysisBasedWarnings {
/// a single function.
unsigned MaxUninitAnalysisBlockVisitsPerFunction;
+ /// Map from expressions missing origin in OriginManager to their counts.
+ llvm::StringMap<unsigned> MissingOriginCount;
+
/// @}
public:
@@ -116,6 +122,9 @@ class AnalysisBasedWarnings {
Policy &getPolicyOverrides() { return PolicyOverrides; }
void PrintStats() const;
+
+ void FindMissingOrigins(AnalysisDeclContext &AC,
+ clang::lifetimes::internal::FactManager &FactMgr);
};
} // namespace sema
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp
index 00c7ed90503e7..d183ce976f946 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp
@@ -23,9 +23,11 @@
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Analysis/CFG.h"
#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/StringMap.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/TimeProfiler.h"
+#include "llvm/Support/raw_ostream.h"
#include <memory>
namespace clang::lifetimes {
@@ -69,9 +71,12 @@ void LifetimeSafetyAnalysis::run() {
}
} // namespace internal
-void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC,
- LifetimeSafetyReporter *Reporter) {
- internal::LifetimeSafetyAnalysis Analysis(AC, Reporter);
- Analysis.run();
+std::unique_ptr<internal::LifetimeSafetyAnalysis>
+runLifetimeSafetyAnalysis(AnalysisDeclContext &AC,
+ LifetimeSafetyReporter *Reporter) {
+ std::unique_ptr<internal::LifetimeSafetyAnalysis> Analysis =
+ std::make_unique<internal::LifetimeSafetyAnalysis>(AC, Reporter);
+ Analysis->run();
+ return Analysis;
}
} // namespace clang::lifetimes
diff --git a/clang/lib/Analysis/LifetimeSafety/Origins.cpp b/clang/lib/Analysis/LifetimeSafety/Origins.cpp
index ea51a75324e06..453abf48261c2 100644
--- a/clang/lib/Analysis/LifetimeSafety/Origins.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Origins.cpp
@@ -7,6 +7,9 @@
//===----------------------------------------------------------------------===//
#include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/TypeBase.h"
+#include "llvm/ADT/StringMap.h"
namespace clang::lifetimes::internal {
@@ -22,6 +25,10 @@ void OriginManager::dump(OriginID OID, llvm::raw_ostream &OS) const {
OS << ")";
}
+const llvm::StringMap<unsigned> OriginManager::getMissingOrigins() const {
+ return ExprTypeToMissingOriginCount;
+}
+
Origin &OriginManager::addOrigin(OriginID ID, const clang::ValueDecl &D) {
AllOrigins.emplace_back(ID, &D);
return AllOrigins.back();
@@ -37,6 +44,17 @@ OriginID OriginManager::get(const Expr &E) {
auto It = ExprToOriginID.find(&E);
if (It != ExprToOriginID.end())
return It->second;
+
+ // if the expression has no specific origin, increment the missing origin
+ // counter.
+ std::string ExprStr(E.getStmtClassName());
+ ExprStr = ExprStr + "<" + E.getType().getAsString() + ">";
+ auto CountIt = ExprTypeToMissingOriginCount.find(ExprStr);
+ if (CountIt == ExprTypeToMissingOriginCount.end()) {
+ ExprTypeToMissingOriginCount[ExprStr] = 1;
+ } else {
+ CountIt->second++;
+ }
// If the expression itself has no specific origin, and it's a reference
// to a declaration, its origin is that of the declaration it refers to.
// For pointer types, where we don't pre-emptively create an origin for the
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 140b709dbb651..9160939a85735 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -29,7 +29,9 @@
#include "clang/Analysis/Analyses/CFGReachabilityAnalysis.h"
#include "clang/Analysis/Analyses/CalledOnceCheck.h"
#include "clang/Analysis/Analyses/Consumed.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
#include "clang/Analysis/Analyses/ReachableCode.h"
#include "clang/Analysis/Analyses/ThreadSafety.h"
#include "clang/Analysis/Analyses/UninitializedValues.h"
@@ -52,7 +54,9 @@
#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <deque>
#include <iterator>
@@ -3065,7 +3069,11 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
if (EnableLifetimeSafetyAnalysis && S.getLangOpts().CPlusPlus) {
if (AC.getCFG()) {
lifetimes::LifetimeSafetyReporterImpl LifetimeSafetyReporter(S);
- lifetimes::runLifetimeSafetyAnalysis(AC, &LifetimeSafetyReporter);
+ std::unique_ptr<clang::lifetimes::internal::LifetimeSafetyAnalysis>
+ Analysis =
+ lifetimes::runLifetimeSafetyAnalysis(AC, &LifetimeSafetyReporter);
+ if (S.CollectStats)
+ FindMissingOrigins(AC, Analysis->getFactManager());
}
}
// Check for violations of "called once" parameter properties.
@@ -3131,9 +3139,27 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
}
}
+void clang::sema::AnalysisBasedWarnings::FindMissingOrigins(
+ AnalysisDeclContext &AC, lifetimes::internal::FactManager &FactMgr) {
+ if (AC.getCFG()) {
+ for (const auto &[expr, count] :
+ FactMgr.getOriginMgr().getMissingOrigins()) {
+ MissingOriginCount[expr] += count;
+ }
+ }
+}
+
void clang::sema::AnalysisBasedWarnings::PrintStats() const {
+ llvm::errs() << "\n*** LifetimeSafety Missing Origin Stats "
+ "(expression_type : count) :\n";
+ unsigned totalMissingOrigins = 0;
+ for (const auto &[expr, count] : MissingOriginCount) {
+ llvm::errs() << expr << " : " << count << '\n';
+ totalMissingOrigins += count;
+ }
+ llvm::errs() << "Total missing origins: " << totalMissingOrigins << "\n";
+ llvm::errs() << "****************************************\n";
llvm::errs() << "\n*** Analysis Based Warnings Stats:\n";
-
unsigned NumCFGsBuilt = NumFunctionsAnalyzed - NumFunctionsWithBadCFGs;
unsigned AvgCFGBlocksPerFunction =
!NumCFGsBuilt ? 0 : NumCFGBlocks/NumCFGsBuilt;
>From 3ff81d6435d7da0b4c86459fd71344cf2008acca Mon Sep 17 00:00:00 2001
From: Debadri Basak <debadri1010 at gmail.com>
Date: Wed, 5 Nov 2025 14:22:48 +0000
Subject: [PATCH 2/4] Correcting the signature of getMissingOrigins
---
.../include/clang/Analysis/Analyses/LifetimeSafety/Origins.h | 2 +-
clang/lib/Sema/AnalysisBasedWarnings.cpp | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
index 3f8c8a4d7ce9b..26686a63e9204 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
@@ -79,7 +79,7 @@ class OriginManager {
void dump(OriginID OID, llvm::raw_ostream &OS) const;
- const llvm::StringMap<int> getMissingOrigins() const;
+ const llvm::StringMap<unsigned> getMissingOrigins() const;
private:
OriginID getNextOriginID() { return NextOriginID++; }
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 9160939a85735..77d2013ff3a93 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -3151,14 +3151,14 @@ void clang::sema::AnalysisBasedWarnings::FindMissingOrigins(
void clang::sema::AnalysisBasedWarnings::PrintStats() const {
llvm::errs() << "\n*** LifetimeSafety Missing Origin Stats "
- "(expression_type : count) :\n";
+ "(expression_type : count) :\n\n";
unsigned totalMissingOrigins = 0;
for (const auto &[expr, count] : MissingOriginCount) {
llvm::errs() << expr << " : " << count << '\n';
totalMissingOrigins += count;
}
llvm::errs() << "Total missing origins: " << totalMissingOrigins << "\n";
- llvm::errs() << "****************************************\n";
+ llvm::errs() << "\n****************************************\n";
llvm::errs() << "\n*** Analysis Based Warnings Stats:\n";
unsigned NumCFGsBuilt = NumFunctionsAnalyzed - NumFunctionsWithBadCFGs;
unsigned AvgCFGBlocksPerFunction =
>From 499d3b07efd16cce82960ad2c7db5164e9d97b35 Mon Sep 17 00:00:00 2001
From: Debadri Basak <debadri1010 at gmail.com>
Date: Tue, 18 Nov 2025 13:53:55 +0000
Subject: [PATCH 3/4] Adding a LifetimeSafetyStats struct to keep track of
missing origins and change the signature of runLifetimeSafetyAnalysis to not
return the analysis object
---
.../Analyses/LifetimeSafety/LifetimeSafety.h | 15 ++++++++--
.../clang/Sema/AnalysisBasedWarnings.h | 7 ++---
.../LifetimeSafety/LifetimeSafety.cpp | 23 ++++++++++++---
clang/lib/Sema/AnalysisBasedWarnings.cpp | 28 +++----------------
4 files changed, 38 insertions(+), 35 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index eb532bc8be3a7..cbbaa6e1884ee 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -38,6 +38,12 @@ enum class Confidence : uint8_t {
Definite // Reported as a definite error (-Wlifetime-safety-permissive)
};
+/// A structure to hold the statistics related to LifetimeAnalysis.
+/// Curently it holds only the missing origin details.
+struct LifetimeSafetyStats {
+ llvm::StringMap<unsigned> MissingOriginCount;
+};
+
class LifetimeSafetyReporter {
public:
LifetimeSafetyReporter() = default;
@@ -84,10 +90,13 @@ class LifetimeSafetyAnalysis {
};
} // namespace internal
+// utility function to print missing origin stats.
+void PrintStats(const LifetimeSafetyStats &Stats);
+
/// The main entry point for the analysis.
-std::unique_ptr<internal::LifetimeSafetyAnalysis>
-runLifetimeSafetyAnalysis(AnalysisDeclContext &AC,
- LifetimeSafetyReporter *Reporter);
+void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC,
+ LifetimeSafetyReporter *Reporter,
+ LifetimeSafetyStats &Stats, bool CollectStats);
} // namespace clang::lifetimes
diff --git a/clang/include/clang/Sema/AnalysisBasedWarnings.h b/clang/include/clang/Sema/AnalysisBasedWarnings.h
index 604039ef61cb7..eb334fd0e6390 100644
--- a/clang/include/clang/Sema/AnalysisBasedWarnings.h
+++ b/clang/include/clang/Sema/AnalysisBasedWarnings.h
@@ -15,8 +15,10 @@
#include "clang/AST/Decl.h"
#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"
#include "clang/Analysis/AnalysisDeclContext.h"
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/StringMap.h"
#include <memory>
@@ -99,7 +101,7 @@ class AnalysisBasedWarnings {
unsigned MaxUninitAnalysisBlockVisitsPerFunction;
/// Map from expressions missing origin in OriginManager to their counts.
- llvm::StringMap<unsigned> MissingOriginCount;
+ clang::lifetimes::LifetimeSafetyStats LSStats;
/// @}
@@ -122,9 +124,6 @@ class AnalysisBasedWarnings {
Policy &getPolicyOverrides() { return PolicyOverrides; }
void PrintStats() const;
-
- void FindMissingOrigins(AnalysisDeclContext &AC,
- clang::lifetimes::internal::FactManager &FactMgr);
};
} // namespace sema
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp
index d183ce976f946..5935c5fd6df93 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp
@@ -71,12 +71,27 @@ void LifetimeSafetyAnalysis::run() {
}
} // namespace internal
-std::unique_ptr<internal::LifetimeSafetyAnalysis>
-runLifetimeSafetyAnalysis(AnalysisDeclContext &AC,
- LifetimeSafetyReporter *Reporter) {
+void PrintStats(const LifetimeSafetyStats &Stats) {
+ llvm::errs() << "\n*** LifetimeSafety Missing Origin Stats "
+ "(expression_type : count) :\n\n";
+ unsigned totalMissingOrigins = 0;
+ for (const auto &[expr, count] : Stats.MissingOriginCount) {
+ llvm::errs() << expr << " : " << count << '\n';
+ totalMissingOrigins += count;
+ }
+ llvm::errs() << "Total missing origins: " << totalMissingOrigins << "\n";
+ llvm::errs() << "\n****************************************\n";
+}
+
+void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC,
+ LifetimeSafetyReporter *Reporter,
+ LifetimeSafetyStats &Stats, bool CollectStats) {
std::unique_ptr<internal::LifetimeSafetyAnalysis> Analysis =
std::make_unique<internal::LifetimeSafetyAnalysis>(AC, Reporter);
Analysis->run();
- return Analysis;
+ for (const auto &[expr, count] :
+ Analysis->getFactManager().getOriginMgr().getMissingOrigins()) {
+ Stats.MissingOriginCount[expr] += count;
+ }
}
} // namespace clang::lifetimes
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 77d2013ff3a93..e2ad6eb209059 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -3069,11 +3069,8 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
if (EnableLifetimeSafetyAnalysis && S.getLangOpts().CPlusPlus) {
if (AC.getCFG()) {
lifetimes::LifetimeSafetyReporterImpl LifetimeSafetyReporter(S);
- std::unique_ptr<clang::lifetimes::internal::LifetimeSafetyAnalysis>
- Analysis =
- lifetimes::runLifetimeSafetyAnalysis(AC, &LifetimeSafetyReporter);
- if (S.CollectStats)
- FindMissingOrigins(AC, Analysis->getFactManager());
+ lifetimes::runLifetimeSafetyAnalysis(AC, &LifetimeSafetyReporter, LSStats,
+ S.CollectStats);
}
}
// Check for violations of "called once" parameter properties.
@@ -3139,26 +3136,9 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
}
}
-void clang::sema::AnalysisBasedWarnings::FindMissingOrigins(
- AnalysisDeclContext &AC, lifetimes::internal::FactManager &FactMgr) {
- if (AC.getCFG()) {
- for (const auto &[expr, count] :
- FactMgr.getOriginMgr().getMissingOrigins()) {
- MissingOriginCount[expr] += count;
- }
- }
-}
-
void clang::sema::AnalysisBasedWarnings::PrintStats() const {
- llvm::errs() << "\n*** LifetimeSafety Missing Origin Stats "
- "(expression_type : count) :\n\n";
- unsigned totalMissingOrigins = 0;
- for (const auto &[expr, count] : MissingOriginCount) {
- llvm::errs() << expr << " : " << count << '\n';
- totalMissingOrigins += count;
- }
- llvm::errs() << "Total missing origins: " << totalMissingOrigins << "\n";
- llvm::errs() << "\n****************************************\n";
+
+ clang::lifetimes::PrintStats(LSStats);
llvm::errs() << "\n*** Analysis Based Warnings Stats:\n";
unsigned NumCFGsBuilt = NumFunctionsAnalyzed - NumFunctionsWithBadCFGs;
unsigned AvgCFGBlocksPerFunction =
>From 83417784cd7c3870bf82fef500be01db79eaef6f Mon Sep 17 00:00:00 2001
From: Debadri Basak <debadri1010 at gmail.com>
Date: Wed, 26 Nov 2025 05:32:39 +0000
Subject: [PATCH 4/4] Moving the missing origin calculation logic after
lifetime analysis using an AST visitor
---
.../Analyses/LifetimeSafety/LifetimeSafety.h | 20 +++---
.../Analyses/LifetimeSafety/Origins.h | 28 +++++++-
.../clang/Sema/AnalysisBasedWarnings.h | 4 +-
.../LifetimeSafety/LifetimeSafety.cpp | 17 +++--
clang/lib/Analysis/LifetimeSafety/Origins.cpp | 70 +++++++++++++++----
clang/lib/Sema/AnalysisBasedWarnings.cpp | 3 +-
6 files changed, 112 insertions(+), 30 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
index cbbaa6e1884ee..6c091fa6f6d6b 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h
@@ -54,7 +54,19 @@ class LifetimeSafetyReporter {
Confidence Confidence) {}
};
+// utility function to print missing origin stats.
+void printStats(const LifetimeSafetyStats &Stats);
+
+/// The main entry point for the analysis.
+void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC,
+ LifetimeSafetyReporter *Reporter,
+ LifetimeSafetyStats &Stats, bool CollectStats);
+
namespace internal {
+
+void collectLifetimeStats(AnalysisDeclContext &AC, OriginManager &OM,
+ LifetimeSafetyStats &Stats);
+
/// An object to hold the factories for immutable collections, ensuring
/// that all created states share the same underlying memory management.
struct LifetimeFactory {
@@ -90,14 +102,6 @@ class LifetimeSafetyAnalysis {
};
} // namespace internal
-// utility function to print missing origin stats.
-void PrintStats(const LifetimeSafetyStats &Stats);
-
-/// The main entry point for the analysis.
-void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC,
- LifetimeSafetyReporter *Reporter,
- LifetimeSafetyStats &Stats, bool CollectStats);
-
} // namespace clang::lifetimes
#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_H
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
index 5c0c73f9b321c..baf9bb9be8439 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h
@@ -16,11 +16,17 @@
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
+#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/TypeBase.h"
#include "clang/Analysis/Analyses/LifetimeSafety/Utils.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/raw_ostream.h"
+namespace clang::lifetimes {
+
+struct LifetimeSafetyStats;
+} // namespace clang::lifetimes
+
namespace clang::lifetimes::internal {
using OriginID = utils::ID<struct OriginTag>;
@@ -81,7 +87,10 @@ class OriginManager {
void dump(OriginID OID, llvm::raw_ostream &OS) const;
- const llvm::StringMap<unsigned> getMissingOrigins() const;
+ const llvm::DenseMap<const clang::Expr *, OriginID> &
+ getExprToOriginId() const;
+
+ void collectMissingOrigins(Stmt *FunctionBody, LifetimeSafetyStats &LSStats);
private:
OriginID getNextOriginID() { return NextOriginID++; }
@@ -92,8 +101,23 @@ class OriginManager {
llvm::SmallVector<Origin> AllOrigins;
llvm::DenseMap<const clang::ValueDecl *, OriginID> DeclToOriginID;
llvm::DenseMap<const clang::Expr *, OriginID> ExprToOriginID;
- llvm::StringMap<unsigned> ExprTypeToMissingOriginCount;
};
+
+/// An utility class to traverse the function body in the analysis
+/// context and collect the count of expressions with missing origins.
+class MissingOriginCollector
+ : public RecursiveASTVisitor<MissingOriginCollector> {
+public:
+ MissingOriginCollector(const OriginManager &om,
+ llvm::StringMap<unsigned> &MissingOriginCount)
+ : OM(om), MissingOriginCount(MissingOriginCount) {}
+ bool VisitExpr(Expr *E);
+
+private:
+ const OriginManager &OM;
+ llvm::StringMap<unsigned> &MissingOriginCount;
+};
+
} // namespace clang::lifetimes::internal
#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_ORIGINS_H
diff --git a/clang/include/clang/Sema/AnalysisBasedWarnings.h b/clang/include/clang/Sema/AnalysisBasedWarnings.h
index 221ee60483546..d17486a66b297 100644
--- a/clang/include/clang/Sema/AnalysisBasedWarnings.h
+++ b/clang/include/clang/Sema/AnalysisBasedWarnings.h
@@ -105,7 +105,9 @@ class AnalysisBasedWarnings {
/// a single function.
unsigned MaxUninitAnalysisBlockVisitsPerFunction;
- /// Map from expressions missing origin in OriginManager to their counts.
+ /// Statistics collected during lifetime safety analysis.
+ /// These are accumulated across all analyzed functions and printed
+ /// when -print-stats is enabled.
clang::lifetimes::LifetimeSafetyStats LSStats;
/// @}
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp
index b38640b1c0642..cc0acbd42df39 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp
@@ -20,6 +20,7 @@
#include "clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h"
#include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"
#include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Analysis/CFG.h"
#include "llvm/ADT/FoldingSet.h"
@@ -70,9 +71,17 @@ void LifetimeSafetyAnalysis::run() {
runLifetimeChecker(*LoanPropagation, *LiveOrigins, FactMgr, AC, Reporter);
}
+
+void collectLifetimeStats(AnalysisDeclContext &AC, OriginManager &OM,
+ LifetimeSafetyStats &Stats) {
+ if (!AC.getBody())
+ return;
+ Stmt *FunctionBody = AC.getBody();
+ OM.collectMissingOrigins(FunctionBody, Stats);
+}
} // namespace internal
-void PrintStats(const LifetimeSafetyStats &Stats) {
+void printStats(const LifetimeSafetyStats &Stats) {
llvm::errs() << "\n*** LifetimeSafety Missing Origin Stats "
"(expression_type : count) :\n\n";
unsigned totalMissingOrigins = 0;
@@ -90,9 +99,7 @@ void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC,
std::unique_ptr<internal::LifetimeSafetyAnalysis> Analysis =
std::make_unique<internal::LifetimeSafetyAnalysis>(AC, Reporter);
Analysis->run();
- for (const auto &[expr, count] :
- Analysis->getFactManager().getOriginMgr().getMissingOrigins()) {
- Stats.MissingOriginCount[expr] += count;
- }
+ if (CollectStats)
+ collectLifetimeStats(AC, Analysis->getFactManager().getOriginMgr(), Stats);
}
} // namespace clang::lifetimes
diff --git a/clang/lib/Analysis/LifetimeSafety/Origins.cpp b/clang/lib/Analysis/LifetimeSafety/Origins.cpp
index 96750d9f94990..0f0b20305fbf2 100644
--- a/clang/lib/Analysis/LifetimeSafety/Origins.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Origins.cpp
@@ -9,10 +9,31 @@
#include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
#include "clang/AST/Expr.h"
#include "clang/AST/TypeBase.h"
+#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"
#include "llvm/ADT/StringMap.h"
namespace clang::lifetimes::internal {
+static bool isGslPointerType(QualType QT) {
+ if (const auto *RD = QT->getAsCXXRecordDecl()) {
+ // We need to check the template definition for specializations.
+ if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
+ return CTSD->getSpecializedTemplate()
+ ->getTemplatedDecl()
+ ->hasAttr<PointerAttr>();
+ return RD->hasAttr<PointerAttr>();
+ }
+ return false;
+}
+
+static bool isPointerType(QualType QT) {
+ return QT->isPointerOrReferenceType() || isGslPointerType(QT);
+}
+// Check if a type has an origin.
+static bool hasOrigin(const Expr *E) {
+ return E->isGLValue() || isPointerType(E->getType());
+}
+
void OriginManager::dump(OriginID OID, llvm::raw_ostream &OS) const {
OS << OID << " (";
Origin O = getOrigin(OID);
@@ -25,10 +46,6 @@ void OriginManager::dump(OriginID OID, llvm::raw_ostream &OS) const {
OS << ")";
}
-const llvm::StringMap<unsigned> OriginManager::getMissingOrigins() const {
- return ExprTypeToMissingOriginCount;
-}
-
Origin &OriginManager::addOrigin(OriginID ID, const clang::ValueDecl &D) {
AllOrigins.emplace_back(ID, &D);
return AllOrigins.back();
@@ -49,14 +66,14 @@ OriginID OriginManager::get(const Expr &E) {
// if the expression has no specific origin, increment the missing origin
// counter.
- std::string ExprStr(E.getStmtClassName());
- ExprStr = ExprStr + "<" + E.getType().getAsString() + ">";
- auto CountIt = ExprTypeToMissingOriginCount.find(ExprStr);
- if (CountIt == ExprTypeToMissingOriginCount.end()) {
- ExprTypeToMissingOriginCount[ExprStr] = 1;
- } else {
- CountIt->second++;
- }
+ // std::string ExprStr(E.getStmtClassName());
+ // ExprStr = ExprStr + "<" + E.getType().getAsString() + ">";
+ // auto CountIt = ExprTypeToMissingOriginCount.find(ExprStr);
+ // if (CountIt == ExprTypeToMissingOriginCount.end())
+ // ExprTypeToMissingOriginCount[ExprStr] = 1;
+ // else
+ // CountIt->second++;
+
// If the expression itself has no specific origin, and it's a reference
// to a declaration, its origin is that of the declaration it refers to.
// For pointer types, where we don't pre-emptively create an origin for the
@@ -106,4 +123,33 @@ OriginID OriginManager::getOrCreate(const ValueDecl &D) {
return NewID;
}
+const llvm::DenseMap<const clang::Expr *, OriginID> &
+OriginManager::getExprToOriginId() const {
+ return ExprToOriginID;
+}
+
+void OriginManager::collectMissingOrigins(Stmt *FunctionBody,
+ LifetimeSafetyStats &LSStats) {
+ if (!FunctionBody)
+ return;
+
+ MissingOriginCollector Collector(*this, LSStats.MissingOriginCount);
+ Collector.TraverseStmt(const_cast<Stmt *>(FunctionBody));
+}
+
+bool MissingOriginCollector::VisitExpr(Expr *E) {
+ if (!hasOrigin(E))
+ return true;
+ // Check if we have an origin for this expression
+ const auto &ExprToOriginId = OM.getExprToOriginId();
+ auto It = ExprToOriginId.find(E);
+ if (It == ExprToOriginId.end()) {
+ // No origin found - count this as missing
+ std::string ExprStr = std::string(E->getStmtClassName()) + "<" +
+ E->getType().getAsString() + ">";
+ MissingOriginCount[ExprStr]++;
+ }
+ return true;
+}
+
} // namespace clang::lifetimes::internal
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 2ad2684c7f292..ae549f6b4f877 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -54,7 +54,6 @@
#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/Casting.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
@@ -3165,7 +3164,6 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
void clang::sema::AnalysisBasedWarnings::PrintStats() const {
- clang::lifetimes::PrintStats(LSStats);
llvm::errs() << "\n*** Analysis Based Warnings Stats:\n";
unsigned NumCFGsBuilt = NumFunctionsAnalyzed - NumFunctionsWithBadCFGs;
unsigned AvgCFGBlocksPerFunction =
@@ -3194,4 +3192,5 @@ void clang::sema::AnalysisBasedWarnings::PrintStats() const {
<< " average block visits per function.\n"
<< " " << MaxUninitAnalysisBlockVisitsPerFunction
<< " max block visits per function.\n";
+ clang::lifetimes::printStats(LSStats);
}
More information about the cfe-commits
mailing list