1//===--- RedundantCastingCheck.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 "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"
14
15using namespace clang::ast_matchers;
16
17namespace clang::tidy::readability {
18
19static bool areTypesEqual(QualType S, QualType D) {
20 if (S == D)
21 return true;
22
23 const auto *TS = S->getAs<TypedefType>();
24 const auto *TD = D->getAs<TypedefType>();
25 if (TS != TD)
26 return false;
27
28 QualType PtrS = S->getPointeeType();
29 QualType PtrD = D->getPointeeType();
30
31 if (!PtrS.isNull() && !PtrD.isNull())
32 return areTypesEqual(S: PtrS, D: PtrD);
33
34 const DeducedType *DT = S->getContainedDeducedType();
35 if (DT && DT->isDeduced())
36 return D == DT->getDeducedType();
37
38 return false;
39}
40
41static bool areTypesEqual(QualType TypeS, QualType TypeD,
42 bool IgnoreTypeAliases) {
43 const QualType CTypeS = TypeS.getCanonicalType();
44 const QualType CTypeD = TypeD.getCanonicalType();
45 if (CTypeS != CTypeD)
46 return false;
47
48 return IgnoreTypeAliases || areTypesEqual(S: TypeS.getLocalUnqualifiedType(),
49 D: TypeD.getLocalUnqualifiedType());
50}
51
52static bool areBinaryOperatorOperandsTypesEqualToOperatorResultType(
53 const Expr *E, bool IgnoreTypeAliases) {
54 if (!E)
55 return true;
56 const Expr *WithoutImplicitAndParen = E->IgnoreParenImpCasts();
57 if (!WithoutImplicitAndParen)
58 return true;
59 if (const auto *B = dyn_cast<BinaryOperator>(Val: WithoutImplicitAndParen)) {
60 const QualType Type = WithoutImplicitAndParen->getType();
61 if (Type.isNull())
62 return true;
63
64 const QualType NonReferenceType = Type.getNonReferenceType();
65 const QualType LHSType = B->getLHS()->IgnoreImplicit()->getType();
66 if (LHSType.isNull() || !areTypesEqual(TypeS: LHSType.getNonReferenceType(),
67 TypeD: NonReferenceType, IgnoreTypeAliases))
68 return false;
69 const QualType RHSType = B->getRHS()->IgnoreImplicit()->getType();
70 if (RHSType.isNull() || !areTypesEqual(TypeS: RHSType.getNonReferenceType(),
71 TypeD: NonReferenceType, IgnoreTypeAliases))
72 return false;
73 }
74 return true;
75}
76
77static const Decl *getSourceExprDecl(const Expr *SourceExpr) {
78 const Expr *CleanSourceExpr = SourceExpr->IgnoreParenImpCasts();
79 if (const auto *E = dyn_cast<DeclRefExpr>(Val: CleanSourceExpr)) {
80 return E->getDecl();
81 }
82
83 if (const auto *E = dyn_cast<CallExpr>(Val: CleanSourceExpr)) {
84 return E->getCalleeDecl();
85 }
86
87 if (const auto *E = dyn_cast<MemberExpr>(Val: CleanSourceExpr)) {
88 return E->getMemberDecl();
89 }
90 return nullptr;
91}
92
93RedundantCastingCheck::RedundantCastingCheck(StringRef Name,
94 ClangTidyContext *Context)
95 : ClangTidyCheck(Name, Context),
96 IgnoreMacros(Options.getLocalOrGlobal(LocalName: "IgnoreMacros", Default: true)),
97 IgnoreTypeAliases(Options.getLocalOrGlobal(LocalName: "IgnoreTypeAliases", Default: false)) {}
98
99void RedundantCastingCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
100 Options.store(Options&: Opts, LocalName: "IgnoreMacros", Value: IgnoreMacros);
101 Options.store(Options&: Opts, LocalName: "IgnoreTypeAliases", Value: IgnoreTypeAliases);
102}
103
104void RedundantCastingCheck::registerMatchers(MatchFinder *Finder) {
105 auto SimpleType = qualType(hasCanonicalType(
106 InnerMatcher: qualType(anyOf(builtinType(), references(InnerMatcher: builtinType()),
107 references(InnerMatcher: pointsTo(InnerMatcher: qualType())), pointsTo(InnerMatcher: qualType())))));
108
109 auto BitfieldMemberExpr = memberExpr(member(InnerMatcher: fieldDecl(isBitField())));
110
111 Finder->addMatcher(
112 NodeMatch: explicitCastExpr(
113 unless(hasCastKind(Kind: CK_ConstructorConversion)),
114 unless(hasCastKind(Kind: CK_UserDefinedConversion)),
115 unless(cxxFunctionalCastExpr(hasDestinationType(InnerMatcher: unless(SimpleType)))),
116
117 hasDestinationType(InnerMatcher: qualType().bind(ID: "dstType")),
118 hasSourceExpression(InnerMatcher: anyOf(
119 expr(unless(initListExpr()), unless(BitfieldMemberExpr),
120 hasType(InnerMatcher: qualType().bind(ID: "srcType")))
121 .bind(ID: "source"),
122 initListExpr(unless(hasInit(N: 1, InnerMatcher: expr())),
123 hasInit(N: 0, InnerMatcher: expr(unless(BitfieldMemberExpr),
124 hasType(InnerMatcher: qualType().bind(ID: "srcType")))
125 .bind(ID: "source"))))))
126 .bind(ID: "cast"),
127 Action: this);
128}
129
130void RedundantCastingCheck::check(const MatchFinder::MatchResult &Result) {
131 const auto *SourceExpr = Result.Nodes.getNodeAs<Expr>(ID: "source");
132 auto TypeD = *Result.Nodes.getNodeAs<QualType>(ID: "dstType");
133
134 if (SourceExpr->getValueKind() == VK_LValue &&
135 TypeD.getCanonicalType()->isRValueReferenceType())
136 return;
137
138 const auto TypeS =
139 Result.Nodes.getNodeAs<QualType>(ID: "srcType")->getNonReferenceType();
140 TypeD = TypeD.getNonReferenceType();
141
142 if (!areTypesEqual(TypeS, TypeD, IgnoreTypeAliases))
143 return;
144
145 if (!areBinaryOperatorOperandsTypesEqualToOperatorResultType(
146 E: SourceExpr, IgnoreTypeAliases))
147 return;
148
149 const auto *CastExpr = Result.Nodes.getNodeAs<ExplicitCastExpr>(ID: "cast");
150 if (IgnoreMacros &&
151 (CastExpr->getBeginLoc().isMacroID() ||
152 CastExpr->getEndLoc().isMacroID() || CastExpr->getExprLoc().isMacroID()))
153 return;
154
155 {
156 auto Diag = diag(CastExpr->getExprLoc(),
157 "redundant explicit casting to the same type %0 as the "
158 "sub-expression, remove this casting");
159 Diag << TypeD;
160
161 const SourceManager &SM = *Result.SourceManager;
162 const SourceLocation SourceExprBegin =
163 SM.getExpansionLoc(Loc: SourceExpr->getBeginLoc());
164 const SourceLocation SourceExprEnd =
165 SM.getExpansionLoc(Loc: SourceExpr->getEndLoc());
166
167 if (SourceExprBegin != CastExpr->getBeginLoc())
168 Diag << FixItHint::CreateRemoval(RemoveRange: SourceRange(
169 CastExpr->getBeginLoc(), SourceExprBegin.getLocWithOffset(Offset: -1)));
170
171 const SourceLocation NextToken = Lexer::getLocForEndOfToken(
172 Loc: SourceExprEnd, Offset: 0U, SM, LangOpts: Result.Context->getLangOpts());
173
174 if (SourceExprEnd != CastExpr->getEndLoc()) {
175 Diag << FixItHint::CreateRemoval(
176 RemoveRange: SourceRange(NextToken, CastExpr->getEndLoc()));
177 }
178
179 if (utils::fixit::areParensNeededForStatement(*SourceExpr)) {
180 Diag << FixItHint::CreateInsertion(InsertionLoc: SourceExprBegin, Code: "(")
181 << FixItHint::CreateInsertion(InsertionLoc: NextToken, Code: ")");
182 }
183 }
184
185 const auto *SourceExprDecl = getSourceExprDecl(SourceExpr);
186 if (!SourceExprDecl)
187 return;
188
189 if (const auto *D = dyn_cast<CXXConstructorDecl>(Val: SourceExprDecl)) {
190 diag(D->getLocation(),
191 "source type originates from the invocation of this constructor",
192 DiagnosticIDs::Note);
193 return;
194 }
195
196 if (const auto *D = dyn_cast<FunctionDecl>(Val: SourceExprDecl)) {
197 diag(D->getLocation(),
198 "source type originates from the invocation of this "
199 "%select{function|method}0",
200 DiagnosticIDs::Note)
201 << isa<CXXMethodDecl>(Val: D) << D->getReturnTypeSourceRange();
202 return;
203 }
204
205 if (const auto *D = dyn_cast<FieldDecl>(Val: SourceExprDecl)) {
206 diag(D->getLocation(),
207 "source type originates from referencing this member",
208 DiagnosticIDs::Note)
209 << SourceRange(D->getTypeSpecStartLoc(), D->getTypeSpecEndLoc());
210 return;
211 }
212
213 if (const auto *D = dyn_cast<ParmVarDecl>(Val: SourceExprDecl)) {
214 diag(D->getLocation(),
215 "source type originates from referencing this parameter",
216 DiagnosticIDs::Note)
217 << SourceRange(D->getTypeSpecStartLoc(), D->getTypeSpecEndLoc());
218 return;
219 }
220
221 if (const auto *D = dyn_cast<VarDecl>(Val: SourceExprDecl)) {
222 diag(D->getLocation(),
223 "source type originates from referencing this variable",
224 DiagnosticIDs::Note)
225 << SourceRange(D->getTypeSpecStartLoc(), D->getTypeSpecEndLoc());
226 return;
227 }
228
229 if (const auto *D = dyn_cast<EnumConstantDecl>(Val: SourceExprDecl)) {
230 diag(D->getLocation(),
231 "source type originates from referencing this enum constant",
232 DiagnosticIDs::Note);
233 return;
234 }
235
236 if (const auto *D = dyn_cast<BindingDecl>(Val: SourceExprDecl)) {
237 diag(D->getLocation(),
238 "source type originates from referencing this bound variable",
239 DiagnosticIDs::Note);
240 return;
241 }
242
243 if (const auto *D = dyn_cast<NonTypeTemplateParmDecl>(Val: SourceExprDecl)) {
244 diag(D->getLocation(),
245 "source type originates from referencing this non-type template "
246 "parameter",
247 DiagnosticIDs::Note);
248 return;
249 }
250}
251
252} // namespace clang::tidy::readability
253

source code of clang-tools-extra/clang-tidy/readability/RedundantCastingCheck.cpp