https://github.com/kashika0112 updated https://github.com/llvm/llvm-project/pull/171972
>From 6e668a5548768717f6c2284d61bc09279071ab2f Mon Sep 17 00:00:00 2001 From: Kashika Akhouri <[email protected]> Date: Fri, 12 Dec 2025 08:06:48 +0000 Subject: [PATCH 1/3] Public-private annotation suggestions --- .../Analyses/LifetimeSafety/LifetimeSafety.h | 12 +- clang/include/clang/Basic/DiagnosticGroups.td | 12 +- .../clang/Basic/DiagnosticSemaKinds.td | 13 +- clang/lib/Analysis/LifetimeSafety/Checker.cpp | 22 +++- clang/lib/Sema/AnalysisBasedWarnings.cpp | 29 ++++- .../Sema/warn-lifetime-safety-suggestions.cpp | 122 +++++++++++++----- 6 files changed, 164 insertions(+), 46 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index 31fae55f60486..5a840f17203b5 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -48,9 +48,15 @@ class LifetimeSafetyReporter { SourceLocation ExpiryLoc, Confidence Confidence) {} - // Suggests lifetime bound annotations for function paramters - virtual void suggestAnnotation(const ParmVarDecl *PVD, - const Expr *EscapeExpr) {} + // Suggest private lifetime bound annotations for function parameters internal + // to the existing file. + virtual void suggestAnnotationsPrivate(const ParmVarDecl *ParmToAnnotate, + const Expr *EscapeExpr) {} + + // Suggest public lifetime bound annotations for function parameters external + // to other files. + virtual void suggestAnnotationsPublic(const ParmVarDecl *ParmToAnnotate, + const Expr *EscapeExpr) {} }; /// The main entry point for the analysis. diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index e1dba0195f470..ac05f11ddcf11 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -541,8 +541,18 @@ def LifetimeSafety : DiagGroup<"experimental-lifetime-safety", Experimental warnings to detect use-after-free and related temporal safety bugs based on lifetime safety analysis. }]; } +def LifetimeSafetyPublicSuggestions + : DiagGroup<"experimental-lifetime-safety-public-suggestions">; +def LifetimeSafetyPrivateSuggestions + : DiagGroup<"experimental-lifetime-safety-private-suggestions">; def LifetimeSafetySuggestions - : DiagGroup<"experimental-lifetime-safety-suggestions">; + : DiagGroup<"experimental-lifetime-safety-suggestions", + [LifetimeSafetyPublicSuggestions, + LifetimeSafetyPrivateSuggestions]> { + code Documentation = [{ + Lifetime annotation suggestions for function parameters that should be marked [[clang::lifetimebound]] based on lifetime analysis. + }]; +} def DistributedObjectModifiers : DiagGroup<"distributed-object-modifiers">; def DllexportExplicitInstantiationDecl : DiagGroup<"dllexport-explicit-instantiation-decl">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 381d1fb063eba..69e46aeecf6a9 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10818,9 +10818,16 @@ 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_suggest_lifetimebound - : Warning<"param should be marked [[clang::lifetimebound]]">, - InGroup<LifetimeSafetySuggestions>, +def warn_lifetime_safety_private_suggestion + : Warning<"param with internal linkage should be marked " + "[[clang::lifetimebound]]">, + InGroup<LifetimeSafetyPrivateSuggestions>, + DefaultIgnore; + +def warn_lifetime_safety_public_suggestion + : Warning< + "externally visible param should be marked [[clang::lifetimebound]]">, + InGroup<LifetimeSafetyPublicSuggestions>, DefaultIgnore; def note_lifetime_safety_suggestion_returned_here : Note<"param returned here">; diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index 99071d6b46c1e..cd0454bae2d62 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -20,6 +20,7 @@ #include "clang/Analysis/Analyses/PostOrderCFGView.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" #include "llvm/ADT/DenseMap.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/TimeProfiler.h" @@ -163,8 +164,25 @@ class LifetimeChecker { void suggestAnnotations() { if (!Reporter) return; - for (const auto &[PVD, EscapeExpr] : AnnotationWarningsMap) - Reporter->suggestAnnotation(PVD, EscapeExpr); + SourceManager &SM = AST.getSourceManager(); + for (const auto &[PVD, EscapeExpr] : AnnotationWarningsMap) { + const auto *FD = dyn_cast<FunctionDecl>(PVD->getDeclContext()); + if (!FD) + continue; + // For public functions (external linkage), find the header declaration + // to annotate; otherwise, treat as private and annotate the definition. + if (FD->isExternallyVisible()) { + const FunctionDecl *CanonicalFD = FD->getCanonicalDecl(); + const ParmVarDecl *ParmToAnnotate = PVD; + if (CanonicalFD != FD && SM.getFileID(FD->getLocation()) != + SM.getFileID(CanonicalFD->getLocation())) + if (unsigned Index = PVD->getFunctionScopeIndex(); + Index < CanonicalFD->getNumParams()) + ParmToAnnotate = CanonicalFD->getParamDecl(Index); + Reporter->suggestAnnotationsPublic(ParmToAnnotate, EscapeExpr); + } else + Reporter->suggestAnnotationsPrivate(PVD, EscapeExpr); + } } void inferAnnotations() { diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index 321445dd4e1ff..f2081d2e2bebe 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -2883,14 +2883,33 @@ class LifetimeSafetyReporterImpl : public LifetimeSafetyReporter { << EscapeExpr->getEndLoc(); } - void suggestAnnotation(const ParmVarDecl *PVD, - const Expr *EscapeExpr) override { + void suggestAnnotationsPrivate(const ParmVarDecl *ParmToAnnotate, + const Expr *EscapeExpr) override { SourceLocation InsertionPoint = Lexer::getLocForEndOfToken( - PVD->getEndLoc(), 0, S.getSourceManager(), S.getLangOpts()); - S.Diag(PVD->getBeginLoc(), diag::warn_lifetime_safety_suggest_lifetimebound) - << PVD->getSourceRange() + ParmToAnnotate->getEndLoc(), 0, S.getSourceManager(), S.getLangOpts()); + + S.Diag(ParmToAnnotate->getBeginLoc(), + diag::warn_lifetime_safety_private_suggestion) + << ParmToAnnotate->getSourceRange() + << FixItHint::CreateInsertion(InsertionPoint, + " [[clang::lifetimebound]]"); + + S.Diag(EscapeExpr->getBeginLoc(), + diag::note_lifetime_safety_suggestion_returned_here) + << EscapeExpr->getSourceRange(); + } + + void suggestAnnotationsPublic(const ParmVarDecl *ParmToAnnotate, + const Expr *EscapeExpr) override { + SourceLocation InsertionPoint = Lexer::getLocForEndOfToken( + ParmToAnnotate->getEndLoc(), 0, S.getSourceManager(), S.getLangOpts()); + + S.Diag(ParmToAnnotate->getBeginLoc(), + diag::warn_lifetime_safety_public_suggestion) + << ParmToAnnotate->getSourceRange() << FixItHint::CreateInsertion(InsertionPoint, " [[clang::lifetimebound]]"); + S.Diag(EscapeExpr->getBeginLoc(), diag::note_lifetime_safety_suggestion_returned_here) << EscapeExpr->getSourceRange(); diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp index 9f3ccb7fca770..e3096540d269b 100644 --- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp +++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp @@ -1,4 +1,6 @@ -// RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety -fexperimental-lifetime-safety-inference -Wexperimental-lifetime-safety-suggestions -Wexperimental-lifetime-safety -verify %s +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety -fexperimental-lifetime-safety-inference -Wexperimental-lifetime-safety-suggestions -Wexperimental-lifetime-safety -I%t -verify %t/test_source.cpp struct MyObj { int id; @@ -12,14 +14,52 @@ struct [[gsl::Pointer()]] View { void use() const; }; -View return_view_directly (View a) { // expected-warning {{param should be marked [[clang::lifetimebound]]}}. +//===----------------------------------------------------------------------===// +// Public Annotation Test Cases +//===----------------------------------------------------------------------===// + +//--- test_header.h +#ifndef TEST_HEADER_H +#define TEST_HEADER_H + +struct MyObj { + int id; + ~MyObj() {} // Non-trivial destructor + MyObj operator+(MyObj); +}; + +struct [[gsl::Pointer()]] View { + View(const MyObj&); // Borrows from MyObj + View(); + void use() const; +}; + +View return_view_directly(View a); // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}} + +View conditional_return_view( + View a, // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}} + View b, // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}} + bool c); + +int* return_pointer_directly(int* a); // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}} + +MyObj* return_pointer_object(MyObj* a); // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}} + +inline View inline_header_return_view(View a) { // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}} + return a; // expected-note {{param returned here}} +} + +#endif // TEST_HEADER_H + +//--- test_source.cpp + +#include "test_header.h" + +View return_view_directly(View a) { return a; // expected-note {{param returned here}} } -View conditional_return_view ( - View a, // expected-warning {{param should be marked [[clang::lifetimebound]]}}. - View b, // expected-warning {{param should be marked [[clang::lifetimebound]]}}. - bool c) { +View conditional_return_view(View a, View b, bool c) { View res; if (c) res = a; @@ -28,29 +68,20 @@ View conditional_return_view ( return res; // expected-note 2 {{param returned here}} } -// FIXME: Fails to generate lifetime suggestion for reference types as these are not handled currently. -MyObj& return_reference (MyObj& a, MyObj& b, bool c) { - if(c) { - return a; - } - return b; -} - -// FIXME: Fails to generate lifetime suggestion for reference types as these are not handled currently. -View return_view_from_reference (MyObj& p) { - return p; -} - -int* return_pointer_directly (int* a) { // expected-warning {{param should be marked [[clang::lifetimebound]]}}. +int* return_pointer_directly(int* a) { return a; // expected-note {{param returned here}} } -MyObj* return_pointer_object (MyObj* a) { // expected-warning {{param should be marked [[clang::lifetimebound]]}}. +MyObj* return_pointer_object(MyObj* a) { return a; // expected-note {{param returned here}} } +//===----------------------------------------------------------------------===// +// Private Annotation Test Cases +//===----------------------------------------------------------------------===// +namespace { View only_one_paramter_annotated (View a [[clang::lifetimebound]], - View b, // expected-warning {{param should be marked [[clang::lifetimebound]]}}. + View b, // expected-warning {{param with internal linkage should be marked [[clang::lifetimebound]]}}. bool c) { if(c) return a; @@ -59,11 +90,25 @@ View only_one_paramter_annotated (View a [[clang::lifetimebound]], View reassigned_to_another_parameter ( View a, - View b) { // expected-warning {{param should be marked [[clang::lifetimebound]]}}. + View b) { // expected-warning {{param with internal linkage should be marked [[clang::lifetimebound]]}}. a = b; return a; // expected-note {{param returned here}} } +View private_func_redecl (View a); +View private_func_redecl (View a) { // expected-warning {{param with internal linkage should be marked [[clang::lifetimebound]]}}. + return a; // expected-note {{param returned here}} +} +} + +static View return_view_static (View a) { // expected-warning {{param with internal linkage should be marked [[clang::lifetimebound]]}}. + return a; // expected-note {{param returned here}} +} + +//===----------------------------------------------------------------------===// +// FIXME Test Cases +//===----------------------------------------------------------------------===// + struct ReturnsSelf { const ReturnsSelf& get() const { return *this; @@ -89,16 +134,29 @@ void test_getView_on_temporary() { (void)sv; } +// FIXME: Fails to generate lifetime suggestion for reference types as these are not handled currently. +MyObj& return_reference (MyObj& a, MyObj& b, bool c) { + if(c) { + return a; + } + return b; +} + +// FIXME: Fails to generate lifetime suggestion for reference types as these are not handled currently. +View return_view_from_reference (MyObj& p) { + return p; +} + //===----------------------------------------------------------------------===// // Annotation Inference Test Cases //===----------------------------------------------------------------------===// namespace correct_order_inference { -View return_view_by_func (View a) { // expected-warning {{param should be marked [[clang::lifetimebound]]}}. +View return_view_by_func (View a) { // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}}. return return_view_directly(a); // expected-note {{param returned here}} } -MyObj* return_pointer_by_func (MyObj* a) { // expected-warning {{param should be marked [[clang::lifetimebound]]}}. +MyObj* return_pointer_by_func (MyObj* a) { // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}}. return return_pointer_object(a); // expected-note {{param returned here}} } } // namespace correct_order_inference @@ -111,7 +169,7 @@ View return_view_caller(View a) { return return_view_callee(a); } -View return_view_callee(View a) { // expected-warning {{param should be marked [[clang::lifetimebound]]}}. +View return_view_callee(View a) { // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}}. return a; // expected-note {{param returned here}} } } // namespace incorrect_order_inference_view @@ -124,17 +182,17 @@ MyObj* return_object_caller(MyObj* a) { return return_object_callee(a); } -MyObj* return_object_callee(MyObj* a) { // expected-warning {{param should be marked [[clang::lifetimebound]]}}. +MyObj* return_object_callee(MyObj* a) { // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}}. return a; // expected-note {{param returned here}} } } // namespace incorrect_order_inference_object namespace simple_annotation_inference { -View inference_callee_return_identity(View a) { // expected-warning {{param should be marked [[clang::lifetimebound]]}}. +View inference_callee_return_identity(View a) { // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}}. return a; // expected-note {{param returned here}} } -View inference_caller_forwards_callee(View a) { // expected-warning {{param should be marked [[clang::lifetimebound]]}}. +View inference_caller_forwards_callee(View a) { // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}}. return inference_callee_return_identity(a); // expected-note {{param returned here}} } @@ -147,12 +205,12 @@ View inference_top_level_return_stack_view() { namespace inference_in_order_with_redecls { View inference_callee_return_identity(View a); -View inference_callee_return_identity(View a) { // expected-warning {{param should be marked [[clang::lifetimebound]]}}. +View inference_callee_return_identity(View a) { // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}}. return a; // expected-note {{param returned here}} } View inference_caller_forwards_callee(View a); -View inference_caller_forwards_callee(View a) { // expected-warning {{param should be marked [[clang::lifetimebound]]}}. +View inference_caller_forwards_callee(View a) { // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}}. return inference_callee_return_identity(a); // expected-note {{param returned here}} } @@ -165,7 +223,7 @@ View inference_top_level_return_stack_view() { namespace inference_with_templates { template<typename T> -T* template_identity(T* a) { // expected-warning {{param should be marked [[clang::lifetimebound]]}}. +T* template_identity(T* a) { // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}}. return a; // expected-note {{param returned here}} } >From 8d612c90c11a7865f90e42fcc53deab584bf4315 Mon Sep 17 00:00:00 2001 From: Kashika Akhouri <[email protected]> Date: Fri, 12 Dec 2025 13:44:20 +0000 Subject: [PATCH 2/3] Change diag to cross and intra TU --- .../Analyses/LifetimeSafety/LifetimeSafety.h | 19 +++---- clang/include/clang/Basic/DiagnosticGroups.td | 12 ++--- .../clang/Basic/DiagnosticSemaKinds.td | 14 +++--- clang/lib/Analysis/LifetimeSafety/Checker.cpp | 27 ++++++---- clang/lib/Sema/AnalysisBasedWarnings.cpp | 32 +++++------- .../Sema/warn-lifetime-safety-suggestions.cpp | 50 +++++++------------ 6 files changed, 73 insertions(+), 81 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index 5a840f17203b5..12fdf30318012 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -34,6 +34,12 @@ enum class Confidence : uint8_t { Definite // Reported as a definite error (-Wlifetime-safety-permissive) }; +/// Enum to track functions visible across or within TU. +enum class SuggestionScope { + CrossTU, // For suggestions on declarations visible across Translation Units. + IntraTU // For suggestions on definitions local to a Translation Unit. +}; + class LifetimeSafetyReporter { public: LifetimeSafetyReporter() = default; @@ -48,15 +54,10 @@ class LifetimeSafetyReporter { SourceLocation ExpiryLoc, Confidence Confidence) {} - // Suggest private lifetime bound annotations for function parameters internal - // to the existing file. - virtual void suggestAnnotationsPrivate(const ParmVarDecl *ParmToAnnotate, - const Expr *EscapeExpr) {} - - // Suggest public lifetime bound annotations for function parameters external - // to other files. - virtual void suggestAnnotationsPublic(const ParmVarDecl *ParmToAnnotate, - const Expr *EscapeExpr) {} + // Suggests lifetime bound annotations for function paramters + virtual void suggestAnnotation(SuggestionScope Scope, + const ParmVarDecl *ParmToAnnotate, + const Expr *EscapeExpr) {} }; /// The main entry point for the analysis. diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index ac05f11ddcf11..7538066a7d644 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -541,14 +541,14 @@ def LifetimeSafety : DiagGroup<"experimental-lifetime-safety", Experimental warnings to detect use-after-free and related temporal safety bugs based on lifetime safety analysis. }]; } -def LifetimeSafetyPublicSuggestions - : DiagGroup<"experimental-lifetime-safety-public-suggestions">; -def LifetimeSafetyPrivateSuggestions - : DiagGroup<"experimental-lifetime-safety-private-suggestions">; +def LifetimeSafetyCrossTUSuggestions + : DiagGroup<"experimental-lifetime-safety-cross-tu-suggestions">; +def LifetimeSafetyIntraTUSuggestions + : DiagGroup<"experimental-lifetime-safety-intra-tu-suggestions">; def LifetimeSafetySuggestions : DiagGroup<"experimental-lifetime-safety-suggestions", - [LifetimeSafetyPublicSuggestions, - LifetimeSafetyPrivateSuggestions]> { + [LifetimeSafetyCrossTUSuggestions, + LifetimeSafetyIntraTUSuggestions]> { code Documentation = [{ Lifetime annotation suggestions for function parameters that should be marked [[clang::lifetimebound]] based on lifetime analysis. }]; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 69e46aeecf6a9..74595df8dd3f5 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10818,16 +10818,16 @@ 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_private_suggestion - : Warning<"param with internal linkage should be marked " +def warn_lifetime_safety_intra_tu_suggestion + : Warning<"parameter in intra-TU function should be marked " "[[clang::lifetimebound]]">, - InGroup<LifetimeSafetyPrivateSuggestions>, + InGroup<LifetimeSafetyIntraTUSuggestions>, DefaultIgnore; -def warn_lifetime_safety_public_suggestion - : Warning< - "externally visible param should be marked [[clang::lifetimebound]]">, - InGroup<LifetimeSafetyPublicSuggestions>, +def warn_lifetime_safety_cross_tu_suggestion + : Warning<"parameter in cross-TU function should be marked " + "[[clang::lifetimebound]]">, + InGroup<LifetimeSafetyCrossTUSuggestions>, DefaultIgnore; def note_lifetime_safety_suggestion_returned_here : Note<"param returned here">; diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index cd0454bae2d62..a175b59a2c111 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -161,6 +161,18 @@ 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 FunctionDecl *Definition, + SourceManager &SM) { + const FunctionDecl *CanonicalDecl = Definition->getCanonicalDecl(); + if (CanonicalDecl != Definition && + SM.getFileID(Definition->getLocation()) != + SM.getFileID(CanonicalDecl->getLocation())) + return CanonicalDecl; + return nullptr; + } + void suggestAnnotations() { if (!Reporter) return; @@ -169,19 +181,16 @@ class LifetimeChecker { const auto *FD = dyn_cast<FunctionDecl>(PVD->getDeclContext()); if (!FD) continue; - // For public functions (external linkage), find the header declaration - // to annotate; otherwise, treat as private and annotate the definition. if (FD->isExternallyVisible()) { - const FunctionDecl *CanonicalFD = FD->getCanonicalDecl(); const ParmVarDecl *ParmToAnnotate = PVD; - if (CanonicalFD != FD && SM.getFileID(FD->getLocation()) != - SM.getFileID(CanonicalFD->getLocation())) + if (const FunctionDecl *CrossTUDecl = getCrossTUDecl(FD, SM)) if (unsigned Index = PVD->getFunctionScopeIndex(); - Index < CanonicalFD->getNumParams()) - ParmToAnnotate = CanonicalFD->getParamDecl(Index); - Reporter->suggestAnnotationsPublic(ParmToAnnotate, EscapeExpr); + Index < CrossTUDecl->getNumParams()) + ParmToAnnotate = CrossTUDecl->getParamDecl(Index); + Reporter->suggestAnnotation(SuggestionScope::CrossTU, ParmToAnnotate, + EscapeExpr); } else - Reporter->suggestAnnotationsPrivate(PVD, EscapeExpr); + Reporter->suggestAnnotation(SuggestionScope::IntraTU, PVD, EscapeExpr); } } diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index f2081d2e2bebe..e9a532f9c1790 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -2883,29 +2883,23 @@ class LifetimeSafetyReporterImpl : public LifetimeSafetyReporter { << EscapeExpr->getEndLoc(); } - void suggestAnnotationsPrivate(const ParmVarDecl *ParmToAnnotate, - const Expr *EscapeExpr) override { - SourceLocation InsertionPoint = Lexer::getLocForEndOfToken( - ParmToAnnotate->getEndLoc(), 0, S.getSourceManager(), S.getLangOpts()); - - S.Diag(ParmToAnnotate->getBeginLoc(), - diag::warn_lifetime_safety_private_suggestion) - << ParmToAnnotate->getSourceRange() - << FixItHint::CreateInsertion(InsertionPoint, - " [[clang::lifetimebound]]"); - - S.Diag(EscapeExpr->getBeginLoc(), - diag::note_lifetime_safety_suggestion_returned_here) - << EscapeExpr->getSourceRange(); - } + void suggestAnnotation(SuggestionScope Scope, + const ParmVarDecl *ParmToAnnotate, + const Expr *EscapeExpr) override { + unsigned DiagID; + switch (Scope) { + case SuggestionScope::CrossTU: + DiagID = diag::warn_lifetime_safety_cross_tu_suggestion; + break; + case SuggestionScope::IntraTU: + DiagID = diag::warn_lifetime_safety_intra_tu_suggestion; + break; + } - void suggestAnnotationsPublic(const ParmVarDecl *ParmToAnnotate, - const Expr *EscapeExpr) override { SourceLocation InsertionPoint = Lexer::getLocForEndOfToken( ParmToAnnotate->getEndLoc(), 0, S.getSourceManager(), S.getLangOpts()); - S.Diag(ParmToAnnotate->getBeginLoc(), - diag::warn_lifetime_safety_public_suggestion) + S.Diag(ParmToAnnotate->getBeginLoc(), DiagID) << ParmToAnnotate->getSourceRange() << FixItHint::CreateInsertion(InsertionPoint, " [[clang::lifetimebound]]"); diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp index e3096540d269b..54680eefddcba 100644 --- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp +++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp @@ -2,18 +2,6 @@ // RUN: split-file %s %t // RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety -fexperimental-lifetime-safety-inference -Wexperimental-lifetime-safety-suggestions -Wexperimental-lifetime-safety -I%t -verify %t/test_source.cpp -struct MyObj { - int id; - ~MyObj() {} // Non-trivial destructor - MyObj operator+(MyObj); -}; - -struct [[gsl::Pointer()]] View { - View(const MyObj&); // Borrows from MyObj - View(); - void use() const; -}; - //===----------------------------------------------------------------------===// // Public Annotation Test Cases //===----------------------------------------------------------------------===// @@ -34,18 +22,18 @@ struct [[gsl::Pointer()]] View { void use() const; }; -View return_view_directly(View a); // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}} +View return_view_directly(View a); // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}} View conditional_return_view( - View a, // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}} - View b, // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}} + View a, // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}} + View b, // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}} bool c); -int* return_pointer_directly(int* a); // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}} +int* return_pointer_directly(int* a); // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}} -MyObj* return_pointer_object(MyObj* a); // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}} +MyObj* return_pointer_object(MyObj* a); // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}} -inline View inline_header_return_view(View a) { // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}} +inline View inline_header_return_view(View a) { // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}} return a; // expected-note {{param returned here}} } @@ -81,7 +69,7 @@ MyObj* return_pointer_object(MyObj* a) { //===----------------------------------------------------------------------===// namespace { View only_one_paramter_annotated (View a [[clang::lifetimebound]], - View b, // expected-warning {{param with internal linkage should be marked [[clang::lifetimebound]]}}. + View b, // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. bool c) { if(c) return a; @@ -90,18 +78,18 @@ View only_one_paramter_annotated (View a [[clang::lifetimebound]], View reassigned_to_another_parameter ( View a, - View b) { // expected-warning {{param with internal linkage should be marked [[clang::lifetimebound]]}}. + View b) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. a = b; return a; // expected-note {{param returned here}} } View private_func_redecl (View a); -View private_func_redecl (View a) { // expected-warning {{param with internal linkage should be marked [[clang::lifetimebound]]}}. +View private_func_redecl (View a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. return a; // expected-note {{param returned here}} } } -static View return_view_static (View a) { // expected-warning {{param with internal linkage should be marked [[clang::lifetimebound]]}}. +static View return_view_static (View a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. return a; // expected-note {{param returned here}} } @@ -152,11 +140,11 @@ View return_view_from_reference (MyObj& p) { //===----------------------------------------------------------------------===// namespace correct_order_inference { -View return_view_by_func (View a) { // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}}. +View return_view_by_func (View a) { // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}}. return return_view_directly(a); // expected-note {{param returned here}} } -MyObj* return_pointer_by_func (MyObj* a) { // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}}. +MyObj* return_pointer_by_func (MyObj* a) { // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}}. return return_pointer_object(a); // expected-note {{param returned here}} } } // namespace correct_order_inference @@ -169,7 +157,7 @@ View return_view_caller(View a) { return return_view_callee(a); } -View return_view_callee(View a) { // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}}. +View return_view_callee(View a) { // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}}. return a; // expected-note {{param returned here}} } } // namespace incorrect_order_inference_view @@ -182,17 +170,17 @@ MyObj* return_object_caller(MyObj* a) { return return_object_callee(a); } -MyObj* return_object_callee(MyObj* a) { // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}}. +MyObj* return_object_callee(MyObj* a) { // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}}. return a; // expected-note {{param returned here}} } } // namespace incorrect_order_inference_object namespace simple_annotation_inference { -View inference_callee_return_identity(View a) { // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}}. +View inference_callee_return_identity(View a) { // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}}. return a; // expected-note {{param returned here}} } -View inference_caller_forwards_callee(View a) { // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}}. +View inference_caller_forwards_callee(View a) { // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}}. return inference_callee_return_identity(a); // expected-note {{param returned here}} } @@ -205,12 +193,12 @@ View inference_top_level_return_stack_view() { namespace inference_in_order_with_redecls { View inference_callee_return_identity(View a); -View inference_callee_return_identity(View a) { // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}}. +View inference_callee_return_identity(View a) { // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}}. return a; // expected-note {{param returned here}} } View inference_caller_forwards_callee(View a); -View inference_caller_forwards_callee(View a) { // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}}. +View inference_caller_forwards_callee(View a) { // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}}. return inference_callee_return_identity(a); // expected-note {{param returned here}} } @@ -223,7 +211,7 @@ View inference_top_level_return_stack_view() { namespace inference_with_templates { template<typename T> -T* template_identity(T* a) { // expected-warning {{externally visible param should be marked [[clang::lifetimebound]]}}. +T* template_identity(T* a) { // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}}. return a; // expected-note {{param returned here}} } >From a2f2404005edc3a77af536242929af949f964a8f Mon Sep 17 00:00:00 2001 From: Kashika Akhouri <[email protected]> Date: Mon, 15 Dec 2025 08:34:19 +0000 Subject: [PATCH 3/3] Modify intra-logic and test changes --- clang/lib/Analysis/LifetimeSafety/Checker.cpp | 32 ++++----- .../Sema/warn-lifetime-safety-suggestions.cpp | 68 +++++++++++-------- 2 files changed, 55 insertions(+), 45 deletions(-) diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index a175b59a2c111..ab1667c9ee775 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -163,12 +163,16 @@ 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 FunctionDecl *Definition, + static const FunctionDecl *getCrossTUDecl(const ParmVarDecl &PVD, SourceManager &SM) { - const FunctionDecl *CanonicalDecl = Definition->getCanonicalDecl(); - if (CanonicalDecl != Definition && - SM.getFileID(Definition->getLocation()) != - SM.getFileID(CanonicalDecl->getLocation())) + const auto *FD = dyn_cast<FunctionDecl>(PVD.getDeclContext()); + if (!FD) + return nullptr; + if (!FD->isExternallyVisible()) + return nullptr; + const FunctionDecl *CanonicalDecl = FD->getCanonicalDecl(); + if (CanonicalDecl != FD && SM.getFileID(FD->getLocation()) != + SM.getFileID(CanonicalDecl->getLocation())) return CanonicalDecl; return nullptr; } @@ -178,18 +182,12 @@ class LifetimeChecker { return; SourceManager &SM = AST.getSourceManager(); for (const auto &[PVD, EscapeExpr] : AnnotationWarningsMap) { - const auto *FD = dyn_cast<FunctionDecl>(PVD->getDeclContext()); - if (!FD) - continue; - if (FD->isExternallyVisible()) { - const ParmVarDecl *ParmToAnnotate = PVD; - if (const FunctionDecl *CrossTUDecl = getCrossTUDecl(FD, SM)) - if (unsigned Index = PVD->getFunctionScopeIndex(); - Index < CrossTUDecl->getNumParams()) - ParmToAnnotate = CrossTUDecl->getParamDecl(Index); - Reporter->suggestAnnotation(SuggestionScope::CrossTU, ParmToAnnotate, - EscapeExpr); - } else + if (const FunctionDecl *CrossTUDecl = getCrossTUDecl(*PVD, SM)) + Reporter->suggestAnnotation( + SuggestionScope::CrossTU, + CrossTUDecl->getParamDecl(PVD->getFunctionScopeIndex()), + EscapeExpr); + else Reporter->suggestAnnotation(SuggestionScope::IntraTU, PVD, EscapeExpr); } } diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp index 54680eefddcba..8d967f9128d04 100644 --- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp +++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp @@ -2,9 +2,6 @@ // RUN: split-file %s %t // RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety -fexperimental-lifetime-safety-inference -Wexperimental-lifetime-safety-suggestions -Wexperimental-lifetime-safety -I%t -verify %t/test_source.cpp -//===----------------------------------------------------------------------===// -// Public Annotation Test Cases -//===----------------------------------------------------------------------===// //--- test_header.h #ifndef TEST_HEADER_H @@ -29,12 +26,17 @@ View conditional_return_view( View b, // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}} bool c); -int* return_pointer_directly(int* a); // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}} +int* return_pointer_directly(int* a); // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}} MyObj* return_pointer_object(MyObj* a); // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}} -inline View inline_header_return_view(View a) { // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}} - return a; // expected-note {{param returned here}} +inline View inline_header_return_view(View a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} + return a; // expected-note {{param returned here}} +} + +View redeclared_in_header(View a); +inline View redeclared_in_header(View a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} + return a; // expected-note {{param returned here}} } #endif // TEST_HEADER_H @@ -64,11 +66,21 @@ MyObj* return_pointer_object(MyObj* a) { return a; // expected-note {{param returned here}} } -//===----------------------------------------------------------------------===// -// Private Annotation Test Cases -//===----------------------------------------------------------------------===// + +View return_cross_tu_func_View(View a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. + return return_view_directly(a); // expected-note {{param returned here}} +} + +MyObj* return_cross_tu_func_obj(MyObj* a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. + return return_pointer_object(a); // expected-note {{param returned here}} +} + +int* return_cross_tu_func_pointer(int* a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. + return return_pointer_directly(a); // expected-note {{param returned here}} +} + namespace { -View only_one_paramter_annotated (View a [[clang::lifetimebound]], +View only_one_paramter_annotated(View a [[clang::lifetimebound]], View b, // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. bool c) { if(c) @@ -76,21 +88,21 @@ View only_one_paramter_annotated (View a [[clang::lifetimebound]], return b; // expected-note {{param returned here}} } -View reassigned_to_another_parameter ( +View reassigned_to_another_parameter( View a, View b) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. a = b; return a; // expected-note {{param returned here}} } -View private_func_redecl (View a); -View private_func_redecl (View a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. - return a; // expected-note {{param returned here}} +View intra_tu_func_redecl(View a); +View intra_tu_func_redecl(View a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. + return a; // expected-note {{param returned here}} } } -static View return_view_static (View a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. - return a; // expected-note {{param returned here}} +static View return_view_static(View a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. + return a; // expected-note {{param returned here}} } //===----------------------------------------------------------------------===// @@ -123,15 +135,15 @@ void test_getView_on_temporary() { } // FIXME: Fails to generate lifetime suggestion for reference types as these are not handled currently. -MyObj& return_reference (MyObj& a, MyObj& b, bool c) { - if(c) { +MyObj& return_reference(MyObj& a, MyObj& b, bool c) { + if (c) { return a; } return b; } // FIXME: Fails to generate lifetime suggestion for reference types as these are not handled currently. -View return_view_from_reference (MyObj& p) { +View return_view_from_reference(MyObj& p) { return p; } @@ -140,11 +152,11 @@ View return_view_from_reference (MyObj& p) { //===----------------------------------------------------------------------===// namespace correct_order_inference { -View return_view_by_func (View a) { // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}}. +View return_view_by_func(View a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. return return_view_directly(a); // expected-note {{param returned here}} } -MyObj* return_pointer_by_func (MyObj* a) { // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}}. +MyObj* return_pointer_by_func(MyObj* a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. return return_pointer_object(a); // expected-note {{param returned here}} } } // namespace correct_order_inference @@ -157,7 +169,7 @@ View return_view_caller(View a) { return return_view_callee(a); } -View return_view_callee(View a) { // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}}. +View return_view_callee(View a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. return a; // expected-note {{param returned here}} } } // namespace incorrect_order_inference_view @@ -170,17 +182,17 @@ MyObj* return_object_caller(MyObj* a) { return return_object_callee(a); } -MyObj* return_object_callee(MyObj* a) { // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}}. +MyObj* return_object_callee(MyObj* a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. return a; // expected-note {{param returned here}} } } // namespace incorrect_order_inference_object namespace simple_annotation_inference { -View inference_callee_return_identity(View a) { // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}}. +View inference_callee_return_identity(View a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. return a; // expected-note {{param returned here}} } -View inference_caller_forwards_callee(View a) { // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}}. +View inference_caller_forwards_callee(View a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. return inference_callee_return_identity(a); // expected-note {{param returned here}} } @@ -193,12 +205,12 @@ View inference_top_level_return_stack_view() { namespace inference_in_order_with_redecls { View inference_callee_return_identity(View a); -View inference_callee_return_identity(View a) { // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}}. +View inference_callee_return_identity(View a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. return a; // expected-note {{param returned here}} } View inference_caller_forwards_callee(View a); -View inference_caller_forwards_callee(View a) { // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}}. +View inference_caller_forwards_callee(View a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. return inference_callee_return_identity(a); // expected-note {{param returned here}} } @@ -211,7 +223,7 @@ View inference_top_level_return_stack_view() { namespace inference_with_templates { template<typename T> -T* template_identity(T* a) { // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}}. +T* template_identity(T* a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. return a; // expected-note {{param returned here}} } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
