diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index 53caaa40650e2..9c91355355233 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -21,8 +21,10 @@ #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_H #include "clang/Analysis/Analyses/LifetimeSafety/Facts.h" +#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeStats.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" namespace clang::lifetimes { @@ -62,9 +64,14 @@ class LifetimeSafetyReporter { /// The main entry point for the analysis. void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC, - LifetimeSafetyReporter *Reporter); + 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 { diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeStats.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeStats.h new file mode 100644 index 0000000000000..7a23cf75d1a3b --- /dev/null +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeStats.h @@ -0,0 +1,36 @@ +//===- LifetimeStats.h - Lifetime Safety Statistics --------------*- C++-* -===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===------------------------------------------------------------------------===// +// +// This file declares the data structures and utility function for collection of +// statistics related to Lifetime Safety analysis. +// +//===------------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LIFETIMESTATS_H +#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LIFETIMESTATS_H + +#include "clang/AST/TypeBase.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringMap.h" + +namespace clang::lifetimes { +/// A structure to hold the statistics related to LifetimeAnalysis. +/// These are accumulated across all analyzed functions and printed +/// when -print-stats is enabled. +struct LifetimeSafetyStats { + /// A map from `StmtClassName` to their missing origin counts. + llvm::StringMap ExprStmtClassToMissingOriginCount; + /// A map from `QualType` to their missing origin counts. + llvm::DenseMap ExprTypeToMissingOriginCount; +}; + +/// Utility function to print missing origin stats. +void printStats(const LifetimeSafetyStats &Stats); +} // namespace clang::lifetimes + +#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LIFETIMESTATS_H diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h index d6246183a5ca7..690faae996f0e 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h @@ -17,7 +17,9 @@ #include "clang/AST/Decl.h" #include "clang/AST/Expr.h" #include "clang/AST/TypeBase.h" +#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeStats.h" #include "clang/Analysis/Analyses/LifetimeSafety/Utils.h" +#include "llvm/Support/raw_ostream.h" namespace clang::lifetimes::internal { @@ -150,6 +152,9 @@ class OriginManager { void dump(OriginID OID, llvm::raw_ostream &OS) const; + /// Collects statistics about expressions that lack associated origins. + void collectMissingOrigins(Stmt &FunctionBody, LifetimeSafetyStats &LSStats); + private: OriginID getNextOriginID() { return NextOriginID++; } diff --git a/clang/include/clang/Sema/AnalysisBasedWarnings.h b/clang/include/clang/Sema/AnalysisBasedWarnings.h index 20a2030f56034..0ed61e56825be 100644 --- a/clang/include/clang/Sema/AnalysisBasedWarnings.h +++ b/clang/include/clang/Sema/AnalysisBasedWarnings.h @@ -14,9 +14,8 @@ #define LLVM_CLANG_SEMA_ANALYSISBASEDWARNINGS_H #include "clang/AST/Decl.h" +#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeStats.h" #include "clang/Sema/ScopeInfo.h" -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/MapVector.h" #include namespace clang { @@ -101,6 +100,11 @@ class AnalysisBasedWarnings { /// a single function. unsigned MaxUninitAnalysisBlockVisitsPerFunction; + /// Statistics collected during lifetime safety analysis. + /// These are accumulated across all analyzed functions and printed + /// when -print-stats is enabled. + clang::lifetimes::LifetimeSafetyStats LSStats; + /// @} public: diff --git a/clang/lib/Analysis/LifetimeSafety/CMakeLists.txt b/clang/lib/Analysis/LifetimeSafety/CMakeLists.txt index 5874e8405baf6..e5876e747610a 100644 --- a/clang/lib/Analysis/LifetimeSafety/CMakeLists.txt +++ b/clang/lib/Analysis/LifetimeSafety/CMakeLists.txt @@ -5,6 +5,7 @@ add_clang_library(clangAnalysisLifetimeSafety LifetimeAnnotations.cpp LifetimeSafety.cpp LiveOrigins.cpp + LifetimeStats.cpp Loans.cpp LoanPropagation.cpp Origins.cpp diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp index c0fa640a8ba66..be0d405bd3086 100644 --- a/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp @@ -18,8 +18,10 @@ #include "clang/Analysis/Analyses/LifetimeSafety/Checker.h" #include "clang/Analysis/Analyses/LifetimeSafety/Facts.h" #include "clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h" +#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeStats.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" @@ -90,11 +92,22 @@ void LifetimeSafetyAnalysis::run() { runLifetimeChecker(*LoanPropagation, *LiveOrigins, *FactMgr, AC, Reporter); } + +void collectLifetimeStats(AnalysisDeclContext &AC, OriginManager &OM, + LifetimeSafetyStats &Stats) { + Stmt *FunctionBody = AC.getBody(); + if (FunctionBody == nullptr) + return; + OM.collectMissingOrigins(*FunctionBody, Stats); +} } // namespace internal void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC, - LifetimeSafetyReporter *Reporter) { + LifetimeSafetyReporter *Reporter, + LifetimeSafetyStats &Stats, bool CollectStats) { internal::LifetimeSafetyAnalysis Analysis(AC, Reporter); Analysis.run(); + if (CollectStats) + collectLifetimeStats(AC, Analysis.getFactManager().getOriginMgr(), Stats); } } // namespace clang::lifetimes diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeStats.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeStats.cpp new file mode 100644 index 0000000000000..d4b1bb6a50e06 --- /dev/null +++ b/clang/lib/Analysis/LifetimeSafety/LifetimeStats.cpp @@ -0,0 +1,36 @@ +//===- LifetimeStats.cpp - Lifetime Safety Statistics -*------------ C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the data structures and utility function for collection of +// staticstics related to Lifetimesafety analysis. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeStats.h" +#include "clang/AST/TypeBase.h" +#include "llvm/Support/raw_ostream.h" + +namespace clang::lifetimes { +void printStats(const LifetimeSafetyStats &Stats) { + llvm::errs() << "\n*** LifetimeSafety Missing Origin per QualType: " + "(QualType : count) :\n\n"; + unsigned TotalMissingOrigins = 0; + for (const auto &[ExprType, MissingOriginCount] : Stats.ExprTypeToMissingOriginCount) { + QualType QT = QualType(ExprType, 0); + llvm::errs() << QT.getAsString() << " : " << MissingOriginCount << '\n'; + TotalMissingOrigins += MissingOriginCount; + } + llvm::errs() << "\n\n*** LifetimeSafety Missing Origin per StmtClassName: " + "(StmtClassName : count) :\n\n"; + for (const auto &[ExprStmtClassName, MissingOriginCount] : Stats.ExprStmtClassToMissingOriginCount) { + llvm::errs() << ExprStmtClassName << " : " << MissingOriginCount << '\n'; + } + llvm::errs() << "\nTotal missing origins: " << TotalMissingOrigins << "\n"; + llvm::errs() << "\n****************************************\n"; +} +} // namespace clang::lifetimes diff --git a/clang/lib/Analysis/LifetimeSafety/Origins.cpp b/clang/lib/Analysis/LifetimeSafety/Origins.cpp index b2f1af3d8d7c3..3fb484c575f1f 100644 --- a/clang/lib/Analysis/LifetimeSafety/Origins.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Origins.cpp @@ -7,6 +7,10 @@ //===----------------------------------------------------------------------===// #include "clang/Analysis/Analyses/LifetimeSafety/Origins.h" +#include "clang/AST/Expr.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeStats.h" +#include "llvm/ADT/StringMap.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" #include "clang/AST/DeclCXX.h" @@ -15,6 +19,34 @@ #include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h" namespace clang::lifetimes::internal { +namespace { +/// A utility class to traverse the function body in the analysis +/// context and collect the count of expressions with missing origins. +class MissingOriginCollector + : public RecursiveASTVisitor { +public: + MissingOriginCollector( + const llvm::DenseMap &ExprToOriginList, + LifetimeSafetyStats &LSStats) + : ExprToOriginList(ExprToOriginList), LSStats(LSStats) {} + bool VisitExpr(Expr *E) { + if (!hasOrigins(E)) + return true; + // Check if we have an origin for this expression. + if (!ExprToOriginList.contains(E)) { + // No origin found: count this as missing origin. + LSStats.ExprTypeToMissingOriginCount[E->getType().getTypePtr()]++; + LSStats.ExprStmtClassToMissingOriginCount[std::string( + E->getStmtClassName())]++; + } + return true; + } + +private: + const llvm::DenseMap &ExprToOriginList; + LifetimeSafetyStats &LSStats; +}; +} // namespace bool hasOrigins(QualType QT) { return QT->isPointerOrReferenceType() || isGslPointerType(QT); @@ -157,4 +189,10 @@ const Origin &OriginManager::getOrigin(OriginID ID) const { return AllOrigins[ID.Value]; } +void OriginManager::collectMissingOrigins(Stmt &FunctionBody, + LifetimeSafetyStats &LSStats) { + MissingOriginCollector Collector(this->ExprToList, LSStats); + Collector.TraverseStmt(const_cast(&FunctionBody)); +} + } // namespace clang::lifetimes::internal diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index 9dcae980def25..7b08648080710 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -3135,7 +3135,8 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings( if (EnableLifetimeSafetyAnalysis && S.getLangOpts().CPlusPlus) { if (AC.getCFG()) { lifetimes::LifetimeSafetyReporterImpl LifetimeSafetyReporter(S); - lifetimes::runLifetimeSafetyAnalysis(AC, &LifetimeSafetyReporter); + lifetimes::runLifetimeSafetyAnalysis(AC, &LifetimeSafetyReporter, LSStats, + S.CollectStats); } } // Check for violations of "called once" parameter properties. @@ -3231,4 +3232,5 @@ void clang::sema::AnalysisBasedWarnings::PrintStats() const { << " average block visits per function.\n" << " " << MaxUninitAnalysisBlockVisitsPerFunction << " max block visits per function.\n"; + clang::lifetimes::printStats(LSStats); } diff --git a/clang/test/Sema/warn-lifetime-safety-missing-origin-stats.cpp b/clang/test/Sema/warn-lifetime-safety-missing-origin-stats.cpp new file mode 100644 index 0000000000000..446bbe0e06b13 --- /dev/null +++ b/clang/test/Sema/warn-lifetime-safety-missing-origin-stats.cpp @@ -0,0 +1,5 @@ +// RUN: %clang_cc1 -print-stats -fexperimental-lifetime-safety -Wexperimental-lifetime-safety %s 2>&1 | FileCheck %s + + +// CHECK: *** LifetimeSafety Missing Origin per QualType: (QualType : count) : +// CHECK: *** LifetimeSafety Missing Origin per StmtClassName: (StmtClassName : count) :