[clang] [-Wunsafe-buffer-usage] Add unique_ptr <T[]> accesses (PR #156773)

via cfe-commits cfe-commits at lists.llvm.org
Mon Sep 29 23:33:39 PDT 2025


https://github.com/shreya-jain updated https://github.com/llvm/llvm-project/pull/156773

>From 927b2f61b8f5e0163c065ee4cbbf7ebb6956c34b Mon Sep 17 00:00:00 2001
From: shreya-jain <jainshreya at google.com>
Date: Wed, 3 Sep 2025 00:36:02 -0700
Subject: [PATCH 01/13] Update UnsafeBufferUsage.h

---
 clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index 9b53f1dc1d759..ea41eb3becfcf 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -14,6 +14,7 @@
 #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_UNSAFEBUFFERUSAGE_H
 #define LLVM_CLANG_ANALYSIS_ANALYSES_UNSAFEBUFFERUSAGE_H
 
+#include "clang/AST/ASTTypeTraits.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/Stmt.h"
@@ -139,6 +140,12 @@ class UnsafeBufferUsageHandler {
                             FixItList &&Fixes, const Decl *D,
                             const FixitStrategy &VarTargetTypes) = 0;
 
+  // Invoked when an array subscript operator[] is used on a
+  // std::unique_ptr<T[]>.
+  virtual void handleUnsafeUniquePtrArrayAccess(const DynTypedNode &Node,
+                                                bool IsRelatedToDecl,
+                                                ASTContext &Ctx) = 0;
+
 #ifndef NDEBUG
 public:
   bool areDebugNotesRequested() {

>From bae5f2ee3e04cc2bb54a5a863b9a8113aca227aa Mon Sep 17 00:00:00 2001
From: shreya-jain <jainshreya at google.com>
Date: Wed, 3 Sep 2025 00:38:29 -0700
Subject: [PATCH 02/13] Update UnsafeBufferUsageGadgets.def

---
 .../include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
index 09fa510bc0472..fae5f8b8aa8e3 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
@@ -38,6 +38,7 @@ WARNING_GADGET(PointerArithmetic)
 WARNING_GADGET(UnsafeBufferUsageAttr)
 WARNING_GADGET(UnsafeBufferUsageCtorAttr)
 WARNING_GADGET(DataInvocation)
+WARNING_GADGET(UniquePtrArrayAccess)
 WARNING_OPTIONAL_GADGET(UnsafeLibcFunctionCall)
 WARNING_OPTIONAL_GADGET(SpanTwoParamConstructor) // Uses of `std::span(arg0, arg1)`
 FIXABLE_GADGET(ULCArraySubscript)          // `DRE[any]` in an Unspecified Lvalue Context

>From 284c26214a0758346f78db02c81bfe953a9cd2ae Mon Sep 17 00:00:00 2001
From: shreya-jain <jainshreya at google.com>
Date: Wed, 3 Sep 2025 00:45:48 -0700
Subject: [PATCH 03/13] Update DiagnosticGroups.td

---
 clang/include/clang/Basic/DiagnosticGroups.td | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 0c994e0b5ca4d..4b27a42c6dd81 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1750,7 +1750,8 @@ def ReadOnlyPlacementChecks : DiagGroup<"read-only-types">;
 // Warnings and fixes to support the "safe buffers" programming model.
 def UnsafeBufferUsageInContainer : DiagGroup<"unsafe-buffer-usage-in-container">;
 def UnsafeBufferUsageInLibcCall : DiagGroup<"unsafe-buffer-usage-in-libc-call">;
-def UnsafeBufferUsage : DiagGroup<"unsafe-buffer-usage", [UnsafeBufferUsageInContainer, UnsafeBufferUsageInLibcCall]>;
+def UnsafeBufferUsageInUniquePtrArrayAccess : DiagGroup<"unsafe-buffer-usage-in-unique-ptr-array-access">;
+def UnsafeBufferUsage : DiagGroup<"unsafe-buffer-usage", [UnsafeBufferUsageInContainer, UnsafeBufferUsageInLibcCall, UnsafeBufferUsageInUniquePtrArrayAccess]>;
 
 // Warnings and notes InstallAPI verification.
 def InstallAPIViolation : DiagGroup<"installapi-violation">;

>From 980a75e56bd40fe2f58f5f4e5b4d671fd4448e84 Mon Sep 17 00:00:00 2001
From: shreya-jain <jainshreya at google.com>
Date: Wed, 3 Sep 2025 00:47:47 -0700
Subject: [PATCH 04/13] Update DiagnosticSemaKinds.td

---
 clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index c76385ad73a91..117ba3b79eacc 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13240,6 +13240,8 @@ def note_safe_buffer_usage_suggestions_disabled : Note<
 def warn_unsafe_buffer_usage_in_container : Warning<
   "the two-parameter std::span construction is unsafe as it can introduce mismatch between buffer size and the bound information">,
   InGroup<UnsafeBufferUsageInContainer>, DefaultIgnore;
+def warn_unsafe_buffer_usage_unique_ptr_array_access : Warning<"direct access using operator[] on std::unique_ptr<T[]> is unsafe due to lack of bounds checking">,
+  InGroup<UnsafeBufferUsageInUniquePtrArray>, DefaultIgnore;
 #ifndef NDEBUG
 // Not a user-facing diagnostic. Useful for debugging false negatives in
 // -fsafe-buffer-usage-suggestions (i.e. lack of -Wunsafe-buffer-usage fixits).

>From 11fc596ef00a9e47eca04724b8ae7e35f7aa12d1 Mon Sep 17 00:00:00 2001
From: shreya-jain <jainshreya at google.com>
Date: Wed, 3 Sep 2025 00:53:56 -0700
Subject: [PATCH 05/13] Update UnsafeBufferUsage.cpp

---
 clang/lib/Analysis/UnsafeBufferUsage.cpp | 104 +++++++++++++++++++++++
 1 file changed, 104 insertions(+)

diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 1d7b8722103aa..3dbf4e9a3535b 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -13,6 +13,7 @@
 #include "clang/AST/Attr.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclTemplate.h"
 #include "clang/AST/DynamicRecursiveASTVisitor.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/FormatString.h"
@@ -1318,6 +1319,105 @@ static bool isSupportedVariable(const DeclRefExpr &Node) {
   return D != nullptr && isa<VarDecl>(D);
 }
 
+static bool isUniquePtrArray(const CXXRecordDecl *RecordDecl) {
+  if (!RecordDecl || !RecordDecl->isInStdNamespace() ||
+      RecordDecl->getNameAsString() != "unique_ptr") {
+    return false;
+  }
+
+  const ClassTemplateSpecializationDecl *class_template_specialization_decl =
+      dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl);
+  if (!class_template_specialization_decl) {
+    return false;
+  }
+
+  const TemplateArgumentList &template_args =
+      class_template_specialization_decl->getTemplateArgs();
+
+  if (template_args.size() == 0) {
+    return false;
+  }
+
+  const TemplateArgument &first_arg = template_args[0];
+
+  if (first_arg.getKind() != TemplateArgument::Type) {
+    return false;
+  }
+
+  QualType referred_type = first_arg.getAsType();
+
+  if (referred_type->isArrayType()) {
+    return true;
+  }
+
+  return false;
+}
+
+class UniquePtrArrayAccessGadget : public WarningGadget {
+  static constexpr const char *const AccessorTag = "unique_ptr_array_access";
+  const CXXOperatorCallExpr *TheAccessorExpr;
+
+public:
+  UniquePtrArrayAccessGadget(const MatchResult &Result)
+      : WarningGadget(Kind::UniquePtrArrayAccess),
+        TheAccessorExpr(Result.getNodeAs<CXXOperatorCallExpr>(AccessorTag)) {
+    assert(TheAccessorExpr &&
+           "UniquePtrArrayAccessGadget requires a matched CXXOperatorCallExpr");
+  }
+
+  static bool classof(const Gadget *G) {
+    return G->getKind() == Kind::UniquePtrArrayAccess;
+  }
+
+  static bool matches(const Stmt *S, const ASTContext &Ctx,
+                      MatchResult &Result) {
+
+    const CXXOperatorCallExpr *OpCall = dyn_cast<CXXOperatorCallExpr>(S);
+    if (!OpCall || OpCall->getOperator() != OO_Subscript) {
+      return false;
+    }
+
+    const Expr *Callee = OpCall->getCallee()->IgnoreParenImpCasts();
+    if (!Callee) {
+      return false;
+    }
+
+    const CXXMethodDecl *Method =
+        dyn_cast_or_null<CXXMethodDecl>(OpCall->getDirectCallee());
+    if (!Method) {
+      return false;
+    }
+
+    if (Method->getNameAsString() != "operator[]") {
+      return false;
+    }
+
+    const CXXRecordDecl *RecordDecl = Method->getParent();
+    if (!isUniquePtrArray(RecordDecl)) {
+      return false;
+    }
+
+    Result.addNode(AccessorTag, DynTypedNode::create(*OpCall));
+    return true;
+  }
+  void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
+                             bool IsRelatedToDecl,
+                             ASTContext &Ctx) const override {
+    Handler.handleUnsafeUniquePtrArrayAccess(
+        DynTypedNode::create(*TheAccessorExpr), IsRelatedToDecl, Ctx);
+  }
+
+  SourceLocation getSourceLoc() const override {
+    if (TheAccessorExpr) {
+      return TheAccessorExpr->getOperatorLoc();
+    }
+    return SourceLocation();
+  }
+
+  DeclUseList getClaimedVarUseSites() const override { return {}; }
+  SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
+};
+
 using FixableGadgetList = std::vector<std::unique_ptr<FixableGadget>>;
 using WarningGadgetList = std::vector<std::unique_ptr<WarningGadget>>;
 
@@ -2647,6 +2747,10 @@ std::set<const Expr *> clang::findUnsafePointers(const FunctionDecl *FD) {
     }
   };
 
+  void handleUnsafeUniquePtrArrayAccess(const DynTypedNode &Node,
+                                          bool IsRelatedToDecl,
+                                          ASTContext &Ctx) override {}
+
   FixableGadgetList FixableGadgets;
   WarningGadgetList WarningGadgets;
   DeclUseTracker Tracker;

>From 800d39379f3fca299c5e5ca8f0eee2c9ec9ad7b2 Mon Sep 17 00:00:00 2001
From: shreya-jain <jainshreya at google.com>
Date: Wed, 3 Sep 2025 00:56:33 -0700
Subject: [PATCH 06/13] Update AnalysisBasedWarnings.cpp

---
 clang/lib/Sema/AnalysisBasedWarnings.cpp | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 1b66d83df5171..5f51d38c6cfb9 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -2606,6 +2606,19 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler {
 #endif
   }
 
+  void handleUnsafeUniquePtrArrayAccess(const DynTypedNode &Node,
+                                        bool IsRelatedToDecl,
+                                        ASTContext &Ctx) override {
+    SourceLocation Loc;
+    std::string Message;
+
+    Loc = Node.get<Stmt>()->getBeginLoc();
+    Message = "Direct operator[] access on std::unique_ptr<T[]> is unsafe "
+              "(no bounds check).";
+    S.Diag(Loc, diag::warn_unsafe_buffer_usage_unique_ptr_array_access)
+        << Message << Node.getSourceRange();
+  }
+
   bool isSafeBufferOptOut(const SourceLocation &Loc) const override {
     return S.PP.isSafeBufferOptOut(S.getSourceManager(), Loc);
   }

