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/.
24 Expr
const * ignoreParenAndTemporaryMaterialization(Expr
const * expr
) {
26 expr
= expr
->IgnoreParens();
27 auto e
= dyn_cast
<MaterializeTemporaryExpr
>(expr
);
31 expr
= e
->GetTemporaryExpr();
35 Expr
const * ignoreParenImpCastAndComma(Expr
const * expr
) {
37 expr
= expr
->IgnoreParenImpCasts();
38 BinaryOperator
const * op
= dyn_cast
<BinaryOperator
>(expr
);
39 if (op
== nullptr || op
->getOpcode() != BO_Comma
) {
46 SubstTemplateTypeParmType
const * getAsSubstTemplateTypeParmType(QualType type
)
48 //TODO: unwrap all kinds of (non-SubstTemplateTypeParmType) sugar, not only
51 TypedefType
const * t
= type
->getAs
<TypedefType
>();
53 return dyn_cast
<SubstTemplateTypeParmType
>(type
);
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());
68 if (ps
->size() != specializationType
->getNumArgs()) { //TODO
71 TemplateArgument
const & arg
= specializationType
->getArg(i
- ps
->begin());
72 if (arg
.getKind() != TemplateArgument::Type
) {
75 return arg
.getAsType();
78 bool areSameTypedef(QualType type1
, QualType type2
) {
79 // type1.getTypePtr() == typ2.getTypePtr() fails for e.g. ::sal_Bool vs.
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();
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
) {
106 expr
= ignoreParenImpCastAndComma(expr
);
107 ConditionalOperator
const * co
= dyn_cast
<ConditionalOperator
>(expr
);
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()))
122 std::stack
<Expr
const *> stack
;
123 Expr
const * e
= expr
;
125 e
= ignoreParenImpCastAndComma(e
);
126 MemberExpr
const * me
= dyn_cast
<MemberExpr
>(e
);
134 e
= ignoreParenImpCastAndComma(e
);
135 CXXOperatorCallExpr
const * op
= dyn_cast
<CXXOperatorCallExpr
>(e
);
136 if (op
== nullptr || op
->getOperator() != OO_Subscript
) {
142 if (!stack
.empty()) {
143 TemplateSpecializationType
const * t
144 = e
->getType()->getAs
<TemplateSpecializationType
>();
150 MemberExpr
const * me
= dyn_cast
<MemberExpr
>(stack
.top());
152 TemplateDecl
const * td
153 = t
->getTemplateName().getAsTemplateDecl();
157 SubstTemplateTypeParmType
const * t2
158 = getAsSubstTemplateTypeParmType(
159 me
->getMemberDecl()->getType());
163 ty
= reconstructTemplateArgumentType(td
, t
, t2
);
165 auto const canon
= cast
<TemplateDecl
>(td
->getCanonicalDecl());
167 ty
= reconstructTemplateArgumentType(canon
, t
, t2
);
174 CXXOperatorCallExpr
const * op
175 = dyn_cast
<CXXOperatorCallExpr
>(stack
.top());
176 assert(op
!= nullptr);
177 TemplateDecl
const * d
178 = t
->getTemplateName().getAsTemplateDecl();
180 || (d
->getQualifiedNameAsString()
181 != "com::sun::star::uno::Sequence")
182 || t
->getNumArgs() != 1
183 || t
->getArg(0).getKind() != TemplateArgument::Type
)
187 ty
= t
->getArg(0).getAsType();
191 if (loplugin::TypeCheck(ty
).AnyBoolean()) {
196 t
= ty
->getAs
<TemplateSpecializationType
>();
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()) {
217 if (decl
->isInExternCContext()) {
223 class ImplicitBoolConversion
:
224 public RecursiveASTVisitor
<ImplicitBoolConversion
>, public loplugin::Plugin
227 explicit ImplicitBoolConversion(loplugin::InstantiationData
const & 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
);
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 *>());
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(),
305 return i
== ignoreParenAndTemporaryMaterialization(e
);
307 if (j
== expr
->arg_end()) {
310 std::ptrdiff_t n
= j
- expr
->arg_begin();
313 && static_cast<std::size_t>(n
) >= t
->getNumParams())
315 assert(t
->isVariadic());
316 // ignore bool to int promotions of variadic arguments
320 static_cast<std::size_t>(n
) < t
->getNumParams());
321 if (!(t
->getParamType(n
)->isSpecificBuiltinType(
323 || t
->getParamType(n
)->isSpecificBuiltinType(
325 || t
->getParamType(n
)->isSpecificBuiltinType(
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());
345 && static_cast<std::size_t>(n
) < fd
->getNumParams())
347 SubstTemplateTypeParmType
const * t2
348 = getAsSubstTemplateTypeParmType(
349 fd
->getParamDecl(n
)->getType()
350 .getNonReferenceType());
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()
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(),
386 return i
== ignoreParenAndTemporaryMaterialization(e
);
388 if (j
!= expr
->arg_end()) {
391 // template<typename T> struct S { void f(T); };
395 std::ptrdiff_t n
= j
- expr
->arg_begin();
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());
404 = ignoreParenImpCastAndComma(expr
->getImplicitObjectArgument())
406 if (dyn_cast
<MemberExpr
>(expr
->getCallee())->isArrow()) {
407 ty
= ty
->getAs
<clang::PointerType
>()->getPointeeType();
409 TemplateSpecializationType
const * ct
410 = ty
->getAs
<TemplateSpecializationType
>();
412 SubstTemplateTypeParmType
const * pt
413 = getAsSubstTemplateTypeParmType(
414 d
->getParamDecl(n
)->getType().getNonReferenceType());
416 TemplateDecl
const * td
417 = ct
->getTemplateName().getAsTemplateDecl();
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())
437 bool ImplicitBoolConversion::TraverseCXXConstructExpr(CXXConstructExpr
* expr
) {
438 nested
.push(std::vector
<ImplicitCastExpr
const *>());
439 bool bRet
= RecursiveASTVisitor::TraverseCXXConstructExpr(expr
);
440 checkCXXConstructExpr(expr
);
445 bool ImplicitBoolConversion::TraverseCXXTemporaryObjectExpr(
446 CXXTemporaryObjectExpr
* expr
)
448 nested
.push(std::vector
<ImplicitCastExpr
const *>());
449 bool bRet
= RecursiveASTVisitor::TraverseCXXTemporaryObjectExpr(expr
);
450 checkCXXConstructExpr(expr
);
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()) {
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()) {
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()) {
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())))
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()
528 expr
->getRHS()->IgnoreImpCasts(), i
->getSubExprAsWritten()))
529 || (i
== expr
->getRHS()->IgnoreParens()
531 expr
->getLHS()->IgnoreImpCasts(),
532 i
->getSubExprAsWritten()))))
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()
548 expr
->getRHS()->IgnoreImpCasts(), i
->getSubExprAsWritten()))
549 || (i
== expr
->getRHS()->IgnoreParens()
551 expr
->getLHS()->IgnoreImpCasts(),
552 i
->getSubExprAsWritten()))))
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()
568 expr
->getRHS()->IgnoreImpCasts(), i
->getSubExprAsWritten()))
569 || (i
== expr
->getRHS()->IgnoreParens()
571 expr
->getLHS()->IgnoreImpCasts(),
572 i
->getSubExprAsWritten()))))
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()
588 expr
->getRHS()->IgnoreImpCasts(), i
->getSubExprAsWritten()))
589 || (i
== expr
->getRHS()->IgnoreParens()
591 expr
->getLHS()->IgnoreImpCasts(),
592 i
->getSubExprAsWritten()))))
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()
608 expr
->getRHS()->IgnoreImpCasts(), i
->getSubExprAsWritten()))
609 || (i
== expr
->getRHS()->IgnoreParens()
611 expr
->getLHS()->IgnoreImpCasts(),
612 i
->getSubExprAsWritten()))))
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()
628 expr
->getRHS()->IgnoreImpCasts(), i
->getSubExprAsWritten()))
629 || (i
== expr
->getRHS()->IgnoreParens()
631 expr
->getLHS()->IgnoreImpCasts(),
632 i
->getSubExprAsWritten()))))
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
649 MemberExpr
const * me
= dyn_cast
<MemberExpr
>(expr
->getLHS());
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())))
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))
684 if (!ignoreLocation(expr
) && isBool(expr
->getLHS(), false)
685 && !isBool(expr
->getRHS()->IgnoreParenImpCasts(), false))
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();
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))
710 if (!ignoreLocation(expr
) && isBool(expr
->getLHS(), false)
711 && !isBool(expr
->getRHS()->IgnoreParenImpCasts(), false))
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();
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))
736 if (!ignoreLocation(expr
) && isBool(expr
->getLHS(), false)
737 && !isBool(expr
->getRHS()->IgnoreParenImpCasts(), false))
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();
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
)) {
758 auto ts
= t
->getAs
<TemplateSpecializationType
>();
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()));
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()) {
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
);
793 expr
= ec
->getSubExpr();
795 expr
= expr
->IgnoreParens();
797 assert(!nested
.empty());
798 for (auto i
: nested
.top()) {
799 if (i
!= expr
|| !bExternCIntFunctionDefinition
) {
807 bool ImplicitBoolConversion::TraverseFunctionDecl(FunctionDecl
* decl
) {
809 if (hasCLanguageLinkageType(decl
) && decl
->isThisDeclarationADefinition()) {
810 QualType t
{ decl
->getReturnType() };
811 if (t
->isSpecificBuiltinType(BuiltinType::Int
)
812 || t
->isSpecificBuiltinType(BuiltinType::UInt
))
816 TypedefType
const * t2
= t
->getAs
<TypedefType
>();
817 // cf. rtl_locale_equals (and sal_Int32 can be long):
819 && t2
->getDecl()->getNameAsString() == "sal_Int32")
826 assert(!bExternCIntFunctionDefinition
);
827 bExternCIntFunctionDefinition
= true;
829 bool bRet
= RecursiveASTVisitor::TraverseFunctionDecl(decl
);
831 bExternCIntFunctionDefinition
= false;
836 bool ImplicitBoolConversion::VisitImplicitCastExpr(
837 ImplicitCastExpr
const * expr
)
839 if (ignoreLocation(expr
)) {
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()) {
849 nested
.top().push_back(expr
);
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()
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()))
870 DiagnosticsEngine::Warning
,
871 ("explicit conversion (%0) from %1 to %2 implicitly cast back"
874 << sub
->getCastKindName() << subsub
->getType() << sub
->getType()
875 << expr
->getType() << expr
->getSourceRange();
879 if (expr
->getType()->isBooleanType() && !isBoolExpr(expr
->getSubExpr())
882 CallExpr
const * call
= calls
.top();
884 call
->arg_begin(), call
->arg_end(),
885 [expr
](Expr
const * e
) { return expr
== e
->IgnoreParens(); })
889 DiagnosticsEngine::Warning
,
890 "implicit conversion (%0) of call argument from %1 to %2",
892 << expr
->getCastKindName() << expr
->getSubExpr()->getType()
893 << expr
->getType() << expr
->getSourceRange();
900 bool ImplicitBoolConversion::VisitMaterializeTemporaryExpr(
901 MaterializeTemporaryExpr
const * expr
)
903 if (ignoreLocation(expr
)) {
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()
912 DiagnosticsEngine::Warning
,
913 ("explicit conversion (%0) from %1 to %2 implicitly converted"
916 << sub
->getCastKindName() << subsub
->getType() << sub
->getType()
917 << expr
->getType() << expr
->getSourceRange();
924 bool ImplicitBoolConversion::isExternCFunctionCall(
925 CallExpr
const * expr
, FunctionProtoType
const ** functionType
)
927 assert(functionType
!= nullptr);
928 *functionType
= nullptr;
929 Decl
const * d
= expr
->getCalleeDecl();
931 FunctionDecl
const * fd
= dyn_cast
<FunctionDecl
>(d
);
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
>();
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(
946 VarDecl
const * vd
= dyn_cast
<VarDecl
>(d
);
948 clang::PointerType
const * pt
= vd
->getType()
949 ->getAs
<clang::PointerType
>();
951 = ((pt
== nullptr ? vd
->getType() : pt
->getPointeeType())
952 ->getAs
<FunctionProtoType
>());
953 return vd
->isExternC();
959 bool ImplicitBoolConversion::isExternCFunctionCallReturningInt(
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();
992 TemplateParameterList
const * ps
993 = td
->getTemplateParameters();
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(
1001 if (arg
.getKind() == TemplateArgument::Type
1002 && (loplugin::TypeCheck(arg
.getAsType())
1016 void ImplicitBoolConversion::reportWarning(ImplicitCastExpr
const * expr
) {
1017 if (compiler
.getLangOpts().CPlusPlus
) {
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: */