bump product version to 5.0.4.1
[LibreOffice.git] / compilerplugins / clang / redundantcast.cxx
blob474dafa11e853fefa2c0d968b62a23480b19b508
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
10 // Warn about certain redundant casts:
12 // * A reinterpret_cast<T*>(...) whose result is then implicitly cast to a void
13 // pointer
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++
24 // code.
26 #include "clang/Sema/Sema.h"
28 #include "compat.hxx"
29 #include "plugin.hxx"
31 namespace {
33 bool isVoidPointer(QualType type) {
34 return type->isPointerType()
35 && type->getAs<PointerType>()->getPointeeType()->isVoidType();
38 class RedundantCast:
39 public RecursiveASTVisitor<RedundantCast>, public loplugin::RewritePlugin
41 public:
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); }
80 private:
81 bool visitBinOp(BinaryOperator const * expr);
84 bool RedundantCast::VisitImplicitCastExpr(const ImplicitCastExpr * expr) {
85 if (ignoreLocation(expr)) {
86 return true;
88 switch (expr->getCastKind()) {
89 case CK_NoOp:
90 if (expr->getType()->isPointerType()
91 || expr->getType()->isObjectType())
93 auto e = dyn_cast<CXXConstCastExpr>(
94 expr->getSubExpr()->IgnoreParenImpCasts());
95 if (e != nullptr) {
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))
103 report(
104 DiagnosticsEngine::Warning,
105 ("redundant const_cast from %0 to %1, result is"
106 " implictly cast to %2"),
107 e->getExprLoc())
108 << e->getSubExprAsWritten()->getType() << e->getType()
109 << expr->getType() << expr->getSourceRange();
113 break;
114 case CK_BitCast:
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()))
126 report(
127 DiagnosticsEngine::Warning,
128 ("redundant const_cast from %0 to %1, result is"
129 " ultimately implictly cast to %2"),
130 cc->getExprLoc())
131 << cc->getSubExprAsWritten()->getType() << cc->getType()
132 << expr->getType() << expr->getSourceRange();
134 e = cc->getSubExpr()->IgnoreParenImpCasts();
136 if (isa<CXXReinterpretCastExpr>(e)) {
137 report(
138 DiagnosticsEngine::Warning,
139 ("redundant reinterpret_cast, result is implicitly cast to"
140 " void pointer"),
141 e->getExprLoc())
142 << e->getSourceRange();
143 } else if (isa<CXXStaticCastExpr>(e)
144 && isVoidPointer(
145 dyn_cast<CXXStaticCastExpr>(e)->getSubExpr()
146 ->IgnoreParenImpCasts()->getType())
147 && !compat::isMacroBodyExpansion(
148 compiler, e->getLocStart()))
150 report(
151 DiagnosticsEngine::Warning,
152 ("redundant static_cast from void pointer, result is"
153 " implicitly cast to void pointer"),
154 e->getExprLoc())
155 << e->getSourceRange();
158 break;
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()))
170 report(
171 DiagnosticsEngine::Warning,
172 ("redundant const_cast from %0 to %1, result is"
173 " ultimately implictly cast to %2"),
174 cc->getExprLoc())
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()))
189 report(
190 DiagnosticsEngine::Warning,
191 ("redundant const_cast from %0 to %1, result is"
192 " ultimately implictly cast to %2"),
193 cc->getExprLoc())
194 << cc->getSubExprAsWritten()->getType() << cc->getType()
195 << expr->getType() << expr->getSourceRange();
197 e = cc->getSubExpr()->IgnoreParenImpCasts();
200 break;
201 default:
202 break;
204 return true;
207 bool RedundantCast::VisitCXXReinterpretCastExpr(
208 CXXReinterpretCastExpr const * expr)
210 if (ignoreLocation(expr)) {
211 return true;
213 if (expr->getSubExpr()->getType()->isVoidPointerType()) {
214 auto t = expr->getType()->getAs<PointerType>();
215 if (t == nullptr || !t->getPointeeType()->isObjectType()) {
216 return true;
218 if (rewriter != nullptr) {
219 auto loc = expr->getLocStart();
220 while (compiler.getSourceManager().isMacroArgExpansion(loc)) {
221 loc = compiler.getSourceManager().getImmediateMacroCallerLoc(
222 loc);
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"))
241 return true;
244 report(
245 DiagnosticsEngine::Warning,
246 "reinterpret_cast from %0 to %1 can be simplified to static_cast",
247 expr->getExprLoc())
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()) {
253 return true;
255 report(
256 DiagnosticsEngine::Warning,
257 ("reinterpret_cast from %0 to %1 can be simplified to static_cast"
258 " or an implicit conversion"),
259 expr->getExprLoc())
260 << expr->getSubExprAsWritten()->getType() << expr->getType()
261 << expr->getSourceRange();
263 return true;
266 bool RedundantCast::VisitCallExpr(CallExpr const * expr) {
267 if (ignoreLocation(expr)) {
268 return true;
270 auto f = expr->getDirectCallee();
271 if (f == nullptr || !f->isVariadic()
272 || expr->getNumArgs() <= f->getNumParams())
274 return true;
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());
280 if (e != nullptr) {
281 report(
282 DiagnosticsEngine::Warning,
283 "redundant const_cast of variadic function argument",
284 e->getExprLoc())
285 << expr->getSourceRange();
289 return true;
292 bool RedundantCast::VisitCXXDeleteExpr(CXXDeleteExpr const * expr) {
293 if (ignoreLocation(expr)) {
294 return true;
296 auto e = dyn_cast<CXXConstCastExpr>(
297 expr->getArgument()->IgnoreParenImpCasts());
298 if (e != nullptr) {
299 report(
300 DiagnosticsEngine::Warning,
301 "redundant const_cast in delete expression", e->getExprLoc())
302 << expr->getSourceRange();
304 return true;
307 bool RedundantCast::visitBinOp(BinaryOperator const * expr) {
308 if (ignoreLocation(expr)) {
309 return true;
311 if (expr->getLHS()->getType()->isPointerType()
312 && expr->getRHS()->getType()->isPointerType())
314 auto e = dyn_cast<CXXConstCastExpr>(
315 expr->getLHS()->IgnoreParenImpCasts());
316 if (e != nullptr) {
317 report(
318 DiagnosticsEngine::Warning,
319 "redundant const_cast on lhs of pointer %select{comparison|subtraction}0 expression",
320 e->getExprLoc())
321 << (expr->getOpcode() == BO_Sub) << expr->getSourceRange();
323 e = dyn_cast<CXXConstCastExpr>(
324 expr->getRHS()->IgnoreParenImpCasts());
325 if (e != nullptr) {
326 report(
327 DiagnosticsEngine::Warning,
328 "redundant const_cast on rhs of pointer %select{comparison|subtraction}0 expression",
329 e->getExprLoc())
330 << (expr->getOpcode() == BO_Sub) << expr->getSourceRange();
333 return true;
336 loplugin::Plugin::Registration<RedundantCast> X("redundantcast", true);
340 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */