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(S: PtrS, D: 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(S: TypeS.getLocalUnqualifiedType(), |
49 | D: 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>(Val: 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(TypeS: LHSType.getNonReferenceType(), |
67 | TypeD: NonReferenceType, IgnoreTypeAliases)) |
68 | return false; |
69 | const QualType RHSType = B->getRHS()->IgnoreImplicit()->getType(); |
70 | if (RHSType.isNull() || !areTypesEqual(TypeS: RHSType.getNonReferenceType(), |
71 | TypeD: 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>(Val: CleanSourceExpr)) { |
80 | return E->getDecl(); |
81 | } |
82 | |
83 | if (const auto *E = dyn_cast<CallExpr>(Val: CleanSourceExpr)) { |
84 | return E->getCalleeDecl(); |
85 | } |
86 | |
87 | if (const auto *E = dyn_cast<MemberExpr>(Val: 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(LocalName: "IgnoreMacros" , Default: true)), |
97 | IgnoreTypeAliases(Options.get(LocalName: "IgnoreTypeAliases" , Default: false)) {} |
98 | |
99 | void RedundantCastingCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { |
100 | Options.store(Options&: Opts, LocalName: "IgnoreMacros" , Value: IgnoreMacros); |
101 | Options.store(Options&: Opts, LocalName: "IgnoreTypeAliases" , Value: IgnoreTypeAliases); |
102 | } |
103 | |
104 | void RedundantCastingCheck::registerMatchers(MatchFinder *Finder) { |
105 | auto SimpleType = qualType(hasCanonicalType( |
106 | InnerMatcher: qualType(anyOf(builtinType(), references(InnerMatcher: builtinType()), |
107 | references(InnerMatcher: pointsTo(InnerMatcher: qualType())), pointsTo(InnerMatcher: qualType()))))); |
108 | |
109 | auto BitfieldMemberExpr = memberExpr(member(InnerMatcher: fieldDecl(isBitField()))); |
110 | |
111 | const ast_matchers::internal::VariadicDynCastAllOfMatcher< |
112 | Stmt, CXXParenListInitExpr> |
113 | cxxParenListInitExpr; // NOLINT(readability-identifier-naming) |
114 | |
115 | Finder->addMatcher( |
116 | NodeMatch: explicitCastExpr( |
117 | unless(hasCastKind(Kind: CK_ConstructorConversion)), |
118 | unless(hasCastKind(Kind: CK_UserDefinedConversion)), |
119 | unless(cxxFunctionalCastExpr(hasDestinationType(InnerMatcher: unless(SimpleType)))), |
120 | |
121 | hasDestinationType(InnerMatcher: qualType().bind(ID: "dstType" )), |
122 | hasSourceExpression(InnerMatcher: anyOf( |
123 | expr(unless(initListExpr()), unless(BitfieldMemberExpr), |
124 | unless(cxxParenListInitExpr()), |
125 | hasType(InnerMatcher: qualType().bind(ID: "srcType" ))) |
126 | .bind(ID: "source" ), |
127 | initListExpr(unless(hasInit(N: 1, InnerMatcher: expr())), |
128 | hasInit(N: 0, InnerMatcher: expr(unless(BitfieldMemberExpr), |
129 | hasType(InnerMatcher: qualType().bind(ID: "srcType" ))) |
130 | .bind(ID: "source" )))))) |
131 | .bind(ID: "cast" ), |
132 | Action: this); |
133 | } |
134 | |
135 | void RedundantCastingCheck::check(const MatchFinder::MatchResult &Result) { |
136 | const auto *SourceExpr = Result.Nodes.getNodeAs<Expr>(ID: "source" ); |
137 | auto TypeD = *Result.Nodes.getNodeAs<QualType>(ID: "dstType" ); |
138 | |
139 | if (SourceExpr->getValueKind() == VK_LValue && |
140 | TypeD.getCanonicalType()->isRValueReferenceType()) |
141 | return; |
142 | |
143 | const auto TypeS = |
144 | Result.Nodes.getNodeAs<QualType>(ID: "srcType" )->getNonReferenceType(); |
145 | TypeD = TypeD.getNonReferenceType(); |
146 | |
147 | if (!areTypesEqual(TypeS, TypeD, IgnoreTypeAliases)) |
148 | return; |
149 | |
150 | if (!areBinaryOperatorOperandsTypesEqualToOperatorResultType( |
151 | E: SourceExpr, IgnoreTypeAliases)) |
152 | return; |
153 | |
154 | const auto *CastExpr = Result.Nodes.getNodeAs<ExplicitCastExpr>(ID: "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(Loc: SourceExpr->getBeginLoc()); |
169 | const SourceLocation SourceExprEnd = |
170 | SM.getExpansionLoc(Loc: SourceExpr->getEndLoc()); |
171 | |
172 | if (SourceExprBegin != CastExpr->getBeginLoc()) |
173 | Diag << FixItHint::CreateRemoval(RemoveRange: SourceRange( |
174 | CastExpr->getBeginLoc(), SourceExprBegin.getLocWithOffset(Offset: -1))); |
175 | |
176 | const SourceLocation NextToken = Lexer::getLocForEndOfToken( |
177 | Loc: SourceExprEnd, Offset: 0U, SM, LangOpts: Result.Context->getLangOpts()); |
178 | |
179 | if (SourceExprEnd != CastExpr->getEndLoc()) { |
180 | Diag << FixItHint::CreateRemoval( |
181 | RemoveRange: SourceRange(NextToken, CastExpr->getEndLoc())); |
182 | } |
183 | |
184 | if (utils::fixit::areParensNeededForStatement(*SourceExpr)) { |
185 | Diag << FixItHint::CreateInsertion(InsertionLoc: SourceExprBegin, Code: "(" ) |
186 | << FixItHint::CreateInsertion(InsertionLoc: NextToken, Code: ")" ); |
187 | } |
188 | } |
189 | |
190 | const auto *SourceExprDecl = getSourceExprDecl(SourceExpr); |
191 | if (!SourceExprDecl) |
192 | return; |
193 | |
194 | if (const auto *D = dyn_cast<CXXConstructorDecl>(Val: 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>(Val: 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>(Val: D) << D->getReturnTypeSourceRange(); |
207 | return; |
208 | } |
209 | |
210 | if (const auto *D = dyn_cast<FieldDecl>(Val: 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>(Val: 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>(Val: 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>(Val: 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>(Val: 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>(Val: 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 | |