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/.
18 #include "clang/Basic/Builtins.h"
20 #include "config_clang.h"
28 Expr
const * ignoreParenAndTemporaryMaterialization(Expr
const * expr
) {
30 expr
= expr
->IgnoreParens();
31 auto e
= dyn_cast
<MaterializeTemporaryExpr
>(expr
);
35 expr
= compat::getSubExpr(e
);
39 Expr
const * ignoreParenImpCastAndComma(Expr
const * expr
) {
41 expr
= expr
->IgnoreParenImpCasts();
42 BinaryOperator
const * op
= dyn_cast
<BinaryOperator
>(expr
);
43 if (op
== nullptr || op
->getOpcode() != BO_Comma
) {
50 SubstTemplateTypeParmType
const * getAsSubstTemplateTypeParmType(QualType type
)
52 //TODO: unwrap all kinds of (non-SubstTemplateTypeParmType) sugar, not only
55 TypedefType
const * t
= type
->getAs
<TypedefType
>();
57 return dyn_cast
<SubstTemplateTypeParmType
>(type
);
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());
72 if (ps
->size() != specializationType
->getNumArgs()) { //TODO
75 TemplateArgument
const & arg
= specializationType
->getArg(i
- ps
->begin());
76 if (arg
.getKind() != TemplateArgument::Type
) {
79 return arg
.getAsType();
82 bool areSameTypedef(QualType type1
, QualType type2
) {
83 // type1.getTypePtr() == typ2.getTypePtr() fails for e.g. ::sal_Bool vs.
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();
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
) {
110 expr
= ignoreParenImpCastAndComma(expr
);
111 ConditionalOperator
const * co
= dyn_cast
<ConditionalOperator
>(expr
);
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()))
126 std::stack
<Expr
const *> stack
;
127 Expr
const * e
= expr
;
129 e
= ignoreParenImpCastAndComma(e
);
130 MemberExpr
const * me
= dyn_cast
<MemberExpr
>(e
);
138 e
= ignoreParenImpCastAndComma(e
);
139 CXXOperatorCallExpr
const * op
= dyn_cast
<CXXOperatorCallExpr
>(e
);
140 if (op
== nullptr || op
->getOperator() != OO_Subscript
) {
146 if (!stack
.empty()) {
147 TemplateSpecializationType
const * t
148 = e
->getType()->getAs
<TemplateSpecializationType
>();
154 MemberExpr
const * me
= dyn_cast
<MemberExpr
>(stack
.top());
156 TemplateDecl
const * td
157 = t
->getTemplateName().getAsTemplateDecl();
161 SubstTemplateTypeParmType
const * t2
162 = getAsSubstTemplateTypeParmType(
163 me
->getMemberDecl()->getType());
167 ty
= reconstructTemplateArgumentType(td
, t
, t2
);
169 auto const canon
= cast
<TemplateDecl
>(td
->getCanonicalDecl());
171 ty
= reconstructTemplateArgumentType(canon
, t
, t2
);
178 CXXOperatorCallExpr
const * op
179 = dyn_cast
<CXXOperatorCallExpr
>(stack
.top());
180 assert(op
!= nullptr);
182 TemplateDecl
const * d
183 = t
->getTemplateName().getAsTemplateDecl();
185 || (d
->getQualifiedNameAsString()
186 != "com::sun::star::uno::Sequence")
187 || t
->getNumArgs() != 1
188 || t
->getArg(0).getKind() != TemplateArgument::Type
)
192 ty
= t
->getArg(0).getAsType();
196 if (loplugin::TypeCheck(ty
).AnyBoolean()) {
201 t
= ty
->getAs
<TemplateSpecializationType
>();
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()) {
222 if (decl
->isInExternCContext()) {
228 class ImplicitBoolConversion
:
229 public loplugin::FilteringPlugin
<ImplicitBoolConversion
>
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
); }
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
); }
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
);
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 *>());
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(),
313 return i
== ignoreParenAndTemporaryMaterialization(e
);
315 if (j
== expr
->arg_end()) {
318 std::ptrdiff_t n
= j
- expr
->arg_begin();
321 && static_cast<std::size_t>(n
) >= t
->getNumParams())
323 assert(t
->isVariadic());
324 // ignore bool to int promotions of variadic arguments
328 static_cast<std::size_t>(n
) < t
->getNumParams());
329 if (!(t
->getParamType(n
)->isSpecificBuiltinType(
331 || t
->getParamType(n
)->isSpecificBuiltinType(
333 || t
->getParamType(n
)->isSpecificBuiltinType(
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());
353 && static_cast<std::size_t>(n
) < fd
->getNumParams())
355 SubstTemplateTypeParmType
const * t2
356 = getAsSubstTemplateTypeParmType(
357 fd
->getParamDecl(n
)->getType()
358 .getNonReferenceType());
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()
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(),
394 return i
== ignoreParenAndTemporaryMaterialization(e
);
396 if (j
!= expr
->arg_end()) {
399 // template<typename T> struct S { void f(T); };
403 std::ptrdiff_t n
= j
- expr
->arg_begin();
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());
412 = ignoreParenImpCastAndComma(expr
->getImplicitObjectArgument())
414 if (dyn_cast
<MemberExpr
>(expr
->getCallee())->isArrow()) {
415 ty
= ty
->getAs
<clang::PointerType
>()->getPointeeType();
417 TemplateSpecializationType
const * ct
418 = ty
->getAs
<TemplateSpecializationType
>();
420 SubstTemplateTypeParmType
const * pt
421 = getAsSubstTemplateTypeParmType(
422 d
->getParamDecl(n
)->getType().getNonReferenceType());
424 TemplateDecl
const * td
425 = ct
->getTemplateName().getAsTemplateDecl();
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())
445 bool ImplicitBoolConversion::TraverseCXXConstructExpr(CXXConstructExpr
* expr
) {
446 nested
.push(std::vector
<ImplicitCastExpr
const *>());
447 bool bRet
= RecursiveASTVisitor::TraverseCXXConstructExpr(expr
);
448 checkCXXConstructExpr(expr
);
453 bool ImplicitBoolConversion::TraverseCXXTemporaryObjectExpr(
454 CXXTemporaryObjectExpr
* expr
)
456 nested
.push(std::vector
<ImplicitCastExpr
const *>());
457 bool bRet
= RecursiveASTVisitor::TraverseCXXTemporaryObjectExpr(expr
);
458 checkCXXConstructExpr(expr
);
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()) {
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()) {
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()) {
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())))
529 bool ImplicitBoolConversion::TraverseBinaryOperator(BinaryOperator
* expr
) {
530 switch (expr
->getOpcode()) {
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()
544 expr
->getRHS()->IgnoreImpCasts(), i
->getSubExprAsWritten()))
545 || (i
== expr
->getRHS()->IgnoreParens()
547 expr
->getLHS()->IgnoreImpCasts(),
548 i
->getSubExprAsWritten()))))
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;
567 MemberExpr
const * me
= dyn_cast
<MemberExpr
>(expr
->getLHS());
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())))
590 return RecursiveASTVisitor::TraverseBinaryOperator(expr
);
594 bool ImplicitBoolConversion::TraverseCompoundAssignOperator(CompoundAssignOperator
* expr
) {
595 switch (expr
->getOpcode()) {
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))
611 if (!ignoreLocation(expr
) && isBool(expr
->getLHS(), false)
612 && !isBool(expr
->getRHS()->IgnoreParenImpCasts(), false))
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();
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
)) {
638 auto ts
= t
->getAs
<TemplateSpecializationType
>();
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()));
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()) {
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
);
673 expr
= ec
->getSubExpr();
675 expr
= expr
->IgnoreParens();
677 assert(!nested
.empty());
678 for (auto i
: nested
.top()) {
679 if (i
!= expr
|| !bExternCIntFunctionDefinition
) {
687 bool ImplicitBoolConversion::TraverseFunctionDecl(FunctionDecl
* decl
) {
689 if (hasCLanguageLinkageType(decl
) && decl
->isThisDeclarationADefinition()) {
690 QualType t
{ decl
->getReturnType() };
691 if (t
->isSpecificBuiltinType(BuiltinType::Int
)
692 || t
->isSpecificBuiltinType(BuiltinType::UInt
))
696 TypedefType
const * t2
= t
->getAs
<TypedefType
>();
697 // cf. rtl_locale_equals (and sal_Int32 can be long):
699 && t2
->getDecl()->getNameAsString() == "sal_Int32")
706 assert(!bExternCIntFunctionDefinition
);
707 bExternCIntFunctionDefinition
= true;
709 bool bRet
= RecursiveASTVisitor::TraverseFunctionDecl(decl
);
711 bExternCIntFunctionDefinition
= false;
716 bool ImplicitBoolConversion::VisitImplicitCastExpr(
717 ImplicitCastExpr
const * expr
)
719 if (ignoreLocation(expr
)) {
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()) {
729 nested
.top().push_back(expr
);
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()
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()))
750 DiagnosticsEngine::Warning
,
751 ("explicit conversion (%0) from %1 to %2 implicitly cast back"
753 compat::getBeginLoc(expr
))
754 << sub
->getCastKindName() << subsub
->getType() << sub
->getType()
755 << expr
->getType() << expr
->getSourceRange();
759 if (expr
->getType()->isBooleanType() && !isBoolExpr(expr
->getSubExpr())
762 CallExpr
const * call
= calls
.top();
764 call
->arg_begin(), call
->arg_end(),
765 [expr
](Expr
const * e
) { return expr
== e
->IgnoreParens(); }))
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();
779 bool ImplicitBoolConversion::VisitMaterializeTemporaryExpr(
780 MaterializeTemporaryExpr
const * expr
)
782 if (ignoreLocation(expr
)) {
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()
791 DiagnosticsEngine::Warning
,
792 ("explicit conversion (%0) from %1 to %2 implicitly converted"
794 compat::getBeginLoc(expr
))
795 << sub
->getCastKindName() << subsub
->getType() << sub
->getType()
796 << expr
->getType() << expr
->getSourceRange();
803 bool ImplicitBoolConversion::isExternCFunctionCall(
804 CallExpr
const * expr
, FunctionProtoType
const ** functionType
)
806 assert(functionType
!= nullptr);
807 *functionType
= nullptr;
808 Decl
const * d
= expr
->getCalleeDecl();
810 FunctionDecl
const * fd
= dyn_cast
<FunctionDecl
>(d
);
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
>();
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(
825 VarDecl
const * vd
= dyn_cast
<VarDecl
>(d
);
827 clang::PointerType
const * pt
= vd
->getType()
828 ->getAs
<clang::PointerType
>();
830 = ((pt
== nullptr ? vd
->getType() : pt
->getPointeeType())
831 ->getAs
<FunctionProtoType
>());
832 return vd
->isExternC();
838 bool ImplicitBoolConversion::isExternCFunctionCallReturningInt(
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();
871 TemplateParameterList
const * ps
872 = td
->getTemplateParameters();
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(
880 if (arg
.getKind() == TemplateArgument::Type
881 && (loplugin::TypeCheck(arg
.getAsType())
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()))
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"))
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: */