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