[clang-tidy] Add check bugprone-unique-ptr-array-mismatch.
authorBalázs Kéri <balazs.keri@ericsson.com>
Wed, 31 May 2023 06:54:40 +0000 (08:54 +0200)
committerBalázs Kéri <balazs.keri@ericsson.com>
Wed, 31 May 2023 07:55:01 +0000 (09:55 +0200)
Reviewed By: PiotrZSL

Differential Revision: https://reviews.llvm.org/D151431

clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
clang-tools-extra/clang-tidy/bugprone/UniquePtrArrayMismatchCheck.cpp [new file with mode: 0644]
clang-tools-extra/clang-tidy/bugprone/UniquePtrArrayMismatchCheck.h [new file with mode: 0644]
clang-tools-extra/docs/ReleaseNotes.rst
clang-tools-extra/docs/clang-tidy/checks/bugprone/unique-ptr-array-mismatch.rst [new file with mode: 0644]
clang-tools-extra/docs/clang-tidy/checks/list.rst
clang-tools-extra/test/clang-tidy/checkers/bugprone/unique-ptr-array-mismatch.cpp [new file with mode: 0644]

index 5e9c7d0..e62e536 100644 (file)
@@ -73,6 +73,7 @@
 #include "UndelegatedConstructorCheck.h"
 #include "UnhandledExceptionAtNewCheck.h"
 #include "UnhandledSelfAssignmentCheck.h"
+#include "UniquePtrArrayMismatchCheck.h"
 #include "UnsafeFunctionsCheck.h"
 #include "UnusedRaiiCheck.h"
 #include "UnusedReturnValueCheck.h"
@@ -207,6 +208,8 @@ public:
         "bugprone-unhandled-self-assignment");
     CheckFactories.registerCheck<UnhandledExceptionAtNewCheck>(
         "bugprone-unhandled-exception-at-new");
+    CheckFactories.registerCheck<UniquePtrArrayMismatchCheck>(
+        "bugprone-unique-ptr-array-mismatch");
     CheckFactories.registerCheck<UnsafeFunctionsCheck>(
         "bugprone-unsafe-functions");
     CheckFactories.registerCheck<UnusedRaiiCheck>("bugprone-unused-raii");
index e70d1b4..363d1a8 100644 (file)
@@ -69,6 +69,7 @@ add_clang_library(clangTidyBugproneModule
   UndelegatedConstructorCheck.cpp
   UnhandledExceptionAtNewCheck.cpp
   UnhandledSelfAssignmentCheck.cpp
+  UniquePtrArrayMismatchCheck.cpp
   UnsafeFunctionsCheck.cpp
   UnusedRaiiCheck.cpp
   UnusedReturnValueCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/UniquePtrArrayMismatchCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UniquePtrArrayMismatchCheck.cpp
new file mode 100644 (file)
index 0000000..8d09b4b
--- /dev/null
@@ -0,0 +1,33 @@
+//===--- UniquePtrArrayMismatchCheck.cpp - clang-tidy ---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "UniquePtrArrayMismatchCheck.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+UniquePtrArrayMismatchCheck::UniquePtrArrayMismatchCheck(
+    StringRef Name, ClangTidyContext *Context)
+    : SmartPtrArrayMismatchCheck(Name, Context, "unique") {}
+
+UniquePtrArrayMismatchCheck::SmartPtrClassMatcher
+UniquePtrArrayMismatchCheck::getSmartPointerClassMatcher() const {
+  auto DeleterDecl = classTemplateSpecializationDecl(
+      hasName("::std::default_delete"), templateArgumentCountIs(1),
+      hasTemplateArgument(0, templateArgument(refersToType(
+                                 qualType(equalsBoundNode(PointerTypeN))))));
+  return classTemplateSpecializationDecl(
+      hasName("::std::unique_ptr"), templateArgumentCountIs(2),
+      hasTemplateArgument(
+          0, templateArgument(refersToType(qualType().bind(PointerTypeN)))),
+      hasTemplateArgument(1, templateArgument(refersToType(
+                                 qualType(hasDeclaration(DeleterDecl))))));
+}
+
+} // namespace clang::tidy::bugprone
diff --git a/clang-tools-extra/clang-tidy/bugprone/UniquePtrArrayMismatchCheck.h b/clang-tools-extra/clang-tidy/bugprone/UniquePtrArrayMismatchCheck.h
new file mode 100644 (file)
index 0000000..fb7531a
--- /dev/null
@@ -0,0 +1,34 @@
+//===--- UniquePtrArrayMismatchCheck.h - clang-tidy -------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNIQUEPTRARRAYMISMATCHCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNIQUEPTRARRAYMISMATCHCHECK_H
+
+#include "SmartPtrArrayMismatchCheck.h"
+
+namespace clang::tidy::bugprone {
+
+/// Finds initializations of C++ unique pointers to non-array type that are
+/// initialized with an array.
+///
+/// Example:
+///
+/// \code
+///   std::unique_ptr<int> PtrArr{new int[10]};
+/// \endcode
+class UniquePtrArrayMismatchCheck : public SmartPtrArrayMismatchCheck {
+public:
+  UniquePtrArrayMismatchCheck(StringRef Name, ClangTidyContext *Context);
+
+protected:
+  SmartPtrClassMatcher getSmartPointerClassMatcher() const override;
+};
+
+} // namespace clang::tidy::bugprone
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNIQUEPTRARRAYMISMATCHCHECK_H
index b336cd2..5e3c46c 100644 (file)
@@ -121,6 +121,12 @@ New checks
   Detect implicit and explicit casts of ``enum`` type into ``bool`` where ``enum`` type
   doesn't have a zero-value enumerator.
 
+- New :doc:`bugprone-unique-ptr-array-mismatch
+  <clang-tidy/checks/bugprone/unique-ptr-array-mismatch>` check.
+
+  Finds initializations of C++ unique pointers to non-array type that are
+  initialized with an array.
+
 - New :doc:`bugprone-unsafe-functions
   <clang-tidy/checks/bugprone/unsafe-functions>` check.
 
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/unique-ptr-array-mismatch.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/unique-ptr-array-mismatch.rst
new file mode 100644 (file)
index 0000000..c003859
--- /dev/null
@@ -0,0 +1,39 @@
+.. title:: clang-tidy - bugprone-unique-ptr-array-mismatch
+
+bugprone-unique-ptr-array-mismatch
+==================================
+
+Finds initializations of C++ unique pointers to non-array type that are
+initialized with an array.
+
+If a pointer ``std::unique_ptr<T>`` is initialized with a new-expression
+``new T[]`` the memory is not deallocated correctly. A plain ``delete`` is used
+in this case to deallocate the target memory. Instead a ``delete[]`` call is
+needed. A ``std::unique_ptr<T[]>`` uses the correct delete operator. The check
+does not emit warning if an ``unique_ptr`` with user-specified deleter type is
+used.
+
+The check offers replacement of ``unique_ptr<T>`` to ``unique_ptr<T[]>`` if it
+is used at a single variable declaration (one variable in one statement).
+
+Example:
+
+.. code-block:: c++
+
+  std::unique_ptr<Foo> x(new Foo[10]); // -> std::unique_ptr<Foo[]> x(new Foo[10]);
+  //                     ^ warning: unique pointer to non-array is initialized with array
+  std::unique_ptr<Foo> x1(new Foo), x2(new Foo[10]); // no replacement
+  //                                   ^ warning: unique pointer to non-array is initialized with array
+
+  D d;
+  std::unique_ptr<Foo, D> x3(new Foo[10], d); // no warning (custom deleter used)
+
+  struct S {
+    std::unique_ptr<Foo> x(new Foo[10]); // no replacement in this case
+    //                     ^ warning: unique pointer to non-array is initialized with array
+  };
+
+This check partially covers the CERT C++ Coding Standard rule
+`MEM51-CPP. Properly deallocate dynamically allocated resources
+<https://wiki.sei.cmu.edu/confluence/display/cplusplus/MEM51-CPP.+Properly+deallocate+dynamically+allocated+resources>`_
+However, only the ``std::unique_ptr`` case is detected by this check.
index 055e6ae..a13f841 100644 (file)
@@ -139,6 +139,7 @@ Clang-Tidy Checks
    `bugprone-undelegated-constructor <bugprone/undelegated-constructor.html>`_,
    `bugprone-unhandled-exception-at-new <bugprone/unhandled-exception-at-new.html>`_,
    `bugprone-unhandled-self-assignment <bugprone/unhandled-self-assignment.html>`_,
