llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-analysis

Author: Kashika Akhouri (kashika0112)

<details>
<summary>Changes</summary>

Introduce between public and private annotation suggestions.

This PR refines the lifetime annotation suggestion feature by introducing a 
distinction between public and private annotation suggestions. 

We have introduced two new warning groups under Lifetime Safety Suggestions:

- `-Wexperimental-lifetime-safety-public-suggestions`: For suggestions on 
public functions (those with external linkage). These are treated as 
high-priority suggestions as they affect the public API of a module. For public 
functions that are declared in a header file and defined in a source file, the 
`[[clang::lifetimebound]]` annotation is now suggested on the declaration in 
the header file.
- `-Wexperimental-lifetime-safety-private-suggestions`: For suggestions on 
private functions (those with internal linkage, e.g., static functions or 
functions in an anonymous namespace). These can be considered lower-priority 
suggestions. For private functions, suggestion is placed on the definition.

Example:
```
// Header file r.h
#include &lt;iostream&gt;
#include &lt;string&gt;

std::string_view public_func(std::string_view a);

inline std::string_view inline_header(std::string_view a) {
    return a;
}

```
```
// Source (cpp file)
#include &lt;iostream&gt;
#include &lt;string&gt;
#include "r.h"

std::string_view public_func(std::string_view a) {
  return a;
}

namespace {
  std::string_view private_func(std::string_view a) {
  return a;
}
}
```

The warnings generated are:
```
./r.h:6:39: warning: externally visible param should be marked 
[[clang::lifetimebound]] [-Wexperimental-lifetime-safety-public-suggestions]
    6 | inline std::string_view inline_header(std::string_view a) {
      |                                       ^~~~~~~~~~~~~~~~~~
      |                                                          
[[clang::lifetimebound]]
./r.h:7:12: note: param returned here
    7 |     return a;
      |            ^
./r.h:4:30: warning: externally visible param should be marked 
[[clang::lifetimebound]] [-Wexperimental-lifetime-safety-public-suggestions]
    4 | std::string_view public_func(std::string_view a);
      |                              ^~~~~~~~~~~~~~~~~~
      |                                                 [[clang::lifetimebound]]
r.cpp:6:10: note: param returned here
    6 |   return a;
      |          ^
r.cpp:10:33: warning: param with internal linkage should be marked 
[[clang::lifetimebound]] [-Wexperimental-lifetime-safety-private-suggestions]
   10 |   std::string_view private_func(std::string_view a) {
      |                                 ^~~~~~~~~~~~~~~~~~
      |                                                    
[[clang::lifetimebound]]
r.cpp:11:10: note: param returned here
   11 |   return a;
      |          ^

```


---
Full diff: https://github.com/llvm/llvm-project/pull/171972.diff


6 Files Affected:

- (modified) 
clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h (+9-3) 
- (modified) clang/include/clang/Basic/DiagnosticGroups.td (+11-1) 
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+10-3) 
- (modified) clang/lib/Analysis/LifetimeSafety/Checker.cpp (+20-2) 
- (modified) clang/lib/Sema/AnalysisBasedWarnings.cpp (+24-5) 
- (modified) clang/test/Sema/warn-lifetime-safety-suggestions.cpp (+90-32) 


``````````diff
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}}
 }
 

``````````

</details>


https://github.com/llvm/llvm-project/pull/171972
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to