From b0bab14b8b5305ddcb4e8b4d8a0e64004fc5425e Mon Sep 17 00:00:00 2001 From: =?utf8?q?Bal=C3=A1zs=20K=C3=A9ri?= Date: Wed, 31 May 2023 08:54:40 +0200 Subject: [PATCH] [clang-tidy] Add check bugprone-unique-ptr-array-mismatch. Reviewed By: PiotrZSL Differential Revision: https://reviews.llvm.org/D151431 --- .../clang-tidy/bugprone/BugproneTidyModule.cpp | 3 + .../clang-tidy/bugprone/CMakeLists.txt | 1 + .../bugprone/UniquePtrArrayMismatchCheck.cpp | 33 ++++++ .../bugprone/UniquePtrArrayMismatchCheck.h | 34 ++++++ clang-tools-extra/docs/ReleaseNotes.rst | 6 + .../checks/bugprone/unique-ptr-array-mismatch.rst | 39 +++++++ clang-tools-extra/docs/clang-tidy/checks/list.rst | 1 + .../bugprone/unique-ptr-array-mismatch.cpp | 127 +++++++++++++++++++++ 8 files changed, 244 insertions(+) create mode 100644 clang-tools-extra/clang-tidy/bugprone/UniquePtrArrayMismatchCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/bugprone/UniquePtrArrayMismatchCheck.h create mode 100644 clang-tools-extra/docs/clang-tidy/checks/bugprone/unique-ptr-array-mismatch.rst create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/unique-ptr-array-mismatch.cpp diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp index 5e9c7d0..e62e536 100644 --- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp @@ -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( "bugprone-unhandled-exception-at-new"); + CheckFactories.registerCheck( + "bugprone-unique-ptr-array-mismatch"); CheckFactories.registerCheck( "bugprone-unsafe-functions"); CheckFactories.registerCheck("bugprone-unused-raii"); diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt index e70d1b4..363d1a8 100644 --- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt @@ -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 index 0000000..8d09b4b --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/UniquePtrArrayMismatchCheck.cpp @@ -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 index 0000000..fb7531a --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/UniquePtrArrayMismatchCheck.h @@ -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 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 diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index b336cd2..5e3c46c 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -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 + ` check. + + Finds initializations of C++ unique pointers to non-array type that are + initialized with an array. + - New :doc:`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 index 0000000..c003859 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/unique-ptr-array-mismatch.rst @@ -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`` 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`` 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`` to ``unique_ptr`` if it +is used at a single variable declaration (one variable in one statement). + +Example: + +.. code-block:: c++ + + std::unique_ptr x(new Foo[10]); // -> std::unique_ptr x(new Foo[10]); + // ^ warning: unique pointer to non-array is initialized with array + std::unique_ptr x1(new Foo), x2(new Foo[10]); // no replacement + // ^ warning: unique pointer to non-array is initialized with array + + D d; + std::unique_ptr x3(new Foo[10], d); // no warning (custom deleter used) + + struct S { + std::unique_ptr 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 +`_ +However, only the ``std::unique_ptr`` case is detected by this check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 055e6ae..a13f841 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -139,6 +139,7 @@ Clang-Tidy Checks `bugprone-undelegated-constructor `_, `bugprone-unhandled-exception-at-new `_, `bugprone-unhandled-self-assignment `_, + `bugprone-unique-ptr-array-mismatch `_, "Yes" `bugprone-unsafe-functions `_, `bugprone-unused-raii `_, "Yes" `bugprone-unused-return-value `_, 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 index 0000000..494e83d --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unique-ptr-array-mismatch.cpp @@ -0,0 +1,127 @@ +// RUN: %check_clang_tidy %s bugprone-unique-ptr-array-mismatch %t + +namespace std { + +template struct default_delete {}; +template struct default_delete {}; + +template> +class unique_ptr { +public: + explicit unique_ptr(T* p) noexcept; + unique_ptr(T* p, Deleter d1 ) noexcept; +}; + +template +class unique_ptr { +public: + template + explicit unique_ptr(U p) noexcept; + template + unique_ptr(U p, Deleter d1) noexcept; +}; + +} // namespace std + +struct A {}; + +using PtrT = std::unique_ptr; +using PtrTArr = std::unique_ptr; + +void f1() { + std::unique_ptr P1{new int}; + std::unique_ptr 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 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 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 P4(new int[10]); + new std::unique_ptr(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 P5(new int[10]); + + A deleter; + std::unique_ptr P6(new int[10], deleter); + std::unique_ptr P7(new int[10]); + std::default_delete def_del; + std::unique_ptr> 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 P1(new A); + std::unique_ptr 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 P2(new A[10]); + std::unique_ptr P3(new A[10]); +} + +void f3() { + std::unique_ptr 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 P1; + std::unique_ptr 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 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); + +void f4() { + f_parm(std::unique_ptr{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 f_ret() { + return std::unique_ptr(new int[10]); + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: unique pointer to non-array is initialized with array [bugprone-unique-ptr-array-mismatch] +} + +template +void f_tmpl() { + std::unique_ptr 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 P1{new T[10]}; +} + +void f5() { + f_tmpl(); +} + +template +void f_tmpl_1() { + std::unique_ptr 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 P1{new T[10]}; +} + +#define CHAR_PTR_TYPE std::unique_ptr +#define CHAR_PTR_VAR(X) \ + X { new char[10] } +#define CHAR_PTR_INIT(X, Y) \ + std::unique_ptr 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_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_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] +} -- 2.7.4