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] +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits