[clang-tools-extra] 315946c - [clang-tidy] Added bugprone-multi-level-implicit-pointer-conversion check

Piotr Zegar via cfe-commits cfe-commits at lists.llvm.org
Thu Jul 27 08:50:52 PDT 2023


Author: Piotr Zegar
Date: 2023-07-27T15:49:43Z
New Revision: 315946c57da2bfff0c26e4fec42e21dac7bfbddc

URL: https://github.com/llvm/llvm-project/commit/315946c57da2bfff0c26e4fec42e21dac7bfbddc
DIFF: https://github.com/llvm/llvm-project/commit/315946c57da2bfff0c26e4fec42e21dac7bfbddc.diff

LOG: [clang-tidy] Added bugprone-multi-level-implicit-pointer-conversion check

Detects implicit conversions between pointers of different levels of
indirection.

Reviewed By: xgupta

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

Added: 
    clang-tools-extra/clang-tidy/bugprone/MultiLevelImplicitPointerConversionCheck.cpp
    clang-tools-extra/clang-tidy/bugprone/MultiLevelImplicitPointerConversionCheck.h
    clang-tools-extra/docs/clang-tidy/checks/bugprone/multi-level-implicit-pointer-conversion.rst
    clang-tools-extra/test/clang-tidy/checkers/bugprone/multi-level-implicit-pointer-conversion.cpp

Modified: 
    clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
    clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
    clang-tools-extra/docs/ReleaseNotes.rst
    clang-tools-extra/docs/clang-tidy/checks/list.rst

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 0cb0924d445e5e..ceb966c655d3a8 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -37,6 +37,7 @@
 #include "MisplacedPointerArithmeticInAllocCheck.h"
 #include "MisplacedWideningCastCheck.h"
 #include "MoveForwardingReferenceCheck.h"
+#include "MultiLevelImplicitPointerConversionCheck.h"
 #include "MultipleNewInOneExpressionCheck.h"
 #include "MultipleStatementMacroCheck.h"
 #include "NoEscapeCheck.h"
@@ -140,6 +141,8 @@ class BugproneModule : public ClangTidyModule {
         "bugprone-misplaced-widening-cast");
     CheckFactories.registerCheck<MoveForwardingReferenceCheck>(
         "bugprone-move-forwarding-reference");
+    CheckFactories.registerCheck<MultiLevelImplicitPointerConversionCheck>(
+        "bugprone-multi-level-implicit-pointer-conversion");
     CheckFactories.registerCheck<MultipleNewInOneExpressionCheck>(
         "bugprone-multiple-new-in-one-expression");
     CheckFactories.registerCheck<MultipleStatementMacroCheck>(

diff  --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index 4076e0d253584b..4fa32aa61a4b09 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -33,6 +33,7 @@ add_clang_library(clangTidyBugproneModule
   MisplacedPointerArithmeticInAllocCheck.cpp
   MisplacedWideningCastCheck.cpp
   MoveForwardingReferenceCheck.cpp
+  MultiLevelImplicitPointerConversionCheck.cpp
   MultipleNewInOneExpressionCheck.cpp
   MultipleStatementMacroCheck.cpp
   NoEscapeCheck.cpp

diff  --git a/clang-tools-extra/clang-tidy/bugprone/MultiLevelImplicitPointerConversionCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/MultiLevelImplicitPointerConversionCheck.cpp
new file mode 100644
index 00000000000000..4dd3cb57e6dd12
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/MultiLevelImplicitPointerConversionCheck.cpp
@@ -0,0 +1,78 @@
+//===--- MultiLevelImplicitPointerConversionCheck.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 "MultiLevelImplicitPointerConversionCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+static unsigned getPointerLevel(const QualType &PtrType) {
+  if (!PtrType->isPointerType())
+    return 0U;
+
+  return 1U + getPointerLevel(PtrType->castAs<PointerType>()->getPointeeType());
+}
+
+namespace {
+
+AST_MATCHER(ImplicitCastExpr, isMultiLevelPointerConversion) {
+  const QualType TargetType = Node.getType()
+                                  .getCanonicalType()
+                                  .getNonReferenceType()
+                                  .getUnqualifiedType();
+  const QualType SourceType = Node.getSubExpr()
+                                  ->getType()
+                                  .getCanonicalType()
+                                  .getNonReferenceType()
+                                  .getUnqualifiedType();
+
+  if (TargetType == SourceType)
+    return false;
+
+  const unsigned TargetPtrLevel = getPointerLevel(TargetType);
+  if (0U == TargetPtrLevel)
+    return false;
+
+  const unsigned SourcePtrLevel = getPointerLevel(SourceType);
+  if (0U == SourcePtrLevel)
+    return false;
+
+  return SourcePtrLevel != TargetPtrLevel;
+}
+
+} // namespace
+
+void MultiLevelImplicitPointerConversionCheck::registerMatchers(
+    MatchFinder *Finder) {
+  Finder->addMatcher(
+      implicitCastExpr(hasCastKind(CK_BitCast), isMultiLevelPointerConversion())
+          .bind("expr"),
+      this);
+}
+
+std::optional<TraversalKind>
+MultiLevelImplicitPointerConversionCheck::getCheckTraversalKind() const {
+  return TK_AsIs;
+}
+
+void MultiLevelImplicitPointerConversionCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *MatchedExpr = Result.Nodes.getNodeAs<ImplicitCastExpr>("expr");
+  QualType Target = MatchedExpr->getType().getDesugaredType(*Result.Context);
+  QualType Source =
+      MatchedExpr->getSubExpr()->getType().getDesugaredType(*Result.Context);
+
+  diag(MatchedExpr->getExprLoc(), "multilevel pointer conversion from %0 to "
+                                  "%1, please use explicit cast")
+      << Source << Target;
+}
+
+} // namespace clang::tidy::bugprone

