Skip to content

Commit

Permalink
[clang-tidy]Add new check bugprone-casting-through-void (#69465)
Browse files Browse the repository at this point in the history
This check detects usage of ``static_cast`` pointer to the other pointer
throght `static_cast` to `void *` in C++ code.
Fixes: #68532
  • Loading branch information
HerrCai0907 committed Oct 20, 2023
1 parent 759cc25 commit 9a5c6f1
Show file tree
Hide file tree
Showing 8 changed files with 209 additions and 0 deletions.
3 changes: 3 additions & 0 deletions clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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>(
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ add_clang_library(clangTidyBugproneModule
BoolPointerImplicitConversionCheck.cpp
BranchCloneCheck.cpp
BugproneTidyModule.cpp
CastingThroughVoidCheck.cpp
ComparePointerToMemberVirtualFunctionCheck.cpp
CopyConstructorInitCheck.cpp
DanglingHandleCheck.cpp
Expand Down
45 changes: 45 additions & 0 deletions clang-tools-extra/clang-tidy/bugprone/CastingThroughVoidCheck.cpp
Original file line number Diff line number Diff line change
@@ -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
32 changes: 32 additions & 0 deletions clang-tools-extra/clang-tidy/bugprone/CastingThroughVoidCheck.h
Original file line number Diff line number Diff line change
@@ -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
5 changes: 5 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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>`,
Expand Down
Original file line number Diff line number Diff line change
@@ -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]
}

0 comments on commit 9a5c6f1

Please sign in to comment.