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.get(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 const ast_matchers::internal::VariadicDynCastAllOfMatcher<
112 Stmt, CXXParenListInitExpr>
113 cxxParenListInitExpr; // NOLINT(readability-identifier-naming)
114
115 Finder->addMatcher(
116 NodeMatch: explicitCastExpr(
117 unless(hasCastKind(Kind: CK_ConstructorConversion)),
118 unless(hasCastKind(Kind: CK_UserDefinedConversion)),
119 unless(cxxFunctionalCastExpr(hasDestinationType(InnerMatcher: unless(SimpleType)))),
120
121 hasDestinationType(InnerMatcher: qualType().bind(ID: "dstType")),
122 hasSourceExpression(InnerMatcher: anyOf(
123 expr(unless(initListExpr()), unless(BitfieldMemberExpr),
124 unless(cxxParenListInitExpr()),
125 hasType(InnerMatcher: qualType().bind(ID: "srcType")))
126 .bind(ID: "source"),
127 initListExpr(unless(hasInit(N: 1, InnerMatcher: expr())),
128 hasInit(N: 0, InnerMatcher: expr(unless(BitfieldMemberExpr),
129 hasType(InnerMatcher: qualType().bind(ID: "srcType")))
130 .bind(ID: "source"))))))
131 .bind(ID: "cast"),
132 Action: this);
133}
134
135void RedundantCastingCheck::check(const MatchFinder::MatchResult &Result) {
136 const auto *SourceExpr = Result.Nodes.getNodeAs<Expr>(ID: "source");
137 auto TypeD = *Result.Nodes.getNodeAs<QualType>(ID: "dstType");
138
139 if (SourceExpr->getValueKind() == VK_LValue &&
140 TypeD.getCanonicalType()->isRValueReferenceType())
141 return;
142
143 const auto TypeS =
144 Result.Nodes.getNodeAs<QualType>(ID: "srcType")->getNonReferenceType();
145 TypeD = TypeD.getNonReferenceType();
146
147 if (!areTypesEqual(TypeS, TypeD, IgnoreTypeAliases))
148 return;
149
150 if (!areBinaryOperatorOperandsTypesEqualToOperatorResultType(
151 E: SourceExpr, IgnoreTypeAliases))
152 return;
153
154 const auto *CastExpr = Result.Nodes.getNodeAs<ExplicitCastExpr>(ID: "cast");
155 if (IgnoreMacros &&
156 (CastExpr->getBeginLoc().isMacroID() ||
157 CastExpr->getEndLoc().isMacroID() || CastExpr->getExprLoc().isMacroID()))
158 return;
159
160 {
161 auto Diag = diag(CastExpr->getExprLoc(),
162 "redundant explicit casting to the same type %0 as the "
163 "sub-expression, remove this casting");
164 Diag << TypeD;
165
166 const SourceManager &SM = *Result.SourceManager;
167 const SourceLocation SourceExprBegin =
168 SM.getExpansionLoc(Loc: SourceExpr->getBeginLoc());
169 const SourceLocation SourceExprEnd =
170 SM.getExpansionLoc(Loc: SourceExpr->getEndLoc());
171
172 if (SourceExprBegin != CastExpr->getBeginLoc())
173 Diag << FixItHint::CreateRemoval(RemoveRange: SourceRange(
174 CastExpr->getBeginLoc(), SourceExprBegin.getLocWithOffset(Offset: -1)));
175
176 const SourceLocation NextToken = Lexer::getLocForEndOfToken(
177 Loc: SourceExprEnd, Offset: 0U, SM, LangOpts: Result.Context->getLangOpts());
178
179 if (SourceExprEnd != CastExpr->getEndLoc()) {
180 Diag << FixItHint::CreateRemoval(
181 RemoveRange: SourceRange(NextToken, CastExpr->getEndLoc()));
182 }
183
184 if (utils::fixit::areParensNeededForStatement(*SourceExpr)) {
185 Diag << FixItHint::CreateInsertion(InsertionLoc: SourceExprBegin, Code: "(")
186 << FixItHint::CreateInsertion(InsertionLoc: NextToken, Code: ")");
187 }
188 }
189
190 const auto *SourceExprDecl = getSourceExprDecl(SourceExpr);
191 if (!SourceExprDecl)
192 return;
193
194 if (const auto *D = dyn_cast<CXXConstructorDecl>(Val: SourceExprDecl)) {
195 diag(D->getLocation(),
196 "source type originates from the invocation of this constructor",
197 DiagnosticIDs::Note);
198 return;
199 }
200
201 if (const auto *D = dyn_cast<FunctionDecl>(Val: SourceExprDecl)) {
202 diag(D->getLocation(),
203 "source type originates from the invocation of this "
204 "%select{function|method}0",
205 DiagnosticIDs::Note)
206 << isa<CXXMethodDecl>(Val: D) << D->getReturnTypeSourceRange();
207 return;
208 }
209
210 if (const auto *D = dyn_cast<FieldDecl>(Val: SourceExprDecl)) {
211 diag(D->getLocation(),
212 "source type originates from referencing this member",
213 DiagnosticIDs::Note)
214 << SourceRange(D->getTypeSpecStartLoc(), D->getTypeSpecEndLoc());
215 return;
216 }
217
218 if (const auto *D = dyn_cast<ParmVarDecl>(Val: SourceExprDecl)) {
219 diag(D->getLocation(),
220 "source type originates from referencing this parameter",
221 DiagnosticIDs::Note)
222 << SourceRange(D->getTypeSpecStartLoc(), D->getTypeSpecEndLoc());
223 return;
224 }
225
226 if (const auto *D = dyn_cast<VarDecl>(Val: SourceExprDecl)) {
227 diag(D->getLocation(),
228 "source type originates from referencing this variable",
229 DiagnosticIDs::Note)
230 << SourceRange(D->getTypeSpecStartLoc(), D->getTypeSpecEndLoc());
231 return;
232 }
233
234 if (const auto *D = dyn_cast<EnumConstantDecl>(Val: SourceExprDecl)) {
235 diag(D->getLocation(),
236 "source type originates from referencing this enum constant",
237 DiagnosticIDs::Note);
238 return;
239 }
240
241 if (const auto *D = dyn_cast<BindingDecl>(Val: SourceExprDecl)) {
242 diag(D->getLocation(),
243 "source type originates from referencing this bound variable",
244 DiagnosticIDs::Note);
245 return;
246 }
247
248 if (const auto *D = dyn_cast<NonTypeTemplateParmDecl>(Val: SourceExprDecl)) {
249 diag(D->getLocation(),
250 "source type originates from referencing this non-type template "
251 "parameter",
252 DiagnosticIDs::Note);
253 return;
254 }
255}
256
257} // namespace clang::tidy::readability
258

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

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