diff  --git a/clang-tools-extra/clang-tidy/bugprone/MultiLevelImplicitPointerConversionCheck.h b/clang-tools-extra/clang-tidy/bugprone/MultiLevelImplicitPointerConversionCheck.h
new file mode 100644
index 00000000000000..13228145ff35fa
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/MultiLevelImplicitPointerConversionCheck.h
@@ -0,0 +1,33 @@
+//===--- MultiLevelImplicitPointerConversionCheck.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_MULTILEVELIMPLICITPOINTERCONVERSIONCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_MULTILEVELIMPLICITPOINTERCONVERSIONCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::bugprone {
+
+/// Detects implicit conversions between pointers of 
diff erent levels of
+/// indirection.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/multi-level-implicit-pointer-conversion.html
+class MultiLevelImplicitPointerConversionCheck : public ClangTidyCheck {
+public:
+  MultiLevelImplicitPointerConversionCheck(StringRef Name,
+                                           ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  std::optional<TraversalKind> getCheckTraversalKind() const override;
+};
+
+} // namespace clang::tidy::bugprone
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_MULTILEVELIMPLICITPOINTERCONVERSIONCHECK_H

diff  --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index d22287d4effe49..4441473774b2c8 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -114,6 +114,12 @@ Improvements to clang-tidy
 New checks
 ^^^^^^^^^^
 
+- New :doc:`bugprone-multi-level-implicit-pointer-conversion
+  <clang-tidy/checks/bugprone/multi-level-implicit-pointer-conversion>` check.
+
+  Detects implicit conversions between pointers of 
diff erent levels of
+  indirection.
+
 - New :doc:`performance-enum-size
   <clang-tidy/checks/performance/enum-size>` check.
 

diff  --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/multi-level-implicit-pointer-conversion.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/multi-level-implicit-pointer-conversion.rst
new file mode 100644
index 00000000000000..e6dc5c13aa0250
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/multi-level-implicit-pointer-conversion.rst
@@ -0,0 +1,44 @@
+.. title:: clang-tidy - bugprone-multi-level-implicit-pointer-conversion
+
+bugprone-multi-level-implicit-pointer-conversion
+================================================
+
+Detects implicit conversions between pointers of 
diff erent levels of
+indirection.
+
+Conversions between pointer types of 
diff erent levels of indirection can be
+dangerous and may lead to undefined behavior, particularly if the converted
+pointer is later cast to a type with a 
diff erent level of indirection.
+For example, converting a pointer to a pointer to an ``int`` (``int**``) to
+a ``void*`` can result in the loss of information about the original level of
+indirection, which can cause problems when attempting to use the converted
+pointer. If the converted pointer is later cast to a type with a 
diff erent
+level of indirection and dereferenced, it may lead to access violations,
+memory corruption, or other undefined behavior.
+
+Consider the following example:
+
+.. code-block:: c++
+
+  void foo(void* ptr);
+
+  int main() {
+    int x = 42;
+    int* ptr = &x;
+    int** ptr_ptr = &ptr;
+    foo(ptr_ptr); // warning will trigger here
+    return 0;
+  }
+
+In this example, ``foo()`` is called with ``ptr_ptr`` as its argument. However,
+``ptr_ptr`` is a ``int**`` pointer, while ``foo()`` expects a ``void*`` pointer.
+This results in an implicit pointer level conversion, which could cause issues
+if ``foo()`` dereferences the pointer assuming it's a ``int*`` pointer.
+
+Using an explicit cast is a recommended solution to prevent issues caused by
+implicit pointer level conversion, as it allows the developer to explicitly
+state their intention and show their reasoning for the type conversion.
+Additionally, it is recommended that developers thoroughly check and verify the
+safety of the conversion before using an explicit cast. This extra level of
+caution can help catch potential issues early on in the development process,
+improving the overall reliability and maintainability of the code.