>From 4c959720dfa0d5889e7f57e4333745902d43a7be Mon Sep 17 00:00:00 2001
From: shreya-jain <jainshreya at google.com>
Date: Wed, 3 Sep 2025 00:58:07 -0700
Subject: [PATCH 07/13] Update warn-unsafe-buffer-usage-debug-unclaimed.cpp

---
 .../warn-unsafe-buffer-usage-debug-unclaimed.cpp  | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/warn-unsafe-buffer-usage-debug-unclaimed.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/warn-unsafe-buffer-usage-debug-unclaimed.cpp
index ab3d925753d47..563f27a97739d 100644
--- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/warn-unsafe-buffer-usage-debug-unclaimed.cpp
+++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/warn-unsafe-buffer-usage-debug-unclaimed.cpp
@@ -32,6 +32,21 @@ void test_unclaimed_use(int *p) { // expected-warning{{'p' is an unsafe pointer
   p[5] = 5;       // expected-note{{used in buffer access here}}
 }
 
+namespace std {
+inline namespace __1 {
+template <class T> class unique_ptr {
+public:
+  T &operator[](long long i) const;
+};
+} // namespace __1
+} // namespace std
+
+void basic_unique_ptr() {
+  std::unique_ptr<int[]> p1;
+  p1[0];  // expected-warning{{direct access using operator[] on
+          // std::unique_ptr<T[]> is unsafe due to lack of bounds checking}}
+}
+
 // CHECK: Root # 1
 // CHECK: |- DeclRefExpr # 4
 // CHECK: |-- UnaryOperator(++) # 1

