-
Notifications
You must be signed in to change notification settings - Fork 10.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[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
- Loading branch information
1 parent
759cc25
commit 9a5c6f1
Showing
8 changed files
with
209 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
45 changes: 45 additions & 0 deletions
45
clang-tools-extra/clang-tidy/bugprone/CastingThroughVoidCheck.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
32
clang-tools-extra/clang-tidy/bugprone/CastingThroughVoidCheck.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
31 changes: 31 additions & 0 deletions
31
clang-tools-extra/docs/clang-tidy/checks/bugprone/casting-through-void.rst
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
91 changes: 91 additions & 0 deletions
91
clang-tools-extra/test/clang-tidy/checkers/bugprone/casting-through-void.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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] | ||
} |