1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 // Warn about certain redundant casts:
12 // * A reinterpret_cast<T*>(...) whose result is then implicitly cast to a void
15 // * A static_cast<T*>(e) where e is of void pointer type and whose result is
16 // then implicitly cast to a void pointer
18 // * Various const_casts that are either not needed (like casting away constness
19 // in a delete expression) or are implicitly cast back afterwards
21 // C-style casts are ignored because it makes this plugin simpler, and they
22 // should eventually be eliminated via loplugin:cstylecast and/or
23 // -Wold-style-cast. That implies that this plugin is only relevant for C++
26 #include "clang/Sema/Sema.h"
33 bool isVoidPointer(QualType type
) {
34 return type
->isPointerType()
35 && type
->getAs
<PointerType
>()->getPointeeType()->isVoidType();
39 public RecursiveASTVisitor
<RedundantCast
>, public loplugin::RewritePlugin
42 explicit RedundantCast(InstantiationData
const & data
): RewritePlugin(data
)
45 virtual void run() override
{
46 if (compiler
.getLangOpts().CPlusPlus
) {
47 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
51 bool VisitImplicitCastExpr(ImplicitCastExpr
const * expr
);
53 bool VisitCXXReinterpretCastExpr(CXXReinterpretCastExpr
const * expr
);
55 bool VisitCallExpr(CallExpr
const * expr
);
57 bool VisitCXXDeleteExpr(CXXDeleteExpr
const * expr
);
59 bool VisitBinSub(BinaryOperator
const * expr
)
60 { return visitBinOp(expr
); }
62 bool VisitBinLT(BinaryOperator
const * expr
)
63 { return visitBinOp(expr
); }
65 bool VisitBinGT(BinaryOperator
const * expr
)
66 { return visitBinOp(expr
); }
68 bool VisitBinLE(BinaryOperator
const * expr
)
69 { return visitBinOp(expr
); }
71 bool VisitBinGE(BinaryOperator
const * expr
)
72 { return visitBinOp(expr
); }
74 bool VisitBinEQ(BinaryOperator
const * expr
)
75 { return visitBinOp(expr
); }
77 bool VisitBinNE(BinaryOperator
const * expr
)
78 { return visitBinOp(expr
); }
81 bool visitBinOp(BinaryOperator
const * expr
);
84 bool RedundantCast::VisitImplicitCastExpr(const ImplicitCastExpr
* expr
) {
85 if (ignoreLocation(expr
)) {
88 switch (expr
->getCastKind()) {
90 if (expr
->getType()->isPointerType()
91 || expr
->getType()->isObjectType())
93 auto e
= dyn_cast
<CXXConstCastExpr
>(
94 expr
->getSubExpr()->IgnoreParenImpCasts());
96 auto t1
= e
->getSubExpr()->getType().getCanonicalType();
97 auto t2
= expr
->getType().getCanonicalType();
98 bool ObjCLifetimeConversion
;
99 if (t1
.getTypePtr() == t2
.getTypePtr()
100 || compiler
.getSema().IsQualificationConversion(
101 t1
, t2
, false, ObjCLifetimeConversion
))
104 DiagnosticsEngine::Warning
,
105 ("redundant const_cast from %0 to %1, result is"
106 " implictly cast to %2"),
108 << e
->getSubExprAsWritten()->getType() << e
->getType()
109 << expr
->getType() << expr
->getSourceRange();
115 if (isVoidPointer(expr
->getType())
116 && expr
->getSubExpr()->getType()->isPointerType())
118 Expr
const * e
= expr
->getSubExpr()->IgnoreParenImpCasts();
119 while (isa
<CXXConstCastExpr
>(e
)) {
120 auto cc
= dyn_cast
<CXXConstCastExpr
>(e
);
121 if (expr
->getType()->getAs
<PointerType
>()->getPointeeType()
122 .isAtLeastAsQualifiedAs(
123 cc
->getSubExpr()->getType()
124 ->getAs
<PointerType
>()->getPointeeType()))
127 DiagnosticsEngine::Warning
,
128 ("redundant const_cast from %0 to %1, result is"
129 " ultimately implictly cast to %2"),
131 << cc
->getSubExprAsWritten()->getType() << cc
->getType()
132 << expr
->getType() << expr
->getSourceRange();
134 e
= cc
->getSubExpr()->IgnoreParenImpCasts();
136 if (isa
<CXXReinterpretCastExpr
>(e
)) {
138 DiagnosticsEngine::Warning
,
139 ("redundant reinterpret_cast, result is implicitly cast to"
142 << e
->getSourceRange();
143 } else if (isa
<CXXStaticCastExpr
>(e
)
145 dyn_cast
<CXXStaticCastExpr
>(e
)->getSubExpr()
146 ->IgnoreParenImpCasts()->getType())
147 && !compat::isMacroBodyExpansion(
148 compiler
, e
->getLocStart()))
151 DiagnosticsEngine::Warning
,
152 ("redundant static_cast from void pointer, result is"
153 " implicitly cast to void pointer"),
155 << e
->getSourceRange();
159 case CK_DerivedToBase
:
160 case CK_UncheckedDerivedToBase
:
161 if (expr
->getType()->isPointerType()) {
162 Expr
const * e
= expr
->getSubExpr()->IgnoreParenImpCasts();
163 while (isa
<CXXConstCastExpr
>(e
)) {
164 auto cc
= dyn_cast
<CXXConstCastExpr
>(e
);
165 if (expr
->getType()->getAs
<PointerType
>()->getPointeeType()
166 .isAtLeastAsQualifiedAs(
167 cc
->getSubExpr()->getType()
168 ->getAs
<PointerType
>()->getPointeeType()))
171 DiagnosticsEngine::Warning
,
172 ("redundant const_cast from %0 to %1, result is"
173 " ultimately implictly cast to %2"),
175 << cc
->getSubExprAsWritten()->getType() << cc
->getType()
176 << expr
->getType() << expr
->getSourceRange();
178 e
= cc
->getSubExpr()->IgnoreParenImpCasts();
180 } else if (expr
->getType()->isReferenceType()) {
181 Expr
const * e
= expr
->getSubExpr()->IgnoreParenImpCasts();
182 while (isa
<CXXConstCastExpr
>(e
)) {
183 auto cc
= dyn_cast
<CXXConstCastExpr
>(e
);
184 if (expr
->getType()->getAs
<ReferenceType
>()->getPointeeType()
185 .isAtLeastAsQualifiedAs(
186 cc
->getSubExpr()->getType()
187 ->getAs
<ReferenceType
>()->getPointeeType()))
190 DiagnosticsEngine::Warning
,
191 ("redundant const_cast from %0 to %1, result is"
192 " ultimately implictly cast to %2"),
194 << cc
->getSubExprAsWritten()->getType() << cc
->getType()
195 << expr
->getType() << expr
->getSourceRange();
197 e
= cc
->getSubExpr()->IgnoreParenImpCasts();
207 bool RedundantCast::VisitCXXReinterpretCastExpr(
208 CXXReinterpretCastExpr
const * expr
)
210 if (ignoreLocation(expr
)) {
213 if (expr
->getSubExpr()->getType()->isVoidPointerType()) {
214 auto t
= expr
->getType()->getAs
<PointerType
>();
215 if (t
== nullptr || !t
->getPointeeType()->isObjectType()) {
218 if (rewriter
!= nullptr) {
219 auto loc
= expr
->getLocStart();
220 while (compiler
.getSourceManager().isMacroArgExpansion(loc
)) {
221 loc
= compiler
.getSourceManager().getImmediateMacroCallerLoc(
224 if (compat::isMacroBodyExpansion(compiler
, loc
)) {
225 auto loc2
= expr
->getLocEnd();
226 while (compiler
.getSourceManager().isMacroArgExpansion(loc2
)) {
227 loc2
= compiler
.getSourceManager()
228 .getImmediateMacroCallerLoc(loc2
);
230 if (compat::isMacroBodyExpansion(compiler
, loc2
)) {
231 //TODO: check loc, loc2 are in same macro body expansion
232 loc
= compiler
.getSourceManager().getSpellingLoc(loc
);
235 auto s
= compiler
.getSourceManager().getCharacterData(loc
);
236 auto n
= Lexer::MeasureTokenLength(
237 loc
, compiler
.getSourceManager(), compiler
.getLangOpts());
238 std::string
tok(s
, n
);
239 if (tok
== "reinterpret_cast" && replaceText(loc
, n
, "static_cast"))
245 DiagnosticsEngine::Warning
,
246 "reinterpret_cast from %0 to %1 can be simplified to static_cast",
248 << expr
->getSubExprAsWritten()->getType() << expr
->getType()
249 << expr
->getSourceRange();
250 } else if (expr
->getType()->isVoidPointerType()) {
251 auto t
= expr
->getSubExpr()->getType()->getAs
<PointerType
>();
252 if (t
== nullptr || !t
->getPointeeType()->isObjectType()) {
256 DiagnosticsEngine::Warning
,
257 ("reinterpret_cast from %0 to %1 can be simplified to static_cast"
258 " or an implicit conversion"),
260 << expr
->getSubExprAsWritten()->getType() << expr
->getType()
261 << expr
->getSourceRange();
266 bool RedundantCast::VisitCallExpr(CallExpr
const * expr
) {
267 if (ignoreLocation(expr
)) {
270 auto f
= expr
->getDirectCallee();
271 if (f
== nullptr || !f
->isVariadic()
272 || expr
->getNumArgs() <= f
->getNumParams())
276 for (auto i
= f
->getNumParams(); i
!= expr
->getNumArgs(); ++i
) {
277 auto a
= expr
->getArg(i
);
278 if (a
->getType()->isPointerType()) {
279 auto e
= dyn_cast
<CXXConstCastExpr
>(a
->IgnoreParenImpCasts());
282 DiagnosticsEngine::Warning
,
283 "redundant const_cast of variadic function argument",
285 << expr
->getSourceRange();
292 bool RedundantCast::VisitCXXDeleteExpr(CXXDeleteExpr
const * expr
) {
293 if (ignoreLocation(expr
)) {
296 auto e
= dyn_cast
<CXXConstCastExpr
>(
297 expr
->getArgument()->IgnoreParenImpCasts());
300 DiagnosticsEngine::Warning
,
301 "redundant const_cast in delete expression", e
->getExprLoc())
302 << expr
->getSourceRange();
307 bool RedundantCast::visitBinOp(BinaryOperator
const * expr
) {
308 if (ignoreLocation(expr
)) {
311 if (expr
->getLHS()->getType()->isPointerType()
312 && expr
->getRHS()->getType()->isPointerType())
314 auto e
= dyn_cast
<CXXConstCastExpr
>(
315 expr
->getLHS()->IgnoreParenImpCasts());
318 DiagnosticsEngine::Warning
,
319 "redundant const_cast on lhs of pointer %select{comparison|subtraction}0 expression",
321 << (expr
->getOpcode() == BO_Sub
) << expr
->getSourceRange();
323 e
= dyn_cast
<CXXConstCastExpr
>(
324 expr
->getRHS()->IgnoreParenImpCasts());
327 DiagnosticsEngine::Warning
,
328 "redundant const_cast on rhs of pointer %select{comparison|subtraction}0 expression",
330 << (expr
->getOpcode() == BO_Sub
) << expr
->getSourceRange();
336 loplugin::Plugin::Registration
<RedundantCast
> X("redundantcast", true);
340 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */