clang-tools 22.0.0git
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) {
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 if (LHSType.isNull() || !areTypesEqual(LHSType.getNonReferenceType(),
68 NonReferenceType, IgnoreTypeAliases))
69 return false;
70 const QualType RHSType = B->getRHS()->IgnoreImplicit()->getType();
71 if (RHSType.isNull() || !areTypesEqual(RHSType.getNonReferenceType(),
72 NonReferenceType, IgnoreTypeAliases))
73 return false;
74 }
75 return true;
76}
77
78static const Decl *getSourceExprDecl(const Expr *SourceExpr) {
79 const Expr *CleanSourceExpr = SourceExpr->IgnoreParenImpCasts();
80 if (const auto *E = dyn_cast<DeclRefExpr>(CleanSourceExpr)) {
81 return E->getDecl();
82 }
83
84 if (const auto *E = dyn_cast<CallExpr>(CleanSourceExpr)) {
85 return E->getCalleeDecl();
86 }
87
88 if (const auto *E = dyn_cast<MemberExpr>(CleanSourceExpr)) {
89 return E->getMemberDecl();
90 }
91 return nullptr;
92}
93
95 ClangTidyContext *Context)
96 : ClangTidyCheck(Name, Context),
97 IgnoreMacros(Options.get("IgnoreMacros", true)),
98 IgnoreTypeAliases(Options.get("IgnoreTypeAliases", false)) {}
99
101 Options.store(Opts, "IgnoreMacros", IgnoreMacros);
102 Options.store(Opts, "IgnoreTypeAliases", IgnoreTypeAliases);
103}
104
106 auto SimpleType = qualType(hasCanonicalType(
107 qualType(anyOf(builtinType(), references(builtinType()),
108 references(pointsTo(qualType())), pointsTo(qualType())))));
109
110 auto BitfieldMemberExpr = memberExpr(member(fieldDecl(isBitField())));
111
112 const ast_matchers::internal::VariadicDynCastAllOfMatcher<
113 Stmt, CXXParenListInitExpr>
114 cxxParenListInitExpr; // NOLINT(readability-identifier-naming)
115
116 Finder->addMatcher(
117 explicitCastExpr(
118 unless(hasCastKind(CK_ConstructorConversion)),
119 unless(hasCastKind(CK_UserDefinedConversion)),
120 unless(cxxFunctionalCastExpr(hasDestinationType(unless(SimpleType)))),
121
122 hasDestinationType(qualType().bind("dstType")),
123 hasSourceExpression(anyOf(
124 expr(unless(initListExpr()), unless(BitfieldMemberExpr),
125 unless(cxxParenListInitExpr()),
126 hasType(qualType().bind("srcType")))
127 .bind("source"),
128 initListExpr(unless(hasInit(1, expr())),
129 hasInit(0, expr(unless(BitfieldMemberExpr),
130 hasType(qualType().bind("srcType")))
131 .bind("source"))))))
132 .bind("cast"),
133 this);
134}
135
136void RedundantCastingCheck::check(const MatchFinder::MatchResult &Result) {
137 const auto *SourceExpr = Result.Nodes.getNodeAs<Expr>("source");
138 auto TypeD = *Result.Nodes.getNodeAs<QualType>("dstType");
139
140 if (SourceExpr->getValueKind() == VK_LValue &&
141 TypeD.getCanonicalType()->isRValueReferenceType())
142 return;
143
144 const auto TypeS =
145 Result.Nodes.getNodeAs<QualType>("srcType")->getNonReferenceType();
146 TypeD = TypeD.getNonReferenceType();
147
148 if (!areTypesEqual(TypeS, TypeD, IgnoreTypeAliases))
149 return;
150
152 SourceExpr, IgnoreTypeAliases))
153 return;
154
155 const auto *CastExpr = Result.Nodes.getNodeAs<ExplicitCastExpr>("cast");
156 if (IgnoreMacros &&
157 (CastExpr->getBeginLoc().isMacroID() ||
158 CastExpr->getEndLoc().isMacroID() || CastExpr->getExprLoc().isMacroID()))
159 return;
160
161 {
162 auto Diag = diag(CastExpr->getExprLoc(),
163 "redundant explicit casting to the same type %0 as the "
164 "sub-expression, remove this casting");
165 Diag << TypeD;
166
167 const SourceManager &SM = *Result.SourceManager;
168 const SourceLocation SourceExprBegin =
169 SM.getExpansionLoc(SourceExpr->getBeginLoc());
170 const SourceLocation SourceExprEnd =
171 SM.getExpansionLoc(SourceExpr->getEndLoc());
172
173 if (SourceExprBegin != CastExpr->getBeginLoc())
174 Diag << FixItHint::CreateRemoval(SourceRange(
175 CastExpr->getBeginLoc(), SourceExprBegin.getLocWithOffset(-1)));
176
177 const SourceLocation NextToken = Lexer::getLocForEndOfToken(
178 SourceExprEnd, 0U, SM, Result.Context->getLangOpts());
179
180 if (SourceExprEnd != CastExpr->getEndLoc()) {
181 Diag << FixItHint::CreateRemoval(
182 SourceRange(NextToken, CastExpr->getEndLoc()));
183 }
184
186 Diag << FixItHint::CreateInsertion(SourceExprBegin, "(")
187 << FixItHint::CreateInsertion(NextToken, ")");
188 }
189 }
190
191 const auto *SourceExprDecl = getSourceExprDecl(SourceExpr);
192 if (!SourceExprDecl)
193 return;
194
195 if (const auto *D = dyn_cast<CXXConstructorDecl>(SourceExprDecl)) {
196 diag(D->getLocation(),
197 "source type originates from the invocation of this constructor",
198 DiagnosticIDs::Note);
199 return;
200 }
201
202 if (const auto *D = dyn_cast<FunctionDecl>(SourceExprDecl)) {
203 diag(D->getLocation(),
204 "source type originates from the invocation of this "
205 "%select{function|method}0",
206 DiagnosticIDs::Note)
207 << isa<CXXMethodDecl>(D) << D->getReturnTypeSourceRange();
208 return;
209 }
210
211 if (const auto *D = dyn_cast<FieldDecl>(SourceExprDecl)) {
212 diag(D->getLocation(),
213 "source type originates from referencing this member",
214 DiagnosticIDs::Note)
215 << SourceRange(D->getTypeSpecStartLoc(), D->getTypeSpecEndLoc());
216 return;
217 }
218
219 if (const auto *D = dyn_cast<ParmVarDecl>(SourceExprDecl)) {
220 diag(D->getLocation(),
221 "source type originates from referencing this parameter",
222 DiagnosticIDs::Note)
223 << SourceRange(D->getTypeSpecStartLoc(), D->getTypeSpecEndLoc());
224 return;
225 }
226
227 if (const auto *D = dyn_cast<VarDecl>(SourceExprDecl)) {
228 diag(D->getLocation(),
229 "source type originates from referencing this variable",
230 DiagnosticIDs::Note)
231 << SourceRange(D->getTypeSpecStartLoc(), D->getTypeSpecEndLoc());
232 return;
233 }
234
235 if (const auto *D = dyn_cast<EnumConstantDecl>(SourceExprDecl)) {
236 diag(D->getLocation(),
237 "source type originates from referencing this enum constant",
238 DiagnosticIDs::Note);
239 return;
240 }
241
242 if (const auto *D = dyn_cast<BindingDecl>(SourceExprDecl)) {
243 diag(D->getLocation(),
244 "source type originates from referencing this bound variable",
245 DiagnosticIDs::Note);
246 return;
247 }
248
249 if (const auto *D = dyn_cast<NonTypeTemplateParmDecl>(SourceExprDecl)) {
250 diag(D->getLocation(),
251 "source type originates from referencing this non-type template "
252 "parameter",
253 DiagnosticIDs::Note);
254 return;
255 }
256}
257
258} // 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 bool areBinaryOperatorOperandsTypesEqualToOperatorResultType(const Expr *E, bool IgnoreTypeAliases)
static const Decl * getSourceExprDecl(const Expr *SourceExpr)
bool areParensNeededForStatement(const Stmt &Node)
llvm::StringMap< ClangTidyValue > OptionMap