Skip to content

Commit 9a5c6f1

Browse files
authored
[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
1 parent 759cc25 commit 9a5c6f1

File tree

8 files changed

+209
-0
lines changed

8 files changed

+209
-0
lines changed

clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "BadSignalToKillThreadCheck.h"
1717
#include "BoolPointerImplicitConversionCheck.h"
1818
#include "BranchCloneCheck.h"
19+
#include "CastingThroughVoidCheck.h"
1920
#include "ComparePointerToMemberVirtualFunctionCheck.h"
2021
#include "CopyConstructorInitCheck.h"
2122
#include "DanglingHandleCheck.h"
@@ -104,6 +105,8 @@ class BugproneModule : public ClangTidyModule {
104105
CheckFactories.registerCheck<BoolPointerImplicitConversionCheck>(
105106
"bugprone-bool-pointer-implicit-conversion");
106107
CheckFactories.registerCheck<BranchCloneCheck>("bugprone-branch-clone");
108+
CheckFactories.registerCheck<CastingThroughVoidCheck>(
109+
"bugprone-casting-through-void");
107110
CheckFactories.registerCheck<ComparePointerToMemberVirtualFunctionCheck>(
108111
"bugprone-compare-pointer-to-member-virtual-function");
109112
CheckFactories.registerCheck<CopyConstructorInitCheck>(

clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ add_clang_library(clangTidyBugproneModule
1111
BoolPointerImplicitConversionCheck.cpp
1212
BranchCloneCheck.cpp
1313
BugproneTidyModule.cpp
14+
CastingThroughVoidCheck.cpp
1415
ComparePointerToMemberVirtualFunctionCheck.cpp
1516
CopyConstructorInitCheck.cpp
1617
DanglingHandleCheck.cpp
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//===--- CastingThroughVoidCheck.cpp - clang-tidy -------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "CastingThroughVoidCheck.h"
10+
#include "clang/AST/ASTContext.h"
11+
#include "clang/AST/Expr.h"
12+
#include "clang/AST/Type.h"
13+
#include "clang/ASTMatchers/ASTMatchFinder.h"
14+
#include "clang/ASTMatchers/ASTMatchers.h"
15+
#include "llvm/ADT/StringSet.h"
16+
17+
using namespace clang::ast_matchers;
18+
19+
namespace clang::tidy::bugprone {
20+
21+
void CastingThroughVoidCheck::registerMatchers(MatchFinder *Finder) {
22+
Finder->addMatcher(
23+
explicitCastExpr(
24+
hasDestinationType(
25+
qualType(unless(hasCanonicalType(pointsTo(voidType()))))
26+
.bind("target_type")),
27+
hasSourceExpression(
28+
explicitCastExpr(
29+
hasSourceExpression(
30+
expr(hasType(qualType().bind("source_type")))),
31+
hasDestinationType(
32+
qualType(pointsTo(voidType())).bind("void_type")))
33+
.bind("cast"))),
34+
this);
35+
}
36+
37+
void CastingThroughVoidCheck::check(const MatchFinder::MatchResult &Result) {
38+
const auto TT = *Result.Nodes.getNodeAs<QualType>("target_type");
39+
const auto ST = *Result.Nodes.getNodeAs<QualType>("source_type");
40+
const auto VT = *Result.Nodes.getNodeAs<QualType>("void_type");
41+
const auto *CE = Result.Nodes.getNodeAs<ExplicitCastExpr>("cast");
42+
diag(CE->getExprLoc(), "do not cast %0 to %1 through %2") << ST << TT << VT;
43+
}
44+
45+
} // namespace clang::tidy::bugprone
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//===--- CastingThroughVoidCheck.h - clang-tidy -----------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_CASTINGTHROUGHVOIDCHECK_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_CASTINGTHROUGHVOIDCHECK_H
11+
12+
#include "../ClangTidyCheck.h"
13+
14+
namespace clang::tidy::bugprone {
15+
16+
/// Detects unsafe or redundant two-step casting operations involving ``void*``.
17+
/// For the user-facing documentation see:
18+
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/casting-through-void.html
19+
class CastingThroughVoidCheck : public ClangTidyCheck {
20+
public:
21+
CastingThroughVoidCheck(StringRef Name, ClangTidyContext *Context)
22+
: ClangTidyCheck(Name, Context) {}
23+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
24+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
25+
std::optional<TraversalKind> getCheckTraversalKind() const override {
26+
return TK_IgnoreUnlessSpelledInSource;
27+
}
28+
};
29+
30+
} // namespace clang::tidy::bugprone
31+
32+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_CASTINGTHROUGHVOIDCHECK_H

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,11 @@ Improvements to clang-tidy
130130
New checks
131131
^^^^^^^^^^
132132

133+
- New :doc:`bugprone-casting-through-void
134+
<clang-tidy/checks/bugprone/casting-through-void>` check.
135+
136+
Detects unsafe or redundant two-step casting operations involving ``void*``.
137+
133138
- New :doc:`bugprone-compare-pointer-to-member-virtual-function
134139
<clang-tidy/checks/bugprone/compare-pointer-to-member-virtual-function>` check.
135140

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
.. title:: clang-tidy - bugprone-casting-through-void
2+
3+
bugprone-casting-through-void
4+
=============================
5+
6+
Detects unsafe or redundant two-step casting operations involving ``void*``.
7+
8+
Two-step type conversions via ``void*`` are discouraged for several reasons.
9+
10+
- They obscure code and impede its understandability, complicating maintenance.
11+
- These conversions bypass valuable compiler support, erasing warnings related
12+
to pointer alignment. It may violate strict aliasing rule and leading to
13+
undefined behavior.
14+
- In scenarios involving multiple inheritance, ambiguity and unexpected outcomes
15+
can arise due to the loss of type information, posing runtime issues.
16+
17+
In summary, avoiding two-step type conversions through ``void*`` ensures clearer code,
18+
maintains essential compiler warnings, and prevents ambiguity and potential runtime
19+
errors, particularly in complex inheritance scenarios.
20+
21+
Examples:
22+
23+
.. code-block:: c++
24+
25+
using IntegerPointer = int *;
26+
double *ptr;
27+
28+
static_cast<IntegerPointer>(static_cast<void *>(ptr)); // WRONG
29+
reinterpret_cast<IntegerPointer>(reinterpret_cast<void *>(ptr)); // WRONG
30+
(IntegerPointer)(void *)ptr; // WRONG
31+
IntegerPointer(static_cast<void *>(ptr)); // WRONG

clang-tools-extra/docs/clang-tidy/checks/list.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ Clang-Tidy Checks
8282
:doc:`bugprone-bad-signal-to-kill-thread <bugprone/bad-signal-to-kill-thread>`,
8383
:doc:`bugprone-bool-pointer-implicit-conversion <bugprone/bool-pointer-implicit-conversion>`, "Yes"
8484
:doc:`bugprone-branch-clone <bugprone/branch-clone>`,
85+
:doc:`bugprone-casting-through-void <bugprone/casting-through-void>`,
8586
:doc:`bugprone-compare-pointer-to-member-virtual-function <bugprone/compare-pointer-to-member-virtual-function>`,
8687
:doc:`bugprone-copy-constructor-init <bugprone/copy-constructor-init>`, "Yes"
8788
:doc:`bugprone-dangling-handle <bugprone/dangling-handle>`,
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// RUN: %check_clang_tidy %s bugprone-casting-through-void %t
2+
3+
using V = void*;
4+
using CV = const void*;
5+
6+
int i = 100;
7+
double d = 100;
8+
const int ci = 100;
9+
const double cd = 100;
10+
11+
void normal_test() {
12+
static_cast<int *>(static_cast<void *>(&d));
13+
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: do not cast 'double *' to 'int *' through 'void *' [bugprone-casting-through-void]
14+
static_cast<int *>(static_cast<V>(&d));
15+
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: do not cast 'double *' to 'int *' through 'V' (aka 'void *') [bugprone-casting-through-void]
16+
static_cast<int *>(static_cast<void *>(&i));
17+
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: do not cast 'int *' to 'int *' through 'void *' [bugprone-casting-through-void]
18+
19+
static_cast<void *>(static_cast<void *>(&i));
20+
}
21+
22+
void const_pointer_test() {
23+
static_cast<int *const>(static_cast<void *>(&d));
24+
// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: do not cast 'double *' to 'int *const' through 'void *' [bugprone-casting-through-void]
25+
static_cast<int *const>(static_cast<V>(&d));
26+
// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: do not cast 'double *' to 'int *const' through 'V' (aka 'void *') [bugprone-casting-through-void]
27+
static_cast<int *const>(static_cast<void *>(&i));
28+
// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: do not cast 'int *' to 'int *const' through 'void *' [bugprone-casting-through-void]
29+
30+
static_cast<void *const>(static_cast<void *>(&i));
31+
}
32+
33+
void const_test() {
34+
static_cast<const int *>(static_cast<const void *>(&d));
35+
// CHECK-MESSAGES: :[[@LINE-1]]:28: warning: do not cast 'double *' to 'const int *' through 'const void *' [bugprone-casting-through-void]
36+
static_cast<const int *>(static_cast<const V>(&d));
37+
// CHECK-MESSAGES: :[[@LINE-1]]:28: warning: do not cast 'double *' to 'const int *' through 'const V' (aka 'void *const') [bugprone-casting-through-void]
38+
static_cast<const int *>(static_cast<const void *>(&i));
39+
// CHECK-MESSAGES: :[[@LINE-1]]:28: warning: do not cast 'int *' to 'const int *' through 'const void *' [bugprone-casting-through-void]
40+
41+
static_cast<const void *>(static_cast<const void *>(&i));
42+
43+
static_cast<const int *>(static_cast<const void *>(&cd));
44+
// CHECK-MESSAGES: :[[@LINE-1]]:28: warning: do not cast 'const double *' to 'const int *' through 'const void *' [bugprone-casting-through-void]
45+
static_cast<const int *>(static_cast<const CV>(&cd));
46+
// 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]
47+
static_cast<const int *>(static_cast<const void *>(&ci));
48+
// CHECK-MESSAGES: :[[@LINE-1]]:28: warning: do not cast 'const int *' to 'const int *' through 'const void *' [bugprone-casting-through-void]
49+
50+
static_cast<const void *>(static_cast<const void *>(&ci));
51+
}
52+
53+
54+
void reinterpret_cast_test() {
55+
static_cast<int *>(reinterpret_cast<void *>(&d));
56+
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: do not cast 'double *' to 'int *' through 'void *' [bugprone-casting-through-void]
57+
reinterpret_cast<int *>(static_cast<void *>(&d));
58+
// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: do not cast 'double *' to 'int *' through 'void *' [bugprone-casting-through-void]
59+
reinterpret_cast<int *>(reinterpret_cast<void *>(&d));
60+
// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: do not cast 'double *' to 'int *' through 'void *' [bugprone-casting-through-void]
61+
62+
static_cast<void *>(reinterpret_cast<void *>(&i));
63+
reinterpret_cast<void *>(reinterpret_cast<void *>(&i));
64+
reinterpret_cast<void *>(static_cast<void *>(&i));
65+
}
66+
67+
void c_style_cast_test() {
68+
static_cast<int *>((void *)&d);
69+
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: do not cast 'double *' to 'int *' through 'void *' [bugprone-casting-through-void]
70+
(int *)(void *)&d;
71+
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: do not cast 'double *' to 'int *' through 'void *' [bugprone-casting-through-void]
72+
static_cast<int *>((void *)&d);
73+
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: do not cast 'double *' to 'int *' through 'void *' [bugprone-casting-through-void]
74+
75+
static_cast<void *>((void *)&i);
76+
}
77+
78+
struct A {
79+
A(void*);
80+
};
81+
using I = int *;
82+
void cxx_functional_cast() {
83+
A(static_cast<void*>(&d));
84+
I(static_cast<void*>(&d));
85+
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not cast 'double *' to 'I' (aka 'int *') through 'void *' [bugprone-casting-through-void]
86+
}
87+
88+
void bit_cast() {
89+
__builtin_bit_cast(int *, static_cast<void *>(&d));
90+
// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: do not cast 'double *' to 'int *' through 'void *' [bugprone-casting-through-void]
91+
}

0 commit comments

Comments
 (0)