xref: /llvm-project/clang-tools-extra/clang-tidy/readability/RedundantCastingCheck.cpp (revision dc2963c8d77229ca1b20663beddef2323cc69a88)
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 
9 #include "RedundantCastingCheck.h"
10 #include "../utils/FixItHintUtils.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Lex/Lexer.h"
14 
15 using namespace clang::ast_matchers;
16 
17 namespace clang::tidy::readability {
18 
19 static 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 
41 static 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 
52 static bool areBinaryOperatorOperandsTypesEqualToOperatorResultType(
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 
77 static 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 
93 RedundantCastingCheck::RedundantCastingCheck(StringRef Name,
94                                              ClangTidyContext *Context)
95     : ClangTidyCheck(Name, Context),
96       IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)),
97       IgnoreTypeAliases(Options.get("IgnoreTypeAliases", false)) {}
98 
99 void RedundantCastingCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
100   Options.store(Opts, "IgnoreMacros", IgnoreMacros);
101   Options.store(Opts, "IgnoreTypeAliases", IgnoreTypeAliases);
102 }
103 
104 void RedundantCastingCheck::registerMatchers(MatchFinder *Finder) {
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 
135 void 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 
150   if (!areBinaryOperatorOperandsTypesEqualToOperatorResultType(
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 
184     if (utils::fixit::areParensNeededForStatement(*SourceExpr)) {
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
258