Remove exec bits from docx
[LibreOffice.git] / compilerplugins / clang / implicitboolconversion.cxx
blob64bc97ff49992e1a3b7d2fe2b1886c0f2430f0af
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 "check.hxx"
21 #include "compat.hxx"
22 #include "plugin.hxx"
24 namespace {
26 Expr const * ignoreParenAndTemporaryMaterialization(Expr const * expr) {
27 for (;;) {
28 expr = expr->IgnoreParens();
29 auto e = dyn_cast<MaterializeTemporaryExpr>(expr);
30 if (e == nullptr) {
31 return expr;
33 expr = e->getSubExpr();
37 Expr const * ignoreParenImpCastAndComma(Expr const * expr) {
38 for (;;) {
39 expr = expr->IgnoreParenImpCasts();
40 BinaryOperator const * op = dyn_cast<BinaryOperator>(expr);
41 if (op == nullptr || op->getOpcode() != BO_Comma) {
42 return expr;
44 expr = op->getRHS();
48 SubstTemplateTypeParmType const * getAsSubstTemplateTypeParmType(QualType type)
50 //TODO: unwrap all kinds of (non-SubstTemplateTypeParmType) sugar, not only
51 // TypedefType sugar:
52 for (;;) {
53 TypedefType const * t = type->getAs<TypedefType>();
54 if (t == nullptr) {
55 return dyn_cast<SubstTemplateTypeParmType>(type);
57 type = t->desugar();
61 QualType reconstructTemplateArgumentType(
62 TemplateDecl const * decl, TemplateSpecializationType const * specializationType,
63 SubstTemplateTypeParmType const * parmType)
65 TemplateParameterList const * ps = decl->getTemplateParameters();
66 auto i = std::find(ps->begin(), ps->end(), compat::getReplacedParameter(parmType));
67 if (i == ps->end()) {
68 return {};
70 auto const args = specializationType->template_arguments();
71 if (ps->size() != args.size()) { //TODO
72 return {};
74 TemplateArgument const & arg = args[i - ps->begin()];
75 if (arg.getKind() != TemplateArgument::Type) {
76 return {};
78 return arg.getAsType();
81 bool areSameTypedef(QualType type1, QualType type2) {
82 // type1.getTypePtr() == typ2.getTypePtr() fails for e.g. ::sal_Bool vs.
83 // sal_Bool:
84 auto t1 = type1->getAs<TypedefType>();
85 auto t2 = type2->getAs<TypedefType>();
86 return t1 != nullptr && t2 != nullptr && t1->getDecl() == t2->getDecl();
89 bool isBool(Expr const * expr, bool allowTypedefs = true) {
90 auto t = expr->getType();
91 return allowTypedefs
92 ? bool(loplugin::TypeCheck(t).AnyBoolean()) : t->isBooleanType();
95 bool isMatchingBool(Expr const * expr, Expr const * comparisonExpr) {
96 return isBool(expr, false)
97 || areSameTypedef(expr->getType(), comparisonExpr->getType());
100 bool isSalBool(QualType type) {
101 auto t = type->getAs<TypedefType>();
102 return t != nullptr && t->getDecl()->getName() == "sal_Bool";
105 bool isBoolExpr(Expr const * expr) {
106 if (isBool(expr)) {
107 return true;
109 expr = ignoreParenImpCastAndComma(expr);
110 ConditionalOperator const * co = dyn_cast<ConditionalOperator>(expr);
111 if (co != nullptr) {
112 ImplicitCastExpr const * ic1 = dyn_cast<ImplicitCastExpr>(
113 co->getTrueExpr()->IgnoreParens());
114 ImplicitCastExpr const * ic2 = dyn_cast<ImplicitCastExpr>(
115 co->getFalseExpr()->IgnoreParens());
116 if (ic1 != nullptr && ic2 != nullptr
117 && ic1->getType()->isSpecificBuiltinType(BuiltinType::Int)
118 && isBoolExpr(ic1->getSubExpr()->IgnoreParens())
119 && ic2->getType()->isSpecificBuiltinType(BuiltinType::Int)
120 && isBoolExpr(ic2->getSubExpr()->IgnoreParens()))
122 return true;
125 std::stack<Expr const *> stack;
126 Expr const * e = expr;
127 for (;;) {
128 e = ignoreParenImpCastAndComma(e);
129 MemberExpr const * me = dyn_cast<MemberExpr>(e);
130 if (me == nullptr) {
131 break;
133 stack.push(e);
134 e = me->getBase();
136 for (;;) {
137 e = ignoreParenImpCastAndComma(e);
138 CXXOperatorCallExpr const * op = dyn_cast<CXXOperatorCallExpr>(e);
139 if (op == nullptr || op->getOperator() != OO_Subscript) {
140 break;
142 stack.push(e);
143 e = op->getArg(0);
145 if (!stack.empty()) {
146 TemplateSpecializationType const * t
147 = e->getType()->getAs<TemplateSpecializationType>();
148 for (;;) {
149 if (t == nullptr) {
150 break;
152 QualType ty;
153 MemberExpr const * me = dyn_cast<MemberExpr>(stack.top());
154 if (me != nullptr) {
155 TemplateDecl const * td
156 = t->getTemplateName().getAsTemplateDecl();
157 if (td == nullptr) {
158 break;
160 SubstTemplateTypeParmType const * t2
161 = getAsSubstTemplateTypeParmType(
162 me->getMemberDecl()->getType());
163 if (t2 == nullptr) {
164 break;
166 ty = reconstructTemplateArgumentType(td, t, t2);
167 if (ty.isNull()) {
168 auto const canon = cast<TemplateDecl>(td->getCanonicalDecl());
169 if (canon != td) {
170 ty = reconstructTemplateArgumentType(canon, t, t2);
173 if (ty.isNull()) {
174 break;
176 } else {
177 CXXOperatorCallExpr const * op
178 = dyn_cast<CXXOperatorCallExpr>(stack.top());
179 assert(op != nullptr);
180 (void)op;
181 TemplateDecl const * d
182 = t->getTemplateName().getAsTemplateDecl();
183 if (d == nullptr) {
184 break;
186 auto const dc = loplugin::DeclCheck(d->getTemplatedDecl());
187 auto const args = t->template_arguments();
188 if (dc.ClassOrStruct("array").StdNamespace() && args.size() >= 2
189 && args[0].getKind() == TemplateArgument::Type)
191 ty = args[0].getAsType();
192 } else if (dc.Class("Sequence").Namespace("uno").Namespace("star").Namespace("sun")
193 .Namespace("com").GlobalNamespace()
194 && args.size() == 1
195 && args[0].getKind() == TemplateArgument::Type)
197 ty = args[0].getAsType();
198 } else {
199 break;
202 stack.pop();
203 if (stack.empty()) {
204 if (loplugin::TypeCheck(ty).AnyBoolean()) {
205 return true;
207 break;
209 t = ty->getAs<TemplateSpecializationType>();
212 return false;
215 // It appears that, given a function declaration, there is no way to determine
216 // the language linkage of the function's type, only of the function's name
217 // (via FunctionDecl::isExternC); however, in a case like
219 // extern "C" { static void f(); }
221 // the function's name does not have C language linkage while the function's
222 // type does (as clarified in C++11 [decl.link]); cf. <http://clang-developers.
223 // 42468.n3.nabble.com/Language-linkage-of-function-type-tt4037248.html>
224 // "Language linkage of function type":
225 bool hasCLanguageLinkageType(FunctionDecl const * decl) {
226 assert(decl != nullptr);
227 if (decl->isExternC()) {
228 return true;
230 if (decl->isInExternCContext()) {
231 return true;
233 return false;
236 class ImplicitBoolConversion:
237 public loplugin::FilteringPlugin<ImplicitBoolConversion>
239 public:
240 explicit ImplicitBoolConversion(loplugin::InstantiationData const & data):
241 FilteringPlugin(data) {}
243 virtual void run() override
244 { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
246 bool TraverseCallExpr(CallExpr * expr);
248 bool TraverseCXXMemberCallExpr(CXXMemberCallExpr * expr);
250 bool TraverseCXXConstructExpr(CXXConstructExpr * expr);
252 bool TraverseCXXTemporaryObjectExpr(CXXTemporaryObjectExpr * expr);
254 bool TraverseCStyleCastExpr(CStyleCastExpr * expr);
256 bool TraverseCXXStaticCastExpr(CXXStaticCastExpr * expr);
258 bool TraverseCXXFunctionalCastExpr(CXXFunctionalCastExpr * expr);
260 bool TraverseConditionalOperator(ConditionalOperator * expr);
262 bool TraverseBinaryOperator(BinaryOperator * expr);
264 bool TraverseCompoundAssignOperator(CompoundAssignOperator * expr);
266 bool TraverseInitListExpr(InitListExpr * expr);
268 bool TraverseReturnStmt(ReturnStmt * stmt);
270 bool TraverseFunctionDecl(FunctionDecl * decl);
272 bool VisitImplicitCastExpr(ImplicitCastExpr const * expr);
274 bool VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr const * expr);
276 private:
277 bool isExternCFunctionCall(
278 CallExpr const * expr, FunctionProtoType const ** functionType);
280 bool isExternCFunctionCallReturningInt(Expr const * expr);
282 void checkCXXConstructExpr(CXXConstructExpr const * expr);
284 void reportWarning(ImplicitCastExpr const * expr);
286 std::stack<std::vector<ImplicitCastExpr const *>> nested;
287 std::stack<CallExpr const *> calls;
288 bool bExternCIntFunctionDefinition = false;
291 bool ImplicitBoolConversion::TraverseCallExpr(CallExpr * expr) {
292 nested.push(std::vector<ImplicitCastExpr const *>());
293 calls.push(expr);
294 bool bRet = RecursiveASTVisitor::TraverseCallExpr(expr);
295 FunctionProtoType const * t;
296 bool bExt = isExternCFunctionCall(expr, &t);
297 assert(!nested.empty());
298 for (auto i: nested.top()) {
299 auto j = std::find_if(
300 expr->arg_begin(), expr->arg_end(),
301 [&i](Expr * e) {
302 return i == ignoreParenAndTemporaryMaterialization(e);
304 if (j == expr->arg_end()) {
305 reportWarning(i);
306 } else {
307 std::ptrdiff_t n = j - expr->arg_begin();
308 assert(n >= 0);
309 if (t != nullptr
310 && static_cast<std::size_t>(n) >= t->getNumParams())
312 assert(t->isVariadic());
313 // ignore bool to int promotions of variadic arguments
314 } else if (bExt) {
315 if (t != nullptr) {
316 assert(
317 static_cast<std::size_t>(n) < t->getNumParams());
318 if (!(t->getParamType(n)->isSpecificBuiltinType(
319 BuiltinType::Int)
320 || t->getParamType(n)->isSpecificBuiltinType(
321 BuiltinType::UInt)
322 || t->getParamType(n)->isSpecificBuiltinType(
323 BuiltinType::Long)))
325 reportWarning(i);
327 } else {
328 reportWarning(i);
330 } else {
331 // Filter out
333 // template<typename T> void f(T);
334 // f<sal_Bool>(true);
336 DeclRefExpr const * dr = dyn_cast<DeclRefExpr>(
337 expr->getCallee()->IgnoreParenImpCasts());
338 if (dr != nullptr && dr->hasExplicitTemplateArgs()) {
339 FunctionDecl const * fd
340 = dyn_cast<FunctionDecl>(dr->getDecl());
341 if (fd != nullptr
342 && static_cast<std::size_t>(n) < fd->getNumParams())
344 SubstTemplateTypeParmType const * t2
345 = getAsSubstTemplateTypeParmType(
346 fd->getParamDecl(n)->getType()
347 .getNonReferenceType());
348 if (t2 != nullptr) {
349 //TODO: fix this superficial nonsense check:
350 if (dr->getNumTemplateArgs() == 1) {
351 auto const ta = dr->getTemplateArgs();
352 if ((ta[0].getArgument().getKind()
353 == TemplateArgument::Type)
354 && (loplugin::TypeCheck(
355 ta[0].getTypeSourceInfo()
356 ->getType())
357 .AnyBoolean()))
359 continue;
365 reportWarning(i);
369 calls.pop();
370 nested.pop();
371 return bRet;
374 bool ImplicitBoolConversion::TraverseCXXMemberCallExpr(CXXMemberCallExpr * expr)
376 nested.push(std::vector<ImplicitCastExpr const *>());
377 bool bRet = RecursiveASTVisitor::TraverseCXXMemberCallExpr(expr);
378 assert(!nested.empty());
379 for (auto i: nested.top()) {
380 auto j = std::find_if(
381 expr->arg_begin(), expr->arg_end(),
382 [&i](Expr * e) {
383 return i == ignoreParenAndTemporaryMaterialization(e);
385 if (j != expr->arg_end()) {
386 // Filter out
388 // template<typename T> struct S { void f(T); };
389 // S<sal_Bool> s;
390 // s.f(true);
392 std::ptrdiff_t n = j - expr->arg_begin();
393 assert(n >= 0);
394 CXXMethodDecl const * d = expr->getMethodDecl();
395 if (static_cast<std::size_t>(n) >= d->getNumParams()) {
396 // Ignore bool to int promotions of variadic arguments:
397 assert(d->isVariadic());
398 continue;
400 QualType ty
401 = ignoreParenImpCastAndComma(expr->getImplicitObjectArgument())
402 ->getType();
403 if (dyn_cast<MemberExpr>(expr->getCallee())->isArrow()) {
404 ty = ty->getAs<clang::PointerType>()->getPointeeType();
406 TemplateSpecializationType const * ct
407 = ty->getAs<TemplateSpecializationType>();
408 if (ct != nullptr) {
409 SubstTemplateTypeParmType const * pt
410 = getAsSubstTemplateTypeParmType(
411 d->getParamDecl(n)->getType().getNonReferenceType());
412 if (pt != nullptr) {
413 TemplateDecl const * td
414 = ct->getTemplateName().getAsTemplateDecl();
415 if (td != nullptr) {
416 //TODO: fix this superficial nonsense check:
417 auto const args = ct->template_arguments();
418 if (args.size() >= 1
419 && args[0].getKind() == TemplateArgument::Type
420 && (loplugin::TypeCheck(args[0].getAsType())
421 .AnyBoolean()))
423 continue;
429 reportWarning(i);
431 nested.pop();
432 return bRet;
435 bool ImplicitBoolConversion::TraverseCXXConstructExpr(CXXConstructExpr * expr) {
436 nested.push(std::vector<ImplicitCastExpr const *>());
437 bool bRet = RecursiveASTVisitor::TraverseCXXConstructExpr(expr);
438 checkCXXConstructExpr(expr);
439 nested.pop();
440 return bRet;
443 bool ImplicitBoolConversion::TraverseCXXTemporaryObjectExpr(
444 CXXTemporaryObjectExpr * expr)
446 nested.push(std::vector<ImplicitCastExpr const *>());
447 bool bRet = RecursiveASTVisitor::TraverseCXXTemporaryObjectExpr(expr);
448 checkCXXConstructExpr(expr);
449 nested.pop();
450 return bRet;
453 bool ImplicitBoolConversion::TraverseCStyleCastExpr(CStyleCastExpr * expr) {
454 nested.push(std::vector<ImplicitCastExpr const *>());
455 bool bRet = RecursiveASTVisitor::TraverseCStyleCastExpr(expr);
456 assert(!nested.empty());
457 for (auto i: nested.top()) {
458 if (i != expr->getSubExpr()->IgnoreParens()) {
459 reportWarning(i);
462 nested.pop();
463 return bRet;
466 bool ImplicitBoolConversion::TraverseCXXStaticCastExpr(CXXStaticCastExpr * expr)
468 nested.push(std::vector<ImplicitCastExpr const *>());
469 bool bRet = RecursiveASTVisitor::TraverseCXXStaticCastExpr(expr);
470 assert(!nested.empty());
471 for (auto i: nested.top()) {
472 if (i != expr->getSubExpr()->IgnoreParens()) {
473 reportWarning(i);
476 nested.pop();
477 return bRet;
480 bool ImplicitBoolConversion::TraverseCXXFunctionalCastExpr(
481 CXXFunctionalCastExpr * expr)
483 nested.push(std::vector<ImplicitCastExpr const *>());
484 bool bRet = RecursiveASTVisitor::TraverseCXXFunctionalCastExpr(expr);
485 assert(!nested.empty());
486 for (auto i: nested.top()) {
487 if (i != expr->getSubExpr()->IgnoreParens()) {
488 reportWarning(i);
491 nested.pop();
492 return bRet;
495 bool ImplicitBoolConversion::TraverseConditionalOperator(
496 ConditionalOperator * expr)
498 nested.push(std::vector<ImplicitCastExpr const *>());
499 bool bRet = RecursiveASTVisitor::TraverseConditionalOperator(expr);
500 assert(!nested.empty());
501 for (auto i: nested.top()) {
502 if (!((i == expr->getTrueExpr()->IgnoreParens()
503 && (isBoolExpr(expr->getFalseExpr()->IgnoreParenImpCasts())
504 || isExternCFunctionCallReturningInt(expr->getFalseExpr())))
505 || (i == expr->getFalseExpr()->IgnoreParens()
506 && (isBoolExpr(expr->getTrueExpr()->IgnoreParenImpCasts())
507 || isExternCFunctionCallReturningInt(
508 expr->getTrueExpr())))
509 || (!compiler.getLangOpts().CPlusPlus
510 && i == expr->getCond()->IgnoreParens())))
512 reportWarning(i);
515 nested.pop();
516 return bRet;
519 bool ImplicitBoolConversion::TraverseBinaryOperator(BinaryOperator * expr) {
520 switch (expr->getOpcode()) {
521 case BO_LT:
522 case BO_LE:
523 case BO_GT:
524 case BO_GE:
525 case BO_EQ:
526 case BO_NE:
528 nested.push(std::vector<ImplicitCastExpr const *>());
529 bool bRet = RecursiveASTVisitor::TraverseBinaryOperator(expr);
530 assert(!nested.empty());
531 for (auto i: nested.top()) {
532 if (!((i == expr->getLHS()->IgnoreParens()
533 && isMatchingBool(
534 expr->getRHS()->IgnoreImpCasts(), i->getSubExprAsWritten()))
535 || (i == expr->getRHS()->IgnoreParens()
536 && isMatchingBool(
537 expr->getLHS()->IgnoreImpCasts(),
538 i->getSubExprAsWritten()))))
540 reportWarning(i);
543 nested.pop();
544 return bRet;
546 case BO_Assign:
548 nested.push(std::vector<ImplicitCastExpr const *>());
549 bool bRet = RecursiveASTVisitor::TraverseBinaryOperator(expr);
550 // gtk-2.0/gtk/gtktogglebutton.h: struct _GtkToggleButton:
551 // guint GSEAL (active) : 1;
552 // even though <http://www.gtk.org/api/2.6/gtk/GtkToggleButton.html>:
553 // "active" gboolean : Read / Write
554 // qt5/QtGui/qaccessible.h: struct State:
555 // quint64 disabled : 1;
556 bool bExt = false;
557 MemberExpr const * me = dyn_cast<MemberExpr>(expr->getLHS());
558 if (me != nullptr) {
559 FieldDecl const * fd = dyn_cast<FieldDecl>(me->getMemberDecl());
560 if (fd != nullptr && fd->isBitField()
561 && fd->getBitWidthValue(compiler.getASTContext()) == 1)
563 auto const check = loplugin::TypeCheck(fd->getType());
564 bExt = check.Typedef("guint").GlobalNamespace()
565 || check.Typedef("quint64").GlobalNamespace();
568 assert(!nested.empty());
569 for (auto i: nested.top()) {
570 if (i != expr->getRHS()->IgnoreParens()
571 || !(bExt || isBoolExpr(expr->getLHS())))
573 reportWarning(i);
576 nested.pop();
577 return bRet;
579 default:
580 return RecursiveASTVisitor::TraverseBinaryOperator(expr);
584 bool ImplicitBoolConversion::TraverseCompoundAssignOperator(CompoundAssignOperator * expr) {
585 switch (expr->getOpcode()) {
586 case BO_AndAssign:
587 case BO_OrAssign:
588 case BO_XorAssign:
590 nested.push(std::vector<ImplicitCastExpr const *>());
591 bool bRet = RecursiveASTVisitor::TraverseCompoundAssignOperator(expr);
592 assert(!nested.empty());
593 for (auto i: nested.top()) {
594 if (i != expr->getRHS()->IgnoreParens()
595 || !isBool(expr->getLHS()->IgnoreParens(), false))
597 reportWarning(i);
600 nested.pop();
601 if (!ignoreLocation(expr) && isBool(expr->getLHS(), false)
602 && !isBool(expr->getRHS()->IgnoreParenImpCasts(), false))
604 report(
605 DiagnosticsEngine::Warning, "mix of %0 and %1 in operator %2",
606 expr->getRHS()->getBeginLoc())
607 << expr->getLHS()->getType()
608 << expr->getRHS()->IgnoreParenImpCasts()->getType()
609 << expr->getOpcodeStr()
610 << expr->getSourceRange();
612 return bRet;
614 default:
615 return RecursiveASTVisitor::TraverseCompoundAssignOperator(expr);
619 bool ImplicitBoolConversion::TraverseInitListExpr(InitListExpr * expr) {
620 nested.push(std::vector<ImplicitCastExpr const *>());
621 auto const e = expr->isSemanticForm() ? expr : expr->getSemanticForm();
622 auto const ret = TraverseSynOrSemInitListExpr(e, nullptr);
623 assert(!nested.empty());
624 for (auto i: nested.top()) {
625 if (std::find(e->begin(), e->end(), i) == e->end()
626 || !i->getType()->isSpecificBuiltinType(clang::BuiltinType::UChar))
628 reportWarning(i);
631 nested.pop();
632 return ret;
635 bool ImplicitBoolConversion::TraverseReturnStmt(ReturnStmt * stmt) {
636 nested.push(std::vector<ImplicitCastExpr const *>());
637 bool bRet = RecursiveASTVisitor::TraverseReturnStmt(stmt);
638 Expr const * expr = stmt->getRetValue();
639 if (expr != nullptr) {
640 ExprWithCleanups const * ec = dyn_cast<ExprWithCleanups>(expr);
641 if (ec != nullptr) {
642 expr = ec->getSubExpr();
644 expr = expr->IgnoreParens();
646 assert(!nested.empty());
647 for (auto i: nested.top()) {
648 if (i != expr || !bExternCIntFunctionDefinition) {
649 reportWarning(i);
652 nested.pop();
653 return bRet;
656 bool ImplicitBoolConversion::TraverseFunctionDecl(FunctionDecl * decl) {
657 bool bExt = false;
658 if (hasCLanguageLinkageType(decl) && decl->isThisDeclarationADefinition()) {
659 QualType t { decl->getReturnType() };
660 if (t->isSpecificBuiltinType(BuiltinType::Int)
661 || t->isSpecificBuiltinType(BuiltinType::UInt))
663 bExt = true;
664 } else {
665 TypedefType const * t2 = t->getAs<TypedefType>();
666 // cf. rtl_locale_equals (and sal_Int32 can be long):
667 if (t2 != nullptr
668 && t2->getDecl()->getNameAsString() == "sal_Int32")
670 bExt = true;
674 if (bExt) {
675 assert(!bExternCIntFunctionDefinition);
676 bExternCIntFunctionDefinition = true;
678 bool bRet = RecursiveASTVisitor::TraverseFunctionDecl(decl);
679 if (bExt) {
680 bExternCIntFunctionDefinition = false;
682 return bRet;
685 bool ImplicitBoolConversion::VisitImplicitCastExpr(
686 ImplicitCastExpr const * expr)
688 if (ignoreLocation(expr)) {
689 return true;
691 if (isBool(compat::getSubExprAsWritten(expr)) && !isBool(expr)) {
692 // Ignore NoOp from 'sal_Bool' (aka 'unsigned char') to 'const unsigned
693 // char' in makeAny(b) with b of type sal_Bool:
694 if (expr->getCastKind() == CK_NoOp) {
695 return true;
697 // Ignore implicit conversions from bool to int in
699 // #define _G_STR_NONNULL(x) (x + !x)
701 // from
702 // <https://gitlab.gnome.org/GNOME/glib/-/commit/48730d2b30473c5eeda2badf9a65d380304477c3>
703 // "gstrfuncs: Add back x + !x warning workaround":
704 if (auto const sub = dyn_cast<UnaryOperator>(compat::getSubExprAsWritten(expr))) {
705 if (sub->getOpcode() == UO_LNot) {
706 auto const l = expr->getBeginLoc();
707 if (compiler.getSourceManager().isMacroBodyExpansion(l)
708 && Lexer::getImmediateMacroName(
709 l, compiler.getSourceManager(), compiler.getLangOpts())
710 == "_G_STR_NONNULL")
712 return true;
716 if (nested.empty()) {
717 reportWarning(expr);
718 } else {
719 nested.top().push_back(expr);
721 return true;
723 if (auto const sub = dyn_cast<ExplicitCastExpr>(
724 compat::getSubExprAsWritten(expr)))
726 auto const subsub = compat::getSubExprAsWritten(sub);
727 if (subsub->getType().IgnoreParens() == expr->getType().IgnoreParens()
728 && isBool(subsub))
730 // Ignore "normalizing cast" bool(b) from sal_Bool b to bool, then
731 // implicitly cast back again to sal_Bool:
732 if (dyn_cast<CXXFunctionalCastExpr>(sub) != nullptr
733 && sub->getType()->isBooleanType() && isSalBool(expr->getType())
734 && isSalBool(subsub->getType()))
736 return true;
738 report(
739 DiagnosticsEngine::Warning,
740 ("explicit conversion (%0) from %1 to %2 implicitly cast back"
741 " to %3"),
742 expr->getBeginLoc())
743 << sub->getCastKindName() << subsub->getType() << sub->getType()
744 << expr->getType() << expr->getSourceRange();
745 return true;
748 if (expr->getType()->isBooleanType() && !isBoolExpr(expr->getSubExpr())
749 && !calls.empty())
751 CallExpr const * call = calls.top();
752 if (std::any_of(
753 call->arg_begin(), call->arg_end(),
754 [expr](Expr const * e) { return expr == e->IgnoreParens(); }))
756 report(
757 DiagnosticsEngine::Warning,
758 "implicit conversion (%0) of call argument from %1 to %2",
759 expr->getBeginLoc())
760 << expr->getCastKindName() << expr->getSubExpr()->getType()
761 << expr->getType() << expr->getSourceRange();
762 return true;
765 return true;
768 bool ImplicitBoolConversion::VisitMaterializeTemporaryExpr(
769 MaterializeTemporaryExpr const * expr)
771 if (ignoreLocation(expr)) {
772 return true;
774 if (auto const sub = dyn_cast<ExplicitCastExpr>(expr->getSubExpr())) {
775 auto const subsub = compat::getSubExprAsWritten(sub);
776 if (subsub->getType().IgnoreParens() == expr->getType().IgnoreParens()
777 && isBool(subsub))
779 report(
780 DiagnosticsEngine::Warning,
781 ("explicit conversion (%0) from %1 to %2 implicitly converted"
782 " back to %3"),
783 expr->getBeginLoc())
784 << sub->getCastKindName() << subsub->getType() << sub->getType()
785 << expr->getType() << expr->getSourceRange();
786 return true;
789 return true;
792 bool ImplicitBoolConversion::isExternCFunctionCall(
793 CallExpr const * expr, FunctionProtoType const ** functionType)
795 assert(functionType != nullptr);
796 *functionType = nullptr;
797 Decl const * d = expr->getCalleeDecl();
798 if (d != nullptr) {
799 FunctionDecl const * fd = dyn_cast<FunctionDecl>(d);
800 if (fd != nullptr) {
801 clang::PointerType const * pt = fd->getType()
802 ->getAs<clang::PointerType>();
803 QualType t2(pt == nullptr ? fd->getType() : pt->getPointeeType());
804 *functionType = t2->getAs<FunctionProtoType>();
805 assert(
806 *functionType != nullptr || !compiler.getLangOpts().CPlusPlus
807 || (fd->getBuiltinID() != Builtin::NotBuiltin
808 && isa<FunctionNoProtoType>(t2)));
809 // __builtin_*s have no proto type?
810 return fd->isExternC()
811 || compiler.getSourceManager().isInExternCSystemHeader(
812 fd->getLocation());
814 VarDecl const * vd = dyn_cast<VarDecl>(d);
815 if (vd != nullptr) {
816 clang::PointerType const * pt = vd->getType()
817 ->getAs<clang::PointerType>();
818 *functionType
819 = ((pt == nullptr ? vd->getType() : pt->getPointeeType())
820 ->getAs<FunctionProtoType>());
821 return vd->isExternC();
824 return false;
827 bool ImplicitBoolConversion::isExternCFunctionCallReturningInt(
828 Expr const * expr)
830 CallExpr const * e = dyn_cast<CallExpr>(expr->IgnoreParenImpCasts());
831 FunctionProtoType const * t;
832 return e != nullptr && e->getType()->isSpecificBuiltinType(BuiltinType::Int)
833 && isExternCFunctionCall(e, &t);
836 void ImplicitBoolConversion::checkCXXConstructExpr(
837 CXXConstructExpr const * expr)
839 assert(!nested.empty());
840 for (auto i: nested.top()) {
841 auto j = std::find_if(
842 expr->arg_begin(), expr->arg_end(),
843 [&i](Expr const * e) {
844 return i == ignoreParenAndTemporaryMaterialization(e);
846 if (j != expr->arg_end()) {
847 TemplateSpecializationType const * t1 = expr->getType()->
848 getAs<TemplateSpecializationType>();
849 if (t1 == nullptr) {
850 //TODO:
851 if (i->getType()->isSpecificBuiltinType(clang::BuiltinType::UChar)) {
852 continue;
854 } else {
855 SubstTemplateTypeParmType const * t2 = nullptr;
856 CXXConstructorDecl const * d = expr->getConstructor();
857 if (d->getNumParams() == expr->getNumArgs()) { //TODO: better check
858 t2 = getAsSubstTemplateTypeParmType(
859 d->getParamDecl(j - expr->arg_begin())->getType()
860 .getNonReferenceType());
862 if (t2 != nullptr) {
863 TemplateDecl const * td
864 = t1->getTemplateName().getAsTemplateDecl();
865 if (td != nullptr) {
866 TemplateParameterList const * ps
867 = td->getTemplateParameters();
868 auto k = std::find(
869 ps->begin(), ps->end(),
870 compat::getReplacedParameter(t2));
871 if (k != ps->end()) {
872 auto const args = t1->template_arguments();
873 if (ps->size() == args.size()) { //TODO
874 TemplateArgument const & arg = args[
875 k - ps->begin()];
876 if (arg.getKind() == TemplateArgument::Type
877 && (loplugin::TypeCheck(arg.getAsType())
878 .AnyBoolean()))
880 continue;
888 reportWarning(i);
892 void ImplicitBoolConversion::reportWarning(ImplicitCastExpr const * expr) {
893 if (compiler.getLangOpts().CPlusPlus) {
894 if (expr->getCastKind() == CK_ConstructorConversion) {
895 auto const t1 = expr->getType();
896 if (auto const t2 = t1->getAs<TemplateSpecializationType>()) {
897 auto const args = t2->template_arguments();
898 assert(args.size() >= 1);
899 auto const a = args[0];
900 if (a.getKind() == TemplateArgument::Type && a.getAsType()->isBooleanType()
901 && (loplugin::TypeCheck(t1).TemplateSpecializationClass()
902 .ClassOrStruct("atomic").StdNamespace()))
904 return;
908 report(
909 DiagnosticsEngine::Warning,
910 "implicit conversion (%0) from %1 to %2", expr->getBeginLoc())
911 << expr->getCastKindName() << expr->getSubExprAsWritten()->getType()
912 << expr->getType() << expr->getSourceRange();
916 loplugin::Plugin::Registration<ImplicitBoolConversion> X(
917 "implicitboolconversion");
921 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */