Version 6.1.0.2, tag libreoffice-6.1.0.2
[LibreOffice.git] / compilerplugins / clang / implicitboolconversion.cxx
blobd7e80f56ab36083682319bebd8c36c8c2e38e6ee
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 "check.hxx"
19 #include "compat.hxx"
20 #include "plugin.hxx"
22 namespace {
24 Expr const * ignoreParenAndTemporaryMaterialization(Expr const * expr) {
25 for (;;) {
26 expr = expr->IgnoreParens();
27 auto e = dyn_cast<MaterializeTemporaryExpr>(expr);
28 if (e == nullptr) {
29 return expr;
31 expr = e->GetTemporaryExpr();
35 Expr const * ignoreParenImpCastAndComma(Expr const * expr) {
36 for (;;) {
37 expr = expr->IgnoreParenImpCasts();
38 BinaryOperator const * op = dyn_cast<BinaryOperator>(expr);
39 if (op == nullptr || op->getOpcode() != BO_Comma) {
40 return expr;
42 expr = op->getRHS();
46 SubstTemplateTypeParmType const * getAsSubstTemplateTypeParmType(QualType type)
48 //TODO: unwrap all kinds of (non-SubstTemplateTypeParmType) sugar, not only
49 // TypedefType sugar:
50 for (;;) {
51 TypedefType const * t = type->getAs<TypedefType>();
52 if (t == nullptr) {
53 return dyn_cast<SubstTemplateTypeParmType>(type);
55 type = t->desugar();
59 QualType reconstructTemplateArgumentType(
60 TemplateDecl const * decl, TemplateSpecializationType const * specializationType,
61 SubstTemplateTypeParmType const * parmType)
63 TemplateParameterList const * ps = decl->getTemplateParameters();
64 auto i = std::find(ps->begin(), ps->end(), parmType->getReplacedParameter()->getDecl());
65 if (i == ps->end()) {
66 return {};
68 if (ps->size() != specializationType->getNumArgs()) { //TODO
69 return {};
71 TemplateArgument const & arg = specializationType->getArg(i - ps->begin());
72 if (arg.getKind() != TemplateArgument::Type) {
73 return {};
75 return arg.getAsType();
78 bool areSameTypedef(QualType type1, QualType type2) {
79 // type1.getTypePtr() == typ2.getTypePtr() fails for e.g. ::sal_Bool vs.
80 // sal_Bool:
81 auto t1 = type1->getAs<TypedefType>();
82 auto t2 = type2->getAs<TypedefType>();
83 return t1 != nullptr && t2 != nullptr && t1->getDecl() == t2->getDecl();
86 bool isBool(Expr const * expr, bool allowTypedefs = true) {
87 auto t = expr->getType();
88 return allowTypedefs
89 ? bool(loplugin::TypeCheck(t).AnyBoolean()) : t->isBooleanType();
92 bool isMatchingBool(Expr const * expr, Expr const * comparisonExpr) {
93 return isBool(expr, false)
94 || areSameTypedef(expr->getType(), comparisonExpr->getType());
97 bool isSalBool(QualType type) {
98 auto t = type->getAs<TypedefType>();
99 return t != nullptr && t->getDecl()->getName() == "sal_Bool";
102 bool isBoolExpr(Expr const * expr) {
103 if (isBool(expr)) {
104 return true;
106 expr = ignoreParenImpCastAndComma(expr);
107 ConditionalOperator const * co = dyn_cast<ConditionalOperator>(expr);
108 if (co != nullptr) {
109 ImplicitCastExpr const * ic1 = dyn_cast<ImplicitCastExpr>(
110 co->getTrueExpr()->IgnoreParens());
111 ImplicitCastExpr const * ic2 = dyn_cast<ImplicitCastExpr>(
112 co->getFalseExpr()->IgnoreParens());
113 if (ic1 != nullptr && ic2 != nullptr
114 && ic1->getType()->isSpecificBuiltinType(BuiltinType::Int)
115 && isBoolExpr(ic1->getSubExpr()->IgnoreParens())
116 && ic2->getType()->isSpecificBuiltinType(BuiltinType::Int)
117 && isBoolExpr(ic2->getSubExpr()->IgnoreParens()))
119 return true;
122 std::stack<Expr const *> stack;
123 Expr const * e = expr;
124 for (;;) {
125 e = ignoreParenImpCastAndComma(e);
126 MemberExpr const * me = dyn_cast<MemberExpr>(e);
127 if (me == nullptr) {
128 break;
130 stack.push(e);
131 e = me->getBase();
133 for (;;) {
134 e = ignoreParenImpCastAndComma(e);
135 CXXOperatorCallExpr const * op = dyn_cast<CXXOperatorCallExpr>(e);
136 if (op == nullptr || op->getOperator() != OO_Subscript) {
137 break;
139 stack.push(e);
140 e = op->getArg(0);
142 if (!stack.empty()) {
143 TemplateSpecializationType const * t
144 = e->getType()->getAs<TemplateSpecializationType>();
145 for (;;) {
146 if (t == nullptr) {
147 break;
149 QualType ty;
150 MemberExpr const * me = dyn_cast<MemberExpr>(stack.top());
151 if (me != nullptr) {
152 TemplateDecl const * td
153 = t->getTemplateName().getAsTemplateDecl();
154 if (td == nullptr) {
155 break;
157 SubstTemplateTypeParmType const * t2
158 = getAsSubstTemplateTypeParmType(
159 me->getMemberDecl()->getType());
160 if (t2 == nullptr) {
161 break;
163 ty = reconstructTemplateArgumentType(td, t, t2);
164 if (ty.isNull()) {
165 auto const canon = cast<TemplateDecl>(td->getCanonicalDecl());
166 if (canon != td) {
167 ty = reconstructTemplateArgumentType(canon, t, t2);
170 if (ty.isNull()) {
171 break;
173 } else {
174 CXXOperatorCallExpr const * op
175 = dyn_cast<CXXOperatorCallExpr>(stack.top());
176 assert(op != nullptr);
177 TemplateDecl const * d
178 = t->getTemplateName().getAsTemplateDecl();
179 if (d == nullptr
180 || (d->getQualifiedNameAsString()
181 != "com::sun::star::uno::Sequence")
182 || t->getNumArgs() != 1
183 || t->getArg(0).getKind() != TemplateArgument::Type)
185 break;
187 ty = t->getArg(0).getAsType();
189 stack.pop();
190 if (stack.empty()) {
191 if (loplugin::TypeCheck(ty).AnyBoolean()) {
192 return true;
194 break;
196 t = ty->getAs<TemplateSpecializationType>();
199 return false;
202 // It appears that, given a function declaration, there is no way to determine
203 // the language linkage of the function's type, only of the function's name
204 // (via FunctionDecl::isExternC); however, in a case like
206 // extern "C" { static void f(); }
208 // the function's name does not have C language linkage while the function's
209 // type does (as clarified in C++11 [decl.link]); cf. <http://clang-developers.
210 // 42468.n3.nabble.com/Language-linkage-of-function-type-tt4037248.html>
211 // "Language linkage of function type":
212 bool hasCLanguageLinkageType(FunctionDecl const * decl) {
213 assert(decl != nullptr);
214 if (decl->isExternC()) {
215 return true;
217 if (decl->isInExternCContext()) {
218 return true;
220 return false;
223 class ImplicitBoolConversion:
224 public RecursiveASTVisitor<ImplicitBoolConversion>, public loplugin::Plugin
226 public:
227 explicit ImplicitBoolConversion(loplugin::InstantiationData const & data):
228 Plugin(data) {}
230 virtual void run() override
231 { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
233 bool TraverseCallExpr(CallExpr * expr);
235 bool TraverseCXXMemberCallExpr(CXXMemberCallExpr * expr);
237 bool TraverseCXXConstructExpr(CXXConstructExpr * expr);
239 bool TraverseCXXTemporaryObjectExpr(CXXTemporaryObjectExpr * expr);
241 bool TraverseCStyleCastExpr(CStyleCastExpr * expr);
243 bool TraverseCXXStaticCastExpr(CXXStaticCastExpr * expr);
245 bool TraverseCXXFunctionalCastExpr(CXXFunctionalCastExpr * expr);
247 bool TraverseConditionalOperator(ConditionalOperator * expr);
249 bool TraverseBinLT(BinaryOperator * expr);
251 bool TraverseBinLE(BinaryOperator * expr);
253 bool TraverseBinGT(BinaryOperator * expr);
255 bool TraverseBinGE(BinaryOperator * expr);
257 bool TraverseBinEQ(BinaryOperator * expr);
259 bool TraverseBinNE(BinaryOperator * expr);
261 bool TraverseBinAssign(BinaryOperator * expr);
263 bool TraverseBinAndAssign(CompoundAssignOperator * expr);
265 bool TraverseBinOrAssign(CompoundAssignOperator * expr);
267 bool TraverseBinXorAssign(CompoundAssignOperator * expr);
269 bool TraverseCXXStdInitializerListExpr(CXXStdInitializerListExpr * expr);
271 bool TraverseReturnStmt(ReturnStmt * stmt);
273 bool TraverseFunctionDecl(FunctionDecl * decl);
275 bool VisitImplicitCastExpr(ImplicitCastExpr const * expr);
277 bool VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr const * expr);
279 private:
280 bool isExternCFunctionCall(
281 CallExpr const * expr, FunctionProtoType const ** functionType);
283 bool isExternCFunctionCallReturningInt(Expr const * expr);
285 void checkCXXConstructExpr(CXXConstructExpr const * expr);
287 void reportWarning(ImplicitCastExpr const * expr);
289 std::stack<std::vector<ImplicitCastExpr const *>> nested;
290 std::stack<CallExpr const *> calls;
291 bool bExternCIntFunctionDefinition = false;
294 bool ImplicitBoolConversion::TraverseCallExpr(CallExpr * expr) {
295 nested.push(std::vector<ImplicitCastExpr const *>());
296 calls.push(expr);
297 bool bRet = RecursiveASTVisitor::TraverseCallExpr(expr);
298 FunctionProtoType const * t;
299 bool bExt = isExternCFunctionCall(expr, &t);
300 assert(!nested.empty());
301 for (auto i: nested.top()) {
302 auto j = std::find_if(
303 expr->arg_begin(), expr->arg_end(),
304 [&i](Expr * e) {
305 return i == ignoreParenAndTemporaryMaterialization(e);
307 if (j == expr->arg_end()) {
308 reportWarning(i);
309 } else {
310 std::ptrdiff_t n = j - expr->arg_begin();
311 assert(n >= 0);
312 if (t != nullptr
313 && static_cast<std::size_t>(n) >= t->getNumParams())
315 assert(t->isVariadic());
316 // ignore bool to int promotions of variadic arguments
317 } else if (bExt) {
318 if (t != nullptr) {
319 assert(
320 static_cast<std::size_t>(n) < t->getNumParams());
321 if (!(t->getParamType(n)->isSpecificBuiltinType(
322 BuiltinType::Int)
323 || t->getParamType(n)->isSpecificBuiltinType(
324 BuiltinType::UInt)
325 || t->getParamType(n)->isSpecificBuiltinType(
326 BuiltinType::Long)))
328 reportWarning(i);
330 } else {
331 reportWarning(i);
333 } else {
334 // Filter out
336 // template<typename T> void f(T);
337 // f<sal_Bool>(true);
339 DeclRefExpr const * dr = dyn_cast<DeclRefExpr>(
340 expr->getCallee()->IgnoreParenImpCasts());
341 if (dr != nullptr && dr->hasExplicitTemplateArgs()) {
342 FunctionDecl const * fd
343 = dyn_cast<FunctionDecl>(dr->getDecl());
344 if (fd != nullptr
345 && static_cast<std::size_t>(n) < fd->getNumParams())
347 SubstTemplateTypeParmType const * t2
348 = getAsSubstTemplateTypeParmType(
349 fd->getParamDecl(n)->getType()
350 .getNonReferenceType());
351 if (t2 != nullptr) {
352 //TODO: fix this superficial nonsense check:
353 if (dr->getNumTemplateArgs() == 1) {
354 auto const ta = dr->getTemplateArgs();
355 if ((ta[0].getArgument().getKind()
356 == TemplateArgument::Type)
357 && (loplugin::TypeCheck(
358 ta[0].getTypeSourceInfo()
359 ->getType())
360 .AnyBoolean()))
362 continue;
368 reportWarning(i);
372 calls.pop();
373 nested.pop();
374 return bRet;
377 bool ImplicitBoolConversion::TraverseCXXMemberCallExpr(CXXMemberCallExpr * expr)
379 nested.push(std::vector<ImplicitCastExpr const *>());
380 bool bRet = RecursiveASTVisitor::TraverseCXXMemberCallExpr(expr);
381 assert(!nested.empty());
382 for (auto i: nested.top()) {
383 auto j = std::find_if(
384 expr->arg_begin(), expr->arg_end(),
385 [&i](Expr * e) {
386 return i == ignoreParenAndTemporaryMaterialization(e);
388 if (j != expr->arg_end()) {
389 // Filter out
391 // template<typename T> struct S { void f(T); };
392 // S<sal_Bool> s;
393 // s.f(true);
395 std::ptrdiff_t n = j - expr->arg_begin();
396 assert(n >= 0);
397 CXXMethodDecl const * d = expr->getMethodDecl();
398 if (static_cast<std::size_t>(n) >= d->getNumParams()) {
399 // Ignore bool to int promotions of variadic arguments:
400 assert(d->isVariadic());
401 continue;
403 QualType ty
404 = ignoreParenImpCastAndComma(expr->getImplicitObjectArgument())
405 ->getType();
406 if (dyn_cast<MemberExpr>(expr->getCallee())->isArrow()) {
407 ty = ty->getAs<clang::PointerType>()->getPointeeType();
409 TemplateSpecializationType const * ct
410 = ty->getAs<TemplateSpecializationType>();
411 if (ct != nullptr) {
412 SubstTemplateTypeParmType const * pt
413 = getAsSubstTemplateTypeParmType(
414 d->getParamDecl(n)->getType().getNonReferenceType());
415 if (pt != nullptr) {
416 TemplateDecl const * td
417 = ct->getTemplateName().getAsTemplateDecl();
418 if (td != nullptr) {
419 //TODO: fix this superficial nonsense check:
420 if (ct->getNumArgs() >= 1
421 && ct->getArg(0).getKind() == TemplateArgument::Type
422 && (loplugin::TypeCheck(ct->getArg(0).getAsType())
423 .AnyBoolean()))
425 continue;
431 reportWarning(i);
433 nested.pop();
434 return bRet;
437 bool ImplicitBoolConversion::TraverseCXXConstructExpr(CXXConstructExpr * expr) {
438 nested.push(std::vector<ImplicitCastExpr const *>());
439 bool bRet = RecursiveASTVisitor::TraverseCXXConstructExpr(expr);
440 checkCXXConstructExpr(expr);
441 nested.pop();
442 return bRet;
445 bool ImplicitBoolConversion::TraverseCXXTemporaryObjectExpr(
446 CXXTemporaryObjectExpr * expr)
448 nested.push(std::vector<ImplicitCastExpr const *>());
449 bool bRet = RecursiveASTVisitor::TraverseCXXTemporaryObjectExpr(expr);
450 checkCXXConstructExpr(expr);
451 nested.pop();
452 return bRet;
455 bool ImplicitBoolConversion::TraverseCStyleCastExpr(CStyleCastExpr * expr) {
456 nested.push(std::vector<ImplicitCastExpr const *>());
457 bool bRet = RecursiveASTVisitor::TraverseCStyleCastExpr(expr);
458 assert(!nested.empty());
459 for (auto i: nested.top()) {
460 if (i != expr->getSubExpr()->IgnoreParens()) {
461 reportWarning(i);
464 nested.pop();
465 return bRet;
468 bool ImplicitBoolConversion::TraverseCXXStaticCastExpr(CXXStaticCastExpr * expr)
470 nested.push(std::vector<ImplicitCastExpr const *>());
471 bool bRet = RecursiveASTVisitor::TraverseCXXStaticCastExpr(expr);
472 assert(!nested.empty());
473 for (auto i: nested.top()) {
474 if (i != expr->getSubExpr()->IgnoreParens()) {
475 reportWarning(i);
478 nested.pop();
479 return bRet;
482 bool ImplicitBoolConversion::TraverseCXXFunctionalCastExpr(
483 CXXFunctionalCastExpr * expr)
485 nested.push(std::vector<ImplicitCastExpr const *>());
486 bool bRet = RecursiveASTVisitor::TraverseCXXFunctionalCastExpr(expr);
487 assert(!nested.empty());
488 for (auto i: nested.top()) {
489 if (i != expr->getSubExpr()->IgnoreParens()) {
490 reportWarning(i);
493 nested.pop();
494 return bRet;
497 bool ImplicitBoolConversion::TraverseConditionalOperator(
498 ConditionalOperator * expr)
500 nested.push(std::vector<ImplicitCastExpr const *>());
501 bool bRet = RecursiveASTVisitor::TraverseConditionalOperator(expr);
502 assert(!nested.empty());
503 for (auto i: nested.top()) {
504 if (!((i == expr->getTrueExpr()->IgnoreParens()
505 && (isBoolExpr(expr->getFalseExpr()->IgnoreParenImpCasts())
506 || isExternCFunctionCallReturningInt(expr->getFalseExpr())))
507 || (i == expr->getFalseExpr()->IgnoreParens()
508 && (isBoolExpr(expr->getTrueExpr()->IgnoreParenImpCasts())
509 || isExternCFunctionCallReturningInt(
510 expr->getTrueExpr())))
511 || (!compiler.getLangOpts().CPlusPlus
512 && i == expr->getCond()->IgnoreParens())))
514 reportWarning(i);
517 nested.pop();
518 return bRet;
521 bool ImplicitBoolConversion::TraverseBinLT(BinaryOperator * expr) {
522 nested.push(std::vector<ImplicitCastExpr const *>());
523 bool bRet = RecursiveASTVisitor::TraverseBinLT(expr);
524 assert(!nested.empty());
525 for (auto i: nested.top()) {
526 if (!((i == expr->getLHS()->IgnoreParens()
527 && isMatchingBool(
528 expr->getRHS()->IgnoreImpCasts(), i->getSubExprAsWritten()))
529 || (i == expr->getRHS()->IgnoreParens()
530 && isMatchingBool(
531 expr->getLHS()->IgnoreImpCasts(),
532 i->getSubExprAsWritten()))))
534 reportWarning(i);
537 nested.pop();
538 return bRet;
541 bool ImplicitBoolConversion::TraverseBinLE(BinaryOperator * expr) {
542 nested.push(std::vector<ImplicitCastExpr const *>());
543 bool bRet = RecursiveASTVisitor::TraverseBinLE(expr);
544 assert(!nested.empty());
545 for (auto i: nested.top()) {
546 if (!((i == expr->getLHS()->IgnoreParens()
547 && isMatchingBool(
548 expr->getRHS()->IgnoreImpCasts(), i->getSubExprAsWritten()))
549 || (i == expr->getRHS()->IgnoreParens()
550 && isMatchingBool(
551 expr->getLHS()->IgnoreImpCasts(),
552 i->getSubExprAsWritten()))))
554 reportWarning(i);
557 nested.pop();
558 return bRet;
561 bool ImplicitBoolConversion::TraverseBinGT(BinaryOperator * expr) {
562 nested.push(std::vector<ImplicitCastExpr const *>());
563 bool bRet = RecursiveASTVisitor::TraverseBinGT(expr);
564 assert(!nested.empty());
565 for (auto i: nested.top()) {
566 if (!((i == expr->getLHS()->IgnoreParens()
567 && isMatchingBool(
568 expr->getRHS()->IgnoreImpCasts(), i->getSubExprAsWritten()))
569 || (i == expr->getRHS()->IgnoreParens()
570 && isMatchingBool(
571 expr->getLHS()->IgnoreImpCasts(),
572 i->getSubExprAsWritten()))))
574 reportWarning(i);
577 nested.pop();
578 return bRet;
581 bool ImplicitBoolConversion::TraverseBinGE(BinaryOperator * expr) {
582 nested.push(std::vector<ImplicitCastExpr const *>());
583 bool bRet = RecursiveASTVisitor::TraverseBinGE(expr);
584 assert(!nested.empty());
585 for (auto i: nested.top()) {
586 if (!((i == expr->getLHS()->IgnoreParens()
587 && isMatchingBool(
588 expr->getRHS()->IgnoreImpCasts(), i->getSubExprAsWritten()))
589 || (i == expr->getRHS()->IgnoreParens()
590 && isMatchingBool(
591 expr->getLHS()->IgnoreImpCasts(),
592 i->getSubExprAsWritten()))))
594 reportWarning(i);
597 nested.pop();
598 return bRet;
601 bool ImplicitBoolConversion::TraverseBinEQ(BinaryOperator * expr) {
602 nested.push(std::vector<ImplicitCastExpr const *>());
603 bool bRet = RecursiveASTVisitor::TraverseBinEQ(expr);
604 assert(!nested.empty());
605 for (auto i: nested.top()) {
606 if (!((i == expr->getLHS()->IgnoreParens()
607 && isMatchingBool(
608 expr->getRHS()->IgnoreImpCasts(), i->getSubExprAsWritten()))
609 || (i == expr->getRHS()->IgnoreParens()
610 && isMatchingBool(
611 expr->getLHS()->IgnoreImpCasts(),
612 i->getSubExprAsWritten()))))
614 reportWarning(i);
617 nested.pop();
618 return bRet;
621 bool ImplicitBoolConversion::TraverseBinNE(BinaryOperator * expr) {
622 nested.push(std::vector<ImplicitCastExpr const *>());
623 bool bRet = RecursiveASTVisitor::TraverseBinNE(expr);
624 assert(!nested.empty());
625 for (auto i: nested.top()) {
626 if (!((i == expr->getLHS()->IgnoreParens()
627 && isMatchingBool(
628 expr->getRHS()->IgnoreImpCasts(), i->getSubExprAsWritten()))
629 || (i == expr->getRHS()->IgnoreParens()
630 && isMatchingBool(
631 expr->getLHS()->IgnoreImpCasts(),
632 i->getSubExprAsWritten()))))
634 reportWarning(i);
637 nested.pop();
638 return bRet;
641 bool ImplicitBoolConversion::TraverseBinAssign(BinaryOperator * expr) {
642 nested.push(std::vector<ImplicitCastExpr const *>());
643 bool bRet = RecursiveASTVisitor::TraverseBinAssign(expr);
644 // /usr/include/gtk-2.0/gtk/gtktogglebutton.h: struct _GtkToggleButton:
645 // guint GSEAL (active) : 1;
646 // even though <http://www.gtk.org/api/2.6/gtk/GtkToggleButton.html>:
647 // "active" gboolean : Read / Write
648 bool bExt = false;
649 MemberExpr const * me = dyn_cast<MemberExpr>(expr->getLHS());
650 if (me != nullptr) {
651 FieldDecl const * fd = dyn_cast<FieldDecl>(me->getMemberDecl());
652 if (fd != nullptr && fd->isBitField()
653 && fd->getBitWidthValue(compiler.getASTContext()) == 1)
655 TypedefType const * t = fd->getType()->getAs<TypedefType>();
656 bExt = t != nullptr && t->getDecl()->getNameAsString() == "guint";
659 assert(!nested.empty());
660 for (auto i: nested.top()) {
661 if (i != expr->getRHS()->IgnoreParens()
662 || !(bExt || isBoolExpr(expr->getLHS())))
664 reportWarning(i);
667 nested.pop();
668 return bRet;
671 bool ImplicitBoolConversion::TraverseBinAndAssign(CompoundAssignOperator * expr)
673 nested.push(std::vector<ImplicitCastExpr const *>());
674 bool bRet = RecursiveASTVisitor::TraverseBinAndAssign(expr);
675 assert(!nested.empty());
676 for (auto i: nested.top()) {
677 if (i != expr->getRHS()->IgnoreParens()
678 || !isBool(expr->getLHS()->IgnoreParens(), false))
680 reportWarning(i);
683 nested.pop();
684 if (!ignoreLocation(expr) && isBool(expr->getLHS(), false)
685 && !isBool(expr->getRHS()->IgnoreParenImpCasts(), false))
687 report(
688 DiagnosticsEngine::Warning, "mix of %0 and %1 in operator &=",
689 expr->getRHS()->getLocStart())
690 << expr->getLHS()->getType()
691 << expr->getRHS()->IgnoreParenImpCasts()->getType()
692 << expr->getSourceRange();
694 return bRet;
697 bool ImplicitBoolConversion::TraverseBinOrAssign(CompoundAssignOperator * expr)
699 nested.push(std::vector<ImplicitCastExpr const *>());
700 bool bRet = RecursiveASTVisitor::TraverseBinOrAssign(expr);
701 assert(!nested.empty());
702 for (auto i: nested.top()) {
703 if (i != expr->getRHS()->IgnoreParens()
704 || !isBool(expr->getLHS()->IgnoreParens(), false))
706 reportWarning(i);
709 nested.pop();
710 if (!ignoreLocation(expr) && isBool(expr->getLHS(), false)
711 && !isBool(expr->getRHS()->IgnoreParenImpCasts(), false))
713 report(
714 DiagnosticsEngine::Warning, "mix of %0 and %1 in operator |=",
715 expr->getRHS()->getLocStart())
716 << expr->getLHS()->getType()
717 << expr->getRHS()->IgnoreParenImpCasts()->getType()
718 << expr->getSourceRange();
720 return bRet;
723 bool ImplicitBoolConversion::TraverseBinXorAssign(CompoundAssignOperator * expr)
725 nested.push(std::vector<ImplicitCastExpr const *>());
726 bool bRet = RecursiveASTVisitor::TraverseBinXorAssign(expr);
727 assert(!nested.empty());
728 for (auto i: nested.top()) {
729 if (i != expr->getRHS()->IgnoreParens()
730 || !isBool(expr->getLHS()->IgnoreParens(), false))
732 reportWarning(i);
735 nested.pop();
736 if (!ignoreLocation(expr) && isBool(expr->getLHS(), false)
737 && !isBool(expr->getRHS()->IgnoreParenImpCasts(), false))
739 report(
740 DiagnosticsEngine::Warning, "mix of %0 and %1 in operator ^=",
741 expr->getRHS()->getLocStart())
742 << expr->getLHS()->getType()
743 << expr->getRHS()->IgnoreParenImpCasts()->getType()
744 << expr->getSourceRange();
746 return bRet;
749 bool ImplicitBoolConversion::TraverseCXXStdInitializerListExpr(
750 CXXStdInitializerListExpr * expr)
752 // Must be some std::initializer_list<T>; check whether T is sal_Bool (i.e.,
753 // unsigned char) [TODO: check for real sal_Bool instead]:
754 auto t = expr->getType();
755 if (auto et = dyn_cast<ElaboratedType>(t)) {
756 t = et->desugar();
758 auto ts = t->getAs<TemplateSpecializationType>();
759 if (ts == nullptr
760 || !ts->getArg(0).getAsType()->isSpecificBuiltinType(
761 clang::BuiltinType::UChar))
763 return RecursiveASTVisitor::TraverseCXXStdInitializerListExpr(expr);
765 // Avoid warnings for code like
767 // Sequence<sal_Bool> arBool({true, false, true});
769 auto e = dyn_cast<InitListExpr>(
770 ignoreParenAndTemporaryMaterialization(expr->getSubExpr()));
771 if (e == nullptr) {
772 return RecursiveASTVisitor::TraverseCXXStdInitializerListExpr(expr);
774 nested.push(std::vector<ImplicitCastExpr const *>());
775 bool ret = RecursiveASTVisitor::TraverseCXXStdInitializerListExpr(expr);
776 assert(!nested.empty());
777 for (auto i: nested.top()) {
778 if (std::find(e->begin(), e->end(), i) == e->end()) {
779 reportWarning(i);
782 nested.pop();
783 return ret;
786 bool ImplicitBoolConversion::TraverseReturnStmt(ReturnStmt * stmt) {
787 nested.push(std::vector<ImplicitCastExpr const *>());
788 bool bRet = RecursiveASTVisitor::TraverseReturnStmt(stmt);
789 Expr const * expr = stmt->getRetValue();
790 if (expr != nullptr) {
791 ExprWithCleanups const * ec = dyn_cast<ExprWithCleanups>(expr);
792 if (ec != nullptr) {
793 expr = ec->getSubExpr();
795 expr = expr->IgnoreParens();
797 assert(!nested.empty());
798 for (auto i: nested.top()) {
799 if (i != expr || !bExternCIntFunctionDefinition) {
800 reportWarning(i);
803 nested.pop();
804 return bRet;
807 bool ImplicitBoolConversion::TraverseFunctionDecl(FunctionDecl * decl) {
808 bool bExt = false;
809 if (hasCLanguageLinkageType(decl) && decl->isThisDeclarationADefinition()) {
810 QualType t { decl->getReturnType() };
811 if (t->isSpecificBuiltinType(BuiltinType::Int)
812 || t->isSpecificBuiltinType(BuiltinType::UInt))
814 bExt = true;
815 } else {
816 TypedefType const * t2 = t->getAs<TypedefType>();
817 // cf. rtl_locale_equals (and sal_Int32 can be long):
818 if (t2 != nullptr
819 && t2->getDecl()->getNameAsString() == "sal_Int32")
821 bExt = true;
825 if (bExt) {
826 assert(!bExternCIntFunctionDefinition);
827 bExternCIntFunctionDefinition = true;
829 bool bRet = RecursiveASTVisitor::TraverseFunctionDecl(decl);
830 if (bExt) {
831 bExternCIntFunctionDefinition = false;
833 return bRet;
836 bool ImplicitBoolConversion::VisitImplicitCastExpr(
837 ImplicitCastExpr const * expr)
839 if (ignoreLocation(expr)) {
840 return true;
842 if (isBool(expr->getSubExprAsWritten()) && !isBool(expr)) {
843 // Ignore NoOp from 'sal_Bool' (aka 'unsigned char') to 'const unsigned
844 // char' in makeAny(b) with b of type sal_Bool:
845 if (expr->getCastKind() != CK_NoOp) {
846 if (nested.empty()) {
847 reportWarning(expr);
848 } else {
849 nested.top().push_back(expr);
852 return true;
854 if (auto const sub = dyn_cast<ExplicitCastExpr>(
855 compat::getSubExprAsWritten(expr)))
857 auto const subsub = compat::getSubExprAsWritten(sub);
858 if (subsub->getType().IgnoreParens() == expr->getType().IgnoreParens()
859 && isBool(subsub))
861 // Ignore "normalizing cast" bool(b) from sal_Bool b to bool, then
862 // implicitly cast back again to sal_Bool:
863 if (dyn_cast<CXXFunctionalCastExpr>(sub) != nullptr
864 && sub->getType()->isBooleanType() && isSalBool(expr->getType())
865 && isSalBool(subsub->getType()))
867 return true;
869 report(
870 DiagnosticsEngine::Warning,
871 ("explicit conversion (%0) from %1 to %2 implicitly cast back"
872 " to %3"),
873 expr->getLocStart())
874 << sub->getCastKindName() << subsub->getType() << sub->getType()
875 << expr->getType() << expr->getSourceRange();
876 return true;
879 if (expr->getType()->isBooleanType() && !isBoolExpr(expr->getSubExpr())
880 && !calls.empty())
882 CallExpr const * call = calls.top();
883 if (std::find_if(
884 call->arg_begin(), call->arg_end(),
885 [expr](Expr const * e) { return expr == e->IgnoreParens(); })
886 != call->arg_end())
888 report(
889 DiagnosticsEngine::Warning,
890 "implicit conversion (%0) of call argument from %1 to %2",
891 expr->getLocStart())
892 << expr->getCastKindName() << expr->getSubExpr()->getType()
893 << expr->getType() << expr->getSourceRange();
894 return true;
897 return true;
900 bool ImplicitBoolConversion::VisitMaterializeTemporaryExpr(
901 MaterializeTemporaryExpr const * expr)
903 if (ignoreLocation(expr)) {
904 return true;
906 if (auto const sub = dyn_cast<ExplicitCastExpr>(expr->GetTemporaryExpr())) {
907 auto const subsub = compat::getSubExprAsWritten(sub);
908 if (subsub->getType().IgnoreParens() == expr->getType().IgnoreParens()
909 && isBool(subsub))
911 report(
912 DiagnosticsEngine::Warning,
913 ("explicit conversion (%0) from %1 to %2 implicitly converted"
914 " back to %3"),
915 expr->getLocStart())
916 << sub->getCastKindName() << subsub->getType() << sub->getType()
917 << expr->getType() << expr->getSourceRange();
918 return true;
921 return true;
924 bool ImplicitBoolConversion::isExternCFunctionCall(
925 CallExpr const * expr, FunctionProtoType const ** functionType)
927 assert(functionType != nullptr);
928 *functionType = nullptr;
929 Decl const * d = expr->getCalleeDecl();
930 if (d != nullptr) {
931 FunctionDecl const * fd = dyn_cast<FunctionDecl>(d);
932 if (fd != nullptr) {
933 clang::PointerType const * pt = fd->getType()
934 ->getAs<clang::PointerType>();
935 QualType t2(pt == nullptr ? fd->getType() : pt->getPointeeType());
936 *functionType = t2->getAs<FunctionProtoType>();
937 assert(
938 *functionType != nullptr || !compiler.getLangOpts().CPlusPlus
939 || (fd->getBuiltinID() != Builtin::NotBuiltin
940 && isa<FunctionNoProtoType>(t2)));
941 // __builtin_*s have no proto type?
942 return fd->isExternC()
943 || compiler.getSourceManager().isInExternCSystemHeader(
944 fd->getLocation());
946 VarDecl const * vd = dyn_cast<VarDecl>(d);
947 if (vd != nullptr) {
948 clang::PointerType const * pt = vd->getType()
949 ->getAs<clang::PointerType>();
950 *functionType
951 = ((pt == nullptr ? vd->getType() : pt->getPointeeType())
952 ->getAs<FunctionProtoType>());
953 return vd->isExternC();
956 return false;
959 bool ImplicitBoolConversion::isExternCFunctionCallReturningInt(
960 Expr const * expr)
962 CallExpr const * e = dyn_cast<CallExpr>(expr->IgnoreParenImpCasts());
963 FunctionProtoType const * t;
964 return e != nullptr && e->getType()->isSpecificBuiltinType(BuiltinType::Int)
965 && isExternCFunctionCall(e, &t);
968 void ImplicitBoolConversion::checkCXXConstructExpr(
969 CXXConstructExpr const * expr)
971 assert(!nested.empty());
972 for (auto i: nested.top()) {
973 auto j = std::find_if(
974 expr->arg_begin(), expr->arg_end(),
975 [&i](Expr const * e) {
976 return i == ignoreParenAndTemporaryMaterialization(e);
978 if (j != expr->arg_end()) {
979 TemplateSpecializationType const * t1 = expr->getType()->
980 getAs<TemplateSpecializationType>();
981 SubstTemplateTypeParmType const * t2 = nullptr;
982 CXXConstructorDecl const * d = expr->getConstructor();
983 if (d->getNumParams() == expr->getNumArgs()) { //TODO: better check
984 t2 = getAsSubstTemplateTypeParmType(
985 d->getParamDecl(j - expr->arg_begin())->getType()
986 .getNonReferenceType());
988 if (t1 != nullptr && t2 != nullptr) {
989 TemplateDecl const * td
990 = t1->getTemplateName().getAsTemplateDecl();
991 if (td != nullptr) {
992 TemplateParameterList const * ps
993 = td->getTemplateParameters();
994 auto i = std::find(
995 ps->begin(), ps->end(),
996 t2->getReplacedParameter()->getDecl());
997 if (i != ps->end()) {
998 if (ps->size() == t1->getNumArgs()) { //TODO
999 TemplateArgument const & arg = t1->getArg(
1000 i - ps->begin());
1001 if (arg.getKind() == TemplateArgument::Type
1002 && (loplugin::TypeCheck(arg.getAsType())
1003 .AnyBoolean()))
1005 continue;
1012 reportWarning(i);
1016 void ImplicitBoolConversion::reportWarning(ImplicitCastExpr const * expr) {
1017 if (compiler.getLangOpts().CPlusPlus) {
1018 report(
1019 DiagnosticsEngine::Warning,
1020 "implicit conversion (%0) from %1 to %2", expr->getLocStart())
1021 << expr->getCastKindName() << expr->getSubExprAsWritten()->getType()
1022 << expr->getType() << expr->getSourceRange();
1026 loplugin::Plugin::Registration<ImplicitBoolConversion> X(
1027 "implicitboolconversion");
1031 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */