[TargetVersion] Only enable on RISC-V and AArch64 (#115991)
[llvm-project.git] / clang-tools-extra / clang-tidy / readability / RedundantCastingCheck.cpp
blobb9ff0e81cbc5226916906a8668e2730f960e548b
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 //===----------------------------------------------------------------------===//
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"
15 using namespace clang::ast_matchers;
17 namespace clang::tidy::readability {
19 static bool areTypesEqual(QualType S, QualType D) {
20 if (S == D)
21 return true;
23 const auto *TS = S->getAs<TypedefType>();
24 const auto *TD = D->getAs<TypedefType>();
25 if (TS != TD)
26 return false;
28 QualType PtrS = S->getPointeeType();
29 QualType PtrD = D->getPointeeType();
31 if (!PtrS.isNull() && !PtrD.isNull())
32 return areTypesEqual(PtrS, PtrD);
34 const DeducedType *DT = S->getContainedDeducedType();
35 if (DT && DT->isDeduced())
36 return D == DT->getDeducedType();
38 return false;
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;
48 return IgnoreTypeAliases || areTypesEqual(TypeS.getLocalUnqualifiedType(),
49 TypeD.getLocalUnqualifiedType());
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;
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;
74 return true;
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();
83 if (const auto *E = dyn_cast<CallExpr>(CleanSourceExpr)) {
84 return E->getCalleeDecl();
87 if (const auto *E = dyn_cast<MemberExpr>(CleanSourceExpr)) {
88 return E->getMemberDecl();
90 return nullptr;
93 RedundantCastingCheck::RedundantCastingCheck(StringRef Name,
94 ClangTidyContext *Context)
95 : ClangTidyCheck(Name, Context),
96 IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)),
97 IgnoreTypeAliases(Options.getLocalOrGlobal("IgnoreTypeAliases", false)) {}
99 void RedundantCastingCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
100 Options.store(Opts, "IgnoreMacros", IgnoreMacros);
101 Options.store(Opts, "IgnoreTypeAliases", IgnoreTypeAliases);
104 void RedundantCastingCheck::registerMatchers(MatchFinder *Finder) {
105 auto SimpleType = qualType(hasCanonicalType(
106 qualType(anyOf(builtinType(), references(builtinType()),
107 references(pointsTo(qualType())), pointsTo(qualType())))));
109 auto BitfieldMemberExpr = memberExpr(member(fieldDecl(isBitField())));
111 Finder->addMatcher(
112 explicitCastExpr(
113 unless(hasCastKind(CK_ConstructorConversion)),
114 unless(hasCastKind(CK_UserDefinedConversion)),
115 unless(cxxFunctionalCastExpr(hasDestinationType(unless(SimpleType)))),
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);
130 void RedundantCastingCheck::check(const MatchFinder::MatchResult &Result) {
131 const auto *SourceExpr = Result.Nodes.getNodeAs<Expr>("source");
132 auto TypeD = *Result.Nodes.getNodeAs<QualType>("dstType");
134 if (SourceExpr->getValueKind() == VK_LValue &&
135 TypeD.getCanonicalType()->isRValueReferenceType())
136 return;
138 const auto TypeS =
139 Result.Nodes.getNodeAs<QualType>("srcType")->getNonReferenceType();
140 TypeD = TypeD.getNonReferenceType();
142 if (!areTypesEqual(TypeS, TypeD, IgnoreTypeAliases))
143 return;
145 if (!areBinaryOperatorOperandsTypesEqualToOperatorResultType(
146 SourceExpr, IgnoreTypeAliases))
147 return;
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;
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;
161 const SourceManager &SM = *Result.SourceManager;
162 const SourceLocation SourceExprBegin =
163 SM.getExpansionLoc(SourceExpr->getBeginLoc());
164 const SourceLocation SourceExprEnd =
165 SM.getExpansionLoc(SourceExpr->getEndLoc());
167 if (SourceExprBegin != CastExpr->getBeginLoc())
168 Diag << FixItHint::CreateRemoval(SourceRange(
169 CastExpr->getBeginLoc(), SourceExprBegin.getLocWithOffset(-1)));
171 const SourceLocation NextToken = Lexer::getLocForEndOfToken(
172 SourceExprEnd, 0U, SM, Result.Context->getLangOpts());
174 if (SourceExprEnd != CastExpr->getEndLoc()) {
175 Diag << FixItHint::CreateRemoval(
176 SourceRange(NextToken, CastExpr->getEndLoc()));
179 if (utils::fixit::areParensNeededForStatement(*SourceExpr)) {
180 Diag << FixItHint::CreateInsertion(SourceExprBegin, "(")
181 << FixItHint::CreateInsertion(NextToken, ")");
185 const auto *SourceExprDecl = getSourceExprDecl(SourceExpr);
186 if (!SourceExprDecl)
187 return;
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;
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;
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;
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;
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;
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;
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;
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;
252 } // namespace clang::tidy::readability