1 //===--- RedundantCastingCheck.cpp - clang-tidy ---------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "RedundantCastingCheck.h"
10 #include "../utils/FixItHintUtils.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Lex/Lexer.h"
15 using namespace clang::ast_matchers
;
17 namespace clang::tidy::readability
{
19 static bool areTypesEqual(QualType S
, QualType D
) {
23 const auto *TS
= S
->getAs
<TypedefType
>();
24 const auto *TD
= D
->getAs
<TypedefType
>();
28 QualType PtrS
= S
->getPointeeType();
29 QualType PtrD
= D
->getPointeeType();
31 if (!PtrS
.isNull() && !PtrD
.isNull())
32 return areTypesEqual(PtrS
, PtrD
);
34 const DeducedType
*DT
= S
->getContainedDeducedType();
35 if (DT
&& DT
->isDeduced())
36 return D
== DT
->getDeducedType();
41 static bool areTypesEqual(QualType TypeS
, QualType TypeD
,
42 bool IgnoreTypeAliases
) {
43 const QualType CTypeS
= TypeS
.getCanonicalType();
44 const QualType CTypeD
= TypeD
.getCanonicalType();
48 return IgnoreTypeAliases
|| areTypesEqual(TypeS
.getLocalUnqualifiedType(),
49 TypeD
.getLocalUnqualifiedType());
52 static bool areBinaryOperatorOperandsTypesEqualToOperatorResultType(
53 const Expr
*E
, bool IgnoreTypeAliases
) {
56 const Expr
*WithoutImplicitAndParen
= E
->IgnoreParenImpCasts();
57 if (!WithoutImplicitAndParen
)
59 if (const auto *B
= dyn_cast
<BinaryOperator
>(WithoutImplicitAndParen
)) {
60 const QualType Type
= WithoutImplicitAndParen
->getType();
64 const QualType NonReferenceType
= Type
.getNonReferenceType();
65 const QualType LHSType
= B
->getLHS()->IgnoreImplicit()->getType();
66 if (LHSType
.isNull() || !areTypesEqual(LHSType
.getNonReferenceType(),
67 NonReferenceType
, IgnoreTypeAliases
))
69 const QualType RHSType
= B
->getRHS()->IgnoreImplicit()->getType();
70 if (RHSType
.isNull() || !areTypesEqual(RHSType
.getNonReferenceType(),
71 NonReferenceType
, IgnoreTypeAliases
))
77 static const Decl
*getSourceExprDecl(const Expr
*SourceExpr
) {
78 const Expr
*CleanSourceExpr
= SourceExpr
->IgnoreParenImpCasts();
79 if (const auto *E
= dyn_cast
<DeclRefExpr
>(CleanSourceExpr
)) {
83 if (const auto *E
= dyn_cast
<CallExpr
>(CleanSourceExpr
)) {
84 return E
->getCalleeDecl();
87 if (const auto *E
= dyn_cast
<MemberExpr
>(CleanSourceExpr
)) {
88 return E
->getMemberDecl();
93 RedundantCastingCheck::RedundantCastingCheck(StringRef Name
,
94 ClangTidyContext
*Context
)
95 : ClangTidyCheck(Name
, Context
),
96 IgnoreMacros(Options
.getLocalOrGlobal("IgnoreMacros", true)),
97 IgnoreTypeAliases(Options
.getLocalOrGlobal("IgnoreTypeAliases", false)) {}
99 void RedundantCastingCheck::storeOptions(ClangTidyOptions::OptionMap
&Opts
) {
100 Options
.store(Opts
, "IgnoreMacros", IgnoreMacros
);
101 Options
.store(Opts
, "IgnoreTypeAliases", IgnoreTypeAliases
);
104 void RedundantCastingCheck::registerMatchers(MatchFinder
*Finder
) {
105 auto SimpleType
= qualType(hasCanonicalType(
106 qualType(anyOf(builtinType(), references(builtinType()),
107 references(pointsTo(qualType())), pointsTo(qualType())))));
109 auto BitfieldMemberExpr
= memberExpr(member(fieldDecl(isBitField())));
113 unless(hasCastKind(CK_ConstructorConversion
)),
114 unless(hasCastKind(CK_UserDefinedConversion
)),
115 unless(cxxFunctionalCastExpr(hasDestinationType(unless(SimpleType
)))),
117 hasDestinationType(qualType().bind("dstType")),
118 hasSourceExpression(anyOf(
119 expr(unless(initListExpr()), unless(BitfieldMemberExpr
),
120 hasType(qualType().bind("srcType")))
122 initListExpr(unless(hasInit(1, expr())),
123 hasInit(0, expr(unless(BitfieldMemberExpr
),
124 hasType(qualType().bind("srcType")))
130 void RedundantCastingCheck::check(const MatchFinder::MatchResult
&Result
) {
131 const auto *SourceExpr
= Result
.Nodes
.getNodeAs
<Expr
>("source");
132 auto TypeD
= *Result
.Nodes
.getNodeAs
<QualType
>("dstType");
134 if (SourceExpr
->getValueKind() == VK_LValue
&&
135 TypeD
.getCanonicalType()->isRValueReferenceType())
139 Result
.Nodes
.getNodeAs
<QualType
>("srcType")->getNonReferenceType();
140 TypeD
= TypeD
.getNonReferenceType();
142 if (!areTypesEqual(TypeS
, TypeD
, IgnoreTypeAliases
))
145 if (!areBinaryOperatorOperandsTypesEqualToOperatorResultType(
146 SourceExpr
, IgnoreTypeAliases
))
149 const auto *CastExpr
= Result
.Nodes
.getNodeAs
<ExplicitCastExpr
>("cast");
151 (CastExpr
->getBeginLoc().isMacroID() ||
152 CastExpr
->getEndLoc().isMacroID() || CastExpr
->getExprLoc().isMacroID()))
156 auto Diag
= diag(CastExpr
->getExprLoc(),
157 "redundant explicit casting to the same type %0 as the "
158 "sub-expression, remove this casting");
161 const SourceManager
&SM
= *Result
.SourceManager
;
162 const SourceLocation SourceExprBegin
=
163 SM
.getExpansionLoc(SourceExpr
->getBeginLoc());
164 const SourceLocation SourceExprEnd
=
165 SM
.getExpansionLoc(SourceExpr
->getEndLoc());
167 if (SourceExprBegin
!= CastExpr
->getBeginLoc())
168 Diag
<< FixItHint::CreateRemoval(SourceRange(
169 CastExpr
->getBeginLoc(), SourceExprBegin
.getLocWithOffset(-1)));
171 const SourceLocation NextToken
= Lexer::getLocForEndOfToken(
172 SourceExprEnd
, 0U, SM
, Result
.Context
->getLangOpts());
174 if (SourceExprEnd
!= CastExpr
->getEndLoc()) {
175 Diag
<< FixItHint::CreateRemoval(
176 SourceRange(NextToken
, CastExpr
->getEndLoc()));
179 if (utils::fixit::areParensNeededForStatement(*SourceExpr
)) {
180 Diag
<< FixItHint::CreateInsertion(SourceExprBegin
, "(")
181 << FixItHint::CreateInsertion(NextToken
, ")");
185 const auto *SourceExprDecl
= getSourceExprDecl(SourceExpr
);
189 if (const auto *D
= dyn_cast
<CXXConstructorDecl
>(SourceExprDecl
)) {
190 diag(D
->getLocation(),
191 "source type originates from the invocation of this constructor",
192 DiagnosticIDs::Note
);
196 if (const auto *D
= dyn_cast
<FunctionDecl
>(SourceExprDecl
)) {
197 diag(D
->getLocation(),
198 "source type originates from the invocation of this "
199 "%select{function|method}0",
201 << isa
<CXXMethodDecl
>(D
) << D
->getReturnTypeSourceRange();
205 if (const auto *D
= dyn_cast
<FieldDecl
>(SourceExprDecl
)) {
206 diag(D
->getLocation(),
207 "source type originates from referencing this member",
209 << SourceRange(D
->getTypeSpecStartLoc(), D
->getTypeSpecEndLoc());
213 if (const auto *D
= dyn_cast
<ParmVarDecl
>(SourceExprDecl
)) {
214 diag(D
->getLocation(),
215 "source type originates from referencing this parameter",
217 << SourceRange(D
->getTypeSpecStartLoc(), D
->getTypeSpecEndLoc());
221 if (const auto *D
= dyn_cast
<VarDecl
>(SourceExprDecl
)) {
222 diag(D
->getLocation(),
223 "source type originates from referencing this variable",
225 << SourceRange(D
->getTypeSpecStartLoc(), D
->getTypeSpecEndLoc());
229 if (const auto *D
= dyn_cast
<EnumConstantDecl
>(SourceExprDecl
)) {
230 diag(D
->getLocation(),
231 "source type originates from referencing this enum constant",
232 DiagnosticIDs::Note
);
236 if (const auto *D
= dyn_cast
<BindingDecl
>(SourceExprDecl
)) {
237 diag(D
->getLocation(),
238 "source type originates from referencing this bound variable",
239 DiagnosticIDs::Note
);
243 if (const auto *D
= dyn_cast
<NonTypeTemplateParmDecl
>(SourceExprDecl
)) {
244 diag(D
->getLocation(),
245 "source type originates from referencing this non-type template "
247 DiagnosticIDs::Note
);
252 } // namespace clang::tidy::readability