https://github.com/kashika0112 updated https://github.com/llvm/llvm-project/pull/176703
>From e49d9e1fa109dc83add2bae21e01e8a9316a87fd Mon Sep 17 00:00:00 2001 From: Kashika Akhouri <[email protected]> Date: Mon, 19 Jan 2026 08:03:57 +0000 Subject: [PATCH 1/8] Add suggestion and inference for implicit this --- .../Analysis/Analyses/LifetimeSafety/Facts.h | 2 +- .../Analyses/LifetimeSafety/LifetimeSafety.h | 4 + .../Analysis/Analyses/LifetimeSafety/Loans.h | 30 ++++-- .../Analyses/LifetimeSafety/Origins.h | 14 ++- .../clang/Basic/DiagnosticSemaKinds.td | 12 +++ clang/lib/Analysis/LifetimeSafety/Checker.cpp | 93 ++++++++++++------- .../LifetimeSafety/FactsGenerator.cpp | 10 +- .../LifetimeSafety/LifetimeAnnotations.cpp | 2 + clang/lib/Analysis/LifetimeSafety/Origins.cpp | 13 +++ clang/lib/Sema/AnalysisBasedWarnings.cpp | 24 +++++ .../Sema/warn-lifetime-safety-suggestions.cpp | 15 ++- 11 files changed, 169 insertions(+), 50 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h index a66925b7302ca..1bb34e6986857 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h @@ -195,7 +195,7 @@ class TestPointFact : public Fact { class FactManager { public: FactManager(const AnalysisDeclContext &AC, const CFG &Cfg) - : OriginMgr(AC.getASTContext()) { + : OriginMgr(AC.getASTContext(), AC.getDecl()) { BlockToFacts.resize(Cfg.getNumBlockIDs()); } diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index 9c91355355233..2199ae023fb26 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -60,6 +60,10 @@ class LifetimeSafetyReporter { virtual void suggestAnnotation(SuggestionScope Scope, const ParmVarDecl *ParmToAnnotate, const Expr *EscapeExpr) {} + + // Suggests lifetime bound annotations for implicit this + virtual void suggestAnnotation(SuggestionScope Scope, const CXXMethodDecl *MD, + const Expr *EscapeExpr) {} }; /// The main entry point for the analysis. diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h index ee1634a6f5ea2..a366e3e811cbf 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h @@ -15,6 +15,7 @@ #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_LOANS_H #include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/ExprCXX.h" #include "clang/Analysis/Analyses/LifetimeSafety/Utils.h" #include "llvm/Support/raw_ostream.h" @@ -100,10 +101,11 @@ class PathLoan : public Loan { static bool classof(const Loan *L) { return L->getKind() == Kind::Path; } }; -/// A placeholder loan held by a function parameter, representing a borrow from -/// the caller's scope. +/// A placeholder loan held by a function parameter or an implicit 'this' +/// object, representing a borrow from the caller's scope. /// -/// Created at function entry for each pointer or reference parameter with an +/// Created at function entry for each pointer or reference parameter or for +/// the implicit 'this' parameter of instance methods, with an /// origin. Unlike PathLoan, placeholder loans: /// - Have no IssueExpr (created at function entry, not at a borrow site) /// - Have no AccessPath (the borrowed object is not visible to the function) @@ -111,17 +113,27 @@ class PathLoan : public Loan { /// invalidations (e.g., vector::push_back) /// /// When a placeholder loan escapes the function (e.g., via return), it -/// indicates the parameter should be marked [[clang::lifetimebound]], enabling -/// lifetime annotation suggestions. +/// indicates the parameter or method should be marked [[clang::lifetimebound]], +/// enabling lifetime annotation suggestions. class PlaceholderLoan : public Loan { - /// The function parameter that holds this placeholder loan. - const ParmVarDecl *PVD; + /// The function parameter or method (representing 'this') that holds this + /// placeholder loan. + llvm::PointerUnion<const ParmVarDecl *, const CXXMethodDecl *> ParamOrMethod; public: PlaceholderLoan(LoanID ID, const ParmVarDecl *PVD) - : Loan(Kind::Placeholder, ID), PVD(PVD) {} + : Loan(Kind::Placeholder, ID), ParamOrMethod(PVD) {} - const ParmVarDecl *getParmVarDecl() const { return PVD; } + PlaceholderLoan(LoanID ID, const CXXMethodDecl *MD) + : Loan(Kind::Placeholder, ID), ParamOrMethod(MD) {} + + const ParmVarDecl *getParmVarDecl() const { + return ParamOrMethod.dyn_cast<const ParmVarDecl *>(); + } + + const CXXMethodDecl *getMethodDecl() const { + return ParamOrMethod.dyn_cast<const CXXMethodDecl *>(); + } void dump(llvm::raw_ostream &OS) const override; diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h index 690faae996f0e..60b607c77da4e 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h @@ -15,6 +15,7 @@ #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_ORIGINS_H #include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/Expr.h" #include "clang/AST/TypeBase.h" #include "clang/Analysis/Analyses/LifetimeSafety/LifetimeStats.h" @@ -124,7 +125,8 @@ bool doesDeclHaveStorage(const ValueDecl *D); /// variables and expressions. class OriginManager { public: - explicit OriginManager(ASTContext &AST) : AST(AST) {} + explicit OriginManager(ASTContext &AST, const Decl *D) + : AST(AST), CurrentDecl(D) {} /// Gets or creates the OriginList for a given ValueDecl. /// @@ -144,6 +146,15 @@ class OriginManager { /// \returns The OriginList, or nullptr for non-pointer rvalues. OriginList *getOrCreateList(const Expr *E); + /// Gets or creates the OriginList for the implicit 'this' parameter of a + /// given CXXMethodDecl. + /// + /// Creates a list structure mirroring the levels of indirection in the + /// method's 'this' type (e.g., `S*` for a non-static method of class `S`). + /// + /// \returns The OriginList for the implicit object parameter. + OriginList *getOrCreateList(const CXXMethodDecl *MD); + const Origin &getOrigin(OriginID ID) const; llvm::ArrayRef<Origin> getOrigins() const { return AllOrigins; } @@ -172,6 +183,7 @@ class OriginManager { llvm::BumpPtrAllocator ListAllocator; llvm::DenseMap<const clang::ValueDecl *, OriginList *> DeclToList; llvm::DenseMap<const clang::Expr *, OriginList *> ExprToList; + const Decl *CurrentDecl; }; } // namespace clang::lifetimes::internal diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 1ab3f537d36a3..c624a719e2583 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10832,6 +10832,18 @@ def warn_lifetime_safety_cross_tu_suggestion InGroup<LifetimeSafetyCrossTUSuggestions>, DefaultIgnore; +def warn_lifetime_safety_this_intra_tu_suggestion + : Warning<"implict this in intra-TU function should be marked " + "[[clang::lifetimebound]]">, + InGroup<LifetimeSafetyIntraTUSuggestions>, + DefaultIgnore; + +def warn_lifetime_safety_this_cross_tu_suggestion + : Warning<"implicit this in cross-TU function should be marked " + "[[clang::lifetimebound]]">, + InGroup<LifetimeSafetyCrossTUSuggestions>, + DefaultIgnore; + def note_lifetime_safety_suggestion_returned_here : Note<"param returned here">; // For non-floating point, expressions of the form x == x or x != x diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index f7383126fac38..ffdaad5026b3d 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -49,10 +49,13 @@ struct PendingWarning { Confidence ConfidenceLevel; }; +using AnnotationTarget = + llvm::PointerUnion<const ParmVarDecl *, const CXXMethodDecl *>; + class LifetimeChecker { private: llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap; - llvm::DenseMap<const ParmVarDecl *, const Expr *> AnnotationWarningsMap; + llvm::DenseMap<AnnotationTarget, const Expr *> AnnotationWarningsMap; const LoanPropagationAnalysis &LoanPropagation; const LiveOriginsAnalysis &LiveOrigins; const FactManager &FactMgr; @@ -88,10 +91,14 @@ class LifetimeChecker { for (LoanID LID : EscapedLoans) { const Loan *L = FactMgr.getLoanMgr().getLoan(LID); if (const auto *PL = dyn_cast<PlaceholderLoan>(L)) { - const ParmVarDecl *PVD = PL->getParmVarDecl(); - if (PVD->hasAttr<LifetimeBoundAttr>()) - continue; - AnnotationWarningsMap.try_emplace(PVD, OEF->getEscapeExpr()); + if (const auto *PVD = PL->getParmVarDecl()) { + if (PVD->hasAttr<LifetimeBoundAttr>()) + continue; + AnnotationWarningsMap.try_emplace(PVD, OEF->getEscapeExpr()); + } else if (const auto *MD = PL->getMethodDecl()) { + if (!implicitObjectParamIsLifetimeBound(MD)) + AnnotationWarningsMap.try_emplace(MD, OEF->getEscapeExpr()); + } } } } @@ -164,50 +171,70 @@ class LifetimeChecker { /// Returns the declaration of a function that is visible across translation /// units, if such a declaration exists and is different from the definition. - static const FunctionDecl *getCrossTUDecl(const ParmVarDecl &PVD, + static const FunctionDecl *getCrossTUDecl(const FunctionDecl &FD, SourceManager &SM) { - const auto *FD = dyn_cast<FunctionDecl>(PVD.getDeclContext()); - if (!FD) - return nullptr; - if (!FD->isExternallyVisible()) + if (!FD.isExternallyVisible()) return nullptr; - const FileID DefinitionFile = SM.getFileID(FD->getLocation()); - for (const FunctionDecl *Redecl : FD->redecls()) + const FileID DefinitionFile = SM.getFileID(FD.getLocation()); + for (const FunctionDecl *Redecl : FD.redecls()) if (SM.getFileID(Redecl->getLocation()) != DefinitionFile) return Redecl; return nullptr; } + static const FunctionDecl *getCrossTUDecl(const ParmVarDecl &PVD, + SourceManager &SM) { + if (const auto *FD = dyn_cast<FunctionDecl>(PVD.getDeclContext())) + return getCrossTUDecl(*FD, SM); + return nullptr; + } + void suggestAnnotations() { if (!Reporter) return; SourceManager &SM = AST.getSourceManager(); - for (const auto &[PVD, EscapeExpr] : AnnotationWarningsMap) { - if (const FunctionDecl *CrossTUDecl = getCrossTUDecl(*PVD, SM)) - Reporter->suggestAnnotation( - SuggestionScope::CrossTU, - CrossTUDecl->getParamDecl(PVD->getFunctionScopeIndex()), - EscapeExpr); - else - Reporter->suggestAnnotation(SuggestionScope::IntraTU, PVD, EscapeExpr); + for (const auto &[Target, EscapeExpr] : AnnotationWarningsMap) { + if (const auto *PVD = Target.dyn_cast<const ParmVarDecl *>()) { + if (const FunctionDecl *CrossTUDecl = getCrossTUDecl(*PVD, SM)) + Reporter->suggestAnnotation( + SuggestionScope::CrossTU, + CrossTUDecl->getParamDecl(PVD->getFunctionScopeIndex()), + EscapeExpr); + else + Reporter->suggestAnnotation(SuggestionScope::IntraTU, PVD, + EscapeExpr); + } else if (const auto *MD = Target.dyn_cast<const CXXMethodDecl *>()) { + if (const FunctionDecl *CrossTUDecl = getCrossTUDecl(*MD, SM)) + Reporter->suggestAnnotation(SuggestionScope::CrossTU, + cast<const CXXMethodDecl>(CrossTUDecl), + EscapeExpr); + else + Reporter->suggestAnnotation(SuggestionScope::IntraTU, MD, EscapeExpr); + } } } void inferAnnotations() { - for (const auto &[ConstPVD, EscapeExpr] : AnnotationWarningsMap) { - ParmVarDecl *PVD = const_cast<ParmVarDecl *>(ConstPVD); - const auto *FD = dyn_cast<FunctionDecl>(PVD->getDeclContext()); - if (!FD) - continue; - // Propagates inferred attributes via the most recent declaration to - // ensure visibility for callers in post-order analysis. - FD = getDeclWithMergedLifetimeBoundAttrs(FD); - ParmVarDecl *InferredPVD = const_cast<ParmVarDecl *>( - FD->getParamDecl(PVD->getFunctionScopeIndex())); - if (!InferredPVD->hasAttr<LifetimeBoundAttr>()) - InferredPVD->addAttr( - LifetimeBoundAttr::CreateImplicit(AST, PVD->getLocation())); + for (const auto &[Target, EscapeExpr] : AnnotationWarningsMap) { + if (const auto *MD = Target.dyn_cast<const CXXMethodDecl *>()) { + CXXMethodDecl *MutableMD = const_cast<CXXMethodDecl *>(MD); + if (!MutableMD->hasAttr<LifetimeBoundAttr>()) + MutableMD->addAttr( + LifetimeBoundAttr::CreateImplicit(AST, MutableMD->getLocation())); + } else if (const auto *PVD = Target.dyn_cast<const ParmVarDecl *>()) { + const auto *FD = dyn_cast<FunctionDecl>(PVD->getDeclContext()); + if (!FD) + continue; + // Propagates inferred attributes via the most recent declaration to + // ensure visibility for callers in post-order analysis. + FD = getDeclWithMergedLifetimeBoundAttrs(FD); + ParmVarDecl *InferredPVD = const_cast<ParmVarDecl *>( + FD->getParamDecl(PVD->getFunctionScopeIndex())); + if (!InferredPVD->hasAttr<LifetimeBoundAttr>()) + InferredPVD->addAttr( + LifetimeBoundAttr::CreateImplicit(AST, PVD->getLocation())); + } } } }; diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp index 54e19b58bd7d5..7093f0a0e005f 100644 --- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp +++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp @@ -610,10 +610,18 @@ void FactsGenerator::markUseAsWrite(const DeclRefExpr *DRE) { // parameter at the function's entry. llvm::SmallVector<Fact *> FactsGenerator::issuePlaceholderLoans() { const auto *FD = dyn_cast<FunctionDecl>(AC.getDecl()); - if (!FD) + if (!FD || FD->isImplicit()) return {}; llvm::SmallVector<Fact *> PlaceholderLoanFacts; + const Decl *D = AC.getDecl(); + if (const auto *MD = dyn_cast<CXXMethodDecl>(D); MD && MD->isInstance()) { + OriginList *List = FactMgr.getOriginMgr().getOrCreateList(MD); + const PlaceholderLoan *L = + FactMgr.getLoanMgr().createLoan<PlaceholderLoan>(MD); + PlaceholderLoanFacts.push_back( + FactMgr.createFact<IssueFact>(L->getID(), List->getOuterOriginID())); + } for (const ParmVarDecl *PVD : FD->parameters()) { OriginList *List = getOriginsList(*PVD); if (!List) diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp index 2772fe20de19b..284163ebc4bc3 100644 --- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp +++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp @@ -54,6 +54,8 @@ bool isAssignmentOperatorLifetimeBound(const CXXMethodDecl *CMD) { bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) { FD = getDeclWithMergedLifetimeBoundAttrs(FD); + if (FD->hasAttr<LifetimeBoundAttr>()) + return true; const TypeSourceInfo *TSI = FD->getTypeSourceInfo(); if (!TSI) return false; diff --git a/clang/lib/Analysis/LifetimeSafety/Origins.cpp b/clang/lib/Analysis/LifetimeSafety/Origins.cpp index ca933f612eb08..8349d18267b62 100644 --- a/clang/lib/Analysis/LifetimeSafety/Origins.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Origins.cpp @@ -137,6 +137,12 @@ OriginList *OriginManager::getOrCreateList(const Expr *E) { return It->second; QualType Type = E->getType(); + // Special handling for 'this' expressions to share origins with the method's + // implicit object parameter. + if (isa<CXXThisExpr>(E)) { + if (const auto *MD = dyn_cast<CXXMethodDecl>(CurrentDecl)) + return getOrCreateList(MD); + } // Special handling for DeclRefExpr to share origins with the underlying decl. if (auto *DRE = dyn_cast<DeclRefExpr>(E)) { @@ -169,6 +175,13 @@ OriginList *OriginManager::getOrCreateList(const Expr *E) { return ExprToList[E] = buildListForType(Type, E); } +OriginList *OriginManager::getOrCreateList(const CXXMethodDecl *MD) { + auto It = DeclToList.find(MD); + if (It != DeclToList.end()) + return It->second; + return DeclToList[MD] = buildListForType(MD->getThisType(), MD); +} + void OriginManager::dump(OriginID OID, llvm::raw_ostream &OS) const { OS << OID << " ("; Origin O = getOrigin(OID); diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index d297d9e80cf2f..e79a38dd786f4 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -2919,6 +2919,30 @@ class LifetimeSafetyReporterImpl : public LifetimeSafetyReporter { << EscapeExpr->getSourceRange(); } + void suggestAnnotation(SuggestionScope Scope, const CXXMethodDecl *MD, + const Expr *EscapeExpr) override { + unsigned DiagID = (Scope == SuggestionScope::CrossTU) + ? diag::warn_lifetime_safety_this_cross_tu_suggestion + : diag::warn_lifetime_safety_this_intra_tu_suggestion; + + SourceLocation InsertionPoint; + if (auto *TSI = MD->getTypeSourceInfo()) + InsertionPoint = + Lexer::getLocForEndOfToken(TSI->getTypeLoc().getEndLoc(), 0, + S.getSourceManager(), S.getLangOpts()); + else + InsertionPoint = MD->getLocation(); + + S.Diag(InsertionPoint, DiagID) + << MD->getNameInfo().getSourceRange() + << FixItHint::CreateInsertion(InsertionPoint, + " [[clang::lifetimebound]]"); + + S.Diag(EscapeExpr->getBeginLoc(), + diag::note_lifetime_safety_suggestion_returned_here) + << EscapeExpr->getSourceRange(); + } + private: Sema &S; }; diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp index 6e3a6f1fd9117..2550346903175 100644 --- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp +++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp @@ -161,16 +161,21 @@ static View return_view_static(View a) { // expected-warning {{parameter in int return a; // expected-note {{param returned here}} } -//===----------------------------------------------------------------------===// -// FIXME Test Cases -//===----------------------------------------------------------------------===// +struct ReturnThis { + const ReturnThis& get() { // expected-warning {{implict this in intra-TU function should be marked [[clang::lifetimebound]]}}. + return *this; // expected-note {{param returned here}} + } +}; struct ReturnsSelf { - const ReturnsSelf& get() const { - return *this; + const ReturnsSelf& get() const { // expected-warning {{implict this in intra-TU function should be marked [[clang::lifetimebound]]}}. + return *this; // expected-note {{param returned here}} } }; +//===----------------------------------------------------------------------===// +// FIXME Test Cases +//===----------------------------------------------------------------------===// struct ViewProvider { MyObj data; View getView() const { >From 90166af1bfebadf187a72bb9a748307a992dd45b Mon Sep 17 00:00:00 2001 From: Kashika Akhouri <[email protected]> Date: Thu, 22 Jan 2026 06:38:41 +0000 Subject: [PATCH 2/8] Add implicit this lifetime attribute to TSI instead of decl --- .../Analyses/LifetimeSafety/LifetimeSafety.h | 4 ++ .../clang/Basic/DiagnosticSemaKinds.td | 2 +- clang/lib/Analysis/LifetimeSafety/Checker.cpp | 10 ++-- .../LifetimeSafety/FactsGenerator.cpp | 2 +- clang/lib/Sema/AnalysisBasedWarnings.cpp | 32 +++++++++--- .../Sema/warn-lifetime-safety-suggestions.cpp | 50 ++++++++++++------- 6 files changed, 65 insertions(+), 35 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index 2199ae023fb26..6af29e01ce74d 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -64,6 +64,10 @@ class LifetimeSafetyReporter { // Suggests lifetime bound annotations for implicit this virtual void suggestAnnotation(SuggestionScope Scope, const CXXMethodDecl *MD, const Expr *EscapeExpr) {} + + // Adds inferred lifetime bound attribute for implicit this to its + // TypeSourceInfo + virtual void addLifetimeBoundToImplicitThis(const CXXMethodDecl *MD) {} }; /// The main entry point for the analysis. diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 2d401cf8a37d7..cecfd426a4d75 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10833,7 +10833,7 @@ def warn_lifetime_safety_cross_tu_suggestion DefaultIgnore; def warn_lifetime_safety_this_intra_tu_suggestion - : Warning<"implict this in intra-TU function should be marked " + : Warning<"implicit this in intra-TU function should be marked " "[[clang::lifetimebound]]">, InGroup<LifetimeSafetyIntraTUSuggestions>, DefaultIgnore; diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index ffdaad5026b3d..72d9296f9aac8 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -194,7 +194,7 @@ class LifetimeChecker { if (!Reporter) return; SourceManager &SM = AST.getSourceManager(); - for (const auto &[Target, EscapeExpr] : AnnotationWarningsMap) { + for (auto [Target, EscapeExpr] : AnnotationWarningsMap) { if (const auto *PVD = Target.dyn_cast<const ParmVarDecl *>()) { if (const FunctionDecl *CrossTUDecl = getCrossTUDecl(*PVD, SM)) Reporter->suggestAnnotation( @@ -216,12 +216,10 @@ class LifetimeChecker { } void inferAnnotations() { - for (const auto &[Target, EscapeExpr] : AnnotationWarningsMap) { + for (auto [Target, EscapeExpr] : AnnotationWarningsMap) { if (const auto *MD = Target.dyn_cast<const CXXMethodDecl *>()) { - CXXMethodDecl *MutableMD = const_cast<CXXMethodDecl *>(MD); - if (!MutableMD->hasAttr<LifetimeBoundAttr>()) - MutableMD->addAttr( - LifetimeBoundAttr::CreateImplicit(AST, MutableMD->getLocation())); + if (!implicitObjectParamIsLifetimeBound(MD)) + Reporter->addLifetimeBoundToImplicitThis(cast<CXXMethodDecl>(MD)); } else if (const auto *PVD = Target.dyn_cast<const ParmVarDecl *>()) { const auto *FD = dyn_cast<FunctionDecl>(PVD->getDeclContext()); if (!FD) diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp index 99cef7d5db0a6..233e3e6553618 100644 --- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp +++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp @@ -628,7 +628,7 @@ void FactsGenerator::markUseAsWrite(const DeclRefExpr *DRE) { // parameter at the function's entry. llvm::SmallVector<Fact *> FactsGenerator::issuePlaceholderLoans() { const auto *FD = dyn_cast<FunctionDecl>(AC.getDecl()); - if (!FD || FD->isImplicit()) + if (!FD) return {}; llvm::SmallVector<Fact *> PlaceholderLoanFacts; diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index acdbd298e57a6..38b0c0360db7b 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "clang/Sema/AnalysisBasedWarnings.h" +#include "TypeLocBuilder.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" @@ -30,6 +31,7 @@ #include "clang/Analysis/Analyses/CFGReachabilityAnalysis.h" #include "clang/Analysis/Analyses/CalledOnceCheck.h" #include "clang/Analysis/Analyses/Consumed.h" +#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h" #include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h" #include "clang/Analysis/Analyses/ReachableCode.h" #include "clang/Analysis/Analyses/ThreadSafety.h" @@ -2934,23 +2936,37 @@ class LifetimeSafetyReporterImpl : public LifetimeSafetyReporter { : diag::warn_lifetime_safety_this_intra_tu_suggestion; SourceLocation InsertionPoint; - if (auto *TSI = MD->getTypeSourceInfo()) - InsertionPoint = - Lexer::getLocForEndOfToken(TSI->getTypeLoc().getEndLoc(), 0, - S.getSourceManager(), S.getLangOpts()); - else - InsertionPoint = MD->getLocation(); - + InsertionPoint = Lexer::getLocForEndOfToken( + MD->getTypeSourceInfo()->getTypeLoc().getEndLoc(), 0, + S.getSourceManager(), S.getLangOpts()); S.Diag(InsertionPoint, DiagID) << MD->getNameInfo().getSourceRange() << FixItHint::CreateInsertion(InsertionPoint, " [[clang::lifetimebound]]"); - S.Diag(EscapeExpr->getBeginLoc(), diag::note_lifetime_safety_suggestion_returned_here) << EscapeExpr->getSourceRange(); } + void addLifetimeBoundToImplicitThis(const CXXMethodDecl *MD) override { + CXXMethodDecl *MutableMD = const_cast<CXXMethodDecl *>(MD); + ASTContext &Ctx = S.getASTContext(); + if (lifetimes::implicitObjectParamIsLifetimeBound(MutableMD)) + return; + auto *Attr = + LifetimeBoundAttr::CreateImplicit(Ctx, MutableMD->getLocation()); + QualType MethodType = MutableMD->getType(); + QualType AttributedType = + Ctx.getAttributedType(Attr, MethodType, MethodType); + TypeLocBuilder TLB; + if (TypeSourceInfo *TSI = MutableMD->getTypeSourceInfo()) + TLB.pushFullCopy(TSI->getTypeLoc()); + AttributedTypeLoc TyLoc = TLB.push<AttributedTypeLoc>(AttributedType); + TyLoc.setAttr(Attr); + MutableMD->setType(AttributedType); + MutableMD->setTypeSourceInfo(TLB.getTypeSourceInfo(Ctx, AttributedType)); + } + private: Sema &S; }; diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp index ca62668409bf9..de49878911213 100644 --- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp +++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp @@ -12,6 +12,8 @@ struct View; struct [[gsl::Owner]] MyObj { int id; + MyObj(int i) : id(i) {} + MyObj() {} ~MyObj() {} // Non-trivial destructor MyObj operator+(MyObj); @@ -161,38 +163,48 @@ static View return_view_static(View a) { // expected-warning {{parameter in int return a; // expected-note {{param returned here}} } -struct ReturnThis { - const ReturnThis& get() { // expected-warning {{implict this in intra-TU function should be marked [[clang::lifetimebound]]}}. - return *this; // expected-note {{param returned here}} - } -}; - struct ReturnsSelf { - const ReturnsSelf& get() const { // expected-warning {{implict this in intra-TU function should be marked [[clang::lifetimebound]]}}. + ReturnsSelf() {} + ~ReturnsSelf() {} + const ReturnsSelf& get() const { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}}. return *this; // expected-note {{param returned here}} } }; -//===----------------------------------------------------------------------===// -// FIXME Test Cases -//===----------------------------------------------------------------------===// +struct ReturnThisAnnotated { + const ReturnThisAnnotated& get() [[clang::lifetimebound]] { return *this; } +}; + struct ViewProvider { + ViewProvider() {} + ViewProvider(int d) : data(d) {} + ~ViewProvider() {} MyObj data; - View getView() const { - return data; + View getView() const { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}}. + return data; // expected-note {{param returned here}} } }; -// FIXME: Fails to generate lifetime suggestions for the implicit 'this' parameter, as this feature is not yet implemented. -void test_get_on_temporary() { - const ReturnsSelf& s_ref = ReturnsSelf().get(); - (void)s_ref; +View return_view_field(const ViewProvider& v) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. + return v.data; // expected-note {{param returned here}} +} + +void test_get_on_temporary_pointer() { + const ReturnsSelf* s_ref = &ReturnsSelf().get(); // expected-warning {{object whose reference is captured does not live long enough}}. + // expected-note@-1 {{destroyed here}} + (void)s_ref; // expected-note {{later used here}} +} + +void test_get_on_temporary_ref() { + const ReturnsSelf& s_ref = ReturnsSelf().get(); // expected-warning {{object whose reference is captured does not live long enough}}. + // expected-note@-1 {{destroyed here}} + (void)s_ref; // expected-note {{later used here}} } -// FIXME: Fails to generate lifetime suggestions for the implicit 'this' parameter, as this feature is not yet implemented. void test_getView_on_temporary() { - View sv = ViewProvider{1}.getView(); - (void)sv; + View sv = ViewProvider{1}.getView(); // expected-warning {{object whose reference is captured does not live long enough}}. + // expected-note@-1 {{destroyed here}} + (void)sv; // expected-note {{later used here}} } //===----------------------------------------------------------------------===// >From 841afbabb1e27ca1c52dbcadd701b8184369bf5b Mon Sep 17 00:00:00 2001 From: Kashika Akhouri <[email protected]> Date: Thu, 22 Jan 2026 06:44:01 +0000 Subject: [PATCH 3/8] Remove obsolete check --- clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp index f76b655edda17..93c7a86d0a57c 100644 --- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp +++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp @@ -75,8 +75,6 @@ bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) { // Attribute merging doesn't work well with attributes on function types (like // 'this' param). We need to check all redeclarations. for (const FunctionDecl *Redecl : FD->redecls()) { - if (Redecl->hasAttr<LifetimeBoundAttr>()) - return true; const TypeSourceInfo *TSI = Redecl->getTypeSourceInfo(); if (TSI && getLifetimeBoundAttrFromFunctionType(*TSI)) return true; >From 2a21d049bb411fba846f85bf5802a8c1d5399710 Mon Sep 17 00:00:00 2001 From: Kashika Akhouri <[email protected]> Date: Thu, 22 Jan 2026 12:52:45 +0000 Subject: [PATCH 4/8] Add cross-TU test --- clang/lib/Analysis/LifetimeSafety/Checker.cpp | 2 +- clang/lib/Sema/AnalysisBasedWarnings.cpp | 2 +- clang/test/Sema/warn-lifetime-safety-suggestions.cpp | 8 ++++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index 72d9296f9aac8..471dfb14ec0a7 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -207,7 +207,7 @@ class LifetimeChecker { } else if (const auto *MD = Target.dyn_cast<const CXXMethodDecl *>()) { if (const FunctionDecl *CrossTUDecl = getCrossTUDecl(*MD, SM)) Reporter->suggestAnnotation(SuggestionScope::CrossTU, - cast<const CXXMethodDecl>(CrossTUDecl), + cast<CXXMethodDecl>(CrossTUDecl), EscapeExpr); else Reporter->suggestAnnotation(SuggestionScope::IntraTU, MD, EscapeExpr); diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index 38b0c0360db7b..2bd5fe1605db1 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -2950,9 +2950,9 @@ class LifetimeSafetyReporterImpl : public LifetimeSafetyReporter { void addLifetimeBoundToImplicitThis(const CXXMethodDecl *MD) override { CXXMethodDecl *MutableMD = const_cast<CXXMethodDecl *>(MD); - ASTContext &Ctx = S.getASTContext(); if (lifetimes::implicitObjectParamIsLifetimeBound(MutableMD)) return; + ASTContext &Ctx = S.getASTContext(); auto *Attr = LifetimeBoundAttr::CreateImplicit(Ctx, MutableMD->getLocation()); QualType MethodType = MutableMD->getType(); diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp index de49878911213..131fc62ccf29b 100644 --- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp +++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp @@ -48,6 +48,10 @@ inline View redeclared_in_header(View a) { // expected-warning {{parameter in i return a; // expected-note {{param returned here}} } +struct ReturnThis { + const ReturnThis& get() const; // expected-warning {{implicit this in cross-TU function should be marked [[clang::lifetimebound]]}}. +}; + #endif // TEST_HEADER_H //--- test_source.cpp @@ -163,6 +167,10 @@ static View return_view_static(View a) { // expected-warning {{parameter in int return a; // expected-note {{param returned here}} } +const ReturnThis& ReturnThis::get() const { + return *this; // expected-note {{param returned here}} +} + struct ReturnsSelf { ReturnsSelf() {} ~ReturnsSelf() {} >From 96438953394b1ce6a5cca90bdcb308d5e51e6879 Mon Sep 17 00:00:00 2001 From: Kashika Akhouri <[email protected]> Date: Fri, 23 Jan 2026 06:57:36 +0000 Subject: [PATCH 5/8] Resolving comments and adding more tests --- .../Analyses/LifetimeSafety/LifetimeSafety.h | 17 +++---- .../clang/Basic/DiagnosticSemaKinds.td | 8 ++-- clang/lib/Analysis/LifetimeSafety/Checker.cpp | 47 ++++++++++++------- .../LifetimeSafety/FactsGenerator.cpp | 3 +- clang/lib/Sema/AnalysisBasedWarnings.cpp | 19 ++++---- .../Sema/warn-lifetime-safety-suggestions.cpp | 40 +++++++++++++++- 6 files changed, 93 insertions(+), 41 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index 6af29e01ce74d..472d32249dfd6 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -56,17 +56,18 @@ class LifetimeSafetyReporter { SourceLocation ExpiryLoc, Confidence Confidence) {} - // Suggests lifetime bound annotations for function paramters - virtual void suggestAnnotation(SuggestionScope Scope, - const ParmVarDecl *ParmToAnnotate, - const Expr *EscapeExpr) {} + // Suggests lifetime bound annotations for function paramters. + virtual void SuggestLifetimeboundToParmVar(SuggestionScope Scope, + const ParmVarDecl *ParmToAnnotate, + const Expr *EscapeExpr) {} - // Suggests lifetime bound annotations for implicit this - virtual void suggestAnnotation(SuggestionScope Scope, const CXXMethodDecl *MD, - const Expr *EscapeExpr) {} + // Suggests lifetime bound annotations for implicit this. + virtual void SuggestLifetimeboundToImplicitThis(SuggestionScope Scope, + const CXXMethodDecl *MD, + const Expr *EscapeExpr) {} // Adds inferred lifetime bound attribute for implicit this to its - // TypeSourceInfo + // TypeSourceInfo. virtual void addLifetimeBoundToImplicitThis(const CXXMethodDecl *MD) {} }; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index cecfd426a4d75..eb05d8537ecfb 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10820,25 +10820,25 @@ def note_lifetime_safety_used_here : Note<"later used here">; def note_lifetime_safety_destroyed_here : Note<"destroyed here">; def note_lifetime_safety_returned_here : Note<"returned here">; -def warn_lifetime_safety_intra_tu_suggestion +def warn_lifetime_safety_intra_tu_param_suggestion : Warning<"parameter in intra-TU function should be marked " "[[clang::lifetimebound]]">, InGroup<LifetimeSafetyIntraTUSuggestions>, DefaultIgnore; -def warn_lifetime_safety_cross_tu_suggestion +def warn_lifetime_safety_cross_tu_param_suggestion : Warning<"parameter in cross-TU function should be marked " "[[clang::lifetimebound]]">, InGroup<LifetimeSafetyCrossTUSuggestions>, DefaultIgnore; -def warn_lifetime_safety_this_intra_tu_suggestion +def warn_lifetime_safety_intra_tu_this_suggestion : Warning<"implicit this in intra-TU function should be marked " "[[clang::lifetimebound]]">, InGroup<LifetimeSafetyIntraTUSuggestions>, DefaultIgnore; -def warn_lifetime_safety_this_cross_tu_suggestion +def warn_lifetime_safety_cross_tu_this_suggestion : Warning<"implicit this in cross-TU function should be marked " "[[clang::lifetimebound]]">, InGroup<LifetimeSafetyCrossTUSuggestions>, diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index 471dfb14ec0a7..a0a24ce657f72 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -190,28 +190,41 @@ class LifetimeChecker { return nullptr; } + static void SuggestWithScopeForParmVar(LifetimeSafetyReporter *Reporter, + const ParmVarDecl *PVD, + SourceManager &SM, + const Expr *EscapeExpr) { + if (const FunctionDecl *CrossTUDecl = getCrossTUDecl(*PVD, SM)) + Reporter->SuggestLifetimeboundToParmVar( + SuggestionScope::CrossTU, + CrossTUDecl->getParamDecl(PVD->getFunctionScopeIndex()), EscapeExpr); + else + Reporter->SuggestLifetimeboundToParmVar(SuggestionScope::IntraTU, PVD, + EscapeExpr); + } + + static void SuggestWithScopeForImplicitThis(LifetimeSafetyReporter *Reporter, + const CXXMethodDecl *MD, + SourceManager &SM, + const Expr *EscapeExpr) { + if (const FunctionDecl *CrossTUDecl = getCrossTUDecl(*MD, SM)) + Reporter->SuggestLifetimeboundToImplicitThis( + SuggestionScope::CrossTU, cast<CXXMethodDecl>(CrossTUDecl), + EscapeExpr); + else + Reporter->SuggestLifetimeboundToImplicitThis(SuggestionScope::IntraTU, MD, + EscapeExpr); + } + void suggestAnnotations() { if (!Reporter) return; SourceManager &SM = AST.getSourceManager(); for (auto [Target, EscapeExpr] : AnnotationWarningsMap) { - if (const auto *PVD = Target.dyn_cast<const ParmVarDecl *>()) { - if (const FunctionDecl *CrossTUDecl = getCrossTUDecl(*PVD, SM)) - Reporter->suggestAnnotation( - SuggestionScope::CrossTU, - CrossTUDecl->getParamDecl(PVD->getFunctionScopeIndex()), - EscapeExpr); - else - Reporter->suggestAnnotation(SuggestionScope::IntraTU, PVD, - EscapeExpr); - } else if (const auto *MD = Target.dyn_cast<const CXXMethodDecl *>()) { - if (const FunctionDecl *CrossTUDecl = getCrossTUDecl(*MD, SM)) - Reporter->suggestAnnotation(SuggestionScope::CrossTU, - cast<CXXMethodDecl>(CrossTUDecl), - EscapeExpr); - else - Reporter->suggestAnnotation(SuggestionScope::IntraTU, MD, EscapeExpr); - } + if (const auto *PVD = Target.dyn_cast<const ParmVarDecl *>()) + SuggestWithScopeForParmVar(Reporter, PVD, SM, EscapeExpr); + else if (const auto *MD = Target.dyn_cast<const CXXMethodDecl *>()) + SuggestWithScopeForImplicitThis(Reporter, MD, SM, EscapeExpr); } } diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp index 233e3e6553618..8a27a5df85376 100644 --- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp +++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp @@ -632,8 +632,7 @@ llvm::SmallVector<Fact *> FactsGenerator::issuePlaceholderLoans() { return {}; llvm::SmallVector<Fact *> PlaceholderLoanFacts; - const Decl *D = AC.getDecl(); - if (const auto *MD = dyn_cast<CXXMethodDecl>(D); MD && MD->isInstance()) { + if (const auto *MD = dyn_cast<CXXMethodDecl>(FD); MD && MD->isInstance()) { OriginList *List = FactMgr.getOriginMgr().getOrCreateList(MD); const PlaceholderLoan *L = FactMgr.getLoanMgr().createLoan<PlaceholderLoan>(MD); diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index 2bd5fe1605db1..fb89456d9855e 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -2903,16 +2903,16 @@ class LifetimeSafetyReporterImpl : public LifetimeSafetyReporter { << EscapeExpr->getEndLoc(); } - void suggestAnnotation(SuggestionScope Scope, - const ParmVarDecl *ParmToAnnotate, - const Expr *EscapeExpr) override { + void SuggestLifetimeboundToParmVar(SuggestionScope Scope, + const ParmVarDecl *ParmToAnnotate, + const Expr *EscapeExpr) override { unsigned DiagID; switch (Scope) { case SuggestionScope::CrossTU: - DiagID = diag::warn_lifetime_safety_cross_tu_suggestion; + DiagID = diag::warn_lifetime_safety_cross_tu_param_suggestion; break; case SuggestionScope::IntraTU: - DiagID = diag::warn_lifetime_safety_intra_tu_suggestion; + DiagID = diag::warn_lifetime_safety_intra_tu_param_suggestion; break; } @@ -2929,11 +2929,12 @@ class LifetimeSafetyReporterImpl : public LifetimeSafetyReporter { << EscapeExpr->getSourceRange(); } - void suggestAnnotation(SuggestionScope Scope, const CXXMethodDecl *MD, - const Expr *EscapeExpr) override { + void SuggestLifetimeboundToImplicitThis(SuggestionScope Scope, + const CXXMethodDecl *MD, + const Expr *EscapeExpr) override { unsigned DiagID = (Scope == SuggestionScope::CrossTU) - ? diag::warn_lifetime_safety_this_cross_tu_suggestion - : diag::warn_lifetime_safety_this_intra_tu_suggestion; + ? diag::warn_lifetime_safety_cross_tu_this_suggestion + : diag::warn_lifetime_safety_intra_tu_this_suggestion; SourceLocation InsertionPoint; InsertionPoint = Lexer::getLocForEndOfToken( diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp index 131fc62ccf29b..19726c5175eb5 100644 --- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp +++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp @@ -52,6 +52,11 @@ struct ReturnThis { const ReturnThis& get() const; // expected-warning {{implicit this in cross-TU function should be marked [[clang::lifetimebound]]}}. }; +struct ReturnThisPointer { + const ReturnThisPointer* get() const; // expected-warning {{implicit this in cross-TU function should be marked [[clang::lifetimebound]]}}. +}; + + #endif // TEST_HEADER_H //--- test_source.cpp @@ -171,6 +176,10 @@ const ReturnThis& ReturnThis::get() const { return *this; // expected-note {{param returned here}} } +const ReturnThisPointer* ReturnThisPointer::get() const { + return this; // expected-note {{param returned here}} +} + struct ReturnsSelf { ReturnsSelf() {} ~ReturnsSelf() {} @@ -184,7 +193,6 @@ struct ReturnThisAnnotated { }; struct ViewProvider { - ViewProvider() {} ViewProvider(int d) : data(d) {} ~ViewProvider() {} MyObj data; @@ -215,6 +223,36 @@ void test_getView_on_temporary() { (void)sv; // expected-note {{later used here}} } +void test_get_on_temporary_copy() { + ReturnsSelf copy = ReturnsSelf().get(); + + (void)copy; +} + +struct MemberReturn { + MyObj data; + + MyObj& getRef() { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}}. + return data; // expected-note {{param returned here}} + } + + MyObj& getRefExplicit() { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}}. + return this->data; // expected-note {{param returned here}} + } + + MyObj& getRefDereference() { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}}. + return (*this).data; // expected-note {{param returned here}} + } + + const MyObj* getPtr() { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}}. + return &data; // expected-note {{param returned here}} + } + + const MyObj* getPtrExplicit() { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}}. + return &(this->data); // expected-note {{param returned here}} + } +}; + //===----------------------------------------------------------------------===// // Annotation Inference Test Cases //===----------------------------------------------------------------------===// >From c9eb252f6d2c0bd0322ece68feae604aebfc25c3 Mon Sep 17 00:00:00 2001 From: Kashika Akhouri <[email protected]> Date: Fri, 23 Jan 2026 07:03:17 +0000 Subject: [PATCH 6/8] Nit changes --- .../Analyses/LifetimeSafety/LifetimeSafety.h | 4 ++-- clang/lib/Analysis/LifetimeSafety/Checker.cpp | 16 ++++++++-------- clang/lib/Sema/AnalysisBasedWarnings.cpp | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index 472d32249dfd6..6c802ac2db556 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -57,12 +57,12 @@ class LifetimeSafetyReporter { Confidence Confidence) {} // Suggests lifetime bound annotations for function paramters. - virtual void SuggestLifetimeboundToParmVar(SuggestionScope Scope, + virtual void suggestLifetimeboundToParmVar(SuggestionScope Scope, const ParmVarDecl *ParmToAnnotate, const Expr *EscapeExpr) {} // Suggests lifetime bound annotations for implicit this. - virtual void SuggestLifetimeboundToImplicitThis(SuggestionScope Scope, + virtual void suggestLifetimeboundToImplicitThis(SuggestionScope Scope, const CXXMethodDecl *MD, const Expr *EscapeExpr) {} diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index a0a24ce657f72..7496b261173ae 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -190,29 +190,29 @@ class LifetimeChecker { return nullptr; } - static void SuggestWithScopeForParmVar(LifetimeSafetyReporter *Reporter, + static void suggestWithScopeForParmVar(LifetimeSafetyReporter *Reporter, const ParmVarDecl *PVD, SourceManager &SM, const Expr *EscapeExpr) { if (const FunctionDecl *CrossTUDecl = getCrossTUDecl(*PVD, SM)) - Reporter->SuggestLifetimeboundToParmVar( + Reporter->suggestLifetimeboundToParmVar( SuggestionScope::CrossTU, CrossTUDecl->getParamDecl(PVD->getFunctionScopeIndex()), EscapeExpr); else - Reporter->SuggestLifetimeboundToParmVar(SuggestionScope::IntraTU, PVD, + Reporter->suggestLifetimeboundToParmVar(SuggestionScope::IntraTU, PVD, EscapeExpr); } - static void SuggestWithScopeForImplicitThis(LifetimeSafetyReporter *Reporter, + static void suggestWithScopeForImplicitThis(LifetimeSafetyReporter *Reporter, const CXXMethodDecl *MD, SourceManager &SM, const Expr *EscapeExpr) { if (const FunctionDecl *CrossTUDecl = getCrossTUDecl(*MD, SM)) - Reporter->SuggestLifetimeboundToImplicitThis( + Reporter->suggestLifetimeboundToImplicitThis( SuggestionScope::CrossTU, cast<CXXMethodDecl>(CrossTUDecl), EscapeExpr); else - Reporter->SuggestLifetimeboundToImplicitThis(SuggestionScope::IntraTU, MD, + Reporter->suggestLifetimeboundToImplicitThis(SuggestionScope::IntraTU, MD, EscapeExpr); } @@ -222,9 +222,9 @@ class LifetimeChecker { SourceManager &SM = AST.getSourceManager(); for (auto [Target, EscapeExpr] : AnnotationWarningsMap) { if (const auto *PVD = Target.dyn_cast<const ParmVarDecl *>()) - SuggestWithScopeForParmVar(Reporter, PVD, SM, EscapeExpr); + suggestWithScopeForParmVar(Reporter, PVD, SM, EscapeExpr); else if (const auto *MD = Target.dyn_cast<const CXXMethodDecl *>()) - SuggestWithScopeForImplicitThis(Reporter, MD, SM, EscapeExpr); + suggestWithScopeForImplicitThis(Reporter, MD, SM, EscapeExpr); } } diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index fb89456d9855e..b4c549116d05f 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -2903,7 +2903,7 @@ class LifetimeSafetyReporterImpl : public LifetimeSafetyReporter { << EscapeExpr->getEndLoc(); } - void SuggestLifetimeboundToParmVar(SuggestionScope Scope, + void suggestLifetimeboundToParmVar(SuggestionScope Scope, const ParmVarDecl *ParmToAnnotate, const Expr *EscapeExpr) override { unsigned DiagID; @@ -2929,7 +2929,7 @@ class LifetimeSafetyReporterImpl : public LifetimeSafetyReporter { << EscapeExpr->getSourceRange(); } - void SuggestLifetimeboundToImplicitThis(SuggestionScope Scope, + void suggestLifetimeboundToImplicitThis(SuggestionScope Scope, const CXXMethodDecl *MD, const Expr *EscapeExpr) override { unsigned DiagID = (Scope == SuggestionScope::CrossTU) >From c8ce3a33a4c60c4719562845fc45eee5f64db755 Mon Sep 17 00:00:00 2001 From: Kashika Akhouri <[email protected]> Date: Fri, 23 Jan 2026 15:39:04 +0000 Subject: [PATCH 7/8] Move TSI modification to Sema --- clang/include/clang/Sema/Sema.h | 5 ++++ clang/lib/Sema/AnalysisBasedWarnings.cpp | 30 ++++-------------------- clang/lib/Sema/SemaDecl.cpp | 18 ++++++++++++++ 3 files changed, 28 insertions(+), 25 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 23eb954ce774c..57bf19cc5532f 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -33,6 +33,7 @@ #include "clang/AST/StmtCXX.h" #include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" +#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h" #include "clang/Basic/AttrSubjectMatchRules.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/CapturedStmt.h" @@ -1256,6 +1257,10 @@ class Sema final : public SemaBase { bool ForceComplain = false, bool (*IsPlausibleResult)(QualType) = nullptr); + // Adds inferred lifetime bound attribute for implicit this to its + // TypeSourceInfo. + void addLifetimeBoundToImplicitThis(const CXXMethodDecl *MD); + /// Figure out if an expression could be turned into a call. /// /// Use this when trying to recover from an error where the programmer may diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index b4c549116d05f..b4bd035c67720 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -2906,15 +2906,10 @@ class LifetimeSafetyReporterImpl : public LifetimeSafetyReporter { void suggestLifetimeboundToParmVar(SuggestionScope Scope, const ParmVarDecl *ParmToAnnotate, const Expr *EscapeExpr) override { - unsigned DiagID; - switch (Scope) { - case SuggestionScope::CrossTU: - DiagID = diag::warn_lifetime_safety_cross_tu_param_suggestion; - break; - case SuggestionScope::IntraTU: - DiagID = diag::warn_lifetime_safety_intra_tu_param_suggestion; - break; - } + unsigned DiagID = + (Scope == SuggestionScope::CrossTU) + ? diag::warn_lifetime_safety_cross_tu_param_suggestion + : diag::warn_lifetime_safety_intra_tu_param_suggestion; SourceLocation InsertionPoint = Lexer::getLocForEndOfToken( ParmToAnnotate->getEndLoc(), 0, S.getSourceManager(), S.getLangOpts()); @@ -2950,22 +2945,7 @@ class LifetimeSafetyReporterImpl : public LifetimeSafetyReporter { } void addLifetimeBoundToImplicitThis(const CXXMethodDecl *MD) override { - CXXMethodDecl *MutableMD = const_cast<CXXMethodDecl *>(MD); - if (lifetimes::implicitObjectParamIsLifetimeBound(MutableMD)) - return; - ASTContext &Ctx = S.getASTContext(); - auto *Attr = - LifetimeBoundAttr::CreateImplicit(Ctx, MutableMD->getLocation()); - QualType MethodType = MutableMD->getType(); - QualType AttributedType = - Ctx.getAttributedType(Attr, MethodType, MethodType); - TypeLocBuilder TLB; - if (TypeSourceInfo *TSI = MutableMD->getTypeSourceInfo()) - TLB.pushFullCopy(TSI->getTypeLoc()); - AttributedTypeLoc TyLoc = TLB.push<AttributedTypeLoc>(AttributedType); - TyLoc.setAttr(Attr); - MutableMD->setType(AttributedType); - MutableMD->setTypeSourceInfo(TLB.getTypeSourceInfo(Ctx, AttributedType)); + S.addLifetimeBoundToImplicitThis(const_cast<CXXMethodDecl *>(MD)); } private: diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index ae779d6830d9b..753cfce642c22 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -14735,6 +14735,24 @@ StmtResult Sema::ActOnCXXForRangeIdentifier(Scope *S, SourceLocation IdentLoc, : IdentLoc); } +void Sema::addLifetimeBoundToImplicitThis(const CXXMethodDecl *MD) { + CXXMethodDecl *MutableMD = const_cast<CXXMethodDecl *>(MD); + if (lifetimes::implicitObjectParamIsLifetimeBound(MutableMD)) + return; + auto *Attr = + LifetimeBoundAttr::CreateImplicit(Context, MutableMD->getLocation()); + QualType MethodType = MutableMD->getType(); + QualType AttributedType = + Context.getAttributedType(attr::LifetimeBound, MethodType, MethodType); + TypeLocBuilder TLB; + if (TypeSourceInfo *TSI = MutableMD->getTypeSourceInfo()) + TLB.pushFullCopy(TSI->getTypeLoc()); + AttributedTypeLoc TyLoc = TLB.push<AttributedTypeLoc>(AttributedType); + TyLoc.setAttr(Attr); + MutableMD->setType(AttributedType); + MutableMD->setTypeSourceInfo(TLB.getTypeSourceInfo(Context, AttributedType)); +} + void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { if (var->isInvalidDecl()) return; >From b02401bb4a130188c748b0c4c05caeb388eac091 Mon Sep 17 00:00:00 2001 From: Kashika Akhouri <[email protected]> Date: Fri, 23 Jan 2026 17:34:20 +0000 Subject: [PATCH 8/8] Rename Reporter to SemaHelper --- .../Analyses/LifetimeSafety/Checker.h | 2 +- .../Analyses/LifetimeSafety/LifetimeSafety.h | 22 ++++++++++++++----- .../Analyses/LifetimeSafety/Origins.h | 16 +++++--------- clang/include/clang/Sema/Sema.h | 4 ++-- clang/lib/Analysis/LifetimeSafety/Checker.cpp | 16 +++++++------- .../LifetimeSafety/FactsGenerator.cpp | 2 +- .../LifetimeSafety/LifetimeSafety.cpp | 6 ++--- clang/lib/Analysis/LifetimeSafety/Origins.cpp | 20 ++++++++--------- clang/lib/Sema/AnalysisBasedWarnings.cpp | 12 +++++----- clang/lib/Sema/SemaDecl.cpp | 18 +++++++-------- .../Sema/warn-lifetime-safety-suggestions.cpp | 3 +-- 11 files changed, 61 insertions(+), 60 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Checker.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Checker.h index 03636be7d00c3..08a0b0e3d1d11 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Checker.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Checker.h @@ -28,7 +28,7 @@ namespace clang::lifetimes::internal { void runLifetimeChecker(const LoanPropagationAnalysis &LoanPropagation, const LiveOriginsAnalysis &LiveOrigins, const FactManager &FactMgr, AnalysisDeclContext &ADC, - LifetimeSafetyReporter *Reporter); + LifetimeSafetySemaHelper *Reporter); } // namespace clang::lifetimes::internal diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index 6c802ac2db556..dac80f06a9329 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -42,10 +42,20 @@ enum class SuggestionScope { IntraTU // For suggestions on definitions local to a Translation Unit. }; -class LifetimeSafetyReporter { +/// Abstract interface for operations requiring Sema access. +/// +/// This class exists to break a circular dependency: the LifetimeSafety +/// analysis target cannot directly depend on clangSema (which would create the +/// cycle: clangSema -> clangAnalysis -> clangAnalysisLifetimeSafety -> +/// clangSema). +/// +/// Instead, this interface is implemented in AnalysisBasedWarnings.cpp (part of +/// clangSema), allowing the analysis to report diagnostics and modify the AST +/// through Sema without introducing a circular dependency. +class LifetimeSafetySemaHelper { public: - LifetimeSafetyReporter() = default; - virtual ~LifetimeSafetyReporter() = default; + LifetimeSafetySemaHelper() = default; + virtual ~LifetimeSafetySemaHelper() = default; virtual void reportUseAfterFree(const Expr *IssueExpr, const Expr *UseExpr, SourceLocation FreeLoc, @@ -73,7 +83,7 @@ class LifetimeSafetyReporter { /// The main entry point for the analysis. void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC, - LifetimeSafetyReporter *Reporter, + LifetimeSafetySemaHelper *Reporter, LifetimeSafetyStats &Stats, bool CollectStats); namespace internal { @@ -94,7 +104,7 @@ struct LifetimeFactory { class LifetimeSafetyAnalysis { public: LifetimeSafetyAnalysis(AnalysisDeclContext &AC, - LifetimeSafetyReporter *Reporter); + LifetimeSafetySemaHelper *Reporter); void run(); @@ -107,7 +117,7 @@ class LifetimeSafetyAnalysis { private: AnalysisDeclContext &AC; - LifetimeSafetyReporter *Reporter; + LifetimeSafetySemaHelper *Reporter; LifetimeFactory Factory; std::unique_ptr<FactManager> FactMgr; std::unique_ptr<LiveOriginsAnalysis> LiveOrigins; diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h index 60b607c77da4e..8c638bdcace3f 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Origins.h @@ -125,8 +125,7 @@ bool doesDeclHaveStorage(const ValueDecl *D); /// variables and expressions. class OriginManager { public: - explicit OriginManager(ASTContext &AST, const Decl *D) - : AST(AST), CurrentDecl(D) {} + explicit OriginManager(ASTContext &AST, const Decl *D); /// Gets or creates the OriginList for a given ValueDecl. /// @@ -146,14 +145,9 @@ class OriginManager { /// \returns The OriginList, or nullptr for non-pointer rvalues. OriginList *getOrCreateList(const Expr *E); - /// Gets or creates the OriginList for the implicit 'this' parameter of a - /// given CXXMethodDecl. - /// - /// Creates a list structure mirroring the levels of indirection in the - /// method's 'this' type (e.g., `S*` for a non-static method of class `S`). - /// - /// \returns The OriginList for the implicit object parameter. - OriginList *getOrCreateList(const CXXMethodDecl *MD); + /// Returns the OriginList for the implicit 'this' parameter if the current + /// declaration is an instance method. + std::optional<OriginList *> getThisOrigins() const { return ThisOrigins; } const Origin &getOrigin(OriginID ID) const; @@ -183,7 +177,7 @@ class OriginManager { llvm::BumpPtrAllocator ListAllocator; llvm::DenseMap<const clang::ValueDecl *, OriginList *> DeclToList; llvm::DenseMap<const clang::Expr *, OriginList *> ExprToList; - const Decl *CurrentDecl; + std::optional<OriginList *> ThisOrigins; }; } // namespace clang::lifetimes::internal diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 57bf19cc5532f..0e91fecbb13bb 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1257,9 +1257,9 @@ class Sema final : public SemaBase { bool ForceComplain = false, bool (*IsPlausibleResult)(QualType) = nullptr); - // Adds inferred lifetime bound attribute for implicit this to its + // Adds implicit lifetime bound attribute for implicit this to its // TypeSourceInfo. - void addLifetimeBoundToImplicitThis(const CXXMethodDecl *MD); + void addLifetimeBoundToImplicitThis(CXXMethodDecl *MD); /// Figure out if an expression could be turned into a call. /// diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index 7496b261173ae..f7354f1c2e3f8 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -59,13 +59,13 @@ class LifetimeChecker { const LoanPropagationAnalysis &LoanPropagation; const LiveOriginsAnalysis &LiveOrigins; const FactManager &FactMgr; - LifetimeSafetyReporter *Reporter; + LifetimeSafetySemaHelper *Reporter; ASTContext &AST; public: LifetimeChecker(const LoanPropagationAnalysis &LoanPropagation, const LiveOriginsAnalysis &LiveOrigins, const FactManager &FM, - AnalysisDeclContext &ADC, LifetimeSafetyReporter *Reporter) + AnalysisDeclContext &ADC, LifetimeSafetySemaHelper *Reporter) : LoanPropagation(LoanPropagation), LiveOrigins(LiveOrigins), FactMgr(FM), Reporter(Reporter), AST(ADC.getASTContext()) { for (const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>()) @@ -190,7 +190,7 @@ class LifetimeChecker { return nullptr; } - static void suggestWithScopeForParmVar(LifetimeSafetyReporter *Reporter, + static void suggestWithScopeForParmVar(LifetimeSafetySemaHelper *Reporter, const ParmVarDecl *PVD, SourceManager &SM, const Expr *EscapeExpr) { @@ -203,10 +203,10 @@ class LifetimeChecker { EscapeExpr); } - static void suggestWithScopeForImplicitThis(LifetimeSafetyReporter *Reporter, - const CXXMethodDecl *MD, - SourceManager &SM, - const Expr *EscapeExpr) { + static void + suggestWithScopeForImplicitThis(LifetimeSafetySemaHelper *Reporter, + const CXXMethodDecl *MD, SourceManager &SM, + const Expr *EscapeExpr) { if (const FunctionDecl *CrossTUDecl = getCrossTUDecl(*MD, SM)) Reporter->suggestLifetimeboundToImplicitThis( SuggestionScope::CrossTU, cast<CXXMethodDecl>(CrossTUDecl), @@ -254,7 +254,7 @@ class LifetimeChecker { void runLifetimeChecker(const LoanPropagationAnalysis &LP, const LiveOriginsAnalysis &LO, const FactManager &FactMgr, AnalysisDeclContext &ADC, - LifetimeSafetyReporter *Reporter) { + LifetimeSafetySemaHelper *Reporter) { llvm::TimeTraceScope TimeProfile("LifetimeChecker"); LifetimeChecker Checker(LP, LO, FactMgr, ADC, Reporter); } diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp index 8a27a5df85376..7004f7d25e962 100644 --- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp +++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp @@ -633,7 +633,7 @@ llvm::SmallVector<Fact *> FactsGenerator::issuePlaceholderLoans() { llvm::SmallVector<Fact *> PlaceholderLoanFacts; if (const auto *MD = dyn_cast<CXXMethodDecl>(FD); MD && MD->isInstance()) { - OriginList *List = FactMgr.getOriginMgr().getOrCreateList(MD); + OriginList *List = *FactMgr.getOriginMgr().getThisOrigins(); const PlaceholderLoan *L = FactMgr.getLoanMgr().createLoan<PlaceholderLoan>(MD); PlaceholderLoanFacts.push_back( diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp index be0d405bd3086..c78738b0d4dc1 100644 --- a/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety/LifetimeSafety.cpp @@ -48,8 +48,8 @@ static void DebugOnlyFunction(AnalysisDeclContext &AC, const CFG &Cfg, } #endif -LifetimeSafetyAnalysis::LifetimeSafetyAnalysis(AnalysisDeclContext &AC, - LifetimeSafetyReporter *Reporter) +LifetimeSafetyAnalysis::LifetimeSafetyAnalysis( + AnalysisDeclContext &AC, LifetimeSafetySemaHelper *Reporter) : AC(AC), Reporter(Reporter) {} void LifetimeSafetyAnalysis::run() { @@ -103,7 +103,7 @@ void collectLifetimeStats(AnalysisDeclContext &AC, OriginManager &OM, } // namespace internal void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC, - LifetimeSafetyReporter *Reporter, + LifetimeSafetySemaHelper *Reporter, LifetimeSafetyStats &Stats, bool CollectStats) { internal::LifetimeSafetyAnalysis Analysis(AC, Reporter); Analysis.run(); diff --git a/clang/lib/Analysis/LifetimeSafety/Origins.cpp b/clang/lib/Analysis/LifetimeSafety/Origins.cpp index 8349d18267b62..1be8feda6caf3 100644 --- a/clang/lib/Analysis/LifetimeSafety/Origins.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Origins.cpp @@ -86,6 +86,13 @@ bool doesDeclHaveStorage(const ValueDecl *D) { return !D->getType()->isReferenceType(); } +OriginManager::OriginManager(ASTContext &AST, const Decl *D) : AST(AST) { + if (const auto *MD = llvm::dyn_cast_or_null<CXXMethodDecl>(D); + MD && MD->isInstance()) { + ThisOrigins = buildListForType(MD->getThisType(), MD); + } +} + OriginList *OriginManager::createNode(const ValueDecl *D, QualType QT) { OriginID NewID = getNextOriginID(); AllOrigins.emplace_back(NewID, D, QT.getTypePtrOrNull()); @@ -139,9 +146,9 @@ OriginList *OriginManager::getOrCreateList(const Expr *E) { QualType Type = E->getType(); // Special handling for 'this' expressions to share origins with the method's // implicit object parameter. - if (isa<CXXThisExpr>(E)) { - if (const auto *MD = dyn_cast<CXXMethodDecl>(CurrentDecl)) - return getOrCreateList(MD); + if (llvm::isa<CXXThisExpr>(E)) { + assert(ThisOrigins && "origins for 'this' should be set for a method decl"); + return *ThisOrigins; } // Special handling for DeclRefExpr to share origins with the underlying decl. @@ -175,13 +182,6 @@ OriginList *OriginManager::getOrCreateList(const Expr *E) { return ExprToList[E] = buildListForType(Type, E); } -OriginList *OriginManager::getOrCreateList(const CXXMethodDecl *MD) { - auto It = DeclToList.find(MD); - if (It != DeclToList.end()) - return It->second; - return DeclToList[MD] = buildListForType(MD->getThisType(), MD); -} - void OriginManager::dump(OriginID OID, llvm::raw_ostream &OS) const { OS << OID << " ("; Origin O = getOrigin(OID); diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index b4bd035c67720..954300df42319 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -2874,10 +2874,10 @@ class CallableVisitor : public DynamicRecursiveASTVisitor { namespace clang::lifetimes { namespace { -class LifetimeSafetyReporterImpl : public LifetimeSafetyReporter { +class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { public: - LifetimeSafetyReporterImpl(Sema &S) : S(S) {} + LifetimeSafetySemaHelperImpl(Sema &S) : S(S) {} void reportUseAfterFree(const Expr *IssueExpr, const Expr *UseExpr, SourceLocation FreeLoc, Confidence C) override { @@ -2960,7 +2960,7 @@ LifetimeSafetyTUAnalysis(Sema &S, TranslationUnitDecl *TU, llvm::TimeTraceScope TimeProfile("LifetimeSafetyTUAnalysis"); CallGraph CG; CG.addToCallGraph(TU); - lifetimes::LifetimeSafetyReporterImpl Reporter(S); + lifetimes::LifetimeSafetySemaHelperImpl Reporter(S); for (auto *Node : llvm::post_order(&CG)) { const clang::FunctionDecl *CanonicalFD = dyn_cast_or_null<clang::FunctionDecl>(Node->getDecl()); @@ -3201,9 +3201,9 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings( // stable. if (EnableLifetimeSafetyAnalysis && S.getLangOpts().CPlusPlus) { if (AC.getCFG()) { - lifetimes::LifetimeSafetyReporterImpl LifetimeSafetyReporter(S); - lifetimes::runLifetimeSafetyAnalysis(AC, &LifetimeSafetyReporter, LSStats, - S.CollectStats); + lifetimes::LifetimeSafetySemaHelperImpl LifetimeSafetySemaHelper(S); + lifetimes::runLifetimeSafetyAnalysis(AC, &LifetimeSafetySemaHelper, + LSStats, S.CollectStats); } } // Check for violations of "called once" parameter properties. diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 753cfce642c22..2bc65abf02829 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -14735,22 +14735,20 @@ StmtResult Sema::ActOnCXXForRangeIdentifier(Scope *S, SourceLocation IdentLoc, : IdentLoc); } -void Sema::addLifetimeBoundToImplicitThis(const CXXMethodDecl *MD) { - CXXMethodDecl *MutableMD = const_cast<CXXMethodDecl *>(MD); - if (lifetimes::implicitObjectParamIsLifetimeBound(MutableMD)) +void Sema::addLifetimeBoundToImplicitThis(CXXMethodDecl *MD) { + if (!MD || lifetimes::implicitObjectParamIsLifetimeBound(MD)) return; - auto *Attr = - LifetimeBoundAttr::CreateImplicit(Context, MutableMD->getLocation()); - QualType MethodType = MutableMD->getType(); + auto *Attr = LifetimeBoundAttr::CreateImplicit(Context, MD->getLocation()); + QualType MethodType = MD->getType(); QualType AttributedType = - Context.getAttributedType(attr::LifetimeBound, MethodType, MethodType); + Context.getAttributedType(Attr, MethodType, MethodType); TypeLocBuilder TLB; - if (TypeSourceInfo *TSI = MutableMD->getTypeSourceInfo()) + if (TypeSourceInfo *TSI = MD->getTypeSourceInfo()) TLB.pushFullCopy(TSI->getTypeLoc()); AttributedTypeLoc TyLoc = TLB.push<AttributedTypeLoc>(AttributedType); TyLoc.setAttr(Attr); - MutableMD->setType(AttributedType); - MutableMD->setTypeSourceInfo(TLB.getTypeSourceInfo(Context, AttributedType)); + MD->setType(AttributedType); + MD->setTypeSourceInfo(TLB.getTypeSourceInfo(Context, AttributedType)); } void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp index 19726c5175eb5..319391dffe5c0 100644 --- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp +++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp @@ -224,8 +224,7 @@ void test_getView_on_temporary() { } void test_get_on_temporary_copy() { - ReturnsSelf copy = ReturnsSelf().get(); - + ReturnsSelf copy = ReturnsSelf().get(); (void)copy; } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
