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