>From 38a2d442253070c76719d06c8abc642d0452db61 Mon Sep 17 00:00:00 2001
From: Shreya Jain <jainshreya at google.com>
Date: Wed, 3 Sep 2025 23:01:48 +0000
Subject: [PATCH 08/13] fix compilation issues and test

---
 clang/include/clang/Basic/DiagnosticSemaKinds.td      |  2 +-
 clang/lib/Analysis/UnsafeBufferUsage.cpp              | 11 +++++------
 .../warn-unsafe-buffer-usage-debug-unclaimed.cpp      |  3 +--
 3 files changed, 7 insertions(+), 9 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 117ba3b79eacc..6bdebde1ec19e 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13241,7 +13241,7 @@ def warn_unsafe_buffer_usage_in_container : Warning<
   "the two-parameter std::span construction is unsafe as it can introduce mismatch between buffer size and the bound information">,
   InGroup<UnsafeBufferUsageInContainer>, DefaultIgnore;
 def warn_unsafe_buffer_usage_unique_ptr_array_access : Warning<"direct access using operator[] on std::unique_ptr<T[]> is unsafe due to lack of bounds checking">,
-  InGroup<UnsafeBufferUsageInUniquePtrArray>, DefaultIgnore;
+  InGroup<UnsafeBufferUsageInUniquePtrArrayAccess>, DefaultIgnore;
 #ifndef NDEBUG
 // Not a user-facing diagnostic. Useful for debugging false negatives in
 // -fsafe-buffer-usage-suggestions (i.e. lack of -Wunsafe-buffer-usage fixits).
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 3dbf4e9a3535b..7a0a0ee7fb694 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -2732,10 +2732,13 @@ std::set<const Expr *> clang::findUnsafePointers(const FunctionDecl *FD) {
                                    const VariableGroupsManager &, FixItList &&,
                                    const Decl *,
                                    const FixitStrategy &) override {}
