clang-tools 20.0.0git
RedundantCastingCheck.cpp
Go to the documentation of this file.
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
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
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(PtrS, 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(TypeS.getLocalUnqualifiedType(),
49 TypeD.getLocalUnqualifiedType());
50}
51
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>(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(LHSType.getNonReferenceType(),
67 NonReferenceType, IgnoreTypeAliases))
68 return false;
69 const QualType RHSType = B->getRHS()->IgnoreImplicit()->getType();
70 if (RHSType.isNull() || !areTypesEqual(RHSType.getNonReferenceType(),
71 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>(CleanSourceExpr)) {
80 return E->getDecl();
81 }
82
83 if (const auto *E = dyn_cast<CallExpr>(CleanSourceExpr)) {
84 return E->getCalleeDecl();
85 }
86
87 if (const auto *E = dyn_cast<MemberExpr>(CleanSourceExpr)) {
88 return E->getMemberDecl();
89 }
90 return nullptr;
91}
92
94 ClangTidyContext *Context)
95 : ClangTidyCheck(Name, Context),
96 IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)),
97 IgnoreTypeAliases(Options.get("IgnoreTypeAliases", false)) {}
98
100 Options.store(Opts, "IgnoreMacros", IgnoreMacros);
101 Options.store(Opts, "IgnoreTypeAliases", IgnoreTypeAliases);
102}
103
105 auto SimpleType = qualType(hasCanonicalType(
106 qualType(anyOf(builtinType(), references(builtinType()),
107 references(pointsTo(qualType())), pointsTo(qualType())))));
108
109 auto BitfieldMemberExpr = memberExpr(member(fieldDecl(isBitField())));
110
111 const ast_matchers::internal::VariadicDynCastAllOfMatcher<
112 Stmt, CXXParenListInitExpr>
113 cxxParenListInitExpr; // NOLINT(readability-identifier-naming)
114
115 Finder->addMatcher(
116 explicitCastExpr(
117 unless(hasCastKind(CK_ConstructorConversion)),
118 unless(hasCastKind(CK_UserDefinedConversion)),
119 unless(cxxFunctionalCastExpr(hasDestinationType(unless(SimpleType)))),
120
121 hasDestinationType(qualType().bind("dstType")),
122 hasSourceExpression(anyOf(
123 expr(unless(initListExpr()), unless(BitfieldMemberExpr),
124 unless(cxxParenListInitExpr()),
125 hasType(qualType().bind("srcType")))
126 .bind("source"),
127 initListExpr(unless(hasInit(1, expr())),
128 hasInit(0, expr(unless(BitfieldMemberExpr),
129 hasType(qualType().bind("srcType")))
130 .bind("source"))))))
131 .bind("cast"),
132 this);
133}
134
135void RedundantCastingCheck::check(const MatchFinder::MatchResult &Result) {
136 const auto *SourceExpr = Result.Nodes.getNodeAs<Expr>("source");
137 auto TypeD = *Result.Nodes.getNodeAs<QualType>("dstType");
138
139 if (SourceExpr->getValueKind() == VK_LValue &&
140 TypeD.getCanonicalType()->isRValueReferenceType())
141 return;
142
143 const auto TypeS =
144 Result.Nodes.getNodeAs<QualType>("srcType")->getNonReferenceType();
145 TypeD = TypeD.getNonReferenceType();
146
147 if (!areTypesEqual(TypeS, TypeD, IgnoreTypeAliases))
148 return;
149
151 SourceExpr, IgnoreTypeAliases))
152 return;
153
154 const auto *CastExpr = Result.Nodes.getNodeAs<ExplicitCastExpr>("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(SourceExpr->getBeginLoc());
169 const SourceLocation SourceExprEnd =
170 SM.getExpansionLoc(SourceExpr->getEndLoc());
171
172 if (SourceExprBegin != CastExpr->getBeginLoc())
173 Diag << FixItHint::CreateRemoval(SourceRange(
174 CastExpr->getBeginLoc(), SourceExprBegin.getLocWithOffset(-1)));
175
176 const SourceLocation NextToken = Lexer::getLocForEndOfToken(
177 SourceExprEnd, 0U, SM, Result.Context->getLangOpts());
178
179 if (SourceExprEnd != CastExpr->getEndLoc()) {
180 Diag << FixItHint::CreateRemoval(
181 SourceRange(NextToken, CastExpr->getEndLoc()));
182 }
183
185 Diag << FixItHint::CreateInsertion(SourceExprBegin, "(")
186 << FixItHint::CreateInsertion(NextToken, ")");
187 }
188 }
189
190 const auto *SourceExprDecl = getSourceExprDecl(SourceExpr);
191 if (!SourceExprDecl)
192 return;
193
194 if (const auto *D = dyn_cast<CXXConstructorDecl>(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>(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>(D) << D->getReturnTypeSourceRange();
207 return;
208 }
209
210 if (const auto *D = dyn_cast<FieldDecl>(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>(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>(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>(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>(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>(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
QualType DeducedType
Definition: AST.cpp:583
const Expr * E
const FunctionDecl * Decl
llvm::SmallString< 256U > Name
NodeType Type
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
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
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
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