bump product version to 7.2.5.1
[LibreOffice.git] / compilerplugins / clang / implicitboolconversion.cxx
blob7e708eb49843123c0b3f38f25dda1711e1acba1a
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 "clang/Basic/Builtins.h"
20 #include "config_clang.h"
22 #include "check.hxx"
23 #include "compat.hxx"
24 #include "plugin.hxx"
26 namespace {
28 Expr const * ignoreParenAndTemporaryMaterialization(Expr const * expr) {
29 for (;;) {
30 expr = expr->IgnoreParens();
31 auto e = dyn_cast<MaterializeTemporaryExpr>(expr);
32 if (e == nullptr) {
33 return expr;
35 expr = compat::getSubExpr(e);
39 Expr const * ignoreParenImpCastAndComma(Expr const * expr) {
40 for (;;) {
41 expr = expr->IgnoreParenImpCasts();
42 BinaryOperator const * op = dyn_cast<BinaryOperator>(expr);
43 if (op == nullptr || op->getOpcode() != BO_Comma) {
44 return expr;
46 expr = op->getRHS();
50 SubstTemplateTypeParmType const * getAsSubstTemplateTypeParmType(QualType type)
52 //TODO: unwrap all kinds of (non-SubstTemplateTypeParmType) sugar, not only
53 // TypedefType sugar:
54 for (;;) {
55 TypedefType const * t = type->getAs<TypedefType>();
56 if (t == nullptr) {
57 return dyn_cast<SubstTemplateTypeParmType>(type);
59 type = t->desugar();
63 QualType reconstructTemplateArgumentType(
64 TemplateDecl const * decl, TemplateSpecializationType const * specializationType,
65 SubstTemplateTypeParmType const * parmType)
67 TemplateParameterList const * ps = decl->getTemplateParameters();
68 auto i = std::find(ps->begin(), ps->end(), parmType->getReplacedParameter()->getDecl());
69 if (i == ps->end()) {
70 return {};
72 if (ps->size() != specializationType->getNumArgs()) { //TODO
73 return {};
75 TemplateArgument const & arg = specializationType->getArg(i - ps->begin());
76 if (arg.getKind() != TemplateArgument::Type) {
77 return {};
79 return arg.getAsType();
82 bool areSameTypedef(QualType type1, QualType type2) {
83 // type1.getTypePtr() == typ2.getTypePtr() fails for e.g. ::sal_Bool vs.
84 // sal_Bool:
85 auto t1 = type1->getAs<TypedefType>();
86 auto t2 = type2->getAs<TypedefType>();
87 return t1 != nullptr && t2 != nullptr && t1->getDecl() == t2->getDecl();
90 bool isBool(Expr const * expr, bool allowTypedefs = true) {
91 auto t = expr->getType();
92 return allowTypedefs
93 ? bool(loplugin::TypeCheck(t).AnyBoolean()) : t->isBooleanType();
96 bool isMatchingBool(Expr const * expr, Expr const * comparisonExpr) {
97 return isBool(expr, false)
98 || areSameTypedef(expr->getType(), comparisonExpr->getType());
101 bool isSalBool(QualType type) {
102 auto t = type->getAs<TypedefType>();
103 return t != nullptr && t->getDecl()->getName() == "sal_Bool";
106 bool isBoolExpr(Expr const * expr) {
107 if (isBool(expr)) {
108 return true;
110 expr = ignoreParenImpCastAndComma(expr);
111 ConditionalOperator const * co = dyn_cast<ConditionalOperator>(expr);
112 if (co != nullptr) {
113 ImplicitCastExpr const * ic1 = dyn_cast<ImplicitCastExpr>(
114 co->getTrueExpr()->IgnoreParens());
115 ImplicitCastExpr const * ic2 = dyn_cast<ImplicitCastExpr>(
116 co->getFalseExpr()->IgnoreParens());
117 if (ic1 != nullptr && ic2 != nullptr
118 && ic1->getType()->isSpecificBuiltinType(BuiltinType::Int)
119 && isBoolExpr(ic1->getSubExpr()->IgnoreParens())
120 && ic2->getType()->isSpecificBuiltinType(BuiltinType::Int)
121 && isBoolExpr(ic2->getSubExpr()->IgnoreParens()))
123 return true;
126 std::stack<Expr const *> stack;
127 Expr const * e = expr;
128 for (;;) {
129 e = ignoreParenImpCastAndComma(e);
130 MemberExpr const * me = dyn_cast<MemberExpr>(e);
131 if (me == nullptr) {
132 break;
134 stack.push(e);
135 e = me->getBase();
137 for (;;) {
138 e = ignoreParenImpCastAndComma(e);
139 CXXOperatorCallExpr const * op = dyn_cast<CXXOperatorCallExpr>(e);
140 if (op == nullptr || op->getOperator() != OO_Subscript) {
141 break;
143 stack.push(e);
144 e = op->getArg(0);
146 if (!stack.empty()) {
147 TemplateSpecializationType const * t
148 = e->getType()->getAs<TemplateSpecializationType>();
149 for (;;) {
150 if (t == nullptr) {
151 break;
153 QualType ty;
154 MemberExpr const * me = dyn_cast<MemberExpr>(stack.top());
155 if (me != nullptr) {
156 TemplateDecl const * td
157 = t->getTemplateName().getAsTemplateDecl();
158 if (td == nullptr) {
159 break;
161 SubstTemplateTypeParmType const * t2
162 = getAsSubstTemplateTypeParmType(
163 me->getMemberDecl()->getType());
164 if (t2 == nullptr) {
165 break;
167 ty = reconstructTemplateArgumentType(td, t, t2);
168 if (ty.isNull()) {
169 auto const canon = cast<TemplateDecl>(td->getCanonicalDecl());
170 if (canon != td) {
171 ty = reconstructTemplateArgumentType(canon, t, t2);
174 if (ty.isNull()) {
175 break;
177 } else {
178 CXXOperatorCallExpr const * op
179 = dyn_cast<CXXOperatorCallExpr>(stack.top());
180 assert(op != nullptr);
181 (void)op;
182 TemplateDecl const * d
183 = t->getTemplateName().getAsTemplateDecl();
184 if (d == nullptr
185 || (d->getQualifiedNameAsString()
186 != "com::sun::star::uno::Sequence")
187 || t->getNumArgs() != 1
188 || t->getArg(0).getKind() != TemplateArgument::Type)
190 break;
192 ty = t->getArg(0).getAsType();
194 stack.pop();
195 if (stack.empty()) {
196 if (loplugin::TypeCheck(ty).AnyBoolean()) {
197 return true;
199 break;
201 t = ty->getAs<TemplateSpecializationType>();
204 return false;
207 // It appears that, given a function declaration, there is no way to determine
208 // the language linkage of the function's type, only of the function's name
209 // (via FunctionDecl::isExternC); however, in a case like
211 // extern "C" { static void f(); }
213 // the function's name does not have C language linkage while the function's
214 // type does (as clarified in C++11 [decl.link]); cf. <http://clang-developers.
215 // 42468.n3.nabble.com/Language-linkage-of-function-type-tt4037248.html>
216 // "Language linkage of function type":
217 bool hasCLanguageLinkageType(FunctionDecl const * decl) {
218 assert(decl != nullptr);
219 if (decl->isExternC()) {
220 return true;
222 if (decl->isInExternCContext()) {
223 return true;
225 return false;
228 class ImplicitBoolConversion:
229 public loplugin::FilteringPlugin<ImplicitBoolConversion>
231 public:
232 explicit ImplicitBoolConversion(loplugin::InstantiationData const & data):
233 FilteringPlugin(data) {}
235 virtual void run() override
236 { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
238 bool TraverseCallExpr(CallExpr * expr);
240 bool TraverseCXXMemberCallExpr(CXXMemberCallExpr * expr);
242 bool TraverseCXXConstructExpr(CXXConstructExpr * expr);
244 bool TraverseCXXTemporaryObjectExpr(CXXTemporaryObjectExpr * expr);
246 bool TraverseCStyleCastExpr(CStyleCastExpr * expr);
248 bool TraverseCXXStaticCastExpr(CXXStaticCastExpr * expr);
250 bool TraverseCXXFunctionalCastExpr(CXXFunctionalCastExpr * expr);
252 bool TraverseConditionalOperator(ConditionalOperator * expr);
254 bool TraverseBinaryOperator(BinaryOperator * expr);
256 #if CLANG_VERSION < 110000
257 bool TraverseBinLT(BinaryOperator * expr) { return TraverseBinaryOperator(expr); }
258 bool TraverseBinLE(BinaryOperator * expr) { return TraverseBinaryOperator(expr); }
259 bool TraverseBinGT(BinaryOperator * expr) { return TraverseBinaryOperator(expr); }
260 bool TraverseBinGE(BinaryOperator * expr) { return TraverseBinaryOperator(expr); }
261 bool TraverseBinEQ(BinaryOperator * expr) { return TraverseBinaryOperator(expr); }
262 bool TraverseBinNE(BinaryOperator * expr) { return TraverseBinaryOperator(expr); }
263 bool TraverseBinAssign(BinaryOperator * expr) { return TraverseBinaryOperator(expr); }
264 #endif
266 bool TraverseCompoundAssignOperator(CompoundAssignOperator * expr);
268 #if CLANG_VERSION < 110000
269 bool TraverseBinAndAssign(CompoundAssignOperator * expr)
270 { return TraverseCompoundAssignOperator(expr); }
271 bool TraverseBinOrAssign(CompoundAssignOperator * expr)
272 { return TraverseCompoundAssignOperator(expr); }
273 bool TraverseBinXorAssign(CompoundAssignOperator * expr)
274 { return TraverseCompoundAssignOperator(expr); }
275 #endif
277 bool TraverseCXXStdInitializerListExpr(CXXStdInitializerListExpr * expr);
279 bool TraverseReturnStmt(ReturnStmt * stmt);
281 bool TraverseFunctionDecl(FunctionDecl * decl);
283 bool VisitImplicitCastExpr(ImplicitCastExpr const * expr);
285 bool VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr const * expr);
287 private:
288 bool isExternCFunctionCall(
289 CallExpr const * expr, FunctionProtoType const ** functionType);
291 bool isExternCFunctionCallReturningInt(Expr const * expr);
293 void checkCXXConstructExpr(CXXConstructExpr const * expr);
295 void reportWarning(ImplicitCastExpr const * expr);
297 std::stack<std::vector<ImplicitCastExpr const *>> nested;
298 std::stack<CallExpr const *> calls;
299 bool bExternCIntFunctionDefinition = false;
302 bool ImplicitBoolConversion::TraverseCallExpr(CallExpr * expr) {
303 nested.push(std::vector<ImplicitCastExpr const *>());
304 calls.push(expr);
305 bool bRet = RecursiveASTVisitor::TraverseCallExpr(expr);
306 FunctionProtoType const * t;
307 bool bExt = isExternCFunctionCall(expr, &t);
308 assert(!nested.empty());
309 for (auto i: nested.top()) {
310 auto j = std::find_if(
311 expr->arg_begin(), expr->arg_end(),
312 [&i](Expr * e) {
313 return i == ignoreParenAndTemporaryMaterialization(e);
315 if (j == expr->arg_end()) {
316 reportWarning(i);
317 } else {
318 std::ptrdiff_t n = j - expr->arg_begin();
319 assert(n >= 0);
320 if (t != nullptr
321 && static_cast<std::size_t>(n) >= t->getNumParams())
323 assert(t->isVariadic());
324 // ignore bool to int promotions of variadic arguments
325 } else if (bExt) {
326 if (t != nullptr) {
327 assert(
328 static_cast<std::size_t>(n) < t->getNumParams());
329 if (!(t->getParamType(n)->isSpecificBuiltinType(
330 BuiltinType::Int)
331 || t->getParamType(n)->isSpecificBuiltinType(
332 BuiltinType::UInt)
333 || t->getParamType(n)->isSpecificBuiltinType(
334 BuiltinType::Long)))
336 reportWarning(i);
338 } else {
339 reportWarning(i);
341 } else {
342 // Filter out
344 // template<typename T> void f(T);
345 // f<sal_Bool>(true);
347 DeclRefExpr const * dr = dyn_cast<DeclRefExpr>(
348 expr->getCallee()->IgnoreParenImpCasts());
349 if (dr != nullptr && dr->hasExplicitTemplateArgs()) {
350 FunctionDecl const * fd
351 = dyn_cast<FunctionDecl>(dr->getDecl());
352 if (fd != nullptr
353 && static_cast<std::size_t>(n) < fd->getNumParams())
355 SubstTemplateTypeParmType const * t2
356 = getAsSubstTemplateTypeParmType(
357 fd->getParamDecl(n)->getType()
358 .getNonReferenceType());
359 if (t2 != nullptr) {
360 //TODO: fix this superficial nonsense check:
361 if (dr->getNumTemplateArgs() == 1) {
362 auto const ta = dr->getTemplateArgs();
363 if ((ta[0].getArgument().getKind()
364 == TemplateArgument::Type)
365 && (loplugin::TypeCheck(
366 ta[0].getTypeSourceInfo()
367 ->getType())
368 .AnyBoolean()))
370 continue;
376 reportWarning(i);
380 calls.pop();
381 nested.pop();
382 return bRet;
385 bool ImplicitBoolConversion::TraverseCXXMemberCallExpr(CXXMemberCallExpr * expr)
387 nested.push(std::vector<ImplicitCastExpr const *>());
388 bool bRet = RecursiveASTVisitor::TraverseCXXMemberCallExpr(expr);
389 assert(!nested.empty());
390 for (auto i: nested.top()) {
391 auto j = std::find_if(
392 expr->arg_begin(), expr->arg_end(),
393 [&i](Expr * e) {
394 return i == ignoreParenAndTemporaryMaterialization(e);
396 if (j != expr->arg_end()) {
397 // Filter out
399 // template<typename T> struct S { void f(T); };
400 // S<sal_Bool> s;
401 // s.f(true);
403 std::ptrdiff_t n = j - expr->arg_begin();
404 assert(n >= 0);
405 CXXMethodDecl const * d = expr->getMethodDecl();
406 if (static_cast<std::size_t>(n) >= d->getNumParams()) {
407 // Ignore bool to int promotions of variadic arguments:
408 assert(d->isVariadic());
409 continue;
411 QualType ty
412 = ignoreParenImpCastAndComma(expr->getImplicitObjectArgument())
413 ->getType();
414 if (dyn_cast<MemberExpr>(expr->getCallee())->isArrow()) {
415 ty = ty->getAs<clang::PointerType>()->getPointeeType();
417 TemplateSpecializationType const * ct
418 = ty->getAs<TemplateSpecializationType>();
419 if (ct != nullptr) {
420 SubstTemplateTypeParmType const * pt
421 = getAsSubstTemplateTypeParmType(
422 d->getParamDecl(n)->getType().getNonReferenceType());
423 if (pt != nullptr) {
424 TemplateDecl const * td
425 = ct->getTemplateName().getAsTemplateDecl();
426 if (td != nullptr) {
427 //TODO: fix this superficial nonsense check:
428 if (ct->getNumArgs() >= 1
429 && ct->getArg(0).getKind() == TemplateArgument::Type
430 && (loplugin::TypeCheck(ct->getArg(0).getAsType())
431 .AnyBoolean()))
433 continue;
439 reportWarning(i);
441 nested.pop();
442 return bRet;
445 bool ImplicitBoolConversion::TraverseCXXConstructExpr(CXXConstructExpr * expr) {
446 nested.push(std::vector<ImplicitCastExpr const *>());
447 bool bRet = RecursiveASTVisitor::TraverseCXXConstructExpr(expr);
448 checkCXXConstructExpr(expr);
449 nested.pop();
450 return bRet;
453 bool ImplicitBoolConversion::TraverseCXXTemporaryObjectExpr(
454 CXXTemporaryObjectExpr * expr)
456 nested.push(std::vector<ImplicitCastExpr const *>());
457 bool bRet = RecursiveASTVisitor::TraverseCXXTemporaryObjectExpr(expr);
458 checkCXXConstructExpr(expr);
459 nested.pop();
460 return bRet;
463 bool ImplicitBoolConversion::TraverseCStyleCastExpr(CStyleCastExpr * expr) {
464 nested.push(std::vector<ImplicitCastExpr const *>());
465 bool bRet = RecursiveASTVisitor::TraverseCStyleCastExpr(expr);
466 assert(!nested.empty());
467 for (auto i: nested.top()) {
468 if (i != expr->getSubExpr()->IgnoreParens()) {
469 reportWarning(i);
472 nested.pop();
473 return bRet;
476 bool ImplicitBoolConversion::TraverseCXXStaticCastExpr(CXXStaticCastExpr * expr)
478 nested.push(std::vector<ImplicitCastExpr const *>());
479 bool bRet = RecursiveASTVisitor::TraverseCXXStaticCastExpr(expr);
480 assert(!nested.empty());
481 for (auto i: nested.top()) {
482 if (i != expr->getSubExpr()->IgnoreParens()) {
483 reportWarning(i);
486 nested.pop();
487 return bRet;
490 bool ImplicitBoolConversion::TraverseCXXFunctionalCastExpr(
491 CXXFunctionalCastExpr * expr)
493 nested.push(std::vector<ImplicitCastExpr const *>());
494 bool bRet = RecursiveASTVisitor::TraverseCXXFunctionalCastExpr(expr);
495 assert(!nested.empty());
496 for (auto i: nested.top()) {
497 if (i != expr->getSubExpr()->IgnoreParens()) {
498 reportWarning(i);
501 nested.pop();
502 return bRet;
505 bool ImplicitBoolConversion::TraverseConditionalOperator(
506 ConditionalOperator * expr)
508 nested.push(std::vector<ImplicitCastExpr const *>());
509 bool bRet = RecursiveASTVisitor::TraverseConditionalOperator(expr);
510 assert(!nested.empty());
511 for (auto i: nested.top()) {
512 if (!((i == expr->getTrueExpr()->IgnoreParens()
513 && (isBoolExpr(expr->getFalseExpr()->IgnoreParenImpCasts())
514 || isExternCFunctionCallReturningInt(expr->getFalseExpr())))
515 || (i == expr->getFalseExpr()->IgnoreParens()
516 && (isBoolExpr(expr->getTrueExpr()->IgnoreParenImpCasts())
517 || isExternCFunctionCallReturningInt(
518 expr->getTrueExpr())))
519 || (!compiler.getLangOpts().CPlusPlus
520 && i == expr->getCond()->IgnoreParens())))
522 reportWarning(i);
525 nested.pop();
526 return bRet;
529 bool ImplicitBoolConversion::TraverseBinaryOperator(BinaryOperator * expr) {
530 switch (expr->getOpcode()) {
531 case BO_LT:
532 case BO_LE:
533 case BO_GT:
534 case BO_GE:
535 case BO_EQ:
536 case BO_NE:
538 nested.push(std::vector<ImplicitCastExpr const *>());
539 bool bRet = RecursiveASTVisitor::TraverseBinaryOperator(expr);
540 assert(!nested.empty());
541 for (auto i: nested.top()) {
542 if (!((i == expr->getLHS()->IgnoreParens()
543 && isMatchingBool(
544 expr->getRHS()->IgnoreImpCasts(), i->getSubExprAsWritten()))
545 || (i == expr->getRHS()->IgnoreParens()
546 && isMatchingBool(
547 expr->getLHS()->IgnoreImpCasts(),
548 i->getSubExprAsWritten()))))
550 reportWarning(i);
553 nested.pop();
554 return bRet;
556 case BO_Assign:
558 nested.push(std::vector<ImplicitCastExpr const *>());
559 bool bRet = RecursiveASTVisitor::TraverseBinaryOperator(expr);
560 // gtk-2.0/gtk/gtktogglebutton.h: struct _GtkToggleButton:
561 // guint GSEAL (active) : 1;
562 // even though <http://www.gtk.org/api/2.6/gtk/GtkToggleButton.html>:
563 // "active" gboolean : Read / Write
564 // qt5/QtGui/qaccessible.h: struct State:
565 // quint64 disabled : 1;
566 bool bExt = false;
567 MemberExpr const * me = dyn_cast<MemberExpr>(expr->getLHS());
568 if (me != nullptr) {
569 FieldDecl const * fd = dyn_cast<FieldDecl>(me->getMemberDecl());
570 if (fd != nullptr && fd->isBitField()
571 && fd->getBitWidthValue(compiler.getASTContext()) == 1)
573 auto const check = loplugin::TypeCheck(fd->getType());
574 bExt = check.Typedef("guint").GlobalNamespace()
575 || check.Typedef("quint64").GlobalNamespace();
578 assert(!nested.empty());
579 for (auto i: nested.top()) {
580 if (i != expr->getRHS()->IgnoreParens()
581 || !(bExt || isBoolExpr(expr->getLHS())))
583 reportWarning(i);
586 nested.pop();
587 return bRet;
589 default:
590 return RecursiveASTVisitor::TraverseBinaryOperator(expr);
594 bool ImplicitBoolConversion::TraverseCompoundAssignOperator(CompoundAssignOperator * expr) {
595 switch (expr->getOpcode()) {
596 case BO_AndAssign:
597 case BO_OrAssign:
598 case BO_XorAssign:
600 nested.push(std::vector<ImplicitCastExpr const *>());
601 bool bRet = RecursiveASTVisitor::TraverseCompoundAssignOperator(expr);
602 assert(!nested.empty());
603 for (auto i: nested.top()) {
604 if (i != expr->getRHS()->IgnoreParens()
605 || !isBool(expr->getLHS()->IgnoreParens(), false))
607 reportWarning(i);
610 nested.pop();
611 if (!ignoreLocation(expr) && isBool(expr->getLHS(), false)
612 && !isBool(expr->getRHS()->IgnoreParenImpCasts(), false))
614 report(
615 DiagnosticsEngine::Warning, "mix of %0 and %1 in operator %2",
616 compat::getBeginLoc(expr->getRHS()))
617 << expr->getLHS()->getType()
618 << expr->getRHS()->IgnoreParenImpCasts()->getType()
619 << expr->getOpcodeStr()
620 << expr->getSourceRange();
622 return bRet;
624 default:
625 return RecursiveASTVisitor::TraverseCompoundAssignOperator(expr);
629 bool ImplicitBoolConversion::TraverseCXXStdInitializerListExpr(
630 CXXStdInitializerListExpr * expr)
632 // Must be some std::initializer_list<T>; check whether T is sal_Bool (i.e.,
633 // unsigned char) [TODO: check for real sal_Bool instead]:
634 auto t = expr->getType();
635 if (auto et = dyn_cast<ElaboratedType>(t)) {
636 t = et->desugar();
638 auto ts = t->getAs<TemplateSpecializationType>();
639 if (ts == nullptr
640 || !ts->getArg(0).getAsType()->isSpecificBuiltinType(
641 clang::BuiltinType::UChar))
643 return RecursiveASTVisitor::TraverseCXXStdInitializerListExpr(expr);
645 // Avoid warnings for code like
647 // Sequence<sal_Bool> arBool({true, false, true});
649 auto e = dyn_cast<InitListExpr>(
650 ignoreParenAndTemporaryMaterialization(expr->getSubExpr()));
651 if (e == nullptr) {
652 return RecursiveASTVisitor::TraverseCXXStdInitializerListExpr(expr);
654 nested.push(std::vector<ImplicitCastExpr const *>());
655 bool ret = RecursiveASTVisitor::TraverseCXXStdInitializerListExpr(expr);
656 assert(!nested.empty());
657 for (auto i: nested.top()) {
658 if (std::find(e->begin(), e->end(), i) == e->end()) {
659 reportWarning(i);
662 nested.pop();
663 return ret;
666 bool ImplicitBoolConversion::TraverseReturnStmt(ReturnStmt * stmt) {
667 nested.push(std::vector<ImplicitCastExpr const *>());
668 bool bRet = RecursiveASTVisitor::TraverseReturnStmt(stmt);
669 Expr const * expr = stmt->getRetValue();
670 if (expr != nullptr) {
671 ExprWithCleanups const * ec = dyn_cast<ExprWithCleanups>(expr);
672 if (ec != nullptr) {
673 expr = ec->getSubExpr();
675 expr = expr->IgnoreParens();
677 assert(!nested.empty());
678 for (auto i: nested.top()) {
679 if (i != expr || !bExternCIntFunctionDefinition) {
680 reportWarning(i);
683 nested.pop();
684 return bRet;
687 bool ImplicitBoolConversion::TraverseFunctionDecl(FunctionDecl * decl) {
688 bool bExt = false;
689 if (hasCLanguageLinkageType(decl) && decl->isThisDeclarationADefinition()) {
690 QualType t { decl->getReturnType() };
691 if (t->isSpecificBuiltinType(BuiltinType::Int)
692 || t->isSpecificBuiltinType(BuiltinType::UInt))
694 bExt = true;
695 } else {
696 TypedefType const * t2 = t->getAs<TypedefType>();
697 // cf. rtl_locale_equals (and sal_Int32 can be long):
698 if (t2 != nullptr
699 && t2->getDecl()->getNameAsString() == "sal_Int32")
701 bExt = true;
705 if (bExt) {
706 assert(!bExternCIntFunctionDefinition);
707 bExternCIntFunctionDefinition = true;
709 bool bRet = RecursiveASTVisitor::TraverseFunctionDecl(decl);
710 if (bExt) {
711 bExternCIntFunctionDefinition = false;
713 return bRet;
716 bool ImplicitBoolConversion::VisitImplicitCastExpr(
717 ImplicitCastExpr const * expr)
719 if (ignoreLocation(expr)) {
720 return true;
722 if (isBool(compat::getSubExprAsWritten(expr)) && !isBool(expr)) {
723 // Ignore NoOp from 'sal_Bool' (aka 'unsigned char') to 'const unsigned
724 // char' in makeAny(b) with b of type sal_Bool:
725 if (expr->getCastKind() != CK_NoOp) {
726 if (nested.empty()) {
727 reportWarning(expr);
728 } else {
729 nested.top().push_back(expr);
732 return true;
734 if (auto const sub = dyn_cast<ExplicitCastExpr>(
735 compat::getSubExprAsWritten(expr)))
737 auto const subsub = compat::getSubExprAsWritten(sub);
738 if (subsub->getType().IgnoreParens() == expr->getType().IgnoreParens()
739 && isBool(subsub))
741 // Ignore "normalizing cast" bool(b) from sal_Bool b to bool, then
742 // implicitly cast back again to sal_Bool:
743 if (dyn_cast<CXXFunctionalCastExpr>(sub) != nullptr
744 && sub->getType()->isBooleanType() && isSalBool(expr->getType())
745 && isSalBool(subsub->getType()))
747 return true;
749 report(
750 DiagnosticsEngine::Warning,
751 ("explicit conversion (%0) from %1 to %2 implicitly cast back"
752 " to %3"),
753 compat::getBeginLoc(expr))
754 << sub->getCastKindName() << subsub->getType() << sub->getType()
755 << expr->getType() << expr->getSourceRange();
756 return true;
759 if (expr->getType()->isBooleanType() && !isBoolExpr(expr->getSubExpr())
760 && !calls.empty())
762 CallExpr const * call = calls.top();
763 if (std::any_of(
764 call->arg_begin(), call->arg_end(),
765 [expr](Expr const * e) { return expr == e->IgnoreParens(); }))
767 report(
768 DiagnosticsEngine::Warning,
769 "implicit conversion (%0) of call argument from %1 to %2",
770 compat::getBeginLoc(expr))
771 << expr->getCastKindName() << expr->getSubExpr()->getType()
772 << expr->getType() << expr->getSourceRange();
773 return true;
776 return true;
779 bool ImplicitBoolConversion::VisitMaterializeTemporaryExpr(
780 MaterializeTemporaryExpr const * expr)
782 if (ignoreLocation(expr)) {
783 return true;
785 if (auto const sub = dyn_cast<ExplicitCastExpr>(compat::getSubExpr(expr))) {
786 auto const subsub = compat::getSubExprAsWritten(sub);
787 if (subsub->getType().IgnoreParens() == expr->getType().IgnoreParens()
788 && isBool(subsub))
790 report(
791 DiagnosticsEngine::Warning,
792 ("explicit conversion (%0) from %1 to %2 implicitly converted"
793 " back to %3"),
794 compat::getBeginLoc(expr))
795 << sub->getCastKindName() << subsub->getType() << sub->getType()
796 << expr->getType() << expr->getSourceRange();
797 return true;
800 return true;
803 bool ImplicitBoolConversion::isExternCFunctionCall(
804 CallExpr const * expr, FunctionProtoType const ** functionType)
806 assert(functionType != nullptr);
807 *functionType = nullptr;
808 Decl const * d = expr->getCalleeDecl();
809 if (d != nullptr) {
810 FunctionDecl const * fd = dyn_cast<FunctionDecl>(d);
811 if (fd != nullptr) {
812 clang::PointerType const * pt = fd->getType()
813 ->getAs<clang::PointerType>();
814 QualType t2(pt == nullptr ? fd->getType() : pt->getPointeeType());
815 *functionType = t2->getAs<FunctionProtoType>();
816 assert(
817 *functionType != nullptr || !compiler.getLangOpts().CPlusPlus
818 || (fd->getBuiltinID() != Builtin::NotBuiltin
819 && isa<FunctionNoProtoType>(t2)));
820 // __builtin_*s have no proto type?
821 return fd->isExternC()
822 || compiler.getSourceManager().isInExternCSystemHeader(
823 fd->getLocation());
825 VarDecl const * vd = dyn_cast<VarDecl>(d);
826 if (vd != nullptr) {
827 clang::PointerType const * pt = vd->getType()
828 ->getAs<clang::PointerType>();
829 *functionType
830 = ((pt == nullptr ? vd->getType() : pt->getPointeeType())
831 ->getAs<FunctionProtoType>());
832 return vd->isExternC();
835 return false;
838 bool ImplicitBoolConversion::isExternCFunctionCallReturningInt(
839 Expr const * expr)
841 CallExpr const * e = dyn_cast<CallExpr>(expr->IgnoreParenImpCasts());
842 FunctionProtoType const * t;
843 return e != nullptr && e->getType()->isSpecificBuiltinType(BuiltinType::Int)
844 && isExternCFunctionCall(e, &t);
847 void ImplicitBoolConversion::checkCXXConstructExpr(
848 CXXConstructExpr const * expr)
850 assert(!nested.empty());
851 for (auto i: nested.top()) {
852 auto j = std::find_if(
853 expr->arg_begin(), expr->arg_end(),
854 [&i](Expr const * e) {
855 return i == ignoreParenAndTemporaryMaterialization(e);
857 if (j != expr->arg_end()) {
858 TemplateSpecializationType const * t1 = expr->getType()->
859 getAs<TemplateSpecializationType>();
860 SubstTemplateTypeParmType const * t2 = nullptr;
861 CXXConstructorDecl const * d = expr->getConstructor();
862 if (d->getNumParams() == expr->getNumArgs()) { //TODO: better check
863 t2 = getAsSubstTemplateTypeParmType(
864 d->getParamDecl(j - expr->arg_begin())->getType()
865 .getNonReferenceType());
867 if (t1 != nullptr && t2 != nullptr) {
868 TemplateDecl const * td
869 = t1->getTemplateName().getAsTemplateDecl();
870 if (td != nullptr) {
871 TemplateParameterList const * ps
872 = td->getTemplateParameters();
873 auto i = std::find(
874 ps->begin(), ps->end(),
875 t2->getReplacedParameter()->getDecl());
876 if (i != ps->end()) {
877 if (ps->size() == t1->getNumArgs()) { //TODO
878 TemplateArgument const & arg = t1->getArg(
879 i - ps->begin());
880 if (arg.getKind() == TemplateArgument::Type
881 && (loplugin::TypeCheck(arg.getAsType())
882 .AnyBoolean()))
884 continue;
891 reportWarning(i);
895 void ImplicitBoolConversion::reportWarning(ImplicitCastExpr const * expr) {
896 if (compiler.getLangOpts().CPlusPlus) {
897 if (expr->getCastKind() == CK_ConstructorConversion) {
898 auto const t1 = expr->getType();
899 if (auto const t2 = t1->getAs<TemplateSpecializationType>()) {
900 assert(t2->getNumArgs() >= 1);
901 auto const a = t2->getArg(0);
902 if (a.getKind() == TemplateArgument::Type && a.getAsType()->isBooleanType()
903 && (loplugin::TypeCheck(t1).TemplateSpecializationClass()
904 .ClassOrStruct("atomic").StdNamespace()))
906 return;
911 // ignore template magic
912 StringRef aFileName = getFilenameOfLocation(compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(expr)));
913 if (loplugin::hasPathnamePrefix(aFileName, SRCDIR "/include/o3tl/hash_combine.hxx"))
914 return;
916 report(
917 DiagnosticsEngine::Warning,
918 "implicit conversion (%0) from %1 to %2", compat::getBeginLoc(expr))
919 << expr->getCastKindName() << expr->getSubExprAsWritten()->getType()
920 << expr->getType() << expr->getSourceRange();
924 loplugin::Plugin::Registration<ImplicitBoolConversion> X(
925 "implicitboolconversion");
929 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */