-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[clang-tidy] Add readability-redundant-casting check #70595
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
PiotrZSL
merged 8 commits into
llvm:main
from
nokia:67534-clang-tidy-readability-redundant-casting
Jan 20, 2024
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
ff65fcc
[clang-tidy] Add readability-redundant-casting check
PiotrZSL 4e28bb4
Change type1, type2 to srcType, dstType
PiotrZSL d3c974a
Add checks for isNull
PiotrZSL 03b3c93
Add tests and notes for NonTypeTemplateParmDecl
PiotrZSL fab960f
Micro fixes
PiotrZSL caec10e
Remove empty line
PiotrZSL a193a09
Rename function
PiotrZSL cbd8aaa
Added -fno-delayed-template-parsing
PiotrZSL File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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
252 changes: 252 additions & 0 deletions
252
clang-tools-extra/clang-tidy/readability/RedundantCastingCheck.cpp
This file contains hidden or 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,252 @@ | ||
//===--- RedundantCastingCheck.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 "RedundantCastingCheck.h" | ||
#include "../utils/FixItHintUtils.h" | ||
#include "clang/AST/ASTContext.h" | ||
#include "clang/ASTMatchers/ASTMatchFinder.h" | ||
#include "clang/Lex/Lexer.h" | ||
|
||
using namespace clang::ast_matchers; | ||
|
||
namespace clang::tidy::readability { | ||
|
||
static bool areTypesEqual(QualType S, QualType D) { | ||
if (S == D) | ||
return true; | ||
|
||
const auto *TS = S->getAs<TypedefType>(); | ||
const auto *TD = D->getAs<TypedefType>(); | ||
if (TS != TD) | ||
return false; | ||
|
||
QualType PtrS = S->getPointeeType(); | ||
QualType PtrD = D->getPointeeType(); | ||
|
||
if (!PtrS.isNull() && !PtrD.isNull()) | ||
return areTypesEqual(PtrS, PtrD); | ||
|
||
const DeducedType *DT = S->getContainedDeducedType(); | ||
if (DT && DT->isDeduced()) | ||
return D == DT->getDeducedType(); | ||
|
||
return false; | ||
} | ||
|
||
static bool areTypesEqual(QualType TypeS, QualType TypeD, | ||
bool IgnoreTypeAliases) { | ||
const QualType CTypeS = TypeS.getCanonicalType(); | ||
const QualType CTypeD = TypeD.getCanonicalType(); | ||
if (CTypeS != CTypeD) | ||
return false; | ||
|
||
return IgnoreTypeAliases || areTypesEqual(TypeS.getLocalUnqualifiedType(), | ||
TypeD.getLocalUnqualifiedType()); | ||
} | ||
|
||
static bool areBinaryOperatorOperandsTypesEqualToOperatorResultType( | ||
const Expr *E, bool IgnoreTypeAliases) { | ||
if (!E) | ||
return true; | ||
const Expr *WithoutImplicitAndParen = E->IgnoreParenImpCasts(); | ||
if (!WithoutImplicitAndParen) | ||
return true; | ||
if (const auto *B = dyn_cast<BinaryOperator>(WithoutImplicitAndParen)) { | ||
const QualType Type = WithoutImplicitAndParen->getType(); | ||
if (Type.isNull()) | ||
return true; | ||
|
||
const QualType NonReferenceType = Type.getNonReferenceType(); | ||
const QualType LHSType = B->getLHS()->IgnoreImplicit()->getType(); | ||
if (LHSType.isNull() || !areTypesEqual(LHSType.getNonReferenceType(), | ||
NonReferenceType, IgnoreTypeAliases)) | ||
return false; | ||
const QualType RHSType = B->getRHS()->IgnoreImplicit()->getType(); | ||
if (RHSType.isNull() || !areTypesEqual(RHSType.getNonReferenceType(), | ||
NonReferenceType, IgnoreTypeAliases)) | ||
return false; | ||
} | ||
return true; | ||
} | ||
|
||
static const Decl *getSourceExprDecl(const Expr *SourceExpr) { | ||
const Expr *CleanSourceExpr = SourceExpr->IgnoreParenImpCasts(); | ||
if (const auto *E = dyn_cast<DeclRefExpr>(CleanSourceExpr)) { | ||
return E->getDecl(); | ||
} | ||
|
||
if (const auto *E = dyn_cast<CallExpr>(CleanSourceExpr)) { | ||
return E->getCalleeDecl(); | ||
} | ||
|
||
if (const auto *E = dyn_cast<MemberExpr>(CleanSourceExpr)) { | ||
return E->getMemberDecl(); | ||
} | ||
return nullptr; | ||
} | ||
|
||
RedundantCastingCheck::RedundantCastingCheck(StringRef Name, | ||
ClangTidyContext *Context) | ||
: ClangTidyCheck(Name, Context), | ||
IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)), | ||
IgnoreTypeAliases(Options.getLocalOrGlobal("IgnoreTypeAliases", false)) {} | ||
|
||
void RedundantCastingCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { | ||
Options.store(Opts, "IgnoreMacros", IgnoreMacros); | ||
Options.store(Opts, "IgnoreTypeAliases", IgnoreTypeAliases); | ||
} | ||
|
||
void RedundantCastingCheck::registerMatchers(MatchFinder *Finder) { | ||
auto SimpleType = qualType(hasCanonicalType( | ||
qualType(anyOf(builtinType(), references(builtinType()), | ||
references(pointsTo(qualType())), pointsTo(qualType()))))); | ||
|
||
auto BitfieldMemberExpr = memberExpr(member(fieldDecl(isBitField()))); | ||
|
||
Finder->addMatcher( | ||
explicitCastExpr( | ||
unless(hasCastKind(CK_ConstructorConversion)), | ||
unless(hasCastKind(CK_UserDefinedConversion)), | ||
unless(cxxFunctionalCastExpr(hasDestinationType(unless(SimpleType)))), | ||
|
||
hasDestinationType(qualType().bind("dstType")), | ||
hasSourceExpression(anyOf( | ||
expr(unless(initListExpr()), unless(BitfieldMemberExpr), | ||
hasType(qualType().bind("srcType"))) | ||
.bind("source"), | ||
initListExpr(unless(hasInit(1, expr())), | ||
hasInit(0, expr(unless(BitfieldMemberExpr), | ||
hasType(qualType().bind("srcType"))) | ||
.bind("source")))))) | ||
.bind("cast"), | ||
this); | ||
} | ||
|
||
void RedundantCastingCheck::check(const MatchFinder::MatchResult &Result) { | ||
const auto *SourceExpr = Result.Nodes.getNodeAs<Expr>("source"); | ||
auto TypeD = *Result.Nodes.getNodeAs<QualType>("dstType"); | ||
|
||
if (SourceExpr->getValueKind() == VK_LValue && | ||
TypeD.getCanonicalType()->isRValueReferenceType()) | ||
return; | ||
|
||
const auto TypeS = | ||
Result.Nodes.getNodeAs<QualType>("srcType")->getNonReferenceType(); | ||
TypeD = TypeD.getNonReferenceType(); | ||
|
||
if (!areTypesEqual(TypeS, TypeD, IgnoreTypeAliases)) | ||
return; | ||
|
||
if (!areBinaryOperatorOperandsTypesEqualToOperatorResultType( | ||
SourceExpr, IgnoreTypeAliases)) | ||
return; | ||
|
||
const auto *CastExpr = Result.Nodes.getNodeAs<ExplicitCastExpr>("cast"); | ||
if (IgnoreMacros && | ||
(CastExpr->getBeginLoc().isMacroID() || | ||
CastExpr->getEndLoc().isMacroID() || CastExpr->getExprLoc().isMacroID())) | ||
return; | ||
|
||
{ | ||
auto Diag = diag(CastExpr->getExprLoc(), | ||
"redundant explicit casting to the same type %0 as the " | ||
"sub-expression, remove this casting"); | ||
Diag << TypeD; | ||
|
||
const SourceManager &SM = *Result.SourceManager; | ||
const SourceLocation SourceExprBegin = | ||
SM.getExpansionLoc(SourceExpr->getBeginLoc()); | ||
const SourceLocation SourceExprEnd = | ||
SM.getExpansionLoc(SourceExpr->getEndLoc()); | ||
|
||
if (SourceExprBegin != CastExpr->getBeginLoc()) | ||
Diag << FixItHint::CreateRemoval(SourceRange( | ||
CastExpr->getBeginLoc(), SourceExprBegin.getLocWithOffset(-1))); | ||
|
||
const SourceLocation NextToken = Lexer::getLocForEndOfToken( | ||
SourceExprEnd, 0U, SM, Result.Context->getLangOpts()); | ||
|
||
if (SourceExprEnd != CastExpr->getEndLoc()) { | ||
Diag << FixItHint::CreateRemoval( | ||
SourceRange(NextToken, CastExpr->getEndLoc())); | ||
} | ||
|
||
if (utils::fixit::areParensNeededForStatement(*SourceExpr)) { | ||
Diag << FixItHint::CreateInsertion(SourceExprBegin, "(") | ||
<< FixItHint::CreateInsertion(NextToken, ")"); | ||
} | ||
} | ||
|
||
const auto *SourceExprDecl = getSourceExprDecl(SourceExpr); | ||
if (!SourceExprDecl) | ||
return; | ||
|
||
if (const auto *D = dyn_cast<CXXConstructorDecl>(SourceExprDecl)) { | ||
diag(D->getLocation(), | ||
"source type originates from the invocation of this constructor", | ||
DiagnosticIDs::Note); | ||
return; | ||
} | ||
|
||
if (const auto *D = dyn_cast<FunctionDecl>(SourceExprDecl)) { | ||
diag(D->getLocation(), | ||
"source type originates from the invocation of this " | ||
"%select{function|method}0", | ||
DiagnosticIDs::Note) | ||
<< isa<CXXMethodDecl>(D) << D->getReturnTypeSourceRange(); | ||
return; | ||
} | ||
|
||
if (const auto *D = dyn_cast<FieldDecl>(SourceExprDecl)) { | ||
diag(D->getLocation(), | ||
"source type originates from referencing this member", | ||
DiagnosticIDs::Note) | ||
<< SourceRange(D->getTypeSpecStartLoc(), D->getTypeSpecEndLoc()); | ||
return; | ||
} | ||
|
||
if (const auto *D = dyn_cast<ParmVarDecl>(SourceExprDecl)) { | ||
diag(D->getLocation(), | ||
"source type originates from referencing this parameter", | ||
DiagnosticIDs::Note) | ||
<< SourceRange(D->getTypeSpecStartLoc(), D->getTypeSpecEndLoc()); | ||
return; | ||
} | ||
|
||
if (const auto *D = dyn_cast<VarDecl>(SourceExprDecl)) { | ||
diag(D->getLocation(), | ||
"source type originates from referencing this variable", | ||
DiagnosticIDs::Note) | ||
<< SourceRange(D->getTypeSpecStartLoc(), D->getTypeSpecEndLoc()); | ||
return; | ||
} | ||
|
||
if (const auto *D = dyn_cast<EnumConstantDecl>(SourceExprDecl)) { | ||
diag(D->getLocation(), | ||
"source type originates from referencing this enum constant", | ||
DiagnosticIDs::Note); | ||
return; | ||
} | ||
|
||
if (const auto *D = dyn_cast<BindingDecl>(SourceExprDecl)) { | ||
diag(D->getLocation(), | ||
"source type originates from referencing this bound variable", | ||
DiagnosticIDs::Note); | ||
return; | ||
} | ||
|
||
if (const auto *D = dyn_cast<NonTypeTemplateParmDecl>(SourceExprDecl)) { | ||
diag(D->getLocation(), | ||
"source type originates from referencing this non-type template " | ||
"parameter", | ||
DiagnosticIDs::Note); | ||
return; | ||
} | ||
} | ||
|
||
} // namespace clang::tidy::readability |
38 changes: 38 additions & 0 deletions
38
clang-tools-extra/clang-tidy/readability/RedundantCastingCheck.h
This file contains hidden or 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,38 @@ | ||
//===--- RedundantCastingCheck.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_READABILITY_REDUNDANTCASTINGCHECK_H | ||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTCASTINGCHECK_H | ||
|
||
#include "../ClangTidyCheck.h" | ||
|
||
namespace clang::tidy::readability { | ||
|
||
/// Detects explicit type casting operations that involve the same source and | ||
/// destination types, and subsequently recommend their removal. | ||
/// | ||
/// For the user-facing documentation see: | ||
/// http://clang.llvm.org/extra/clang-tidy/checks/readability/redundant-casting.html | ||
class RedundantCastingCheck : public ClangTidyCheck { | ||
public: | ||
RedundantCastingCheck(StringRef Name, ClangTidyContext *Context); | ||
void registerMatchers(ast_matchers::MatchFinder *Finder) override; | ||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override; | ||
void storeOptions(ClangTidyOptions::OptionMap &Opts) override; | ||
std::optional<TraversalKind> getCheckTraversalKind() const override { | ||
return TK_IgnoreUnlessSpelledInSource; | ||
} | ||
|
||
private: | ||
const bool IgnoreMacros; | ||
const bool IgnoreTypeAliases; | ||
}; | ||
|
||
} // namespace clang::tidy::readability | ||
|
||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTCASTINGCHECK_H |
This file contains hidden or 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 hidden or 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 hidden or 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
37 changes: 37 additions & 0 deletions
37
clang-tools-extra/docs/clang-tidy/checks/readability/redundant-casting.rst
This file contains hidden or 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,37 @@ | ||
.. title:: clang-tidy - readability-redundant-casting | ||
|
||
readability-redundant-casting | ||
============================= | ||
|
||
Detects explicit type casting operations that involve the same source and | ||
destination types, and subsequently recommend their removal. Covers a range of | ||
explicit casting operations, including ``static_cast``, ``const_cast``, C-style | ||
casts, and ``reinterpret_cast``. Its primary objective is to enhance code | ||
readability and maintainability by eliminating unnecessary type casting. | ||
|
||
.. code-block:: c++ | ||
|
||
int value = 42; | ||
int result = static_cast<int>(value); | ||
|
||
In this example, the ``static_cast<int>(value)`` is redundant, as it performs | ||
a cast from an ``int`` to another ``int``. | ||
|
||
Casting operations involving constructor conversions, user-defined conversions, | ||
functional casts, type-dependent casts, casts between distinct type aliases that | ||
refer to the same underlying type, as well as bitfield-related casts and casts | ||
directly from lvalue to rvalue, are all disregarded by the check. | ||
|
||
Options | ||
------- | ||
|
||
.. option:: IgnoreMacros | ||
|
||
If set to `true`, the check will not give warnings inside macros. Default | ||
is `true`. | ||
|
||
.. option:: IgnoreTypeAliases | ||
|
||
When set to `false`, the check will consider type aliases, and when set to | ||
`true`, it will resolve all type aliases and operate on the underlying | ||
types. Default is `false`. |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.