diff  --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 1804346da020e2..73aad3c0315ab0 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -103,6 +103,7 @@ Clang-Tidy Checks
    `bugprone-misplaced-pointer-arithmetic-in-alloc <bugprone/misplaced-pointer-arithmetic-in-alloc.html>`_, "Yes"
    `bugprone-misplaced-widening-cast <bugprone/misplaced-widening-cast.html>`_,
    `bugprone-move-forwarding-reference <bugprone/move-forwarding-reference.html>`_, "Yes"
+   `bugprone-multi-level-implicit-pointer-conversion <bugprone/multi-level-implicit-pointer-conversion.html>`_,
    `bugprone-multiple-new-in-one-expression <bugprone/multiple-new-in-one-expression.html>`_,
    `bugprone-multiple-statement-macro <bugprone/multiple-statement-macro.html>`_,
    `bugprone-no-escape <bugprone/no-escape.html>`_,

diff  --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/multi-level-implicit-pointer-conversion.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/multi-level-implicit-pointer-conversion.cpp
new file mode 100644
index 00000000000000..7a56242e4202d6
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/multi-level-implicit-pointer-conversion.cpp
@@ -0,0 +1,65 @@
+// RUN: %check_clang_tidy %s bugprone-multi-level-implicit-pointer-conversion %t
+
+using OneStar = void*;
+using OneStarFancy = OneStar;
+
+void takeFirstLevelVoidPtr(OneStar message);
+void takeFirstLevelConstVoidPtr(const OneStarFancy message);
+void takeFirstLevelConstVoidPtrConst(const void* const message);
+void takeSecondLevelVoidPtr(void** message);
+
+void** getSecondLevelVoidPtr();
+void* getFirstLevelVoidPtr();
+int** getSecondLevelIntPtr();
+int* getFirstLevelIntPtr();
+
+int table[5];
+
+void test()
+{
+  void** secondLevelVoidPtr;
+  int* firstLevelIntPtr;
+
+  // CHECK-MESSAGES: :[[@LINE+1]]:13: warning: multilevel pointer conversion from 'void **' to 'void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]
+  void* a = getSecondLevelVoidPtr();
+
+  void** b = getSecondLevelVoidPtr();
+  void* c = getFirstLevelVoidPtr();
+
+  // CHECK-MESSAGES: :[[@LINE+1]]:13: warning: multilevel pointer conversion from 'int **' to 'void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]
+  void* d = getSecondLevelIntPtr();
+
+  takeFirstLevelVoidPtr(&table);
+
+  takeFirstLevelVoidPtr(firstLevelIntPtr);
+
+  takeFirstLevelVoidPtr(getFirstLevelIntPtr());
+
+  // CHECK-MESSAGES: :[[@LINE+1]]:25: warning: multilevel pointer conversion from 'void **' to 'void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]
+  takeFirstLevelVoidPtr(secondLevelVoidPtr);
+
+  // CHECK-MESSAGES: :[[@LINE+1]]:30: warning: multilevel pointer conversion from 'void **' to 'void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]
+  takeFirstLevelConstVoidPtr(secondLevelVoidPtr);
+
+  // CHECK-MESSAGES: :[[@LINE+1]]:35: warning: multilevel pointer conversion from 'void **' to 'const void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]
+  takeFirstLevelConstVoidPtrConst(secondLevelVoidPtr);
+
+  // CHECK-MESSAGES: :[[@LINE+1]]:35: warning: multilevel pointer conversion from 'void ***' to 'const void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]
+  takeFirstLevelConstVoidPtrConst(&secondLevelVoidPtr);
+
+  takeSecondLevelVoidPtr(secondLevelVoidPtr);
+
+  // CHECK-MESSAGES: :[[@LINE+1]]:25: warning: multilevel pointer conversion from 'void **' to 'void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]
+  takeFirstLevelVoidPtr(getSecondLevelVoidPtr());
+
+  // CHECK-MESSAGES: :[[@LINE+1]]:30: warning: multilevel pointer conversion from 'void **' to 'void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]
+  takeFirstLevelConstVoidPtr(getSecondLevelVoidPtr());
+
+  // CHECK-MESSAGES: :[[@LINE+1]]:35: warning: multilevel pointer conversion from 'void **' to 'const void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]
+  takeFirstLevelConstVoidPtrConst(getSecondLevelVoidPtr());
+
+  // CHECK-MESSAGES: :[[@LINE+1]]:25: warning: multilevel pointer conversion from 'int **' to 'void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]
+  takeFirstLevelVoidPtr(getSecondLevelIntPtr());
+
+  takeSecondLevelVoidPtr(getSecondLevelVoidPtr());
+}


        


More information about the cfe-commits mailing list