-    bool isSafeBufferOptOut(const SourceLocation &) const override {
+    void handleUnsafeUniquePtrArrayAccess(const DynTypedNode &Node,
+                                          bool IsRelatedToDecl,
+                                          ASTContext &Ctx) override {}
+    bool ignoreUnsafeBufferInContainer(const SourceLocation &) const override {
       return false;
     }
-    bool ignoreUnsafeBufferInContainer(const SourceLocation &) const override {
+    bool isSafeBufferOptOut(const SourceLocation &) const override {
       return false;
     }
     bool ignoreUnsafeBufferInLibcCall(const SourceLocation &) const override {
@@ -2747,10 +2750,6 @@ std::set<const Expr *> clang::findUnsafePointers(const FunctionDecl *FD) {
     }
   };
 
-  void handleUnsafeUniquePtrArrayAccess(const DynTypedNode &Node,
-                                          bool IsRelatedToDecl,
-                                          ASTContext &Ctx) override {}
-
   FixableGadgetList FixableGadgets;
   WarningGadgetList WarningGadgets;
   DeclUseTracker Tracker;
diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/warn-unsafe-buffer-usage-debug-unclaimed.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/warn-unsafe-buffer-usage-debug-unclaimed.cpp
index 563f27a97739d..e504d1f97ada3 100644
--- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/warn-unsafe-buffer-usage-debug-unclaimed.cpp
+++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/warn-unsafe-buffer-usage-debug-unclaimed.cpp
@@ -43,8 +43,7 @@ template <class T> class unique_ptr {
 
 void basic_unique_ptr() {
   std::unique_ptr<int[]> p1;
-  p1[0];  // expected-warning{{direct access using operator[] on
-          // std::unique_ptr<T[]> is unsafe due to lack of bounds checking}}
+  p1[0];  // expected-warning{{direct access using operator[] on std::unique_ptr<T[]> is unsafe due to lack of bounds checking}}
 }
 
 // CHECK: Root # 1

>From 5bc65802d014caef52d99def7226b56ce2635fca Mon Sep 17 00:00:00 2001
From: Shreya Jain <jainshreya at google.com>
Date: Tue, 16 Sep 2025 05:56:12 +0000
Subject: [PATCH 09/13] address review comments

---
 clang/lib/Analysis/UnsafeBufferUsage.cpp | 49 ++++++++----------------
 1 file changed, 16 insertions(+), 33 deletions(-)

diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 7a0a0ee7fb694..aa559884310f2 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -1321,47 +1321,36 @@ static bool isSupportedVariable(const DeclRefExpr &Node) {
 
 static bool isUniquePtrArray(const CXXRecordDecl *RecordDecl) {
   if (!RecordDecl || !RecordDecl->isInStdNamespace() ||
-      RecordDecl->getNameAsString() != "unique_ptr") {
+      RecordDecl->getNameAsString() != "unique_ptr")
     return false;
-  }
 
   const ClassTemplateSpecializationDecl *class_template_specialization_decl =
       dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl);
-  if (!class_template_specialization_decl) {
+  if (!class_template_specialization_decl)
     return false;
-  }
 
   const TemplateArgumentList &template_args =
       class_template_specialization_decl->getTemplateArgs();
-
-  if (template_args.size() == 0) {
+  if (template_args.size() == 0)
     return false;
-  }
 
   const TemplateArgument &first_arg = template_args[0];
-
-  if (first_arg.getKind() != TemplateArgument::Type) {
+  if (first_arg.getKind() != TemplateArgument::Type)
     return false;
-  }
 
   QualType referred_type = first_arg.getAsType();
-
-  if (referred_type->isArrayType()) {
-    return true;
-  }
-
-  return false;
+  return referred_type->isArrayType();
 }
 
 class UniquePtrArrayAccessGadget : public WarningGadget {
   static constexpr const char *const AccessorTag = "unique_ptr_array_access";
-  const CXXOperatorCallExpr *TheAccessorExpr;
+  const CXXOperatorCallExpr *AccessorExpr;
 
 public:
   UniquePtrArrayAccessGadget(const MatchResult &Result)
       : WarningGadget(Kind::UniquePtrArrayAccess),
-        TheAccessorExpr(Result.getNodeAs<CXXOperatorCallExpr>(AccessorTag)) {
-    assert(TheAccessorExpr &&
+        AccessorExpr(Result.getNodeAs<CXXOperatorCallExpr>(AccessorTag)) {
+    assert(AccessorExpr &&
            "UniquePtrArrayAccessGadget requires a matched CXXOperatorCallExpr");
   }
 
@@ -1373,29 +1362,24 @@ class UniquePtrArrayAccessGadget : public WarningGadget {
                       MatchResult &Result) {
 
     const CXXOperatorCallExpr *OpCall = dyn_cast<CXXOperatorCallExpr>(S);
-    if (!OpCall || OpCall->getOperator() != OO_Subscript) {
+    if (!OpCall || OpCall->getOperator() != OO_Subscript)
       return false;
-    }
 
     const Expr *Callee = OpCall->getCallee()->IgnoreParenImpCasts();
-    if (!Callee) {
+    if (!Callee)
       return false;
-    }
 
     const CXXMethodDecl *Method =
         dyn_cast_or_null<CXXMethodDecl>(OpCall->getDirectCallee());
-    if (!Method) {
+    if (!Method)
       return false;
-    }
 
-    if (Method->getNameAsString() != "operator[]") {
+    if (Method->getNameAsString() != "operator[]")
       return false;
-    }
 
     const CXXRecordDecl *RecordDecl = Method->getParent();
-    if (!isUniquePtrArray(RecordDecl)) {
+    if (!isUniquePtrArray(RecordDecl))
       return false;
-    }
 
     Result.addNode(AccessorTag, DynTypedNode::create(*OpCall));
     return true;
@@ -1404,13 +1388,12 @@ class UniquePtrArrayAccessGadget : public WarningGadget {
                              bool IsRelatedToDecl,
                              ASTContext &Ctx) const override {
     Handler.handleUnsafeUniquePtrArrayAccess(
-        DynTypedNode::create(*TheAccessorExpr), IsRelatedToDecl, Ctx);
+        DynTypedNode::create(*AccessorExpr), IsRelatedToDecl, Ctx);
   }
 
   SourceLocation getSourceLoc() const override {
-    if (TheAccessorExpr) {
-      return TheAccessorExpr->getOperatorLoc();
-    }
+    if (AccessorExpr)
+      return AccessorExpr->getOperatorLoc();
     return SourceLocation();
   }
 

>From efb19b652d7c51a402bd74da961de1bb1efa9768 Mon Sep 17 00:00:00 2001
From: Shreya Jain <jainshreya at google.com>
Date: Thu, 18 Sep 2025 20:24:36 +0000
Subject: [PATCH 10/13] [-Wunsafe-buffer-usage] Add unique_ptr <T[]> accesses

---
 clang/lib/Analysis/UnsafeBufferUsage.cpp      | 12 +++++-
 clang/lib/Sema/AnalysisBasedWarnings.cpp      |  4 +-
 ...rn-unsafe-buffer-usage-debug-unclaimed.cpp |  2 +-
 .../warn-unsafe-buffer-usage-unique-ptr.cpp   | 43 +++++++++++++++++++
 4 files changed, 56 insertions(+), 5 deletions(-)
 create mode 100644 clang/test/SemaCXX/warn-unsafe-buffer-usage-unique-ptr.cpp

diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index aa559884310f2..bbdcce342390b 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -1319,6 +1319,7 @@ static bool isSupportedVariable(const DeclRefExpr &Node) {
   return D != nullptr && isa<VarDecl>(D);
 }
 
+// Returns true for RecordDecl of type std::unique_ptr<T[]>
 static bool isUniquePtrArray(const CXXRecordDecl *RecordDecl) {
   if (!RecordDecl || !RecordDecl->isInStdNamespace() ||
       RecordDecl->getNameAsString() != "unique_ptr")
@@ -1343,6 +1344,7 @@ static bool isUniquePtrArray(const CXXRecordDecl *RecordDecl) {
 }
 
 class UniquePtrArrayAccessGadget : public WarningGadget {
+private:
   static constexpr const char *const AccessorTag = "unique_ptr_array_access";
   const CXXOperatorCallExpr *AccessorExpr;
 
@@ -1374,13 +1376,21 @@ class UniquePtrArrayAccessGadget : public WarningGadget {
     if (!Method)
       return false;
 
-    if (Method->getNameAsString() != "operator[]")
+    if (Method->getOverloadedOperator() != OO_Subscript)
       return false;
 
     const CXXRecordDecl *RecordDecl = Method->getParent();
     if (!isUniquePtrArray(RecordDecl))
       return false;
 
+    const Expr *IndexExpr = OpCall->getArg(1);
+    llvm::APSInt IndexValue;
+
+    // Allow [0]
+    if (IndexExpr->EvaluateAsInt(IndexValue, Ctx) && IndexValue.isZero()) {
+      return false;
+    }
+
     Result.addNode(AccessorTag, DynTypedNode::create(*OpCall));
     return true;
   }
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 5f51d38c6cfb9..d2c1077c379b7 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -2613,10 +2613,8 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler {
     std::string Message;
 
     Loc = Node.get<Stmt>()->getBeginLoc();
-    Message = "Direct operator[] access on std::unique_ptr<T[]> is unsafe "
-              "(no bounds check).";
     S.Diag(Loc, diag::warn_unsafe_buffer_usage_unique_ptr_array_access)
-        << Message << Node.getSourceRange();
+        << Node.getSourceRange();
   }
 
   bool isSafeBufferOptOut(const SourceLocation &Loc) const override {
diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/warn-unsafe-buffer-usage-debug-unclaimed.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/warn-unsafe-buffer-usage-debug-unclaimed.cpp
index 32a1adcc5692a..8b1b90cfefe0a 100644
--- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/warn-unsafe-buffer-usage-debug-unclaimed.cpp
+++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/warn-unsafe-buffer-usage-debug-unclaimed.cpp
@@ -42,7 +42,7 @@ template <class T> class unique_ptr {
 
 void basic_unique_ptr() {
   std::unique_ptr<int[]> p1;
-  p1[0];  // expected-warning{{direct access using operator[] on std::unique_ptr<T[]> is unsafe due to lack of bounds checking}}
+  p1[1];  // expected-warning{{direct access using operator[] on std::unique_ptr<T[]> is unsafe due to lack of bounds checking}}
 }
 
 // CHECK: Root # 1
diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-unique-ptr.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-unique-ptr.cpp
new file mode 100644
index 0000000000000..b5a779cafb757
--- /dev/null
+++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-unique-ptr.cpp
@@ -0,0 +1,43 @@
+// RUN: %clang_cc1 -Wno-unused-value -Wunsafe-buffer-usage -fsafe-buffer-usage-suggestions -std=c++20 -verify=expected %s
+
+// This debugging facility is only available in debug builds.
+//
+// REQUIRES: asserts
+
+namespace std {
+inline namespace __1 {
+template <class T> class unique_ptr {
+public:
+  T &operator[](long long i) const;
+};
+} // namespace __1
+} // namespace std
+
+void basic_unique_ptr() {
+  std::unique_ptr<int[]> p1;
+  int i = 2;
+
+  p1[0];  // This is allowed
+
+  p1[1];  // expected-warning{{direct access using operator[] on std::unique_ptr<T[]> is unsafe due to lack of bounds checking}}
+
+  p1[i];  // expected-warning{{direct access using operator[] on std::unique_ptr<T[]> is unsafe due to lack of bounds checking}}
+
+  p1[i + 5]; // expected-warning{{direct access using operator[] on std::unique_ptr<T[]> is unsafe due to lack of bounds checking}}
+}
+
+// CHECK: Root # 1
+// CHECK: |- DeclRefExpr # 4
+// CHECK: |-- UnaryOperator(++) # 1
+// CHECK: |--- CompoundStmt # 1
+// CHECK: |-- ImplicitCastExpr(LValueToRValue) # 1
+// CHECK: |--- BinaryOperator(+) # 1
+// CHECK: |---- ParenExpr # 1
+// CHECK: |----- BinaryOperator(+) # 1
+// CHECK: |------ ParenExpr # 1
+// CHECK: |------- UnaryOperator(*) # 1
+// CHECK: |-------- CompoundStmt # 1
+// CHECK: |-- BinaryOperator(-=) # 1
+// CHECK: |--- CompoundStmt # 1
+// CHECK: |-- UnaryOperator(--) # 1
+// CHECK: |--- CompoundStmt # 1

>From 7cefe30f91e3bb85e6f777972311425f3b2a0298 Mon Sep 17 00:00:00 2001
From: Shreya Jain <jainshreya at google.com>
Date: Thu, 18 Sep 2025 20:43:41 +0000
Subject: [PATCH 11/13] [-Wunsafe-buffer-usage] Add unique_ptr <T[]> accesses

---
 .../warn-unsafe-buffer-usage-debug-unclaimed.cpp   | 14 --------------
 1 file changed, 14 deletions(-)

diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/warn-unsafe-buffer-usage-debug-unclaimed.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/warn-unsafe-buffer-usage-debug-unclaimed.cpp
index 8b1b90cfefe0a..64dede2568df1 100644
--- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/warn-unsafe-buffer-usage-debug-unclaimed.cpp
+++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-debug-unclaimed/warn-unsafe-buffer-usage-debug-unclaimed.cpp
@@ -31,20 +31,6 @@ void test_unclaimed_use(int *p) { // expected-warning{{'p' is an unsafe pointer
   p[5] = 5;       // expected-note{{used in buffer access here}}
 }
 
-namespace std {
-inline namespace __1 {
-template <class T> class unique_ptr {
-public:
-  T &operator[](long long i) const;
-};
-} // namespace __1
-} // namespace std
-
-void basic_unique_ptr() {
-  std::unique_ptr<int[]> p1;
-  p1[1];  // expected-warning{{direct access using operator[] on std::unique_ptr<T[]> is unsafe due to lack of bounds checking}}
-}
-
 // CHECK: Root # 1
 // CHECK: |- DeclRefExpr # 4
 // CHECK: |-- UnaryOperator(++) # 1

>From 6cf2c9fbe095c8f9ad130a65154569f22fc845b9 Mon Sep 17 00:00:00 2001
From: Shreya Jain <jainshreya at google.com>
Date: Thu, 18 Sep 2025 22:23:31 +0000
Subject: [PATCH 12/13] [-Wunsafe-buffer-usage] Add unique_ptr <T[]> accesses

---
 clang/lib/Analysis/UnsafeBufferUsage.cpp      |  5 ++---
 .../warn-unsafe-buffer-usage-unique-ptr.cpp   | 19 +++++++++++++++++++
 2 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index bbdcce342390b..69781faa37e33 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -1384,12 +1384,11 @@ class UniquePtrArrayAccessGadget : public WarningGadget {
       return false;
 
     const Expr *IndexExpr = OpCall->getArg(1);
-    llvm::APSInt IndexValue;
+    clang::Expr::EvalResult Eval;
 
     // Allow [0]
-    if (IndexExpr->EvaluateAsInt(IndexValue, Ctx) && IndexValue.isZero()) {
+    if (IndexExpr->EvaluateAsInt(Eval, Ctx) && Eval.Val.getInt().isZero())
       return false;
-    }
 
     Result.addNode(AccessorTag, DynTypedNode::create(*OpCall));
     return true;
diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-unique-ptr.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-unique-ptr.cpp
index b5a779cafb757..9e407034dc26c 100644
--- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-unique-ptr.cpp
+++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-unique-ptr.cpp
@@ -13,17 +13,36 @@ template <class T> class unique_ptr {
 } // namespace __1
 } // namespace std
 
+int get_index() {
+  return 4;
+}
+
 void basic_unique_ptr() {
   std::unique_ptr<int[]> p1;
   int i = 2;
+  const int j = 3;
+  int k = 0;
 
   p1[0];  // This is allowed
 
+  p1[k];  // expected-warning{{direct access using operator[] on std::unique_ptr<T[]> is unsafe due to lack of bounds checking}}
+
   p1[1];  // expected-warning{{direct access using operator[] on std::unique_ptr<T[]> is unsafe due to lack of bounds checking}}
 
+  p1[1L];  // expected-warning{{direct access using operator[] on std::unique_ptr<T[]> is unsafe due to lack of bounds checking}}
+
+  p1[1LL];  // expected-warning{{direct access using operator[] on std::unique_ptr<T[]> is unsafe due to lack of bounds checking}}
+
+  p1[3 * 5];  // expected-warning{{direct access using operator[] on std::unique_ptr<T[]> is unsafe due to lack of bounds checking}}
+
   p1[i];  // expected-warning{{direct access using operator[] on std::unique_ptr<T[]> is unsafe due to lack of bounds checking}}
 
+  p1[j];  // expected-warning{{direct access using operator[] on std::unique_ptr<T[]> is unsafe due to lack of bounds checking}}
+
   p1[i + 5]; // expected-warning{{direct access using operator[] on std::unique_ptr<T[]> is unsafe due to lack of bounds checking}}
+
+  p1[get_index()];  // expected-warning{{direct access using operator[] on std::unique_ptr<T[]> is unsafe due to lack of bounds checking}}
+
 }
 
 // CHECK: Root # 1

>From f805fafb6d7acc31bff249abfcc849ccf4322414 Mon Sep 17 00:00:00 2001
From: Shreya Jain <jainshreya at google.com>
Date: Tue, 30 Sep 2025 06:32:53 +0000
Subject: [PATCH 13/13] [-Wunsafe-buffer-usage] Add unique_ptr <T[]> accesses

---
 .../warn-unsafe-buffer-usage-unique-ptr.cpp   | 19 -------------------
 1 file changed, 19 deletions(-)

diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-unique-ptr.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-unique-ptr.cpp
index 9e407034dc26c..789d8a2e0bb59 100644
--- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-unique-ptr.cpp
+++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-unique-ptr.cpp
@@ -1,9 +1,5 @@
 // RUN: %clang_cc1 -Wno-unused-value -Wunsafe-buffer-usage -fsafe-buffer-usage-suggestions -std=c++20 -verify=expected %s
 
-// This debugging facility is only available in debug builds.
-//
-// REQUIRES: asserts
-
 namespace std {
 inline namespace __1 {
 template <class T> class unique_ptr {
@@ -45,18 +41,3 @@ void basic_unique_ptr() {
 
 }
 
-// CHECK: Root # 1
-// CHECK: |- DeclRefExpr # 4
-// CHECK: |-- UnaryOperator(++) # 1
-// CHECK: |--- CompoundStmt # 1
-// CHECK: |-- ImplicitCastExpr(LValueToRValue) # 1
-// CHECK: |--- BinaryOperator(+) # 1
-// CHECK: |---- ParenExpr # 1
-// CHECK: |----- BinaryOperator(+) # 1
-// CHECK: |------ ParenExpr # 1
-// CHECK: |------- UnaryOperator(*) # 1
-// CHECK: |-------- CompoundStmt # 1
-// CHECK: |-- BinaryOperator(-=) # 1
-// CHECK: |--- CompoundStmt # 1
-// CHECK: |-- UnaryOperator(--) # 1
-// CHECK: |--- CompoundStmt # 1



More information about the cfe-commits mailing list