+   `bugprone-unique-ptr-array-mismatch <bugprone/unique-ptr-array-mismatch.html>`_, "Yes"
    `bugprone-unsafe-functions <bugprone/unsafe-functions.html>`_,
    `bugprone-unused-raii <bugprone/unused-raii.html>`_, "Yes"
    `bugprone-unused-return-value <bugprone/unused-return-value.html>`_,
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unique-ptr-array-mismatch.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unique-ptr-array-mismatch.cpp
new file mode 100644 (file)
index 0000000..494e83d
--- /dev/null
@@ -0,0 +1,127 @@
+// RUN: %check_clang_tidy %s bugprone-unique-ptr-array-mismatch %t
+
+namespace std {
+
+template<class T> struct default_delete {};
+template<class T> struct default_delete<T[]> {};
+
+template<class T, class Deleter = std::default_delete<T>>
+class unique_ptr {
+public:
+  explicit unique_ptr(T* p) noexcept;
+  unique_ptr(T* p, Deleter d1 ) noexcept;
+};
+
+template <class T, class Deleter>
+class unique_ptr<T[], Deleter> {
+public:
+  template<class U>
+  explicit unique_ptr(U p) noexcept;
+  template<class U>
+  unique_ptr(U p, Deleter d1) noexcept;
+};
+
+} // namespace std
+
+struct A {};
+
+using PtrT = std::unique_ptr<A>;
+using PtrTArr = std::unique_ptr<A[]>;
+
+void f1() {
+  std::unique_ptr<int> P1{new int};
+  std::unique_ptr<int> P2{new int[10]};
+  // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: unique pointer to non-array is initialized with array [bugprone-unique-ptr-array-mismatch]
+  // CHECK-FIXES: std::unique_ptr<int[]> P2{new int[10]};
+  // clang-format off
+  std::unique_ptr<  int  > P3{new int[10]};
+  // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: unique pointer to non-array is initialized with array [bugprone-unique-ptr-array-mismatch]
+  // CHECK-FIXES: std::unique_ptr<  int[]  > P3{new int[10]};
+  // clang-format on
+  std::unique_ptr<int> P4(new int[10]);
+  // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: unique pointer to non-array is initialized with array [bugprone-unique-ptr-array-mismatch]
+  // CHECK-FIXES: std::unique_ptr<int[]> P4(new int[10]);
+  new std::unique_ptr<int>(new int[10]);
+  // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: unique pointer to non-array is initialized with array [bugprone-unique-ptr-array-mismatch]
+  std::unique_ptr<int[]> P5(new int[10]);
+
+  A deleter;
+  std::unique_ptr<int, A> P6(new int[10], deleter);
+  std::unique_ptr<int, A> P7(new int[10]);
+  std::default_delete<int[]> def_del;
+  std::unique_ptr<int, std::default_delete<int[]>> P8(new int[10], def_del);
+
+  new PtrT(new A[10]);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: unique pointer to non-array is initialized with array [bugprone-unique-ptr-array-mismatch]
+  new PtrTArr(new A[10]);
+}
+
+void f2() {
+  std::unique_ptr<A> P1(new A);
+  std::unique_ptr<A> P2(new A[10]);
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: unique pointer to non-array is initialized with array [bugprone-unique-ptr-array-mismatch]
+  // CHECK-FIXES: std::unique_ptr<A[]> P2(new A[10]);
+  std::unique_ptr<A[]> P3(new A[10]);
+}
+
+void f3() {
+  std::unique_ptr<int> P1{new int}, P2{new int[10]}, P3{new int[10]};
+  // CHECK-MESSAGES: :[[@LINE-1]]:40: warning: unique pointer to non-array is initialized with array [bugprone-unique-ptr-array-mismatch]
+  // CHECK-MESSAGES: :[[@LINE-2]]:57: warning: unique pointer to non-array is initialized with array [bugprone-unique-ptr-array-mismatch]
+}
+
+struct S {
+  std::unique_ptr<int> P1;
+  std::unique_ptr<int> P2{new int[10]};
+  // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: unique pointer to non-array is initialized with array [bugprone-unique-ptr-array-mismatch]
+  std::unique_ptr<int> P3{new int}, P4{new int[10]};
+  // CHECK-MESSAGES: :[[@LINE-1]]:40: warning: unique pointer to non-array is initialized with array [bugprone-unique-ptr-array-mismatch]
+  S() : P1{new int[10]} {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: unique pointer to non-array is initialized with array [bugprone-unique-ptr-array-mismatch]
+};
+
+void f_parm(std::unique_ptr<int>);
+
+void f4() {
+  f_parm(std::unique_ptr<int>{new int[10]});
+  // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: unique pointer to non-array is initialized with array [bugprone-unique-ptr-array-mismatch]
+}
+
+std::unique_ptr<int> f_ret() {
+  return std::unique_ptr<int>(new int[10]);
+  // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: unique pointer to non-array is initialized with array [bugprone-unique-ptr-array-mismatch]
+}
+
+template <class T>
+void f_tmpl() {
+  std::unique_ptr<T> P1{new T[10]};
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: unique pointer to non-array is initialized with array [bugprone-unique-ptr-array-mismatch]
+  // CHECK-FIXES: std::unique_ptr<T[]> P1{new T[10]};
+}
+
+void f5() {
+  f_tmpl<char>();
+}
+
+template <class T>
+void f_tmpl_1() {
+  std::unique_ptr<T> P1{new T[10]};
+  // FIXME_CHECK-MESSAGES: :[[@LINE-1]]:25: warning: unique pointer to non-array is initialized with array [bugprone-unique-ptr-array-mismatch]
+  // FIXME_CHECK-FIXES: std::unique_ptr<T[]> P1{new T[10]};
+}
+
+#define CHAR_PTR_TYPE std::unique_ptr<char>
+#define CHAR_PTR_VAR(X) \
+  X { new char[10] }
+#define CHAR_PTR_INIT(X, Y) \
+  std::unique_ptr<char> X { Y }
+
+void f6() {
+  CHAR_PTR_TYPE P1{new char[10]};
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: unique pointer to non-array is initialized with array [bugprone-unique-ptr-array-mismatch]
+  std::unique_ptr<char> CHAR_PTR_VAR(P2);
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: unique pointer to non-array is initialized with array [bugprone-unique-ptr-array-mismatch]
+  // CHECK-FIXES: std::unique_ptr<char[]> CHAR_PTR_VAR(P2);
+  CHAR_PTR_INIT(P3, new char[10]);
+  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: unique pointer to non-array is initialized with array [bugprone-unique-ptr-array-mismatch]
+}