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 bool isBool(Expr
const * expr
, bool allowTypedefs
= true) {
40 QualType t1
{ expr
->getType() };
41 if (t1
->isBooleanType()) {
47 // css::uno::Sequence<sal_Bool> s(1);s[0]=false /*unotools/source/config/configitem.cxx*/:
48 if(t1
->isSpecificBuiltinType(BuiltinType::UChar
))return true;
49 TypedefType
const * t2
= t1
->getAs
<TypedefType
>();
53 std::string
name(t2
->getDecl()->getNameAsString());
54 return name
== "sal_Bool" || name
== "BOOL" || name
== "FcBool"
55 || name
== "UBool" || name
== "dbus_bool_t" || name
== "gboolean"
56 || name
== "hb_bool_t";
59 bool isBoolExpr(Expr
const * expr
) {
63 ConditionalOperator
const * co
= dyn_cast
<ConditionalOperator
>(expr
);
65 ImplicitCastExpr
const * ic1
= dyn_cast
<ImplicitCastExpr
>(
66 co
->getTrueExpr()->IgnoreParens());
67 ImplicitCastExpr
const * ic2
= dyn_cast
<ImplicitCastExpr
>(
68 co
->getFalseExpr()->IgnoreParens());
69 if (ic1
!= nullptr && ic2
!= nullptr
70 && ic1
->getType()->isSpecificBuiltinType(BuiltinType::Int
)
71 && isBoolExpr(ic1
->getSubExpr()->IgnoreParens())
72 && ic2
->getType()->isSpecificBuiltinType(BuiltinType::Int
)
73 && isBoolExpr(ic2
->getSubExpr()->IgnoreParens()))
81 // It appears that, given a function declaration, there is no way to determine
82 // the language linkage of the function's type, only of the function's name
83 // (via FunctionDecl::isExternC); however, in a case like
85 // extern "C" { static void f(); }
87 // the function's name does not have C language linkage while the function's
88 // type does (as clarified in C++11 [decl.link]); cf. <http://clang-developers.
89 // 42468.n3.nabble.com/Language-linkage-of-function-type-tt4037248.html>
90 // "Language linkage of function type":
91 bool hasCLanguageLinkageType(FunctionDecl
const * decl
) {
92 assert(decl
!= nullptr);
93 if (decl
->isExternC()) {
96 #if (__clang_major__ == 3 && __clang_minor__ >= 3) || __clang_major__ > 3
97 if (decl
->isInExternCContext()) {
101 if (decl
->getCanonicalDecl()->getDeclContext()->isExternCContext()) {
108 class ImplicitBoolConversion
:
109 public RecursiveASTVisitor
<ImplicitBoolConversion
>, public loplugin::Plugin
112 explicit ImplicitBoolConversion(InstantiationData
const & data
):
115 virtual void run() override
116 { TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl()); }
118 bool TraverseCallExpr(CallExpr
* expr
);
120 bool TraverseCStyleCastExpr(CStyleCastExpr
* expr
);
122 bool TraverseCXXStaticCastExpr(CXXStaticCastExpr
* expr
);
124 bool TraverseCXXFunctionalCastExpr(CXXFunctionalCastExpr
* expr
);
126 bool TraverseConditionalOperator(ConditionalOperator
* expr
);
128 bool TraverseBinLT(BinaryOperator
* expr
);
130 bool TraverseBinLE(BinaryOperator
* expr
);
132 bool TraverseBinGT(BinaryOperator
* expr
);
134 bool TraverseBinGE(BinaryOperator
* expr
);
136 bool TraverseBinEQ(BinaryOperator
* expr
);
138 bool TraverseBinNE(BinaryOperator
* expr
);
140 bool TraverseBinAssign(BinaryOperator
* expr
);
142 bool TraverseBinAndAssign(CompoundAssignOperator
* expr
);
144 bool TraverseBinOrAssign(CompoundAssignOperator
* expr
);
146 bool TraverseBinXorAssign(CompoundAssignOperator
* expr
);
148 bool TraverseReturnStmt(ReturnStmt
* stmt
);
150 bool TraverseFunctionDecl(FunctionDecl
* decl
);
152 bool VisitImplicitCastExpr(ImplicitCastExpr
const * expr
);
155 void reportWarning(ImplicitCastExpr
const * expr
);
157 std::stack
<std::vector
<ImplicitCastExpr
const *>> nested
;
158 std::stack
<CallExpr
const *> calls
;
159 bool externCIntFunctionDefinition
= false;
162 bool ImplicitBoolConversion::TraverseCallExpr(CallExpr
* expr
) {
163 nested
.push(std::vector
<ImplicitCastExpr
const *>());
165 bool ret
= RecursiveASTVisitor::TraverseCallExpr(expr
);
166 Decl
const * d
= expr
->getCalleeDecl();
168 FunctionProtoType
const * t
= nullptr;
170 FunctionDecl
const * fd
= dyn_cast
<FunctionDecl
>(d
);
171 if (fd
!= nullptr && fd
->isExternC()) {
173 PointerType
const * pt
= fd
->getType()->getAs
<PointerType
>();
174 QualType
t2(pt
== nullptr ? fd
->getType() : pt
->getPointeeType());
175 t
= t2
->getAs
<FunctionProtoType
>();
177 t
!= nullptr || !compiler
.getLangOpts().CPlusPlus
178 || (fd
->getBuiltinID() != Builtin::NotBuiltin
179 && isa
<FunctionNoProtoType
>(t2
)));
180 // __builtin_*s have no proto type?
182 VarDecl
const * vd
= dyn_cast
<VarDecl
>(d
);
183 if (vd
!= nullptr && vd
->isExternC())
186 PointerType
const * pt
= vd
->getType()->getAs
<PointerType
>();
187 t
= (pt
== nullptr ? vd
->getType() : pt
->getPointeeType())
188 ->castAs
<FunctionProtoType
>();
192 assert(!nested
.empty());
193 for (auto i
: nested
.top()) {
195 auto j
= std::find_if(
196 expr
->arg_begin(), expr
->arg_end(),
197 [&i
](Expr
* e
) { return i
== e
->IgnoreParens(); });
198 if (j
== expr
->arg_end()) {
200 } else if (t
!= nullptr) {
201 std::ptrdiff_t n
= j
- expr
->arg_begin();
204 static_cast<std::size_t>(n
) < compat::getNumParams(*t
)
206 if (static_cast<std::size_t>(n
) < compat::getNumParams(*t
)
207 && !(compat::getParamType(*t
, n
)->isSpecificBuiltinType(
209 || (compat::getParamType(*t
, n
)->isSpecificBuiltinType(
211 || (compat::getParamType(*t
, n
)->isSpecificBuiltinType(
212 BuiltinType::Long
))))
226 bool ImplicitBoolConversion::TraverseCStyleCastExpr(CStyleCastExpr
* expr
) {
227 nested
.push(std::vector
<ImplicitCastExpr
const *>());
228 bool ret
= RecursiveASTVisitor::TraverseCStyleCastExpr(expr
);
229 assert(!nested
.empty());
230 for (auto i
: nested
.top()) {
231 if (i
!= expr
->getSubExpr()->IgnoreParens()) {
239 bool ImplicitBoolConversion::TraverseCXXStaticCastExpr(CXXStaticCastExpr
* expr
)
241 nested
.push(std::vector
<ImplicitCastExpr
const *>());
242 bool ret
= RecursiveASTVisitor::TraverseCXXStaticCastExpr(expr
);
243 assert(!nested
.empty());
244 for (auto i
: nested
.top()) {
245 if (i
!= expr
->getSubExpr()->IgnoreParens()) {
253 bool ImplicitBoolConversion::TraverseCXXFunctionalCastExpr(
254 CXXFunctionalCastExpr
* expr
)
256 nested
.push(std::vector
<ImplicitCastExpr
const *>());
257 bool ret
= RecursiveASTVisitor::TraverseCXXFunctionalCastExpr(expr
);
258 assert(!nested
.empty());
259 for (auto i
: nested
.top()) {
260 if (i
!= expr
->getSubExpr()->IgnoreParens()) {
268 bool ImplicitBoolConversion::TraverseConditionalOperator(
269 ConditionalOperator
* expr
)
271 nested
.push(std::vector
<ImplicitCastExpr
const *>());
272 bool ret
= RecursiveASTVisitor::TraverseConditionalOperator(expr
);
273 assert(!nested
.empty());
274 for (auto i
: nested
.top()) {
275 if (!((i
== expr
->getTrueExpr()->IgnoreParens()
276 && isBoolExpr(expr
->getFalseExpr()->IgnoreParenImpCasts()))
277 || (i
== expr
->getFalseExpr()->IgnoreParens()
278 && isBoolExpr(expr
->getTrueExpr()->IgnoreParenImpCasts()))))
287 bool ImplicitBoolConversion::TraverseBinLT(BinaryOperator
* expr
) {
288 nested
.push(std::vector
<ImplicitCastExpr
const *>());
289 bool ret
= RecursiveASTVisitor::TraverseBinLT(expr
);
290 assert(!nested
.empty());
291 for (auto i
: nested
.top()) {
292 if (!((i
== expr
->getLHS()->IgnoreParens()
293 && isBool(expr
->getRHS()->IgnoreParenImpCasts(), false))
294 || (i
== expr
->getRHS()->IgnoreParens()
295 && isBool(expr
->getLHS()->IgnoreParenImpCasts(), false))))
304 bool ImplicitBoolConversion::TraverseBinLE(BinaryOperator
* expr
) {
305 nested
.push(std::vector
<ImplicitCastExpr
const *>());
306 bool ret
= RecursiveASTVisitor::TraverseBinLE(expr
);
307 assert(!nested
.empty());
308 for (auto i
: nested
.top()) {
309 if (!((i
== expr
->getLHS()->IgnoreParens()
310 && isBool(expr
->getRHS()->IgnoreParenImpCasts(), false))
311 || (i
== expr
->getRHS()->IgnoreParens()
312 && isBool(expr
->getLHS()->IgnoreParenImpCasts(), false))))
321 bool ImplicitBoolConversion::TraverseBinGT(BinaryOperator
* expr
) {
322 nested
.push(std::vector
<ImplicitCastExpr
const *>());
323 bool ret
= RecursiveASTVisitor::TraverseBinGT(expr
);
324 assert(!nested
.empty());
325 for (auto i
: nested
.top()) {
326 if (!((i
== expr
->getLHS()->IgnoreParens()
327 && isBool(expr
->getRHS()->IgnoreParenImpCasts(), false))
328 || (i
== expr
->getRHS()->IgnoreParens()
329 && isBool(expr
->getLHS()->IgnoreParenImpCasts(), false))))
338 bool ImplicitBoolConversion::TraverseBinGE(BinaryOperator
* expr
) {
339 nested
.push(std::vector
<ImplicitCastExpr
const *>());
340 bool ret
= RecursiveASTVisitor::TraverseBinGE(expr
);
341 assert(!nested
.empty());
342 for (auto i
: nested
.top()) {
343 if (!((i
== expr
->getLHS()->IgnoreParens()
344 && isBool(expr
->getRHS()->IgnoreParenImpCasts(), false))
345 || (i
== expr
->getRHS()->IgnoreParens()
346 && isBool(expr
->getLHS()->IgnoreParenImpCasts(), false))))
355 bool ImplicitBoolConversion::TraverseBinEQ(BinaryOperator
* expr
) {
356 nested
.push(std::vector
<ImplicitCastExpr
const *>());
357 bool ret
= RecursiveASTVisitor::TraverseBinEQ(expr
);
358 assert(!nested
.empty());
359 for (auto i
: nested
.top()) {
360 if (!((i
== expr
->getLHS()->IgnoreParens()
361 && isBool(expr
->getRHS()->IgnoreParenImpCasts(), false))
362 || (i
== expr
->getRHS()->IgnoreParens()
363 && isBool(expr
->getLHS()->IgnoreParenImpCasts(), false))))
372 bool ImplicitBoolConversion::TraverseBinNE(BinaryOperator
* expr
) {
373 nested
.push(std::vector
<ImplicitCastExpr
const *>());
374 bool ret
= RecursiveASTVisitor::TraverseBinNE(expr
);
375 assert(!nested
.empty());
376 for (auto i
: nested
.top()) {
377 if (!((i
== expr
->getLHS()->IgnoreParens()
378 && isBool(expr
->getRHS()->IgnoreParenImpCasts(), false))
379 || (i
== expr
->getRHS()->IgnoreParens()
380 && isBool(expr
->getLHS()->IgnoreParenImpCasts(), false))))
389 // /usr/include/gtk-2.0/gtk/gtktogglebutton.h: struct _GtkToggleButton:
390 // guint GSEAL (active) : 1;
391 // even though <http://www.gtk.org/api/2.6/gtk/GtkToggleButton.html>:
392 // "active" gboolean : Read / Write
393 bool ImplicitBoolConversion::TraverseBinAssign(BinaryOperator
* expr
) {
394 nested
.push(std::vector
<ImplicitCastExpr
const *>());
395 bool ret
= RecursiveASTVisitor::TraverseBinAssign(expr
);
397 MemberExpr
const * me
= dyn_cast
<MemberExpr
>(expr
->getLHS());
399 FieldDecl
const * fd
= dyn_cast
<FieldDecl
>(me
->getMemberDecl());
400 if (fd
!= nullptr && fd
->isBitField()
401 && fd
->getBitWidthValue(compiler
.getASTContext()) == 1)
403 TypedefType
const * t
= fd
->getType()->getAs
<TypedefType
>();
404 ext
= t
!= nullptr && t
->getDecl()->getNameAsString() == "guint";
407 assert(!nested
.empty());
408 for (auto i
: nested
.top()) {
409 if (i
!= expr
->getRHS()->IgnoreParens() || !ext
) {
417 bool ImplicitBoolConversion::TraverseBinAndAssign(CompoundAssignOperator
* expr
)
419 nested
.push(std::vector
<ImplicitCastExpr
const *>());
420 bool ret
= RecursiveASTVisitor::TraverseBinAndAssign(expr
);
421 assert(!nested
.empty());
422 for (auto i
: nested
.top()) {
423 if (i
!= expr
->getRHS()->IgnoreParens()
424 || !isBool(expr
->getLHS()->IgnoreParens(), false))
430 if (!ignoreLocation(expr
) && isBool(expr
->getLHS(), false)
431 && !isBool(expr
->getRHS()->IgnoreParenImpCasts(), false))
434 DiagnosticsEngine::Warning
, "mix of %0 and %1 in operator &=",
435 expr
->getRHS()->getLocStart())
436 << expr
->getLHS()->getType()
437 << expr
->getRHS()->IgnoreParenImpCasts()->getType()
438 << expr
->getSourceRange();
443 bool ImplicitBoolConversion::TraverseBinOrAssign(CompoundAssignOperator
* expr
)
445 nested
.push(std::vector
<ImplicitCastExpr
const *>());
446 bool ret
= RecursiveASTVisitor::TraverseBinOrAssign(expr
);
447 assert(!nested
.empty());
448 for (auto i
: nested
.top()) {
449 if (i
!= expr
->getRHS()->IgnoreParens()
450 || !isBool(expr
->getLHS()->IgnoreParens(), false))
456 if (!ignoreLocation(expr
) && isBool(expr
->getLHS(), false)
457 && !isBool(expr
->getRHS()->IgnoreParenImpCasts(), false))
460 DiagnosticsEngine::Warning
, "mix of %0 and %1 in operator |=",
461 expr
->getRHS()->getLocStart())
462 << expr
->getLHS()->getType()
463 << expr
->getRHS()->IgnoreParenImpCasts()->getType()
464 << expr
->getSourceRange();
469 bool ImplicitBoolConversion::TraverseBinXorAssign(CompoundAssignOperator
* expr
)
471 nested
.push(std::vector
<ImplicitCastExpr
const *>());
472 bool ret
= RecursiveASTVisitor::TraverseBinXorAssign(expr
);
473 assert(!nested
.empty());
474 for (auto i
: nested
.top()) {
475 if (i
!= expr
->getRHS()->IgnoreParens()
476 || !isBool(expr
->getLHS()->IgnoreParens(), false))
482 if (!ignoreLocation(expr
) && isBool(expr
->getLHS(), false)
483 && !isBool(expr
->getRHS()->IgnoreParenImpCasts(), false))
486 DiagnosticsEngine::Warning
, "mix of %0 and %1 in operator ^=",
487 expr
->getRHS()->getLocStart())
488 << expr
->getLHS()->getType()
489 << expr
->getRHS()->IgnoreParenImpCasts()->getType()
490 << expr
->getSourceRange();
495 bool ImplicitBoolConversion::TraverseReturnStmt(ReturnStmt
* stmt
) {
496 nested
.push(std::vector
<ImplicitCastExpr
const *>());
497 bool ret
= RecursiveASTVisitor::TraverseReturnStmt(stmt
);
498 Expr
const * expr
= stmt
->getRetValue();
499 if (expr
!= nullptr) {
500 ExprWithCleanups
const * ec
= dyn_cast
<ExprWithCleanups
>(expr
);
502 expr
= ec
->getSubExpr();
504 expr
= expr
->IgnoreParens();
506 assert(!nested
.empty());
507 for (auto i
: nested
.top()) {
508 if (i
!= expr
|| !externCIntFunctionDefinition
) {
516 bool ImplicitBoolConversion::TraverseFunctionDecl(FunctionDecl
* decl
) {
518 if (hasCLanguageLinkageType(decl
) && decl
->isThisDeclarationADefinition()) {
519 QualType t
{ compat::getReturnType(*decl
) };
520 if (t
->isSpecificBuiltinType(BuiltinType::Int
)
521 || t
->isSpecificBuiltinType(BuiltinType::UInt
))
525 TypedefType
const * t2
= t
->getAs
<TypedefType
>();
526 // cf. rtl_locale_equals (and sal_Int32 can be long):
528 && t2
->getDecl()->getNameAsString() == "sal_Int32")
535 assert(!externCIntFunctionDefinition
);
536 externCIntFunctionDefinition
= true;
538 bool ret
= RecursiveASTVisitor::TraverseFunctionDecl(decl
);
540 externCIntFunctionDefinition
= false;
545 bool ImplicitBoolConversion::VisitImplicitCastExpr(
546 ImplicitCastExpr
const * expr
)
548 if (ignoreLocation(expr
)) {
551 if (expr
->getSubExprAsWritten()->getType()->isBooleanType()
554 if (nested
.empty()) {
557 nested
.top().push_back(expr
);
561 ExplicitCastExpr
const * sub
= dyn_cast
<ExplicitCastExpr
>(
562 expr
->getSubExpr()->IgnoreParenImpCasts());
564 && (sub
->getSubExpr()->IgnoreParenImpCasts()->getType().IgnoreParens()
565 == expr
->getType().IgnoreParens())
566 && isBool(sub
->getSubExpr()->IgnoreParenImpCasts()))
569 DiagnosticsEngine::Warning
,
570 "explicit conversion (%0) from %1 to %2 implicitly cast back to %3",
572 << sub
->getCastKindName()
573 << sub
->getSubExpr()->IgnoreParenImpCasts()->getType()
574 << sub
->getType() << expr
->getType() << expr
->getSourceRange();
577 if (expr
->getType()->isBooleanType() && !isBool(expr
->getSubExpr())
580 CallExpr
const * call
= calls
.top();
582 call
->arg_begin(), call
->arg_end(),
583 [expr
](Expr
const * e
) { return expr
== e
->IgnoreParens(); })
587 DiagnosticsEngine::Warning
,
588 "implicit conversion (%0) of call argument from %1 to %2",
590 << expr
->getCastKindName() << expr
->getSubExpr()->getType()
591 << expr
->getType() << expr
->getSourceRange();
598 void ImplicitBoolConversion::reportWarning(ImplicitCastExpr
const * expr
) {
600 DiagnosticsEngine::Warning
,
601 "implicit conversion (%0) from bool to %1", expr
->getLocStart())
602 << expr
->getCastKindName() << expr
->getType() << expr
->getSourceRange();
605 loplugin::Plugin::Registration
<ImplicitBoolConversion
> X(
606 "implicitboolconversion");
610 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */