bump product version to 5.0.4.1
[LibreOffice.git] / compilerplugins / clang / implicitboolconversion.cxx
blob3f255ff35efe82eec6d61289d09499a54c9aed25
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 Expr const * ignoreParenAndTemporaryMaterialization(Expr const * expr) {
40 for (;;) {
41 expr = expr->IgnoreParens();
42 auto e = dyn_cast<MaterializeTemporaryExpr>(expr);
43 if (e == nullptr) {
44 return expr;
46 expr = e->GetTemporaryExpr();
50 Expr const * ignoreParenImpCastAndComma(Expr const * expr) {
51 for (;;) {
52 expr = expr->IgnoreParenImpCasts();
53 BinaryOperator const * op = dyn_cast<BinaryOperator>(expr);
54 if (op == nullptr || op->getOpcode() != BO_Comma) {
55 return expr;
57 expr = op->getRHS();
61 SubstTemplateTypeParmType const * getAsSubstTemplateTypeParmType(QualType type)
63 //TODO: unwrap all kinds of (non-SubstTemplateTypeParmType) sugar, not only
64 // TypedefType sugar:
65 for (;;) {
66 TypedefType const * t = type->getAs<TypedefType>();
67 if (t == nullptr) {
68 return dyn_cast<SubstTemplateTypeParmType>(type);
70 type = t->desugar();
74 bool areSameTypedef(QualType type1, QualType type2) {
75 // type1.getTypePtr() == typ2.getTypePtr() fails for e.g. ::sal_Bool vs.
76 // sal_Bool:
77 auto t1 = type1->getAs<TypedefType>();
78 auto t2 = type2->getAs<TypedefType>();
79 return t1 != nullptr && t2 != nullptr && t1->getDecl() == t2->getDecl();
82 bool isBool(QualType type, bool allowTypedefs = true) {
83 if (type->isBooleanType()) {
84 return true;
86 if (!allowTypedefs) {
87 return false;
89 TypedefType const * t2 = type->getAs<TypedefType>();
90 if (t2 == nullptr) {
91 return false;
93 std::string name(t2->getDecl()->getNameAsString());
94 return name == "sal_Bool" || name == "BOOL" || name == "Boolean"
95 || name == "FT_Bool" || name == "FcBool" || name == "GLboolean"
96 || name == "NPBool" || name == "UBool" || name == "dbus_bool_t"
97 || name == "gboolean" || name == "hb_bool_t" || name == "jboolean";
100 bool isBool(Expr const * expr, bool allowTypedefs = true) {
101 return isBool(expr->getType(), allowTypedefs);
104 bool isMatchingBool(Expr const * expr, Expr const * comparisonExpr) {
105 return isBool(expr, false)
106 || areSameTypedef(expr->getType(), comparisonExpr->getType());
109 bool isBoolExpr(Expr const * expr) {
110 if (isBool(expr)) {
111 return true;
113 expr = ignoreParenImpCastAndComma(expr);
114 ConditionalOperator const * co = dyn_cast<ConditionalOperator>(expr);
115 if (co != nullptr) {
116 ImplicitCastExpr const * ic1 = dyn_cast<ImplicitCastExpr>(
117 co->getTrueExpr()->IgnoreParens());
118 ImplicitCastExpr const * ic2 = dyn_cast<ImplicitCastExpr>(
119 co->getFalseExpr()->IgnoreParens());
120 if (ic1 != nullptr && ic2 != nullptr
121 && ic1->getType()->isSpecificBuiltinType(BuiltinType::Int)
122 && isBoolExpr(ic1->getSubExpr()->IgnoreParens())
123 && ic2->getType()->isSpecificBuiltinType(BuiltinType::Int)
124 && isBoolExpr(ic2->getSubExpr()->IgnoreParens()))
126 return true;
129 std::stack<Expr const *> stack;
130 Expr const * e = expr;
131 for (;;) {
132 e = ignoreParenImpCastAndComma(e);
133 MemberExpr const * me = dyn_cast<MemberExpr>(e);
134 if (me == nullptr) {
135 break;
137 stack.push(e);
138 e = me->getBase();
140 for (;;) {
141 e = ignoreParenImpCastAndComma(e);
142 CXXOperatorCallExpr const * op = dyn_cast<CXXOperatorCallExpr>(e);
143 if (op == nullptr || op->getOperator() != OO_Subscript) {
144 break;
146 stack.push(e);
147 e = op->getArg(0);
149 if (!stack.empty()) {
150 TemplateSpecializationType const * t
151 = e->getType()->getAs<TemplateSpecializationType>();
152 for (;;) {
153 if (t == nullptr) {
154 break;
156 QualType ty;
157 MemberExpr const * me = dyn_cast<MemberExpr>(stack.top());
158 if (me != nullptr) {
159 TemplateDecl const * td
160 = t->getTemplateName().getAsTemplateDecl();
161 if (td == nullptr) {
162 break;
164 TemplateParameterList const * ps = td->getTemplateParameters();
165 SubstTemplateTypeParmType const * t2
166 = getAsSubstTemplateTypeParmType(
167 me->getMemberDecl()->getType());
168 if (t2 == nullptr) {
169 break;
171 auto i = std::find(
172 ps->begin(), ps->end(),
173 t2->getReplacedParameter()->getDecl());
174 if (i == ps->end()) {
175 break;
177 if (ps->size() != t->getNumArgs()) { //TODO
178 break;
180 TemplateArgument const & arg = t->getArg(i - ps->begin());
181 if (arg.getKind() != TemplateArgument::Type) {
182 break;
184 ty = arg.getAsType();
185 } else {
186 CXXOperatorCallExpr const * op
187 = dyn_cast<CXXOperatorCallExpr>(stack.top());
188 assert(op != nullptr);
189 TemplateDecl const * d
190 = t->getTemplateName().getAsTemplateDecl();
191 if (d == nullptr
192 || (d->getQualifiedNameAsString()
193 != "com::sun::star::uno::Sequence")
194 || t->getNumArgs() != 1
195 || t->getArg(0).getKind() != TemplateArgument::Type)
197 break;
199 ty = t->getArg(0).getAsType();
201 stack.pop();
202 if (stack.empty()) {
203 if (isBool(ty)) {
204 return true;
206 break;
208 t = ty->getAs<TemplateSpecializationType>();
211 return false;
214 // It appears that, given a function declaration, there is no way to determine
215 // the language linkage of the function's type, only of the function's name
216 // (via FunctionDecl::isExternC); however, in a case like
218 // extern "C" { static void f(); }
220 // the function's name does not have C language linkage while the function's
221 // type does (as clarified in C++11 [decl.link]); cf. <http://clang-developers.
222 // 42468.n3.nabble.com/Language-linkage-of-function-type-tt4037248.html>
223 // "Language linkage of function type":
224 bool hasCLanguageLinkageType(FunctionDecl const * decl) {
225 assert(decl != nullptr);
226 if (decl->isExternC()) {
227 return true;
229 #if (__clang_major__ == 3 && __clang_minor__ >= 3) || __clang_major__ > 3
230 if (decl->isInExternCContext()) {
231 return true;
233 #else
234 if (decl->getCanonicalDecl()->getDeclContext()->isExternCContext()) {
235 return true;
237 #endif
238 return false;
241 class ImplicitBoolConversion:
242 public RecursiveASTVisitor<ImplicitBoolConversion>, public loplugin::Plugin
244 public:
245 explicit ImplicitBoolConversion(InstantiationData const & data):
246 Plugin(data) {}
248 virtual void run() override
249 { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
251 bool TraverseCallExpr(CallExpr * expr);
253 bool TraverseCXXMemberCallExpr(CXXMemberCallExpr * expr);
255 bool TraverseCXXConstructExpr(CXXConstructExpr * expr);
257 bool TraverseCXXTemporaryObjectExpr(CXXTemporaryObjectExpr * expr);
259 bool TraverseCStyleCastExpr(CStyleCastExpr * expr);
261 bool TraverseCXXStaticCastExpr(CXXStaticCastExpr * expr);
263 bool TraverseCXXFunctionalCastExpr(CXXFunctionalCastExpr * expr);
265 bool TraverseConditionalOperator(ConditionalOperator * expr);
267 bool TraverseBinLT(BinaryOperator * expr);
269 bool TraverseBinLE(BinaryOperator * expr);
271 bool TraverseBinGT(BinaryOperator * expr);
273 bool TraverseBinGE(BinaryOperator * expr);
275 bool TraverseBinEQ(BinaryOperator * expr);
277 bool TraverseBinNE(BinaryOperator * expr);
279 bool TraverseBinAssign(BinaryOperator * expr);
281 bool TraverseBinAndAssign(CompoundAssignOperator * expr);
283 bool TraverseBinOrAssign(CompoundAssignOperator * expr);
285 bool TraverseBinXorAssign(CompoundAssignOperator * expr);
287 bool TraverseReturnStmt(ReturnStmt * stmt);
289 bool TraverseFunctionDecl(FunctionDecl * decl);
291 bool VisitImplicitCastExpr(ImplicitCastExpr const * expr);
293 private:
294 bool isExternCFunctionCall(
295 CallExpr const * expr, FunctionProtoType const ** functionType);
297 bool isExternCFunctionCallReturningInt(Expr const * expr);
299 void checkCXXConstructExpr(CXXConstructExpr const * expr);
301 void reportWarning(ImplicitCastExpr const * expr);
303 std::stack<std::vector<ImplicitCastExpr const *>> nested;
304 std::stack<CallExpr const *> calls;
305 bool externCIntFunctionDefinition = false;
308 bool ImplicitBoolConversion::TraverseCallExpr(CallExpr * expr) {
309 nested.push(std::vector<ImplicitCastExpr const *>());
310 calls.push(expr);
311 bool ret = RecursiveASTVisitor::TraverseCallExpr(expr);
312 FunctionProtoType const * t;
313 bool ext = isExternCFunctionCall(expr, &t);
314 assert(!nested.empty());
315 for (auto i: nested.top()) {
316 auto j = std::find_if(
317 expr->arg_begin(), expr->arg_end(),
318 [&i](Expr * e) {
319 return i == ignoreParenAndTemporaryMaterialization(e);
321 if (j == expr->arg_end()) {
322 reportWarning(i);
323 } else {
324 std::ptrdiff_t n = j - expr->arg_begin();
325 assert(n >= 0);
326 if (t != nullptr
327 && static_cast<std::size_t>(n) >= compat::getNumParams(*t))
329 assert(t->isVariadic());
330 // ignore bool to int promotions of variadic arguments
331 } else if (ext) {
332 if (t != nullptr) {
333 assert(
334 static_cast<std::size_t>(n) < compat::getNumParams(*t));
335 if (!(compat::getParamType(*t, n)->isSpecificBuiltinType(
336 BuiltinType::Int)
337 || compat::getParamType(*t, n)->isSpecificBuiltinType(
338 BuiltinType::UInt)
339 || compat::getParamType(*t, n)->isSpecificBuiltinType(
340 BuiltinType::Long)))
342 reportWarning(i);
344 } else {
345 reportWarning(i);
347 } else {
348 // Filter out
350 // template<typename T> void f(T);
351 // f<sal_Bool>(true);
353 DeclRefExpr const * dr = dyn_cast<DeclRefExpr>(
354 expr->getCallee()->IgnoreParenImpCasts());
355 if (dr != nullptr && dr->hasExplicitTemplateArgs()) {
356 FunctionDecl const * fd
357 = dyn_cast<FunctionDecl>(dr->getDecl());
358 if (fd != nullptr
359 && static_cast<std::size_t>(n) < fd->getNumParams())
361 SubstTemplateTypeParmType const * t2
362 = getAsSubstTemplateTypeParmType(
363 fd->getParamDecl(n)->getType()
364 .getNonReferenceType());
365 if (t2 != nullptr) {
366 //TODO: fix this superficial nonsense check:
367 ASTTemplateArgumentListInfo const & ai
368 = dr->getExplicitTemplateArgs();
369 if (ai.NumTemplateArgs == 1
370 && (ai[0].getArgument().getKind()
371 == TemplateArgument::Type)
372 && isBool(ai[0].getTypeSourceInfo()->getType()))
374 continue;
379 reportWarning(i);
383 calls.pop();
384 nested.pop();
385 return ret;
388 bool ImplicitBoolConversion::TraverseCXXMemberCallExpr(CXXMemberCallExpr * expr)
390 nested.push(std::vector<ImplicitCastExpr const *>());
391 bool ret = RecursiveASTVisitor::TraverseCXXMemberCallExpr(expr);
392 assert(!nested.empty());
393 for (auto i: nested.top()) {
394 auto j = std::find_if(
395 expr->arg_begin(), expr->arg_end(),
396 [&i](Expr * e) {
397 return i == ignoreParenAndTemporaryMaterialization(e);
399 if (j != expr->arg_end()) {
400 // Filter out
402 // template<typename T> struct S { void f(T); };
403 // S<sal_Bool> s;
404 // s.f(true);
406 std::ptrdiff_t n = j - expr->arg_begin();
407 assert(n >= 0);
408 CXXMethodDecl const * d = expr->getMethodDecl();
409 if (static_cast<std::size_t>(n) >= d->getNumParams()) {
410 // Ignore bool to int promotions of variadic arguments:
411 assert(d->isVariadic());
412 continue;
414 QualType ty
415 = ignoreParenImpCastAndComma(expr->getImplicitObjectArgument())
416 ->getType();
417 if (dyn_cast<MemberExpr>(expr->getCallee())->isArrow()) {
418 ty = ty->getAs<PointerType>()->getPointeeType();
420 TemplateSpecializationType const * ct
421 = ty->getAs<TemplateSpecializationType>();
422 if (ct != nullptr) {
423 SubstTemplateTypeParmType const * pt
424 = getAsSubstTemplateTypeParmType(
425 d->getParamDecl(n)->getType().getNonReferenceType());
426 if (pt != nullptr) {
427 TemplateDecl const * td
428 = ct->getTemplateName().getAsTemplateDecl();
429 if (td != nullptr) {
430 //TODO: fix this superficial nonsense check:
431 if (ct->getNumArgs() >= 1
432 && ct->getArg(0).getKind() == TemplateArgument::Type
433 && isBool(ct->getArg(0).getAsType()))
435 continue;
441 reportWarning(i);
443 nested.pop();
444 return ret;
447 bool ImplicitBoolConversion::TraverseCXXConstructExpr(CXXConstructExpr * expr) {
448 nested.push(std::vector<ImplicitCastExpr const *>());
449 bool ret = RecursiveASTVisitor::TraverseCXXConstructExpr(expr);
450 checkCXXConstructExpr(expr);
451 nested.pop();
452 return ret;
455 bool ImplicitBoolConversion::TraverseCXXTemporaryObjectExpr(
456 CXXTemporaryObjectExpr * expr)
458 nested.push(std::vector<ImplicitCastExpr const *>());
459 bool ret = RecursiveASTVisitor::TraverseCXXTemporaryObjectExpr(expr);
460 checkCXXConstructExpr(expr);
461 nested.pop();
462 return ret;
465 bool ImplicitBoolConversion::TraverseCStyleCastExpr(CStyleCastExpr * expr) {
466 nested.push(std::vector<ImplicitCastExpr const *>());
467 bool ret = RecursiveASTVisitor::TraverseCStyleCastExpr(expr);
468 assert(!nested.empty());
469 for (auto i: nested.top()) {
470 if (i != expr->getSubExpr()->IgnoreParens()) {
471 reportWarning(i);
474 nested.pop();
475 return ret;
478 bool ImplicitBoolConversion::TraverseCXXStaticCastExpr(CXXStaticCastExpr * expr)
480 nested.push(std::vector<ImplicitCastExpr const *>());
481 bool ret = RecursiveASTVisitor::TraverseCXXStaticCastExpr(expr);
482 assert(!nested.empty());
483 for (auto i: nested.top()) {
484 if (i != expr->getSubExpr()->IgnoreParens()) {
485 reportWarning(i);
488 nested.pop();
489 return ret;
492 bool ImplicitBoolConversion::TraverseCXXFunctionalCastExpr(
493 CXXFunctionalCastExpr * expr)
495 nested.push(std::vector<ImplicitCastExpr const *>());
496 bool ret = RecursiveASTVisitor::TraverseCXXFunctionalCastExpr(expr);
497 assert(!nested.empty());
498 for (auto i: nested.top()) {
499 if (i != expr->getSubExpr()->IgnoreParens()) {
500 reportWarning(i);
503 nested.pop();
504 return ret;
507 bool ImplicitBoolConversion::TraverseConditionalOperator(
508 ConditionalOperator * expr)
510 nested.push(std::vector<ImplicitCastExpr const *>());
511 bool ret = RecursiveASTVisitor::TraverseConditionalOperator(expr);
512 assert(!nested.empty());
513 for (auto i: nested.top()) {
514 if (!((i == expr->getTrueExpr()->IgnoreParens()
515 && (isBoolExpr(expr->getFalseExpr()->IgnoreParenImpCasts())
516 || isExternCFunctionCallReturningInt(expr->getFalseExpr())))
517 || (i == expr->getFalseExpr()->IgnoreParens()
518 && (isBoolExpr(expr->getTrueExpr()->IgnoreParenImpCasts())
519 || isExternCFunctionCallReturningInt(
520 expr->getTrueExpr())))
521 || (!compiler.getLangOpts().CPlusPlus
522 && i == expr->getCond()->IgnoreParens())))
524 reportWarning(i);
527 nested.pop();
528 return ret;
531 bool ImplicitBoolConversion::TraverseBinLT(BinaryOperator * expr) {
532 nested.push(std::vector<ImplicitCastExpr const *>());
533 bool ret = RecursiveASTVisitor::TraverseBinLT(expr);
534 assert(!nested.empty());
535 for (auto i: nested.top()) {
536 if (!((i == expr->getLHS()->IgnoreParens()
537 && isMatchingBool(
538 expr->getRHS()->IgnoreImpCasts(), i->getSubExprAsWritten()))
539 || (i == expr->getRHS()->IgnoreParens()
540 && isMatchingBool(
541 expr->getLHS()->IgnoreImpCasts(),
542 i->getSubExprAsWritten()))))
544 reportWarning(i);
547 nested.pop();
548 return ret;
551 bool ImplicitBoolConversion::TraverseBinLE(BinaryOperator * expr) {
552 nested.push(std::vector<ImplicitCastExpr const *>());
553 bool ret = RecursiveASTVisitor::TraverseBinLE(expr);
554 assert(!nested.empty());
555 for (auto i: nested.top()) {
556 if (!((i == expr->getLHS()->IgnoreParens()
557 && isMatchingBool(
558 expr->getRHS()->IgnoreImpCasts(), i->getSubExprAsWritten()))
559 || (i == expr->getRHS()->IgnoreParens()
560 && isMatchingBool(
561 expr->getLHS()->IgnoreImpCasts(),
562 i->getSubExprAsWritten()))))
564 reportWarning(i);
567 nested.pop();
568 return ret;
571 bool ImplicitBoolConversion::TraverseBinGT(BinaryOperator * expr) {
572 nested.push(std::vector<ImplicitCastExpr const *>());
573 bool ret = RecursiveASTVisitor::TraverseBinGT(expr);
574 assert(!nested.empty());
575 for (auto i: nested.top()) {
576 if (!((i == expr->getLHS()->IgnoreParens()
577 && isMatchingBool(
578 expr->getRHS()->IgnoreImpCasts(), i->getSubExprAsWritten()))
579 || (i == expr->getRHS()->IgnoreParens()
580 && isMatchingBool(
581 expr->getLHS()->IgnoreImpCasts(),
582 i->getSubExprAsWritten()))))
584 reportWarning(i);
587 nested.pop();
588 return ret;
591 bool ImplicitBoolConversion::TraverseBinGE(BinaryOperator * expr) {
592 nested.push(std::vector<ImplicitCastExpr const *>());
593 bool ret = RecursiveASTVisitor::TraverseBinGE(expr);
594 assert(!nested.empty());
595 for (auto i: nested.top()) {
596 if (!((i == expr->getLHS()->IgnoreParens()
597 && isMatchingBool(
598 expr->getRHS()->IgnoreImpCasts(), i->getSubExprAsWritten()))
599 || (i == expr->getRHS()->IgnoreParens()
600 && isMatchingBool(
601 expr->getLHS()->IgnoreImpCasts(),
602 i->getSubExprAsWritten()))))
604 reportWarning(i);
607 nested.pop();
608 return ret;
611 bool ImplicitBoolConversion::TraverseBinEQ(BinaryOperator * expr) {
612 nested.push(std::vector<ImplicitCastExpr const *>());
613 bool ret = RecursiveASTVisitor::TraverseBinEQ(expr);
614 assert(!nested.empty());
615 for (auto i: nested.top()) {
616 if (!((i == expr->getLHS()->IgnoreParens()
617 && isMatchingBool(
618 expr->getRHS()->IgnoreImpCasts(), i->getSubExprAsWritten()))
619 || (i == expr->getRHS()->IgnoreParens()
620 && isMatchingBool(
621 expr->getLHS()->IgnoreImpCasts(),
622 i->getSubExprAsWritten()))))
624 reportWarning(i);
627 nested.pop();
628 return ret;
631 bool ImplicitBoolConversion::TraverseBinNE(BinaryOperator * expr) {
632 nested.push(std::vector<ImplicitCastExpr const *>());
633 bool ret = RecursiveASTVisitor::TraverseBinNE(expr);
634 assert(!nested.empty());
635 for (auto i: nested.top()) {
636 if (!((i == expr->getLHS()->IgnoreParens()
637 && isMatchingBool(
638 expr->getRHS()->IgnoreImpCasts(), i->getSubExprAsWritten()))
639 || (i == expr->getRHS()->IgnoreParens()
640 && isMatchingBool(
641 expr->getLHS()->IgnoreImpCasts(),
642 i->getSubExprAsWritten()))))
644 reportWarning(i);
647 nested.pop();
648 return ret;
651 bool ImplicitBoolConversion::TraverseBinAssign(BinaryOperator * expr) {
652 nested.push(std::vector<ImplicitCastExpr const *>());
653 bool ret = RecursiveASTVisitor::TraverseBinAssign(expr);
654 // /usr/include/gtk-2.0/gtk/gtktogglebutton.h: struct _GtkToggleButton:
655 // guint GSEAL (active) : 1;
656 // even though <http://www.gtk.org/api/2.6/gtk/GtkToggleButton.html>:
657 // "active" gboolean : Read / Write
658 bool ext = false;
659 MemberExpr const * me = dyn_cast<MemberExpr>(expr->getLHS());
660 if (me != nullptr) {
661 FieldDecl const * fd = dyn_cast<FieldDecl>(me->getMemberDecl());
662 if (fd != nullptr && fd->isBitField()
663 && fd->getBitWidthValue(compiler.getASTContext()) == 1)
665 TypedefType const * t = fd->getType()->getAs<TypedefType>();
666 ext = t != nullptr && t->getDecl()->getNameAsString() == "guint";
669 assert(!nested.empty());
670 for (auto i: nested.top()) {
671 if (i != expr->getRHS()->IgnoreParens()
672 || !(ext || isBoolExpr(expr->getLHS())))
674 reportWarning(i);
677 nested.pop();
678 return ret;
681 bool ImplicitBoolConversion::TraverseBinAndAssign(CompoundAssignOperator * expr)
683 nested.push(std::vector<ImplicitCastExpr const *>());
684 bool ret = RecursiveASTVisitor::TraverseBinAndAssign(expr);
685 assert(!nested.empty());
686 for (auto i: nested.top()) {
687 if (i != expr->getRHS()->IgnoreParens()
688 || !isBool(expr->getLHS()->IgnoreParens(), false))
690 reportWarning(i);
693 nested.pop();
694 if (!ignoreLocation(expr) && isBool(expr->getLHS(), false)
695 && !isBool(expr->getRHS()->IgnoreParenImpCasts(), false))
697 report(
698 DiagnosticsEngine::Warning, "mix of %0 and %1 in operator &=",
699 expr->getRHS()->getLocStart())
700 << expr->getLHS()->getType()
701 << expr->getRHS()->IgnoreParenImpCasts()->getType()
702 << expr->getSourceRange();
704 return ret;
707 bool ImplicitBoolConversion::TraverseBinOrAssign(CompoundAssignOperator * expr)
709 nested.push(std::vector<ImplicitCastExpr const *>());
710 bool ret = RecursiveASTVisitor::TraverseBinOrAssign(expr);
711 assert(!nested.empty());
712 for (auto i: nested.top()) {
713 if (i != expr->getRHS()->IgnoreParens()
714 || !isBool(expr->getLHS()->IgnoreParens(), false))
716 reportWarning(i);
719 nested.pop();
720 if (!ignoreLocation(expr) && isBool(expr->getLHS(), false)
721 && !isBool(expr->getRHS()->IgnoreParenImpCasts(), false))
723 report(
724 DiagnosticsEngine::Warning, "mix of %0 and %1 in operator |=",
725 expr->getRHS()->getLocStart())
726 << expr->getLHS()->getType()
727 << expr->getRHS()->IgnoreParenImpCasts()->getType()
728 << expr->getSourceRange();
730 return ret;
733 bool ImplicitBoolConversion::TraverseBinXorAssign(CompoundAssignOperator * expr)
735 nested.push(std::vector<ImplicitCastExpr const *>());
736 bool ret = RecursiveASTVisitor::TraverseBinXorAssign(expr);
737 assert(!nested.empty());
738 for (auto i: nested.top()) {
739 if (i != expr->getRHS()->IgnoreParens()
740 || !isBool(expr->getLHS()->IgnoreParens(), false))
742 reportWarning(i);
745 nested.pop();
746 if (!ignoreLocation(expr) && isBool(expr->getLHS(), false)
747 && !isBool(expr->getRHS()->IgnoreParenImpCasts(), false))
749 report(
750 DiagnosticsEngine::Warning, "mix of %0 and %1 in operator ^=",
751 expr->getRHS()->getLocStart())
752 << expr->getLHS()->getType()
753 << expr->getRHS()->IgnoreParenImpCasts()->getType()
754 << expr->getSourceRange();
756 return ret;
759 bool ImplicitBoolConversion::TraverseReturnStmt(ReturnStmt * stmt) {
760 nested.push(std::vector<ImplicitCastExpr const *>());
761 bool ret = RecursiveASTVisitor::TraverseReturnStmt(stmt);
762 Expr const * expr = stmt->getRetValue();
763 if (expr != nullptr) {
764 ExprWithCleanups const * ec = dyn_cast<ExprWithCleanups>(expr);
765 if (ec != nullptr) {
766 expr = ec->getSubExpr();
768 expr = expr->IgnoreParens();
770 assert(!nested.empty());
771 for (auto i: nested.top()) {
772 if (i != expr || !externCIntFunctionDefinition) {
773 reportWarning(i);
776 nested.pop();
777 return ret;
780 bool ImplicitBoolConversion::TraverseFunctionDecl(FunctionDecl * decl) {
781 bool ext = false;
782 if (hasCLanguageLinkageType(decl) && decl->isThisDeclarationADefinition()) {
783 QualType t { compat::getReturnType(*decl) };
784 if (t->isSpecificBuiltinType(BuiltinType::Int)
785 || t->isSpecificBuiltinType(BuiltinType::UInt))
787 ext = true;
788 } else {
789 TypedefType const * t2 = t->getAs<TypedefType>();
790 // cf. rtl_locale_equals (and sal_Int32 can be long):
791 if (t2 != nullptr
792 && t2->getDecl()->getNameAsString() == "sal_Int32")
794 ext = true;
798 if (ext) {
799 assert(!externCIntFunctionDefinition);
800 externCIntFunctionDefinition = true;
802 bool ret = RecursiveASTVisitor::TraverseFunctionDecl(decl);
803 if (ext) {
804 externCIntFunctionDefinition = false;
806 return ret;
809 bool ImplicitBoolConversion::VisitImplicitCastExpr(
810 ImplicitCastExpr const * expr)
812 if (ignoreLocation(expr)) {
813 return true;
815 if (isBool(expr->getSubExprAsWritten()) && !isBool(expr)) {
816 // Ignore NoOp from 'sal_Bool' (aka 'unsigned char') to 'const unsigned
817 // char' in makeAny(b) with b of type sal_Bool:
818 if (expr->getCastKind() != CK_NoOp) {
819 if (nested.empty()) {
820 reportWarning(expr);
821 } else {
822 nested.top().push_back(expr);
825 return true;
827 ExplicitCastExpr const * sub = dyn_cast<ExplicitCastExpr>(
828 expr->getSubExpr()->IgnoreParenImpCasts());
829 if (sub != nullptr
830 && (sub->getSubExpr()->IgnoreParenImpCasts()->getType().IgnoreParens()
831 == expr->getType().IgnoreParens())
832 && isBool(sub->getSubExpr()->IgnoreParenImpCasts()))
834 report(
835 DiagnosticsEngine::Warning,
836 "explicit conversion (%0) from %1 to %2 implicitly cast back to %3",
837 expr->getLocStart())
838 << sub->getCastKindName()
839 << sub->getSubExpr()->IgnoreParenImpCasts()->getType()
840 << sub->getType() << expr->getType() << expr->getSourceRange();
841 return true;
843 if (expr->getType()->isBooleanType() && !isBoolExpr(expr->getSubExpr())
844 && !calls.empty())
846 CallExpr const * call = calls.top();
847 if (std::find_if(
848 call->arg_begin(), call->arg_end(),
849 [expr](Expr const * e) { return expr == e->IgnoreParens(); })
850 != call->arg_end())
852 report(
853 DiagnosticsEngine::Warning,
854 "implicit conversion (%0) of call argument from %1 to %2",
855 expr->getLocStart())
856 << expr->getCastKindName() << expr->getSubExpr()->getType()
857 << expr->getType() << expr->getSourceRange();
858 return true;
861 return true;
864 bool ImplicitBoolConversion::isExternCFunctionCall(
865 CallExpr const * expr, FunctionProtoType const ** functionType)
867 assert(functionType != nullptr);
868 *functionType = nullptr;
869 Decl const * d = expr->getCalleeDecl();
870 if (d != nullptr) {
871 FunctionDecl const * fd = dyn_cast<FunctionDecl>(d);
872 if (fd != nullptr) {
873 PointerType const * pt = fd->getType()->getAs<PointerType>();
874 QualType t2(pt == nullptr ? fd->getType() : pt->getPointeeType());
875 *functionType = t2->getAs<FunctionProtoType>();
876 assert(
877 *functionType != nullptr || !compiler.getLangOpts().CPlusPlus
878 || (fd->getBuiltinID() != Builtin::NotBuiltin
879 && isa<FunctionNoProtoType>(t2)));
880 // __builtin_*s have no proto type?
881 return fd->isExternC()
882 || compiler.getSourceManager().isInExternCSystemHeader(
883 fd->getLocation());
885 VarDecl const * vd = dyn_cast<VarDecl>(d);
886 if (vd != nullptr) {
887 PointerType const * pt = vd->getType()->getAs<PointerType>();
888 *functionType
889 = ((pt == nullptr ? vd->getType() : pt->getPointeeType())
890 ->getAs<FunctionProtoType>());
891 return vd->isExternC();
894 return false;
897 bool ImplicitBoolConversion::isExternCFunctionCallReturningInt(
898 Expr const * expr)
900 CallExpr const * e = dyn_cast<CallExpr>(expr->IgnoreParenImpCasts());
901 FunctionProtoType const * t;
902 return e != nullptr && e->getType()->isSpecificBuiltinType(BuiltinType::Int)
903 && isExternCFunctionCall(e, &t);
906 void ImplicitBoolConversion::checkCXXConstructExpr(
907 CXXConstructExpr const * expr)
909 assert(!nested.empty());
910 for (auto i: nested.top()) {
911 auto j = std::find_if(
912 expr->arg_begin(), expr->arg_end(),
913 [&i](Expr const * e) {
914 return i == ignoreParenAndTemporaryMaterialization(e);
916 if (j != expr->arg_end()) {
917 TemplateSpecializationType const * t1 = expr->getType()->
918 getAs<TemplateSpecializationType>();
919 SubstTemplateTypeParmType const * t2 = nullptr;
920 CXXConstructorDecl const * d = expr->getConstructor();
921 if (d->getNumParams() == expr->getNumArgs()) { //TODO: better check
922 t2 = getAsSubstTemplateTypeParmType(
923 d->getParamDecl(j - expr->arg_begin())->getType()
924 .getNonReferenceType());
926 if (t1 != nullptr && t2 != nullptr) {
927 TemplateDecl const * td
928 = t1->getTemplateName().getAsTemplateDecl();
929 if (td != nullptr) {
930 TemplateParameterList const * ps
931 = td->getTemplateParameters();
932 auto i = std::find(
933 ps->begin(), ps->end(),
934 t2->getReplacedParameter()->getDecl());
935 if (i != ps->end()) {
936 if (ps->size() == t1->getNumArgs()) { //TODO
937 TemplateArgument const & arg = t1->getArg(
938 i - ps->begin());
939 if (arg.getKind() == TemplateArgument::Type
940 && isBool(arg.getAsType()))
942 continue;
949 reportWarning(i);
953 void ImplicitBoolConversion::reportWarning(ImplicitCastExpr const * expr) {
954 if (!compiler.getLangOpts().ObjC2 || compiler.getLangOpts().CPlusPlus) {
955 report(
956 DiagnosticsEngine::Warning,
957 "implicit conversion (%0) from %1 to %2", expr->getLocStart())
958 << expr->getCastKindName() << expr->getSubExprAsWritten()->getType()
959 << expr->getType() << expr->getSourceRange();
963 loplugin::Plugin::Registration<ImplicitBoolConversion> X(
964 "implicitboolconversion");
968 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */