Add rowstride to tiled rendering via LibLO.
[LibreOffice.git] / compilerplugins / clang / implicitboolconversion.cxx
blobd8ef00e6dbefbaaf26ba996eb82d7c53d8ffbcaf
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 #include <algorithm>
11 #include <cassert>
12 #include <cstddef>
13 #include <iterator>
14 #include <stack>
15 #include <string>
16 #include <vector>
18 #include "compat.hxx"
19 #include "plugin.hxx"
21 template<> struct std::iterator_traits<ExprIterator> {
22 typedef std::ptrdiff_t difference_type;
23 typedef Expr * value_type;
24 typedef Expr const ** pointer;
25 typedef Expr const & reference;
26 typedef std::random_access_iterator_tag iterator_category;
29 template<> struct std::iterator_traits<ConstExprIterator> {
30 typedef std::ptrdiff_t difference_type;
31 typedef Expr const * value_type;
32 typedef Expr const ** pointer;
33 typedef Expr const & reference;
34 typedef std::random_access_iterator_tag iterator_category;
37 namespace {
39 bool isBool(Expr const * expr, bool allowTypedefs = true) {
40 QualType t1 { expr->getType() };
41 if (t1->isBooleanType()) {
42 return true;
44 if (!allowTypedefs) {
45 return false;
47 // css::uno::Sequence<sal_Bool> s(1);s[0]=false /*unotools/source/config/configitem.cxx*/:
48 if(t1->isSpecificBuiltinType(BuiltinType::UChar))return true;
49 TypedefType const * t2 = t1->getAs<TypedefType>();
50 if (t2 == nullptr) {
51 return false;
53 std::string name(t2->getDecl()->getNameAsString());
54 return name == "sal_Bool" || name == "BOOL" || name == "FcBool"
55 || name == "UBool" || name == "dbus_bool_t" || name == "gboolean"
56 || name == "hb_bool_t";
59 bool isBoolExpr(Expr const * expr) {
60 if (isBool(expr)) {
61 return true;
63 ConditionalOperator const * co = dyn_cast<ConditionalOperator>(expr);
64 if (co != nullptr) {
65 ImplicitCastExpr const * ic1 = dyn_cast<ImplicitCastExpr>(
66 co->getTrueExpr()->IgnoreParens());
67 ImplicitCastExpr const * ic2 = dyn_cast<ImplicitCastExpr>(
68 co->getFalseExpr()->IgnoreParens());
69 if (ic1 != nullptr && ic2 != nullptr
70 && ic1->getType()->isSpecificBuiltinType(BuiltinType::Int)
71 && isBoolExpr(ic1->getSubExpr()->IgnoreParens())
72 && ic2->getType()->isSpecificBuiltinType(BuiltinType::Int)
73 && isBoolExpr(ic2->getSubExpr()->IgnoreParens()))
75 return true;
78 return false;
81 // It appears that, given a function declaration, there is no way to determine
82 // the language linkage of the function's type, only of the function's name
83 // (via FunctionDecl::isExternC); however, in a case like
85 // extern "C" { static void f(); }
87 // the function's name does not have C language linkage while the function's
88 // type does (as clarified in C++11 [decl.link]); cf. <http://clang-developers.
89 // 42468.n3.nabble.com/Language-linkage-of-function-type-tt4037248.html>
90 // "Language linkage of function type":
91 bool hasCLanguageLinkageType(FunctionDecl const * decl) {
92 assert(decl != nullptr);
93 if (decl->isExternC()) {
94 return true;
96 #if (__clang_major__ == 3 && __clang_minor__ >= 3) || __clang_major__ > 3
97 if (decl->isInExternCContext()) {
98 return true;
100 #else
101 if (decl->getCanonicalDecl()->getDeclContext()->isExternCContext()) {
102 return true;
104 #endif
105 return false;
108 class ImplicitBoolConversion:
109 public RecursiveASTVisitor<ImplicitBoolConversion>, public loplugin::Plugin
111 public:
112 explicit ImplicitBoolConversion(InstantiationData const & data):
113 Plugin(data) {}
115 virtual void run() override
116 { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
118 bool TraverseCallExpr(CallExpr * expr);
120 bool TraverseCStyleCastExpr(CStyleCastExpr * expr);
122 bool TraverseCXXStaticCastExpr(CXXStaticCastExpr * expr);
124 bool TraverseCXXFunctionalCastExpr(CXXFunctionalCastExpr * expr);
126 bool TraverseConditionalOperator(ConditionalOperator * expr);
128 bool TraverseBinLT(BinaryOperator * expr);
130 bool TraverseBinLE(BinaryOperator * expr);
132 bool TraverseBinGT(BinaryOperator * expr);
134 bool TraverseBinGE(BinaryOperator * expr);
136 bool TraverseBinEQ(BinaryOperator * expr);
138 bool TraverseBinNE(BinaryOperator * expr);
140 bool TraverseBinAssign(BinaryOperator * expr);
142 bool TraverseBinAndAssign(CompoundAssignOperator * expr);
144 bool TraverseBinOrAssign(CompoundAssignOperator * expr);
146 bool TraverseBinXorAssign(CompoundAssignOperator * expr);
148 bool TraverseReturnStmt(ReturnStmt * stmt);
150 bool TraverseFunctionDecl(FunctionDecl * decl);
152 bool VisitImplicitCastExpr(ImplicitCastExpr const * expr);
154 private:
155 void reportWarning(ImplicitCastExpr const * expr);
157 std::stack<std::vector<ImplicitCastExpr const *>> nested;
158 std::stack<CallExpr const *> calls;
159 bool externCIntFunctionDefinition = false;
162 bool ImplicitBoolConversion::TraverseCallExpr(CallExpr * expr) {
163 nested.push(std::vector<ImplicitCastExpr const *>());
164 calls.push(expr);
165 bool ret = RecursiveASTVisitor::TraverseCallExpr(expr);
166 Decl const * d = expr->getCalleeDecl();
167 bool ext = false;
168 FunctionProtoType const * t = nullptr;
169 if (d != nullptr) {
170 FunctionDecl const * fd = dyn_cast<FunctionDecl>(d);
171 if (fd != nullptr && fd->isExternC()) {
172 ext = true;
173 PointerType const * pt = fd->getType()->getAs<PointerType>();
174 QualType t2(pt == nullptr ? fd->getType() : pt->getPointeeType());
175 t = t2->getAs<FunctionProtoType>();
176 assert(
177 t != nullptr || !compiler.getLangOpts().CPlusPlus
178 || (fd->getBuiltinID() != Builtin::NotBuiltin
179 && isa<FunctionNoProtoType>(t2)));
180 // __builtin_*s have no proto type?
181 } else {
182 VarDecl const * vd = dyn_cast<VarDecl>(d);
183 if (vd != nullptr && vd->isExternC())
185 ext = true;
186 PointerType const * pt = vd->getType()->getAs<PointerType>();
187 t = (pt == nullptr ? vd->getType() : pt->getPointeeType())
188 ->castAs<FunctionProtoType>();
192 assert(!nested.empty());
193 for (auto i: nested.top()) {
194 if (ext) {
195 auto j = std::find_if(
196 expr->arg_begin(), expr->arg_end(),
197 [&i](Expr * e) { return i == e->IgnoreParens(); });
198 if (j == expr->arg_end()) {
199 reportWarning(i);
200 } else if (t != nullptr) {
201 std::ptrdiff_t n = j - expr->arg_begin();
202 assert(n >= 0);
203 assert(
204 static_cast<std::size_t>(n) < compat::getNumParams(*t)
205 || t->isVariadic());
206 if (static_cast<std::size_t>(n) < compat::getNumParams(*t)
207 && !(compat::getParamType(*t, n)->isSpecificBuiltinType(
208 BuiltinType::Int)
209 || (compat::getParamType(*t, n)->isSpecificBuiltinType(
210 BuiltinType::UInt))
211 || (compat::getParamType(*t, n)->isSpecificBuiltinType(
212 BuiltinType::Long))))
214 reportWarning(i);
217 } else {
218 reportWarning(i);
221 calls.pop();
222 nested.pop();
223 return ret;
226 bool ImplicitBoolConversion::TraverseCStyleCastExpr(CStyleCastExpr * expr) {
227 nested.push(std::vector<ImplicitCastExpr const *>());
228 bool ret = RecursiveASTVisitor::TraverseCStyleCastExpr(expr);
229 assert(!nested.empty());
230 for (auto i: nested.top()) {
231 if (i != expr->getSubExpr()->IgnoreParens()) {
232 reportWarning(i);
235 nested.pop();
236 return ret;
239 bool ImplicitBoolConversion::TraverseCXXStaticCastExpr(CXXStaticCastExpr * expr)
241 nested.push(std::vector<ImplicitCastExpr const *>());
242 bool ret = RecursiveASTVisitor::TraverseCXXStaticCastExpr(expr);
243 assert(!nested.empty());
244 for (auto i: nested.top()) {
245 if (i != expr->getSubExpr()->IgnoreParens()) {
246 reportWarning(i);
249 nested.pop();
250 return ret;
253 bool ImplicitBoolConversion::TraverseCXXFunctionalCastExpr(
254 CXXFunctionalCastExpr * expr)
256 nested.push(std::vector<ImplicitCastExpr const *>());
257 bool ret = RecursiveASTVisitor::TraverseCXXFunctionalCastExpr(expr);
258 assert(!nested.empty());
259 for (auto i: nested.top()) {
260 if (i != expr->getSubExpr()->IgnoreParens()) {
261 reportWarning(i);
264 nested.pop();
265 return ret;
268 bool ImplicitBoolConversion::TraverseConditionalOperator(
269 ConditionalOperator * expr)
271 nested.push(std::vector<ImplicitCastExpr const *>());
272 bool ret = RecursiveASTVisitor::TraverseConditionalOperator(expr);
273 assert(!nested.empty());
274 for (auto i: nested.top()) {
275 if (!((i == expr->getTrueExpr()->IgnoreParens()
276 && isBoolExpr(expr->getFalseExpr()->IgnoreParenImpCasts()))
277 || (i == expr->getFalseExpr()->IgnoreParens()
278 && isBoolExpr(expr->getTrueExpr()->IgnoreParenImpCasts()))))
280 reportWarning(i);
283 nested.pop();
284 return ret;
287 bool ImplicitBoolConversion::TraverseBinLT(BinaryOperator * expr) {
288 nested.push(std::vector<ImplicitCastExpr const *>());
289 bool ret = RecursiveASTVisitor::TraverseBinLT(expr);
290 assert(!nested.empty());
291 for (auto i: nested.top()) {
292 if (!((i == expr->getLHS()->IgnoreParens()
293 && isBool(expr->getRHS()->IgnoreParenImpCasts(), false))
294 || (i == expr->getRHS()->IgnoreParens()
295 && isBool(expr->getLHS()->IgnoreParenImpCasts(), false))))
297 reportWarning(i);
300 nested.pop();
301 return ret;
304 bool ImplicitBoolConversion::TraverseBinLE(BinaryOperator * expr) {
305 nested.push(std::vector<ImplicitCastExpr const *>());
306 bool ret = RecursiveASTVisitor::TraverseBinLE(expr);
307 assert(!nested.empty());
308 for (auto i: nested.top()) {
309 if (!((i == expr->getLHS()->IgnoreParens()
310 && isBool(expr->getRHS()->IgnoreParenImpCasts(), false))
311 || (i == expr->getRHS()->IgnoreParens()
312 && isBool(expr->getLHS()->IgnoreParenImpCasts(), false))))
314 reportWarning(i);
317 nested.pop();
318 return ret;
321 bool ImplicitBoolConversion::TraverseBinGT(BinaryOperator * expr) {
322 nested.push(std::vector<ImplicitCastExpr const *>());
323 bool ret = RecursiveASTVisitor::TraverseBinGT(expr);
324 assert(!nested.empty());
325 for (auto i: nested.top()) {
326 if (!((i == expr->getLHS()->IgnoreParens()
327 && isBool(expr->getRHS()->IgnoreParenImpCasts(), false))
328 || (i == expr->getRHS()->IgnoreParens()
329 && isBool(expr->getLHS()->IgnoreParenImpCasts(), false))))
331 reportWarning(i);
334 nested.pop();
335 return ret;
338 bool ImplicitBoolConversion::TraverseBinGE(BinaryOperator * expr) {
339 nested.push(std::vector<ImplicitCastExpr const *>());
340 bool ret = RecursiveASTVisitor::TraverseBinGE(expr);
341 assert(!nested.empty());
342 for (auto i: nested.top()) {
343 if (!((i == expr->getLHS()->IgnoreParens()
344 && isBool(expr->getRHS()->IgnoreParenImpCasts(), false))
345 || (i == expr->getRHS()->IgnoreParens()
346 && isBool(expr->getLHS()->IgnoreParenImpCasts(), false))))
348 reportWarning(i);
351 nested.pop();
352 return ret;
355 bool ImplicitBoolConversion::TraverseBinEQ(BinaryOperator * expr) {
356 nested.push(std::vector<ImplicitCastExpr const *>());
357 bool ret = RecursiveASTVisitor::TraverseBinEQ(expr);
358 assert(!nested.empty());
359 for (auto i: nested.top()) {
360 if (!((i == expr->getLHS()->IgnoreParens()
361 && isBool(expr->getRHS()->IgnoreParenImpCasts(), false))
362 || (i == expr->getRHS()->IgnoreParens()
363 && isBool(expr->getLHS()->IgnoreParenImpCasts(), false))))
365 reportWarning(i);
368 nested.pop();
369 return ret;
372 bool ImplicitBoolConversion::TraverseBinNE(BinaryOperator * expr) {
373 nested.push(std::vector<ImplicitCastExpr const *>());
374 bool ret = RecursiveASTVisitor::TraverseBinNE(expr);
375 assert(!nested.empty());
376 for (auto i: nested.top()) {
377 if (!((i == expr->getLHS()->IgnoreParens()
378 && isBool(expr->getRHS()->IgnoreParenImpCasts(), false))
379 || (i == expr->getRHS()->IgnoreParens()
380 && isBool(expr->getLHS()->IgnoreParenImpCasts(), false))))
382 reportWarning(i);
385 nested.pop();
386 return ret;
389 // /usr/include/gtk-2.0/gtk/gtktogglebutton.h: struct _GtkToggleButton:
390 // guint GSEAL (active) : 1;
391 // even though <http://www.gtk.org/api/2.6/gtk/GtkToggleButton.html>:
392 // "active" gboolean : Read / Write
393 bool ImplicitBoolConversion::TraverseBinAssign(BinaryOperator * expr) {
394 nested.push(std::vector<ImplicitCastExpr const *>());
395 bool ret = RecursiveASTVisitor::TraverseBinAssign(expr);
396 bool ext = false;
397 MemberExpr const * me = dyn_cast<MemberExpr>(expr->getLHS());
398 if (me != nullptr) {
399 FieldDecl const * fd = dyn_cast<FieldDecl>(me->getMemberDecl());
400 if (fd != nullptr && fd->isBitField()
401 && fd->getBitWidthValue(compiler.getASTContext()) == 1)
403 TypedefType const * t = fd->getType()->getAs<TypedefType>();
404 ext = t != nullptr && t->getDecl()->getNameAsString() == "guint";
407 assert(!nested.empty());
408 for (auto i: nested.top()) {
409 if (i != expr->getRHS()->IgnoreParens() || !ext) {
410 reportWarning(i);
413 nested.pop();
414 return ret;
417 bool ImplicitBoolConversion::TraverseBinAndAssign(CompoundAssignOperator * expr)
419 nested.push(std::vector<ImplicitCastExpr const *>());
420 bool ret = RecursiveASTVisitor::TraverseBinAndAssign(expr);
421 assert(!nested.empty());
422 for (auto i: nested.top()) {
423 if (i != expr->getRHS()->IgnoreParens()
424 || !isBool(expr->getLHS()->IgnoreParens(), false))
426 reportWarning(i);
429 nested.pop();
430 if (!ignoreLocation(expr) && isBool(expr->getLHS(), false)
431 && !isBool(expr->getRHS()->IgnoreParenImpCasts(), false))
433 report(
434 DiagnosticsEngine::Warning, "mix of %0 and %1 in operator &=",
435 expr->getRHS()->getLocStart())
436 << expr->getLHS()->getType()
437 << expr->getRHS()->IgnoreParenImpCasts()->getType()
438 << expr->getSourceRange();
440 return ret;
443 bool ImplicitBoolConversion::TraverseBinOrAssign(CompoundAssignOperator * expr)
445 nested.push(std::vector<ImplicitCastExpr const *>());
446 bool ret = RecursiveASTVisitor::TraverseBinOrAssign(expr);
447 assert(!nested.empty());
448 for (auto i: nested.top()) {
449 if (i != expr->getRHS()->IgnoreParens()
450 || !isBool(expr->getLHS()->IgnoreParens(), false))
452 reportWarning(i);
455 nested.pop();
456 if (!ignoreLocation(expr) && isBool(expr->getLHS(), false)
457 && !isBool(expr->getRHS()->IgnoreParenImpCasts(), false))
459 report(
460 DiagnosticsEngine::Warning, "mix of %0 and %1 in operator |=",
461 expr->getRHS()->getLocStart())
462 << expr->getLHS()->getType()
463 << expr->getRHS()->IgnoreParenImpCasts()->getType()
464 << expr->getSourceRange();
466 return ret;
469 bool ImplicitBoolConversion::TraverseBinXorAssign(CompoundAssignOperator * expr)
471 nested.push(std::vector<ImplicitCastExpr const *>());
472 bool ret = RecursiveASTVisitor::TraverseBinXorAssign(expr);
473 assert(!nested.empty());
474 for (auto i: nested.top()) {
475 if (i != expr->getRHS()->IgnoreParens()
476 || !isBool(expr->getLHS()->IgnoreParens(), false))
478 reportWarning(i);
481 nested.pop();
482 if (!ignoreLocation(expr) && isBool(expr->getLHS(), false)
483 && !isBool(expr->getRHS()->IgnoreParenImpCasts(), false))
485 report(
486 DiagnosticsEngine::Warning, "mix of %0 and %1 in operator ^=",
487 expr->getRHS()->getLocStart())
488 << expr->getLHS()->getType()
489 << expr->getRHS()->IgnoreParenImpCasts()->getType()
490 << expr->getSourceRange();
492 return ret;
495 bool ImplicitBoolConversion::TraverseReturnStmt(ReturnStmt * stmt) {
496 nested.push(std::vector<ImplicitCastExpr const *>());
497 bool ret = RecursiveASTVisitor::TraverseReturnStmt(stmt);
498 Expr const * expr = stmt->getRetValue();
499 if (expr != nullptr) {
500 ExprWithCleanups const * ec = dyn_cast<ExprWithCleanups>(expr);
501 if (ec != nullptr) {
502 expr = ec->getSubExpr();
504 expr = expr->IgnoreParens();
506 assert(!nested.empty());
507 for (auto i: nested.top()) {
508 if (i != expr || !externCIntFunctionDefinition) {
509 reportWarning(i);
512 nested.pop();
513 return ret;
516 bool ImplicitBoolConversion::TraverseFunctionDecl(FunctionDecl * decl) {
517 bool ext = false;
518 if (hasCLanguageLinkageType(decl) && decl->isThisDeclarationADefinition()) {
519 QualType t { compat::getReturnType(*decl) };
520 if (t->isSpecificBuiltinType(BuiltinType::Int)
521 || t->isSpecificBuiltinType(BuiltinType::UInt))
523 ext = true;
524 } else {
525 TypedefType const * t2 = t->getAs<TypedefType>();
526 // cf. rtl_locale_equals (and sal_Int32 can be long):
527 if (t2 != nullptr
528 && t2->getDecl()->getNameAsString() == "sal_Int32")
530 ext = true;
534 if (ext) {
535 assert(!externCIntFunctionDefinition);
536 externCIntFunctionDefinition = true;
538 bool ret = RecursiveASTVisitor::TraverseFunctionDecl(decl);
539 if (ext) {
540 externCIntFunctionDefinition = false;
542 return ret;
545 bool ImplicitBoolConversion::VisitImplicitCastExpr(
546 ImplicitCastExpr const * expr)
548 if (ignoreLocation(expr)) {
549 return true;
551 if (expr->getSubExprAsWritten()->getType()->isBooleanType()
552 && !isBool(expr))
554 if (nested.empty()) {
555 reportWarning(expr);
556 } else {
557 nested.top().push_back(expr);
559 return true;
561 ExplicitCastExpr const * sub = dyn_cast<ExplicitCastExpr>(
562 expr->getSubExpr()->IgnoreParenImpCasts());
563 if (sub != nullptr
564 && (sub->getSubExpr()->IgnoreParenImpCasts()->getType().IgnoreParens()
565 == expr->getType().IgnoreParens())
566 && isBool(sub->getSubExpr()->IgnoreParenImpCasts()))
568 report(
569 DiagnosticsEngine::Warning,
570 "explicit conversion (%0) from %1 to %2 implicitly cast back to %3",
571 expr->getLocStart())
572 << sub->getCastKindName()
573 << sub->getSubExpr()->IgnoreParenImpCasts()->getType()
574 << sub->getType() << expr->getType() << expr->getSourceRange();
575 return true;
577 if (expr->getType()->isBooleanType() && !isBool(expr->getSubExpr())
578 && !calls.empty())
580 CallExpr const * call = calls.top();
581 if (std::find_if(
582 call->arg_begin(), call->arg_end(),
583 [expr](Expr const * e) { return expr == e->IgnoreParens(); })
584 != call->arg_end())
586 report(
587 DiagnosticsEngine::Warning,
588 "implicit conversion (%0) of call argument from %1 to %2",
589 expr->getLocStart())
590 << expr->getCastKindName() << expr->getSubExpr()->getType()
591 << expr->getType() << expr->getSourceRange();
592 return true;
595 return true;
598 void ImplicitBoolConversion::reportWarning(ImplicitCastExpr const * expr) {
599 report(
600 DiagnosticsEngine::Warning,
601 "implicit conversion (%0) from bool to %1", expr->getLocStart())
602 << expr->getCastKindName() << expr->getType() << expr->getSourceRange();
605 loplugin::Plugin::Registration<ImplicitBoolConversion> X(
606 "implicitboolconversion");
610 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */