-
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] Added bugprone-multi-level-implicit-pointer-conversion c…
…heck Detects implicit conversions between pointers of different levels of indirection. Reviewed By: xgupta Differential Revision: https://reviews.llvm.org/D149084
- Loading branch information
Showing
8 changed files
with
231 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
78 changes: 78 additions & 0 deletions
78
clang-tools-extra/clang-tidy/bugprone/MultiLevelImplicitPointerConversionCheck.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,78 @@ | ||
//===--- MultiLevelImplicitPointerConversionCheck.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 "MultiLevelImplicitPointerConversionCheck.h" | ||
#include "clang/AST/ASTContext.h" | ||
#include "clang/ASTMatchers/ASTMatchFinder.h" | ||
|
||
using namespace clang::ast_matchers; | ||
|
||
namespace clang::tidy::bugprone { | ||
|
||
static unsigned getPointerLevel(const QualType &PtrType) { | ||
if (!PtrType->isPointerType()) | ||
return 0U; | ||
|
||
return 1U + getPointerLevel(PtrType->castAs<PointerType>()->getPointeeType()); | ||
} | ||
|
||
namespace { | ||
|
||
AST_MATCHER(ImplicitCastExpr, isMultiLevelPointerConversion) { | ||
const QualType TargetType = Node.getType() | ||
.getCanonicalType() | ||
.getNonReferenceType() | ||
.getUnqualifiedType(); | ||
const QualType SourceType = Node.getSubExpr() | ||
->getType() | ||
.getCanonicalType() | ||
.getNonReferenceType() | ||
.getUnqualifiedType(); | ||
|
||
if (TargetType == SourceType) | ||
return false; | ||
|
||
const unsigned TargetPtrLevel = getPointerLevel(TargetType); | ||
if (0U == TargetPtrLevel) | ||
return false; | ||
|
||
const unsigned SourcePtrLevel = getPointerLevel(SourceType); | ||
if (0U == SourcePtrLevel) | ||
return false; | ||
|
||
return SourcePtrLevel != TargetPtrLevel; | ||
} | ||
|
||
} // namespace | ||
|
||
void MultiLevelImplicitPointerConversionCheck::registerMatchers( | ||
MatchFinder *Finder) { | ||
Finder->addMatcher( | ||
implicitCastExpr(hasCastKind(CK_BitCast), isMultiLevelPointerConversion()) | ||
.bind("expr"), | ||
this); | ||
} | ||
|
||
std::optional<TraversalKind> | ||
MultiLevelImplicitPointerConversionCheck::getCheckTraversalKind() const { | ||
return TK_AsIs; | ||
} | ||
|
||
void MultiLevelImplicitPointerConversionCheck::check( | ||
const MatchFinder::MatchResult &Result) { | ||
const auto *MatchedExpr = Result.Nodes.getNodeAs<ImplicitCastExpr>("expr"); | ||
QualType Target = MatchedExpr->getType().getDesugaredType(*Result.Context); | ||
QualType Source = | ||
MatchedExpr->getSubExpr()->getType().getDesugaredType(*Result.Context); | ||
|
||
diag(MatchedExpr->getExprLoc(), "multilevel pointer conversion from %0 to " | ||
"%1, please use explicit cast") | ||
<< Source << Target; | ||
} | ||
|
||
} // namespace clang::tidy::bugprone |
33 changes: 33 additions & 0 deletions
33
clang-tools-extra/clang-tidy/bugprone/MultiLevelImplicitPointerConversionCheck.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,33 @@ | ||
//===--- MultiLevelImplicitPointerConversionCheck.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_MULTILEVELIMPLICITPOINTERCONVERSIONCHECK_H | ||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_MULTILEVELIMPLICITPOINTERCONVERSIONCHECK_H | ||
|
||
#include "../ClangTidyCheck.h" | ||
|
||
namespace clang::tidy::bugprone { | ||
|
||
/// Detects implicit conversions between pointers of different levels of | ||
/// indirection. | ||
/// | ||
/// For the user-facing documentation see: | ||
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/multi-level-implicit-pointer-conversion.html | ||
class MultiLevelImplicitPointerConversionCheck : public ClangTidyCheck { | ||
public: | ||
MultiLevelImplicitPointerConversionCheck(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; | ||
}; | ||
|
||
} // namespace clang::tidy::bugprone | ||
|
||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_MULTILEVELIMPLICITPOINTERCONVERSIONCHECK_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
44 changes: 44 additions & 0 deletions
44
...tra/docs/clang-tidy/checks/bugprone/multi-level-implicit-pointer-conversion.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,44 @@ | ||
.. title:: clang-tidy - bugprone-multi-level-implicit-pointer-conversion | ||
|
||
bugprone-multi-level-implicit-pointer-conversion | ||
================================================ | ||
|
||
Detects implicit conversions between pointers of different levels of | ||
indirection. | ||
|
||
Conversions between pointer types of different levels of indirection can be | ||
dangerous and may lead to undefined behavior, particularly if the converted | ||
pointer is later cast to a type with a different level of indirection. | ||
For example, converting a pointer to a pointer to an ``int`` (``int**``) to | ||
a ``void*`` can result in the loss of information about the original level of | ||
indirection, which can cause problems when attempting to use the converted | ||
pointer. If the converted pointer is later cast to a type with a different | ||
level of indirection and dereferenced, it may lead to access violations, | ||
memory corruption, or other undefined behavior. | ||
|
||
Consider the following example: | ||
|
||
.. code-block:: c++ | ||
|
||
void foo(void* ptr); | ||
|
||
int main() { | ||
int x = 42; | ||
int* ptr = &x; | ||
int** ptr_ptr = &ptr; | ||
foo(ptr_ptr); // warning will trigger here | ||
return 0; | ||
} | ||
|
||
In this example, ``foo()`` is called with ``ptr_ptr`` as its argument. However, | ||
``ptr_ptr`` is a ``int**`` pointer, while ``foo()`` expects a ``void*`` pointer. | ||
This results in an implicit pointer level conversion, which could cause issues | ||
if ``foo()`` dereferences the pointer assuming it's a ``int*`` pointer. | ||
|
||
Using an explicit cast is a recommended solution to prevent issues caused by | ||
implicit pointer level conversion, as it allows the developer to explicitly | ||
state their intention and show their reasoning for the type conversion. | ||
Additionally, it is recommended that developers thoroughly check and verify the | ||
safety of the conversion before using an explicit cast. This extra level of | ||
caution can help catch potential issues early on in the development process, | ||
improving the overall reliability and maintainability of the code. |
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
65 changes: 65 additions & 0 deletions
65
...tools-extra/test/clang-tidy/checkers/bugprone/multi-level-implicit-pointer-conversion.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,65 @@ | ||
// RUN: %check_clang_tidy %s bugprone-multi-level-implicit-pointer-conversion %t | ||
|
||
using OneStar = void*; | ||
using OneStarFancy = OneStar; | ||
|
||
void takeFirstLevelVoidPtr(OneStar message); | ||
void takeFirstLevelConstVoidPtr(const OneStarFancy message); | ||
void takeFirstLevelConstVoidPtrConst(const void* const message); | ||
void takeSecondLevelVoidPtr(void** message); | ||
|
||
void** getSecondLevelVoidPtr(); | ||
void* getFirstLevelVoidPtr(); | ||
int** getSecondLevelIntPtr(); | ||
int* getFirstLevelIntPtr(); | ||
|
||
int table[5]; | ||
|
||
void test() | ||
{ | ||
void** secondLevelVoidPtr; | ||
int* firstLevelIntPtr; | ||
|
||
// CHECK-MESSAGES: :[[@LINE+1]]:13: warning: multilevel pointer conversion from 'void **' to 'void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion] | ||
void* a = getSecondLevelVoidPtr(); | ||
|
||
void** b = getSecondLevelVoidPtr(); | ||
void* c = getFirstLevelVoidPtr(); | ||
|
||
// CHECK-MESSAGES: :[[@LINE+1]]:13: warning: multilevel pointer conversion from 'int **' to 'void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion] | ||
void* d = getSecondLevelIntPtr(); | ||
|
||
takeFirstLevelVoidPtr(&table); | ||
|
||
takeFirstLevelVoidPtr(firstLevelIntPtr); | ||
|
||
takeFirstLevelVoidPtr(getFirstLevelIntPtr()); | ||
|
||
// CHECK-MESSAGES: :[[@LINE+1]]:25: warning: multilevel pointer conversion from 'void **' to 'void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion] | ||
takeFirstLevelVoidPtr(secondLevelVoidPtr); | ||
|
||
// CHECK-MESSAGES: :[[@LINE+1]]:30: warning: multilevel pointer conversion from 'void **' to 'void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion] | ||
takeFirstLevelConstVoidPtr(secondLevelVoidPtr); | ||
|
||
// CHECK-MESSAGES: :[[@LINE+1]]:35: warning: multilevel pointer conversion from 'void **' to 'const void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion] | ||
takeFirstLevelConstVoidPtrConst(secondLevelVoidPtr); | ||
|
||
// CHECK-MESSAGES: :[[@LINE+1]]:35: warning: multilevel pointer conversion from 'void ***' to 'const void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion] | ||
takeFirstLevelConstVoidPtrConst(&secondLevelVoidPtr); | ||
|
||
takeSecondLevelVoidPtr(secondLevelVoidPtr); | ||
|
||
// CHECK-MESSAGES: :[[@LINE+1]]:25: warning: multilevel pointer conversion from 'void **' to 'void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion] | ||
takeFirstLevelVoidPtr(getSecondLevelVoidPtr()); | ||
|
||
// CHECK-MESSAGES: :[[@LINE+1]]:30: warning: multilevel pointer conversion from 'void **' to 'void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion] | ||
takeFirstLevelConstVoidPtr(getSecondLevelVoidPtr()); | ||
|
||
// CHECK-MESSAGES: :[[@LINE+1]]:35: warning: multilevel pointer conversion from 'void **' to 'const void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion] | ||
takeFirstLevelConstVoidPtrConst(getSecondLevelVoidPtr()); | ||
|
||
// CHECK-MESSAGES: :[[@LINE+1]]:25: warning: multilevel pointer conversion from 'int **' to 'void *', please use explicit cast [bugprone-multi-level-implicit-pointer-conversion] | ||
takeFirstLevelVoidPtr(getSecondLevelIntPtr()); | ||
|
||
takeSecondLevelVoidPtr(getSecondLevelVoidPtr()); | ||
} |