Skip to content

Commit 315946c

Browse files
committed
[clang-tidy] Added bugprone-multi-level-implicit-pointer-conversion check
Detects implicit conversions between pointers of different levels of indirection. Reviewed By: xgupta Differential Revision: https://reviews.llvm.org/D149084
1 parent cbfcf90 commit 315946c

File tree

8 files changed

+231
-0
lines changed

8 files changed

+231
-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
@@ -37,6 +37,7 @@
3737
#include "MisplacedPointerArithmeticInAllocCheck.h"
3838
#include "MisplacedWideningCastCheck.h"
3939
#include "MoveForwardingReferenceCheck.h"
40+
#include "MultiLevelImplicitPointerConversionCheck.h"
4041
#include "MultipleNewInOneExpressionCheck.h"
4142
#include "MultipleStatementMacroCheck.h"
4243
#include "NoEscapeCheck.h"
@@ -140,6 +141,8 @@ class BugproneModule : public ClangTidyModule {
140141
"bugprone-misplaced-widening-cast");
141142
CheckFactories.registerCheck<MoveForwardingReferenceCheck>(
142143
"bugprone-move-forwarding-reference");
144+
CheckFactories.registerCheck<MultiLevelImplicitPointerConversionCheck>(
145+
"bugprone-multi-level-implicit-pointer-conversion");
143146
CheckFactories.registerCheck<MultipleNewInOneExpressionCheck>(
144147
"bugprone-multiple-new-in-one-expression");
145148
CheckFactories.registerCheck<MultipleStatementMacroCheck>(

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ add_clang_library(clangTidyBugproneModule
3333
MisplacedPointerArithmeticInAllocCheck.cpp
3434
MisplacedWideningCastCheck.cpp
3535
MoveForwardingReferenceCheck.cpp
36+
MultiLevelImplicitPointerConversionCheck.cpp
3637
MultipleNewInOneExpressionCheck.cpp
3738
MultipleStatementMacroCheck.cpp
3839
NoEscapeCheck.cpp
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//===--- MultiLevelImplicitPointerConversionCheck.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 "MultiLevelImplicitPointerConversionCheck.h"
10+
#include "clang/AST/ASTContext.h"
11+
#include "clang/ASTMatchers/ASTMatchFinder.h"
12+
13+
using namespace clang::ast_matchers;
14+
15+
namespace clang::tidy::bugprone {
16+
17+
static unsigned getPointerLevel(const QualType &PtrType) {
18+
if (!PtrType->isPointerType())
19+
return 0U;
20+
21+
return 1U + getPointerLevel(PtrType->castAs<PointerType>()->getPointeeType());
22+
}
23+
24+
namespace {
25+
26+
AST_MATCHER(ImplicitCastExpr, isMultiLevelPointerConversion) {
27+
const QualType TargetType = Node.getType()
28+
.getCanonicalType()
29+
.getNonReferenceType()
30+
.getUnqualifiedType();
31+
const QualType SourceType = Node.getSubExpr()
32+
->getType()
33+
.getCanonicalType()
34+
.getNonReferenceType()
35+
.getUnqualifiedType();
36+
37+
if (TargetType == SourceType)
38+
return false;
39+
40+
const unsigned TargetPtrLevel = getPointerLevel(TargetType);
41+
if (0U == TargetPtrLevel)
42+
return false;
43+
44+
const unsigned SourcePtrLevel = getPointerLevel(SourceType);
45+
if (0U == SourcePtrLevel)
46+
return false;
47+
48+
return SourcePtrLevel != TargetPtrLevel;
49+
}
50+
51+
} // namespace
52+
53+
void MultiLevelImplicitPointerConversionCheck::registerMatchers(
54+
MatchFinder *Finder) {
55+
Finder->addMatcher(
56+
implicitCastExpr(hasCastKind(CK_BitCast), isMultiLevelPointerConversion())
57+
.bind("expr"),
58+
this);
59+
}
60+
61+
std::optional<TraversalKind>
62+
MultiLevelImplicitPointerConversionCheck::getCheckTraversalKind() const {
63+
return TK_AsIs;
64+
}
65+
66+
void MultiLevelImplicitPointerConversionCheck::check(
67+
const MatchFinder::MatchResult &Result) {
68+
const auto *MatchedExpr = Result.Nodes.getNodeAs<ImplicitCastExpr>("expr");
69+
QualType Target = MatchedExpr->getType().getDesugaredType(*Result.Context);
70+
QualType Source =
71+
MatchedExpr->getSubExpr()->getType().getDesugaredType(*Result.Context);
72+
73+
diag(MatchedExpr->getExprLoc(), "multilevel pointer conversion from %0 to "
74+
"%1, please use explicit cast")
75+
<< Source << Target;
76+
}
77+
78+
} // namespace clang::tidy::bugprone
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//===--- MultiLevelImplicitPointerConversionCheck.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_MULTILEVELIMPLICITPOINTERCONVERSIONCHECK_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_MULTILEVELIMPLICITPOINTERCONVERSIONCHECK_H
11+
12+
#include "../ClangTidyCheck.h"
13+
14+
namespace clang::tidy::bugprone {
15+
16+
/// Detects implicit conversions between pointers of different levels of
17+
/// indirection.
18+
///
19+
/// For the user-facing documentation see:
20+
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/multi-level-implicit-pointer-conversion.html
21+
class MultiLevelImplicitPointerConversionCheck : public ClangTidyCheck {
22+
public:
23+
MultiLevelImplicitPointerConversionCheck(StringRef Name,
24+
ClangTidyContext *Context)
25+
: ClangTidyCheck(Name, Context) {}
26+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
27+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
28+
std::optional<TraversalKind> getCheckTraversalKind() const override;
29+
};
30+
31+
} // namespace clang::tidy::bugprone
32+
33+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_MULTILEVELIMPLICITPOINTERCONVERSIONCHECK_H

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,12 @@ Improvements to clang-tidy
114114
New checks
115115
^^^^^^^^^^
116116

117+
- New :doc:`bugprone-multi-level-implicit-pointer-conversion
118+
<clang-tidy/checks/bugprone/multi-level-implicit-pointer-conversion>` check.
119+
120+
Detects implicit conversions between pointers of different levels of
121+
indirection.
122+
117123
- New :doc:`performance-enum-size
118124
<clang-tidy/checks/performance/enum-size>` check.
119125

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
.. title:: clang-tidy - bugprone-multi-level-implicit-pointer-conversion
2+
3+
bugprone-multi-level-implicit-pointer-conversion
4+
================================================
5+
6+
Detects implicit conversions between pointers of different levels of
7+
indirection.
8+
9+
Conversions between pointer types of different levels of indirection can be
10+
dangerous and may lead to undefined behavior, particularly if the converted
11+
pointer is later cast to a type with a different level of indirection.
12+
For example, converting a pointer to a pointer to an ``int`` (``int**``) to
13+
a ``void*`` can result in the loss of information about the original level of
14+
indirection, which can cause problems when attempting to use the converted
15+
pointer. If the converted pointer is later cast to a type with a different
16+
level of indirection and dereferenced, it may lead to access violations,
17+
memory corruption, or other undefined behavior.
18+
19+
Consider the following example:
20+
21+
.. code-block:: c++
22+
23+
void foo(void* ptr);
24+
25+
int main() {
26+
int x = 42;
27+
int* ptr = &x;
28+
int** ptr_ptr = &ptr;
29+
foo(ptr_ptr); // warning will trigger here
30+
return 0;
31+
}
32+
33+
In this example, ``foo()`` is called with ``ptr_ptr`` as its argument. However,
34+
``ptr_ptr`` is a ``int**`` pointer, while ``foo()`` expects a ``void*`` pointer.
35+
This results in an implicit pointer level conversion, which could cause issues
36+
if ``foo()`` dereferences the pointer assuming it's a ``int*`` pointer.
37+
38+
Using an explicit cast is a recommended solution to prevent issues caused by
39+
implicit pointer level conversion, as it allows the developer to explicitly
40+
state their intention and show their reasoning for the type conversion.
41+
Additionally, it is recommended that developers thoroughly check and verify the
42+
safety of the conversion before using an explicit cast. This extra level of
43+
caution can help catch potential issues early on in the development process,
44+
improving the overall reliability and maintainability of the code.

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ Clang-Tidy Checks
103103
`bugprone-misplaced-pointer-arithmetic-in-alloc <bugprone/misplaced-pointer-arithmetic-in-alloc.html>`_, "Yes"
104104
`bugprone-misplaced-widening-cast <bugprone/misplaced-widening-cast.html>`_,
105105
`bugprone-move-forwarding-reference <bugprone/move-forwarding-reference.html>`_, "Yes"
106+
`bugprone-multi-level-implicit-pointer-conversion <bugprone/multi-level-implicit-pointer-conversion.html>`_,
106107
`bugprone-multiple-new-in-one-expression <bugprone/multiple-new-in-one-expression.html>`_,
107108
`bugprone-multiple-statement-macro <bugprone/multiple-statement-macro.html>`_,
108109
`bugprone-no-escape <bugprone/no-escape.html>`_,
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// RUN: %check_clang_tidy %s bugprone-multi-level-implicit-pointer-conversion %t
2+
3+
using OneStar = void*;
4+
using OneStarFancy = OneStar;
5+
6+
void takeFirstLevelVoidPtr(OneStar message);
7+
void takeFirstLevelConstVoidPtr(const OneStarFancy message);
8+
void takeFirstLevelConstVoidPtrConst(const void* const message);
9+
void takeSecondLevelVoidPtr(void** message);
10+
11+
void** getSecondLevelVoidPtr();
12+
void* getFirstLevelVoidPtr();
13+
int** getSecondLevelIntPtr();
14+
int* getFirstLevelIntPtr();
15+
16+
int table[5];
17+
18+
void test()
19+
{
20+
void** secondLevelVoidPtr;
21+
int* firstLevelIntPtr;
22+
23+
// CHECK-MESSAGES: :[[@LINE+1]]:13: warning: multilevel pointer conversion from 'void **' to 'void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]
24+
void* a = getSecondLevelVoidPtr();
25+
26+
void** b = getSecondLevelVoidPtr();
27+
void* c = getFirstLevelVoidPtr();
28+
29+
// CHECK-MESSAGES: :[[@LINE+1]]:13: warning: multilevel pointer conversion from 'int **' to 'void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]
30+
void* d = getSecondLevelIntPtr();
31+
32+
takeFirstLevelVoidPtr(&table);
33+
34+
takeFirstLevelVoidPtr(firstLevelIntPtr);
35+
36+
takeFirstLevelVoidPtr(getFirstLevelIntPtr());
37+
38+
// CHECK-MESSAGES: :[[@LINE+1]]:25: warning: multilevel pointer conversion from 'void **' to 'void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]
39+
takeFirstLevelVoidPtr(secondLevelVoidPtr);
40+
41+
// CHECK-MESSAGES: :[[@LINE+1]]:30: warning: multilevel pointer conversion from 'void **' to 'void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]
42+
takeFirstLevelConstVoidPtr(secondLevelVoidPtr);
43+
44+
// CHECK-MESSAGES: :[[@LINE+1]]:35: warning: multilevel pointer conversion from 'void **' to 'const void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]
45+
takeFirstLevelConstVoidPtrConst(secondLevelVoidPtr);
46+
47+
// CHECK-MESSAGES: :[[@LINE+1]]:35: warning: multilevel pointer conversion from 'void ***' to 'const void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]
48+
takeFirstLevelConstVoidPtrConst(&secondLevelVoidPtr);
49+
50+
takeSecondLevelVoidPtr(secondLevelVoidPtr);
51+
52+
// CHECK-MESSAGES: :[[@LINE+1]]:25: warning: multilevel pointer conversion from 'void **' to 'void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]
53+
takeFirstLevelVoidPtr(getSecondLevelVoidPtr());
54+
55+
// CHECK-MESSAGES: :[[@LINE+1]]:30: warning: multilevel pointer conversion from 'void **' to 'void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]
56+
takeFirstLevelConstVoidPtr(getSecondLevelVoidPtr());
57+
58+
// CHECK-MESSAGES: :[[@LINE+1]]:35: warning: multilevel pointer conversion from 'void **' to 'const void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]
59+
takeFirstLevelConstVoidPtrConst(getSecondLevelVoidPtr());
60+
61+
// CHECK-MESSAGES: :[[@LINE+1]]:25: warning: multilevel pointer conversion from 'int **' to 'void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion]
62+
takeFirstLevelVoidPtr(getSecondLevelIntPtr());
63+
64+
takeSecondLevelVoidPtr(getSecondLevelVoidPtr());
65+
}

0 commit comments

Comments
 (0)