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