[clang-tools-extra] 9a5c6f1 - [clang-tidy]Add new check bugprone-casting-through-void (#69465)

via cfe-commits cfe-commits at lists.llvm.org
Fri Oct 20 07:29:11 PDT 2023


Author: Congcong Cai
Date: 2023-10-20T09:29:06-05:00
New Revision: 9a5c6f1760a353fdb4b61ec4ed5b28f4638cc77b

URL: https://github.com/llvm/llvm-project/commit/9a5c6f1760a353fdb4b61ec4ed5b28f4638cc77b
DIFF: https://github.com/llvm/llvm-project/commit/9a5c6f1760a353fdb4b61ec4ed5b28f4638cc77b.diff

LOG: [clang-tidy]Add new check bugprone-casting-through-void (#69465)

This check detects usage of ``static_cast`` pointer to the other pointer
throght `static_cast` to `void *` in C++ code.
Fixes: #68532

Added: 
    clang-tools-extra/clang-tidy/bugprone/CastingThroughVoidCheck.cpp
    clang-tools-extra/clang-tidy/bugprone/CastingThroughVoidCheck.h
    clang-tools-extra/docs/clang-tidy/checks/bugprone/casting-through-void.rst
    clang-tools-extra/test/clang-tidy/checkers/bugprone/casting-through-void.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 543c522899d7a52..7a910037368c832 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -16,6 +16,7 @@
 #include "BadSignalToKillThreadCheck.h"
 #include "BoolPointerImplicitConversionCheck.h"
 #include "BranchCloneCheck.h"
+#include "CastingThroughVoidCheck.h"
 #include "ComparePointerToMemberVirtualFunctionCheck.h"
 #include "CopyConstructorInitCheck.h"
 #include "DanglingHandleCheck.h"
@@ -104,6 +105,8 @@ class BugproneModule : public ClangTidyModule {
     CheckFactories.registerCheck<BoolPointerImplicitConversionCheck>(
         "bugprone-bool-pointer-implicit-conversion");
     CheckFactories.registerCheck<BranchCloneCheck>("bugprone-branch-clone");
+    CheckFactories.registerCheck<CastingThroughVoidCheck>(
+        "bugprone-casting-through-void");
     CheckFactories.registerCheck<ComparePointerToMemberVirtualFunctionCheck>(
         "bugprone-compare-pointer-to-member-virtual-function");
     CheckFactories.registerCheck<CopyConstructorInitCheck>(

diff  --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index 0df9e439b715e5a..d443fd8d1452f16 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -11,6 +11,7 @@ add_clang_library(clangTidyBugproneModule
   BoolPointerImplicitConversionCheck.cpp
   BranchCloneCheck.cpp
   BugproneTidyModule.cpp
+  CastingThroughVoidCheck.cpp
   ComparePointerToMemberVirtualFunctionCheck.cpp
   CopyConstructorInitCheck.cpp
   DanglingHandleCheck.cpp

diff  --git a/clang-tools-extra/clang-tidy/bugprone/CastingThroughVoidCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/CastingThroughVoidCheck.cpp
new file mode 100644
index 000000000000000..4c2416a89aef9b7
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/CastingThroughVoidCheck.cpp
@@ -0,0 +1,45 @@
+//===--- CastingThroughVoidCheck.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 "CastingThroughVoidCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/Type.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "llvm/ADT/StringSet.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+void CastingThroughVoidCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(
+      explicitCastExpr(
+          hasDestinationType(
+              qualType(unless(hasCanonicalType(pointsTo(voidType()))))
+                  .bind("target_type")),
+          hasSourceExpression(
+              explicitCastExpr(
+                  hasSourceExpression(
+                      expr(hasType(qualType().bind("source_type")))),
+                  hasDestinationType(
+                      qualType(pointsTo(voidType())).bind("void_type")))
+                  .bind("cast"))),
+      this);
+}
+
+void CastingThroughVoidCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto TT = *Result.Nodes.getNodeAs<QualType>("target_type");
+  const auto ST = *Result.Nodes.getNodeAs<QualType>("source_type");
+  const auto VT = *Result.Nodes.getNodeAs<QualType>("void_type");
+  const auto *CE = Result.Nodes.getNodeAs<ExplicitCastExpr>("cast");
+  diag(CE->getExprLoc(), "do not cast %0 to %1 through %2") << ST << TT << VT;
+}
+
+} // namespace clang::tidy::bugprone

diff  --git a/clang-tools-extra/clang-tidy/bugprone/CastingThroughVoidCheck.h b/clang-tools-extra/clang-tidy/bugprone/CastingThroughVoidCheck.h
new file mode 100644
index 000000000000000..834676aaf054383
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/CastingThroughVoidCheck.h
@@ -0,0 +1,32 @@
+//===--- CastingThroughVoidCheck.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_CASTINGTHROUGHVOIDCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_CASTINGTHROUGHVOIDCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::bugprone {
+
+/// Detects unsafe or redundant two-step casting operations involving ``void*``.
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/casting-through-void.html
+class CastingThroughVoidCheck : public ClangTidyCheck {
+public:
+  CastingThroughVoidCheck(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 {
+    return TK_IgnoreUnlessSpelledInSource;
+  }
+};
+
+} // namespace clang::tidy::bugprone
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_CASTINGTHROUGHVOIDCHECK_H

diff  --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 366b3abbe1244bf..8ebb7e9e66207bd 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -130,6 +130,11 @@ Improvements to clang-tidy
 New checks
 ^^^^^^^^^^
 
+- New :doc:`bugprone-casting-through-void
+  <clang-tidy/checks/bugprone/casting-through-void>` check.
+
+  Detects unsafe or redundant two-step casting operations involving ``void*``.
+
 - New :doc:`bugprone-compare-pointer-to-member-virtual-function
   <clang-tidy/checks/bugprone/compare-pointer-to-member-virtual-function>` check.
 

diff  --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/casting-through-void.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/casting-through-void.rst
new file mode 100644
index 000000000000000..a9ab478b9a82e13
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/casting-through-void.rst
@@ -0,0 +1,31 @@
+.. title:: clang-tidy - bugprone-casting-through-void
+
+bugprone-casting-through-void
+=============================
+
+Detects unsafe or redundant two-step casting operations involving ``void*``.
+
+Two-step type conversions via ``void*`` are discouraged for several reasons.
+
+- They obscure code and impede its understandability, complicating maintenance.
+- These conversions bypass valuable compiler support, erasing warnings related
+  to pointer alignment. It may violate strict aliasing rule and leading to
+  undefined behavior.
+- In scenarios involving multiple inheritance, ambiguity and unexpected outcomes
+  can arise due to the loss of type information, posing runtime issues.
+
+In summary, avoiding two-step type conversions through ``void*`` ensures clearer code,
+maintains essential compiler warnings, and prevents ambiguity and potential runtime
+errors, particularly in complex inheritance scenarios.
+
+Examples:
+
+.. code-block:: c++
+
+   using IntegerPointer = int *;
+   double *ptr;
+
+   static_cast<IntegerPointer>(static_cast<void *>(ptr)); // WRONG
+   reinterpret_cast<IntegerPointer>(reinterpret_cast<void *>(ptr)); // WRONG
+   (IntegerPointer)(void *)ptr; // WRONG
+   IntegerPointer(static_cast<void *>(ptr)); // WRONG

diff  --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 3ec7e49236101c4..6f987ba1672e3f2 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -82,6 +82,7 @@ Clang-Tidy Checks
    :doc:`bugprone-bad-signal-to-kill-thread <bugprone/bad-signal-to-kill-thread>`,
    :doc:`bugprone-bool-pointer-implicit-conversion <bugprone/bool-pointer-implicit-conversion>`, "Yes"
    :doc:`bugprone-branch-clone <bugprone/branch-clone>`,
+   :doc:`bugprone-casting-through-void <bugprone/casting-through-void>`,
    :doc:`bugprone-compare-pointer-to-member-virtual-function <bugprone/compare-pointer-to-member-virtual-function>`,
    :doc:`bugprone-copy-constructor-init <bugprone/copy-constructor-init>`, "Yes"
    :doc:`bugprone-dangling-handle <bugprone/dangling-handle>`,

diff  --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/casting-through-void.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/casting-through-void.cpp
new file mode 100644
index 000000000000000..3913d2d8a295c75
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/casting-through-void.cpp
@@ -0,0 +1,91 @@
+// RUN: %check_clang_tidy %s bugprone-casting-through-void %t
+
+using V = void*;
+using CV = const void*;
+
+int i = 100;
+double d = 100;
+const int ci = 100;
+const double cd = 100;
+
+void normal_test() {
+  static_cast<int *>(static_cast<void *>(&d));
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: do not cast 'double *' to 'int *' through 'void *' [bugprone-casting-through-void]
+  static_cast<int *>(static_cast<V>(&d));
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: do not cast 'double *' to 'int *' through 'V' (aka 'void *') [bugprone-casting-through-void]
+  static_cast<int *>(static_cast<void *>(&i));
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: do not cast 'int *' to 'int *' through 'void *' [bugprone-casting-through-void]
+
+  static_cast<void *>(static_cast<void *>(&i));
+}
+
+void const_pointer_test() {
+  static_cast<int *const>(static_cast<void *>(&d));
+  // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: do not cast 'double *' to 'int *const' through 'void *' [bugprone-casting-through-void]
+  static_cast<int *const>(static_cast<V>(&d));
+  // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: do not cast 'double *' to 'int *const' through 'V' (aka 'void *') [bugprone-casting-through-void]
+  static_cast<int *const>(static_cast<void *>(&i));
+  // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: do not cast 'int *' to 'int *const' through 'void *' [bugprone-casting-through-void]
+
+  static_cast<void *const>(static_cast<void *>(&i));
+}
+
+void const_test() {
+  static_cast<const int *>(static_cast<const void *>(&d));
+  // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: do not cast 'double *' to 'const int *' through 'const void *' [bugprone-casting-through-void]
+  static_cast<const int *>(static_cast<const V>(&d));
+  // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: do not cast 'double *' to 'const int *' through 'const V' (aka 'void *const') [bugprone-casting-through-void]
+  static_cast<const int *>(static_cast<const void *>(&i));
+  // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: do not cast 'int *' to 'const int *' through 'const void *' [bugprone-casting-through-void]
+
+  static_cast<const void *>(static_cast<const void *>(&i));
+
+  static_cast<const int *>(static_cast<const void *>(&cd));
+  // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: do not cast 'const double *' to 'const int *' through 'const void *' [bugprone-casting-through-void]
+  static_cast<const int *>(static_cast<const CV>(&cd));
+  // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: do not cast 'const double *' to 'const int *' through 'const CV' (aka 'const void *const') [bugprone-casting-through-void]
+  static_cast<const int *>(static_cast<const void *>(&ci));
+  // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: do not cast 'const int *' to 'const int *' through 'const void *' [bugprone-casting-through-void]
+
+  static_cast<const void *>(static_cast<const void *>(&ci));
+}
+
+
+void reinterpret_cast_test() {
+  static_cast<int *>(reinterpret_cast<void *>(&d));
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: do not cast 'double *' to 'int *' through 'void *' [bugprone-casting-through-void]
+  reinterpret_cast<int *>(static_cast<void *>(&d));
+  // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: do not cast 'double *' to 'int *' through 'void *' [bugprone-casting-through-void]
+  reinterpret_cast<int *>(reinterpret_cast<void *>(&d));
+  // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: do not cast 'double *' to 'int *' through 'void *' [bugprone-casting-through-void]
+
+  static_cast<void *>(reinterpret_cast<void *>(&i));
+  reinterpret_cast<void *>(reinterpret_cast<void *>(&i));
+  reinterpret_cast<void *>(static_cast<void *>(&i));
+}
+
+void c_style_cast_test() {
+  static_cast<int *>((void *)&d);
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: do not cast 'double *' to 'int *' through 'void *' [bugprone-casting-through-void]
+  (int *)(void *)&d;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: do not cast 'double *' to 'int *' through 'void *' [bugprone-casting-through-void]
+  static_cast<int *>((void *)&d);
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: do not cast 'double *' to 'int *' through 'void *' [bugprone-casting-through-void]
+
+  static_cast<void *>((void *)&i);
+}
+
+struct A {
+   A(void*);
+};
+using I = int *;
+void cxx_functional_cast() {
+  A(static_cast<void*>(&d));
+  I(static_cast<void*>(&d));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not cast 'double *' to 'I' (aka 'int *') through 'void *' [bugprone-casting-through-void]
+}
+
+void bit_cast() {
+  __builtin_bit_cast(int *, static_cast<void *>(&d));
+  // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: do not cast 'double *' to 'int *' through 'void *' [bugprone-casting-through-void]
+}


        


More information about the cfe-commits mailing list