diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index 74792768e2c57..c8592a623dd2f 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -55,13 +55,14 @@ class LifetimeChecker { const LiveOriginsAnalysis &LiveOrigins; const FactManager &FactMgr; LifetimeSafetyReporter *Reporter; + ASTContext ∾ public: LifetimeChecker(const LoanPropagationAnalysis &LoanPropagation, const LiveOriginsAnalysis &LiveOrigins, const FactManager &FM, AnalysisDeclContext &ADC, LifetimeSafetyReporter *Reporter) : LoanPropagation(LoanPropagation), LiveOrigins(LiveOrigins), FactMgr(FM), - Reporter(Reporter) { + Reporter(Reporter), AC(ADC.getASTContext()) { for (const CFGBlock *B : *ADC.getAnalysis()) for (const Fact *F : FactMgr.getFacts(B)) if (const auto *EF = F->getAs()) @@ -70,6 +71,7 @@ class LifetimeChecker { checkAnnotations(OEF); issuePendingWarnings(); suggestAnnotations(); + inferAnnotations(); } /// Checks if an escaping origin holds a placeholder loan, indicating a @@ -160,6 +162,19 @@ class LifetimeChecker { for (const auto &[PVD, EscapeExpr] : AnnotationWarningsMap) Reporter->suggestAnnotation(PVD, EscapeExpr); } + + void inferAnnotations() { + // FIXME: To maximise inference propagation, functions should be analyzed in + // post-order of the call graph, allowing inferred annotations to propagate + // through the call chain + // FIXME: Add the inferred attribute to all redeclarations of the function, + // not just the definition being analyzed. + for (const auto &[ConstPVD, EscapeExpr] : AnnotationWarningsMap) { + ParmVarDecl *PVD = const_cast(ConstPVD); + if (!PVD->hasAttr()) + PVD->addAttr(LifetimeBoundAttr::CreateImplicit(AC, PVD->getLocation())); + } + } }; } // namespace diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp index c0f675a301d14..248179e8ba7b6 100644 --- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp +++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp @@ -89,6 +89,46 @@ void test_getView_on_temporary() { (void)sv; } +//===----------------------------------------------------------------------===// +// Annotation Inference Test Cases +//===----------------------------------------------------------------------===// + +namespace correct_order_inference { +View return_view_by_func (View a) { // expected-warning {{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]]}}. + return return_pointer_object(a); // expected-note {{param returned here}} +} +} // correct_order_inference + +namespace incorrect_order_inference_view { +View return_view_callee(View a); + +// FIXME: No lifetime annotation suggestion when functions are not present in the callee-before-caller pattern +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]]}}. + return a; // expected-note {{param returned here}} +} +} // incorrect_order_inference_view + +namespace incorrect_order_inference_object { +View return_object_callee(View a); + +// FIXME: No lifetime annotation suggestion warning when functions are not present in the callee-before-caller pattern +View return_object_caller(View a) { + return return_object_callee(a); +} + +View return_object_callee(View a) { // expected-warning {{param should be marked [[clang::lifetimebound]]}}. + return a; // expected-note {{param returned here}} +} +} // incorrect_order_inference_object + //===----------------------------------------------------------------------===// // Negative Test Cases //===----------------------------------------------------------------------===// diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp index 1191469e23df1..8cd1bd8bcc54c 100644 --- a/clang/test/Sema/warn-lifetime-safety.cpp +++ b/clang/test/Sema/warn-lifetime-safety.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety -Wexperimental-lifetime-safety -Wno-dangling -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety -Wexperimental-lifetime-safety -Wno-dangling -Wno-experimental-lifetime-safety-suggestions -verify %s struct MyObj { int id; @@ -552,6 +552,58 @@ const int& get_ref_to_local() { // expected-note@-1 {{returned here}} } +namespace simple_annotation_inference { +View inference_callee_return_identity(View a) { + return a; +} + +View inference_caller_forwards_callee(View a) { + return inference_callee_return_identity(a); +} + +View inference_top_level_return_stack_view() { + MyObj local_stack; + return inference_caller_forwards_callee(local_stack); // expected-warning {{address of stack memory is returned later}} + // expected-note@-1 {{returned here}} +} +} // simple_annotation_inference + +namespace inference_in_order_with_redecls { +View inference_callee_return_identity(View a); +View inference_callee_return_identity(View a) { + return a; +} + +View inference_caller_forwards_callee(View a); +View inference_caller_forwards_callee(View a) { + return inference_callee_return_identity(a); +} + +View inference_top_level_return_stack_view() { + MyObj local_stack; + return inference_caller_forwards_callee(local_stack); // expected-warning {{address of stack memory is returned later}} + // expected-note@-1 {{returned here}} +} +} // namespace inference_in_order_with_redecls + +namespace inference_with_templates { +template +T* template_identity(T* a) { + return a; +} + +template +T* template_caller(T* a) { + return template_identity(a); +} + +// FIXME: Fails to detect UAR as template instantiations are deferred to the end of the Translation Unit. +MyObj* test_template_inference_with_stack() { + MyObj local_stack; + return template_caller(&local_stack); +} +} // inference_with_templates + //===----------------------------------------------------------------------===// // Use-After-Scope & Use-After-Return (Return-Stack-Address) Combined // These are cases where the diagnostic kind is determined by location