1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
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
;
39 Expr
const * ignoreParenAndTemporaryMaterialization(Expr
const * expr
) {
41 expr
= expr
->IgnoreParens();
42 auto e
= dyn_cast
<MaterializeTemporaryExpr
>(expr
);
46 expr
= e
->GetTemporaryExpr();
50 Expr
const * ignoreParenImpCastAndComma(Expr
const * expr
) {
52 expr
= expr
->IgnoreParenImpCasts();
53 BinaryOperator
const * op
= dyn_cast
<BinaryOperator
>(expr
);
54 if (op
== nullptr || op
->getOpcode() != BO_Comma
) {
61 SubstTemplateTypeParmType
const * getAsSubstTemplateTypeParmType(QualType type
)
63 //TODO: unwrap all kinds of (non-SubstTemplateTypeParmType) sugar, not only
66 TypedefType
const * t
= type
->getAs
<TypedefType
>();
68 return dyn_cast
<SubstTemplateTypeParmType
>(type
);
74 bool areSameTypedef(QualType type1
, QualType type2
) {
75 // type1.getTypePtr() == typ2.getTypePtr() fails for e.g. ::sal_Bool vs.
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()) {
89 TypedefType
const * t2
= type
->getAs
<TypedefType
>();
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
) {
113 expr
= ignoreParenImpCastAndComma(expr
);
114 ConditionalOperator
const * co
= dyn_cast
<ConditionalOperator
>(expr
);
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()))
129 std::stack
<Expr
const *> stack
;
130 Expr
const * e
= expr
;
132 e
= ignoreParenImpCastAndComma(e
);
133 MemberExpr
const * me
= dyn_cast
<MemberExpr
>(e
);
141 e
= ignoreParenImpCastAndComma(e
);
142 CXXOperatorCallExpr
const * op
= dyn_cast
<CXXOperatorCallExpr
>(e
);
143 if (op
== nullptr || op
->getOperator() != OO_Subscript
) {
149 if (!stack
.empty()) {
150 TemplateSpecializationType
const * t
151 = e
->getType()->getAs
<TemplateSpecializationType
>();
157 MemberExpr
const * me
= dyn_cast
<MemberExpr
>(stack
.top());
159 TemplateDecl
const * td
160 = t
->getTemplateName().getAsTemplateDecl();
164 TemplateParameterList
const * ps
= td
->getTemplateParameters();
165 SubstTemplateTypeParmType
const * t2
166 = getAsSubstTemplateTypeParmType(
167 me
->getMemberDecl()->getType());
172 ps
->begin(), ps
->end(),
173 t2
->getReplacedParameter()->getDecl());
174 if (i
== ps
->end()) {
177 if (ps
->size() != t
->getNumArgs()) { //TODO
180 TemplateArgument
const & arg
= t
->getArg(i
- ps
->begin());
181 if (arg
.getKind() != TemplateArgument::Type
) {
184 ty
= arg
.getAsType();
186 CXXOperatorCallExpr
const * op
187 = dyn_cast
<CXXOperatorCallExpr
>(stack
.top());
188 assert(op
!= nullptr);
189 TemplateDecl
const * d
190 = t
->getTemplateName().getAsTemplateDecl();
192 || (d
->getQualifiedNameAsString()
193 != "com::sun::star::uno::Sequence")
194 || t
->getNumArgs() != 1
195 || t
->getArg(0).getKind() != TemplateArgument::Type
)
199 ty
= t
->getArg(0).getAsType();
208 t
= ty
->getAs
<TemplateSpecializationType
>();
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()) {
229 #if (__clang_major__ == 3 && __clang_minor__ >= 3) || __clang_major__ > 3
230 if (decl
->isInExternCContext()) {
234 if (decl
->getCanonicalDecl()->getDeclContext()->isExternCContext()) {
241 class ImplicitBoolConversion
:
242 public RecursiveASTVisitor
<ImplicitBoolConversion
>, public loplugin::Plugin
245 explicit ImplicitBoolConversion(InstantiationData
const & 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
);
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 *>());
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(),
319 return i
== ignoreParenAndTemporaryMaterialization(e
);
321 if (j
== expr
->arg_end()) {
324 std::ptrdiff_t n
= j
- expr
->arg_begin();
327 && static_cast<std::size_t>(n
) >= compat::getNumParams(*t
))
329 assert(t
->isVariadic());
330 // ignore bool to int promotions of variadic arguments
334 static_cast<std::size_t>(n
) < compat::getNumParams(*t
));
335 if (!(compat::getParamType(*t
, n
)->isSpecificBuiltinType(
337 || compat::getParamType(*t
, n
)->isSpecificBuiltinType(
339 || compat::getParamType(*t
, n
)->isSpecificBuiltinType(
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());
359 && static_cast<std::size_t>(n
) < fd
->getNumParams())
361 SubstTemplateTypeParmType
const * t2
362 = getAsSubstTemplateTypeParmType(
363 fd
->getParamDecl(n
)->getType()
364 .getNonReferenceType());
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()))
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(),
397 return i
== ignoreParenAndTemporaryMaterialization(e
);
399 if (j
!= expr
->arg_end()) {
402 // template<typename T> struct S { void f(T); };
406 std::ptrdiff_t n
= j
- expr
->arg_begin();
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());
415 = ignoreParenImpCastAndComma(expr
->getImplicitObjectArgument())
417 if (dyn_cast
<MemberExpr
>(expr
->getCallee())->isArrow()) {
418 ty
= ty
->getAs
<PointerType
>()->getPointeeType();
420 TemplateSpecializationType
const * ct
421 = ty
->getAs
<TemplateSpecializationType
>();
423 SubstTemplateTypeParmType
const * pt
424 = getAsSubstTemplateTypeParmType(
425 d
->getParamDecl(n
)->getType().getNonReferenceType());
427 TemplateDecl
const * td
428 = ct
->getTemplateName().getAsTemplateDecl();
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()))
447 bool ImplicitBoolConversion::TraverseCXXConstructExpr(CXXConstructExpr
* expr
) {
448 nested
.push(std::vector
<ImplicitCastExpr
const *>());
449 bool ret
= RecursiveASTVisitor::TraverseCXXConstructExpr(expr
);
450 checkCXXConstructExpr(expr
);
455 bool ImplicitBoolConversion::TraverseCXXTemporaryObjectExpr(
456 CXXTemporaryObjectExpr
* expr
)
458 nested
.push(std::vector
<ImplicitCastExpr
const *>());
459 bool ret
= RecursiveASTVisitor::TraverseCXXTemporaryObjectExpr(expr
);
460 checkCXXConstructExpr(expr
);
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()) {
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()) {
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()) {
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())))
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()
538 expr
->getRHS()->IgnoreImpCasts(), i
->getSubExprAsWritten()))
539 || (i
== expr
->getRHS()->IgnoreParens()
541 expr
->getLHS()->IgnoreImpCasts(),
542 i
->getSubExprAsWritten()))))
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()
558 expr
->getRHS()->IgnoreImpCasts(), i
->getSubExprAsWritten()))
559 || (i
== expr
->getRHS()->IgnoreParens()
561 expr
->getLHS()->IgnoreImpCasts(),
562 i
->getSubExprAsWritten()))))
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()
578 expr
->getRHS()->IgnoreImpCasts(), i
->getSubExprAsWritten()))
579 || (i
== expr
->getRHS()->IgnoreParens()
581 expr
->getLHS()->IgnoreImpCasts(),
582 i
->getSubExprAsWritten()))))
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()
598 expr
->getRHS()->IgnoreImpCasts(), i
->getSubExprAsWritten()))
599 || (i
== expr
->getRHS()->IgnoreParens()
601 expr
->getLHS()->IgnoreImpCasts(),
602 i
->getSubExprAsWritten()))))
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()
618 expr
->getRHS()->IgnoreImpCasts(), i
->getSubExprAsWritten()))
619 || (i
== expr
->getRHS()->IgnoreParens()
621 expr
->getLHS()->IgnoreImpCasts(),
622 i
->getSubExprAsWritten()))))
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()
638 expr
->getRHS()->IgnoreImpCasts(), i
->getSubExprAsWritten()))
639 || (i
== expr
->getRHS()->IgnoreParens()
641 expr
->getLHS()->IgnoreImpCasts(),
642 i
->getSubExprAsWritten()))))
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
659 MemberExpr
const * me
= dyn_cast
<MemberExpr
>(expr
->getLHS());
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())))
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))
694 if (!ignoreLocation(expr
) && isBool(expr
->getLHS(), false)
695 && !isBool(expr
->getRHS()->IgnoreParenImpCasts(), false))
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();
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))
720 if (!ignoreLocation(expr
) && isBool(expr
->getLHS(), false)
721 && !isBool(expr
->getRHS()->IgnoreParenImpCasts(), false))
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();
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))
746 if (!ignoreLocation(expr
) && isBool(expr
->getLHS(), false)
747 && !isBool(expr
->getRHS()->IgnoreParenImpCasts(), false))
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();
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
);
766 expr
= ec
->getSubExpr();
768 expr
= expr
->IgnoreParens();
770 assert(!nested
.empty());
771 for (auto i
: nested
.top()) {
772 if (i
!= expr
|| !externCIntFunctionDefinition
) {
780 bool ImplicitBoolConversion::TraverseFunctionDecl(FunctionDecl
* decl
) {
782 if (hasCLanguageLinkageType(decl
) && decl
->isThisDeclarationADefinition()) {
783 QualType t
{ compat::getReturnType(*decl
) };
784 if (t
->isSpecificBuiltinType(BuiltinType::Int
)
785 || t
->isSpecificBuiltinType(BuiltinType::UInt
))
789 TypedefType
const * t2
= t
->getAs
<TypedefType
>();
790 // cf. rtl_locale_equals (and sal_Int32 can be long):
792 && t2
->getDecl()->getNameAsString() == "sal_Int32")
799 assert(!externCIntFunctionDefinition
);
800 externCIntFunctionDefinition
= true;
802 bool ret
= RecursiveASTVisitor::TraverseFunctionDecl(decl
);
804 externCIntFunctionDefinition
= false;
809 bool ImplicitBoolConversion::VisitImplicitCastExpr(
810 ImplicitCastExpr
const * expr
)
812 if (ignoreLocation(expr
)) {
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()) {
822 nested
.top().push_back(expr
);
827 ExplicitCastExpr
const * sub
= dyn_cast
<ExplicitCastExpr
>(
828 expr
->getSubExpr()->IgnoreParenImpCasts());
830 && (sub
->getSubExpr()->IgnoreParenImpCasts()->getType().IgnoreParens()
831 == expr
->getType().IgnoreParens())
832 && isBool(sub
->getSubExpr()->IgnoreParenImpCasts()))
835 DiagnosticsEngine::Warning
,
836 "explicit conversion (%0) from %1 to %2 implicitly cast back to %3",
838 << sub
->getCastKindName()
839 << sub
->getSubExpr()->IgnoreParenImpCasts()->getType()
840 << sub
->getType() << expr
->getType() << expr
->getSourceRange();
843 if (expr
->getType()->isBooleanType() && !isBoolExpr(expr
->getSubExpr())
846 CallExpr
const * call
= calls
.top();
848 call
->arg_begin(), call
->arg_end(),
849 [expr
](Expr
const * e
) { return expr
== e
->IgnoreParens(); })
853 DiagnosticsEngine::Warning
,
854 "implicit conversion (%0) of call argument from %1 to %2",
856 << expr
->getCastKindName() << expr
->getSubExpr()->getType()
857 << expr
->getType() << expr
->getSourceRange();
864 bool ImplicitBoolConversion::isExternCFunctionCall(
865 CallExpr
const * expr
, FunctionProtoType
const ** functionType
)
867 assert(functionType
!= nullptr);
868 *functionType
= nullptr;
869 Decl
const * d
= expr
->getCalleeDecl();
871 FunctionDecl
const * fd
= dyn_cast
<FunctionDecl
>(d
);
873 PointerType
const * pt
= fd
->getType()->getAs
<PointerType
>();
874 QualType
t2(pt
== nullptr ? fd
->getType() : pt
->getPointeeType());
875 *functionType
= t2
->getAs
<FunctionProtoType
>();
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(
885 VarDecl
const * vd
= dyn_cast
<VarDecl
>(d
);
887 PointerType
const * pt
= vd
->getType()->getAs
<PointerType
>();
889 = ((pt
== nullptr ? vd
->getType() : pt
->getPointeeType())
890 ->getAs
<FunctionProtoType
>());
891 return vd
->isExternC();
897 bool ImplicitBoolConversion::isExternCFunctionCallReturningInt(
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();
930 TemplateParameterList
const * ps
931 = td
->getTemplateParameters();
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(
939 if (arg
.getKind() == TemplateArgument::Type
940 && isBool(arg
.getAsType()))
953 void ImplicitBoolConversion::reportWarning(ImplicitCastExpr
const * expr
) {
954 if (!compiler
.getLangOpts().ObjC2
|| compiler
.getLangOpts().CPlusPlus
) {
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: */