office-gobmx/compilerplugins/clang/implicitboolconversion.cxx
Stephan Bergmann 6f26a72464 Tweak loplugin:implicitboolconversion to allow some more bool -> sal_Bool
...in templated code, to cater for the needs of
<https://gerrit.libreoffice.org/c/core/+/124400> "Prepare for removal of
non-const operator[] from Sequence in testtools".

For one, by defining ImplicitBoolConversion::TraverseInitListExpr, make sure
that Clang versions before and after
<0a42fe70a5>
"[AST] Treat semantic form of InitListExpr as implicit code in traversals"
behave the same.  Old versions of Clang would have erroneously reported

  Sequence<Sequence<sal_Bool>> s2{ { false } };

(and reported

  Sequence<Sequence<sal_Int32>> s4{ { false } };

twice) in compilerplugins/clang/test/implicitboolconversion.cxx when one of the
four combinations of syntactic/semantic visit of the outer/inner InitListExpr
defeated the intended suppression logic in
ImplicitBoolConversion::TraverseCXXStdInitializerListExpr.

And for another, ImplicitBoolConversion::TraverseInitListExpr can subsume the
exising ImplicitBoolConversion::TraverseCXXStdInitializerListExpr.

But for a third, that would still make

  Sequence<Wrap2<sal_Bool>> s6{ { false } };

in compilerplugins/clang/test/implicitboolconversion.cxx emit a false warning,
so add a cheesy "TODO" chicken-out special case to
ImplicitBoolConversion::checkCXXConstructExpr for now.

Change-Id: Ib9a1b78a7812feb98c673b75a357af7737168342
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/124583
Tested-by: Jenkins
Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2021-11-02 20:10:46 +01:00

909 lines
33 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <iterator>
#include <stack>
#include <string>
#include <vector>
#include "clang/Basic/Builtins.h"
#include "config_clang.h"
#include "check.hxx"
#include "compat.hxx"
#include "plugin.hxx"
namespace {
Expr const * ignoreParenAndTemporaryMaterialization(Expr const * expr) {
for (;;) {
expr = expr->IgnoreParens();
auto e = dyn_cast<MaterializeTemporaryExpr>(expr);
if (e == nullptr) {
return expr;
}
expr = compat::getSubExpr(e);
}
}
Expr const * ignoreParenImpCastAndComma(Expr const * expr) {
for (;;) {
expr = expr->IgnoreParenImpCasts();
BinaryOperator const * op = dyn_cast<BinaryOperator>(expr);
if (op == nullptr || op->getOpcode() != BO_Comma) {
return expr;
}
expr = op->getRHS();
}
}
SubstTemplateTypeParmType const * getAsSubstTemplateTypeParmType(QualType type)
{
//TODO: unwrap all kinds of (non-SubstTemplateTypeParmType) sugar, not only
// TypedefType sugar:
for (;;) {
TypedefType const * t = type->getAs<TypedefType>();
if (t == nullptr) {
return dyn_cast<SubstTemplateTypeParmType>(type);
}
type = t->desugar();
}
}
QualType reconstructTemplateArgumentType(
TemplateDecl const * decl, TemplateSpecializationType const * specializationType,
SubstTemplateTypeParmType const * parmType)
{
TemplateParameterList const * ps = decl->getTemplateParameters();
auto i = std::find(ps->begin(), ps->end(), parmType->getReplacedParameter()->getDecl());
if (i == ps->end()) {
return {};
}
if (ps->size() != specializationType->getNumArgs()) { //TODO
return {};
}
TemplateArgument const & arg = specializationType->getArg(i - ps->begin());
if (arg.getKind() != TemplateArgument::Type) {
return {};
}
return arg.getAsType();
}
bool areSameTypedef(QualType type1, QualType type2) {
// type1.getTypePtr() == typ2.getTypePtr() fails for e.g. ::sal_Bool vs.
// sal_Bool:
auto t1 = type1->getAs<TypedefType>();
auto t2 = type2->getAs<TypedefType>();
return t1 != nullptr && t2 != nullptr && t1->getDecl() == t2->getDecl();
}
bool isBool(Expr const * expr, bool allowTypedefs = true) {
auto t = expr->getType();
return allowTypedefs
? bool(loplugin::TypeCheck(t).AnyBoolean()) : t->isBooleanType();
}
bool isMatchingBool(Expr const * expr, Expr const * comparisonExpr) {
return isBool(expr, false)
|| areSameTypedef(expr->getType(), comparisonExpr->getType());
}
bool isSalBool(QualType type) {
auto t = type->getAs<TypedefType>();
return t != nullptr && t->getDecl()->getName() == "sal_Bool";
}
bool isBoolExpr(Expr const * expr) {
if (isBool(expr)) {
return true;
}
expr = ignoreParenImpCastAndComma(expr);
ConditionalOperator const * co = dyn_cast<ConditionalOperator>(expr);
if (co != nullptr) {
ImplicitCastExpr const * ic1 = dyn_cast<ImplicitCastExpr>(
co->getTrueExpr()->IgnoreParens());
ImplicitCastExpr const * ic2 = dyn_cast<ImplicitCastExpr>(
co->getFalseExpr()->IgnoreParens());
if (ic1 != nullptr && ic2 != nullptr
&& ic1->getType()->isSpecificBuiltinType(BuiltinType::Int)
&& isBoolExpr(ic1->getSubExpr()->IgnoreParens())
&& ic2->getType()->isSpecificBuiltinType(BuiltinType::Int)
&& isBoolExpr(ic2->getSubExpr()->IgnoreParens()))
{
return true;
}
}
std::stack<Expr const *> stack;
Expr const * e = expr;
for (;;) {
e = ignoreParenImpCastAndComma(e);
MemberExpr const * me = dyn_cast<MemberExpr>(e);
if (me == nullptr) {
break;
}
stack.push(e);
e = me->getBase();
}
for (;;) {
e = ignoreParenImpCastAndComma(e);
CXXOperatorCallExpr const * op = dyn_cast<CXXOperatorCallExpr>(e);
if (op == nullptr || op->getOperator() != OO_Subscript) {
break;
}
stack.push(e);
e = op->getArg(0);
}
if (!stack.empty()) {
TemplateSpecializationType const * t
= e->getType()->getAs<TemplateSpecializationType>();
for (;;) {
if (t == nullptr) {
break;
}
QualType ty;
MemberExpr const * me = dyn_cast<MemberExpr>(stack.top());
if (me != nullptr) {
TemplateDecl const * td
= t->getTemplateName().getAsTemplateDecl();
if (td == nullptr) {
break;
}
SubstTemplateTypeParmType const * t2
= getAsSubstTemplateTypeParmType(
me->getMemberDecl()->getType());
if (t2 == nullptr) {
break;
}
ty = reconstructTemplateArgumentType(td, t, t2);
if (ty.isNull()) {
auto const canon = cast<TemplateDecl>(td->getCanonicalDecl());
if (canon != td) {
ty = reconstructTemplateArgumentType(canon, t, t2);
}
}
if (ty.isNull()) {
break;
}
} else {
CXXOperatorCallExpr const * op
= dyn_cast<CXXOperatorCallExpr>(stack.top());
assert(op != nullptr);
(void)op;
TemplateDecl const * d
= t->getTemplateName().getAsTemplateDecl();
if (d == nullptr
|| (d->getQualifiedNameAsString()
!= "com::sun::star::uno::Sequence")
|| t->getNumArgs() != 1
|| t->getArg(0).getKind() != TemplateArgument::Type)
{
break;
}
ty = t->getArg(0).getAsType();
}
stack.pop();
if (stack.empty()) {
if (loplugin::TypeCheck(ty).AnyBoolean()) {
return true;
}
break;
}
t = ty->getAs<TemplateSpecializationType>();
}
}
return false;
}
// It appears that, given a function declaration, there is no way to determine
// the language linkage of the function's type, only of the function's name
// (via FunctionDecl::isExternC); however, in a case like
//
// extern "C" { static void f(); }
//
// the function's name does not have C language linkage while the function's
// type does (as clarified in C++11 [decl.link]); cf. <http://clang-developers.
// 42468.n3.nabble.com/Language-linkage-of-function-type-tt4037248.html>
// "Language linkage of function type":
bool hasCLanguageLinkageType(FunctionDecl const * decl) {
assert(decl != nullptr);
if (decl->isExternC()) {
return true;
}
if (decl->isInExternCContext()) {
return true;
}
return false;
}
class ImplicitBoolConversion:
public loplugin::FilteringPlugin<ImplicitBoolConversion>
{
public:
explicit ImplicitBoolConversion(loplugin::InstantiationData const & data):
FilteringPlugin(data) {}
virtual void run() override
{ TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
bool TraverseCallExpr(CallExpr * expr);
bool TraverseCXXMemberCallExpr(CXXMemberCallExpr * expr);
bool TraverseCXXConstructExpr(CXXConstructExpr * expr);
bool TraverseCXXTemporaryObjectExpr(CXXTemporaryObjectExpr * expr);
bool TraverseCStyleCastExpr(CStyleCastExpr * expr);
bool TraverseCXXStaticCastExpr(CXXStaticCastExpr * expr);
bool TraverseCXXFunctionalCastExpr(CXXFunctionalCastExpr * expr);
bool TraverseConditionalOperator(ConditionalOperator * expr);
bool TraverseBinaryOperator(BinaryOperator * expr);
#if CLANG_VERSION < 110000
bool TraverseBinLT(BinaryOperator * expr) { return TraverseBinaryOperator(expr); }
bool TraverseBinLE(BinaryOperator * expr) { return TraverseBinaryOperator(expr); }
bool TraverseBinGT(BinaryOperator * expr) { return TraverseBinaryOperator(expr); }
bool TraverseBinGE(BinaryOperator * expr) { return TraverseBinaryOperator(expr); }
bool TraverseBinEQ(BinaryOperator * expr) { return TraverseBinaryOperator(expr); }
bool TraverseBinNE(BinaryOperator * expr) { return TraverseBinaryOperator(expr); }
bool TraverseBinAssign(BinaryOperator * expr) { return TraverseBinaryOperator(expr); }
#endif
bool TraverseCompoundAssignOperator(CompoundAssignOperator * expr);
#if CLANG_VERSION < 110000
bool TraverseBinAndAssign(CompoundAssignOperator * expr)
{ return TraverseCompoundAssignOperator(expr); }
bool TraverseBinOrAssign(CompoundAssignOperator * expr)
{ return TraverseCompoundAssignOperator(expr); }
bool TraverseBinXorAssign(CompoundAssignOperator * expr)
{ return TraverseCompoundAssignOperator(expr); }
#endif
bool TraverseInitListExpr(InitListExpr * expr);
bool TraverseReturnStmt(ReturnStmt * stmt);
bool TraverseFunctionDecl(FunctionDecl * decl);
bool VisitImplicitCastExpr(ImplicitCastExpr const * expr);
bool VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr const * expr);
private:
bool isExternCFunctionCall(
CallExpr const * expr, FunctionProtoType const ** functionType);
bool isExternCFunctionCallReturningInt(Expr const * expr);
void checkCXXConstructExpr(CXXConstructExpr const * expr);
void reportWarning(ImplicitCastExpr const * expr);
std::stack<std::vector<ImplicitCastExpr const *>> nested;
std::stack<CallExpr const *> calls;
bool bExternCIntFunctionDefinition = false;
};
bool ImplicitBoolConversion::TraverseCallExpr(CallExpr * expr) {
nested.push(std::vector<ImplicitCastExpr const *>());
calls.push(expr);
bool bRet = RecursiveASTVisitor::TraverseCallExpr(expr);
FunctionProtoType const * t;
bool bExt = isExternCFunctionCall(expr, &t);
assert(!nested.empty());
for (auto i: nested.top()) {
auto j = std::find_if(
expr->arg_begin(), expr->arg_end(),
[&i](Expr * e) {
return i == ignoreParenAndTemporaryMaterialization(e);
});
if (j == expr->arg_end()) {
reportWarning(i);
} else {
std::ptrdiff_t n = j - expr->arg_begin();
assert(n >= 0);
if (t != nullptr
&& static_cast<std::size_t>(n) >= t->getNumParams())
{
assert(t->isVariadic());
// ignore bool to int promotions of variadic arguments
} else if (bExt) {
if (t != nullptr) {
assert(
static_cast<std::size_t>(n) < t->getNumParams());
if (!(t->getParamType(n)->isSpecificBuiltinType(
BuiltinType::Int)
|| t->getParamType(n)->isSpecificBuiltinType(
BuiltinType::UInt)
|| t->getParamType(n)->isSpecificBuiltinType(
BuiltinType::Long)))
{
reportWarning(i);
}
} else {
reportWarning(i);
}
} else {
// Filter out
//
// template<typename T> void f(T);
// f<sal_Bool>(true);
//
DeclRefExpr const * dr = dyn_cast<DeclRefExpr>(
expr->getCallee()->IgnoreParenImpCasts());
if (dr != nullptr && dr->hasExplicitTemplateArgs()) {
FunctionDecl const * fd
= dyn_cast<FunctionDecl>(dr->getDecl());
if (fd != nullptr
&& static_cast<std::size_t>(n) < fd->getNumParams())
{
SubstTemplateTypeParmType const * t2
= getAsSubstTemplateTypeParmType(
fd->getParamDecl(n)->getType()
.getNonReferenceType());
if (t2 != nullptr) {
//TODO: fix this superficial nonsense check:
if (dr->getNumTemplateArgs() == 1) {
auto const ta = dr->getTemplateArgs();
if ((ta[0].getArgument().getKind()
== TemplateArgument::Type)
&& (loplugin::TypeCheck(
ta[0].getTypeSourceInfo()
->getType())
.AnyBoolean()))
{
continue;
}
}
}
}
}
reportWarning(i);
}
}
}
calls.pop();
nested.pop();
return bRet;
}
bool ImplicitBoolConversion::TraverseCXXMemberCallExpr(CXXMemberCallExpr * expr)
{
nested.push(std::vector<ImplicitCastExpr const *>());
bool bRet = RecursiveASTVisitor::TraverseCXXMemberCallExpr(expr);
assert(!nested.empty());
for (auto i: nested.top()) {
auto j = std::find_if(
expr->arg_begin(), expr->arg_end(),
[&i](Expr * e) {
return i == ignoreParenAndTemporaryMaterialization(e);
});
if (j != expr->arg_end()) {
// Filter out
//
// template<typename T> struct S { void f(T); };
// S<sal_Bool> s;
// s.f(true);
//
std::ptrdiff_t n = j - expr->arg_begin();
assert(n >= 0);
CXXMethodDecl const * d = expr->getMethodDecl();
if (static_cast<std::size_t>(n) >= d->getNumParams()) {
// Ignore bool to int promotions of variadic arguments:
assert(d->isVariadic());
continue;
}
QualType ty
= ignoreParenImpCastAndComma(expr->getImplicitObjectArgument())
->getType();
if (dyn_cast<MemberExpr>(expr->getCallee())->isArrow()) {
ty = ty->getAs<clang::PointerType>()->getPointeeType();
}
TemplateSpecializationType const * ct
= ty->getAs<TemplateSpecializationType>();
if (ct != nullptr) {
SubstTemplateTypeParmType const * pt
= getAsSubstTemplateTypeParmType(
d->getParamDecl(n)->getType().getNonReferenceType());
if (pt != nullptr) {
TemplateDecl const * td
= ct->getTemplateName().getAsTemplateDecl();
if (td != nullptr) {
//TODO: fix this superficial nonsense check:
if (ct->getNumArgs() >= 1
&& ct->getArg(0).getKind() == TemplateArgument::Type
&& (loplugin::TypeCheck(ct->getArg(0).getAsType())
.AnyBoolean()))
{
continue;
}
}
}
}
}
reportWarning(i);
}
nested.pop();
return bRet;
}
bool ImplicitBoolConversion::TraverseCXXConstructExpr(CXXConstructExpr * expr) {
nested.push(std::vector<ImplicitCastExpr const *>());
bool bRet = RecursiveASTVisitor::TraverseCXXConstructExpr(expr);
checkCXXConstructExpr(expr);
nested.pop();
return bRet;
}
bool ImplicitBoolConversion::TraverseCXXTemporaryObjectExpr(
CXXTemporaryObjectExpr * expr)
{
nested.push(std::vector<ImplicitCastExpr const *>());
bool bRet = RecursiveASTVisitor::TraverseCXXTemporaryObjectExpr(expr);
checkCXXConstructExpr(expr);
nested.pop();
return bRet;
}
bool ImplicitBoolConversion::TraverseCStyleCastExpr(CStyleCastExpr * expr) {
nested.push(std::vector<ImplicitCastExpr const *>());
bool bRet = RecursiveASTVisitor::TraverseCStyleCastExpr(expr);
assert(!nested.empty());
for (auto i: nested.top()) {
if (i != expr->getSubExpr()->IgnoreParens()) {
reportWarning(i);
}
}
nested.pop();
return bRet;
}
bool ImplicitBoolConversion::TraverseCXXStaticCastExpr(CXXStaticCastExpr * expr)
{
nested.push(std::vector<ImplicitCastExpr const *>());
bool bRet = RecursiveASTVisitor::TraverseCXXStaticCastExpr(expr);
assert(!nested.empty());
for (auto i: nested.top()) {
if (i != expr->getSubExpr()->IgnoreParens()) {
reportWarning(i);
}
}
nested.pop();
return bRet;
}
bool ImplicitBoolConversion::TraverseCXXFunctionalCastExpr(
CXXFunctionalCastExpr * expr)
{
nested.push(std::vector<ImplicitCastExpr const *>());
bool bRet = RecursiveASTVisitor::TraverseCXXFunctionalCastExpr(expr);
assert(!nested.empty());
for (auto i: nested.top()) {
if (i != expr->getSubExpr()->IgnoreParens()) {
reportWarning(i);
}
}
nested.pop();
return bRet;
}
bool ImplicitBoolConversion::TraverseConditionalOperator(
ConditionalOperator * expr)
{
nested.push(std::vector<ImplicitCastExpr const *>());
bool bRet = RecursiveASTVisitor::TraverseConditionalOperator(expr);
assert(!nested.empty());
for (auto i: nested.top()) {
if (!((i == expr->getTrueExpr()->IgnoreParens()
&& (isBoolExpr(expr->getFalseExpr()->IgnoreParenImpCasts())
|| isExternCFunctionCallReturningInt(expr->getFalseExpr())))
|| (i == expr->getFalseExpr()->IgnoreParens()
&& (isBoolExpr(expr->getTrueExpr()->IgnoreParenImpCasts())
|| isExternCFunctionCallReturningInt(
expr->getTrueExpr())))
|| (!compiler.getLangOpts().CPlusPlus
&& i == expr->getCond()->IgnoreParens())))
{
reportWarning(i);
}
}
nested.pop();
return bRet;
}
bool ImplicitBoolConversion::TraverseBinaryOperator(BinaryOperator * expr) {
switch (expr->getOpcode()) {
case BO_LT:
case BO_LE:
case BO_GT:
case BO_GE:
case BO_EQ:
case BO_NE:
{
nested.push(std::vector<ImplicitCastExpr const *>());
bool bRet = RecursiveASTVisitor::TraverseBinaryOperator(expr);
assert(!nested.empty());
for (auto i: nested.top()) {
if (!((i == expr->getLHS()->IgnoreParens()
&& isMatchingBool(
expr->getRHS()->IgnoreImpCasts(), i->getSubExprAsWritten()))
|| (i == expr->getRHS()->IgnoreParens()
&& isMatchingBool(
expr->getLHS()->IgnoreImpCasts(),
i->getSubExprAsWritten()))))
{
reportWarning(i);
}
}
nested.pop();
return bRet;
}
case BO_Assign:
{
nested.push(std::vector<ImplicitCastExpr const *>());
bool bRet = RecursiveASTVisitor::TraverseBinaryOperator(expr);
// gtk-2.0/gtk/gtktogglebutton.h: struct _GtkToggleButton:
// guint GSEAL (active) : 1;
// even though <http://www.gtk.org/api/2.6/gtk/GtkToggleButton.html>:
// "active" gboolean : Read / Write
// qt5/QtGui/qaccessible.h: struct State:
// quint64 disabled : 1;
bool bExt = false;
MemberExpr const * me = dyn_cast<MemberExpr>(expr->getLHS());
if (me != nullptr) {
FieldDecl const * fd = dyn_cast<FieldDecl>(me->getMemberDecl());
if (fd != nullptr && fd->isBitField()
&& fd->getBitWidthValue(compiler.getASTContext()) == 1)
{
auto const check = loplugin::TypeCheck(fd->getType());
bExt = check.Typedef("guint").GlobalNamespace()
|| check.Typedef("quint64").GlobalNamespace();
}
}
assert(!nested.empty());
for (auto i: nested.top()) {
if (i != expr->getRHS()->IgnoreParens()
|| !(bExt || isBoolExpr(expr->getLHS())))
{
reportWarning(i);
}
}
nested.pop();
return bRet;
}
default:
return RecursiveASTVisitor::TraverseBinaryOperator(expr);
}
}
bool ImplicitBoolConversion::TraverseCompoundAssignOperator(CompoundAssignOperator * expr) {
switch (expr->getOpcode()) {
case BO_AndAssign:
case BO_OrAssign:
case BO_XorAssign:
{
nested.push(std::vector<ImplicitCastExpr const *>());
bool bRet = RecursiveASTVisitor::TraverseCompoundAssignOperator(expr);
assert(!nested.empty());
for (auto i: nested.top()) {
if (i != expr->getRHS()->IgnoreParens()
|| !isBool(expr->getLHS()->IgnoreParens(), false))
{
reportWarning(i);
}
}
nested.pop();
if (!ignoreLocation(expr) && isBool(expr->getLHS(), false)
&& !isBool(expr->getRHS()->IgnoreParenImpCasts(), false))
{
report(
DiagnosticsEngine::Warning, "mix of %0 and %1 in operator %2",
compat::getBeginLoc(expr->getRHS()))
<< expr->getLHS()->getType()
<< expr->getRHS()->IgnoreParenImpCasts()->getType()
<< expr->getOpcodeStr()
<< expr->getSourceRange();
}
return bRet;
}
default:
return RecursiveASTVisitor::TraverseCompoundAssignOperator(expr);
}
}
bool ImplicitBoolConversion::TraverseInitListExpr(InitListExpr * expr) {
nested.push(std::vector<ImplicitCastExpr const *>());
auto const e = expr->isSemanticForm() ? expr : expr->getSemanticForm();
auto const ret = TraverseSynOrSemInitListExpr(e, nullptr);
assert(!nested.empty());
for (auto i: nested.top()) {
if (std::find(e->begin(), e->end(), i) == e->end()
|| !i->getType()->isSpecificBuiltinType(clang::BuiltinType::UChar))
{
reportWarning(i);
}
}
nested.pop();
return ret;
}
bool ImplicitBoolConversion::TraverseReturnStmt(ReturnStmt * stmt) {
nested.push(std::vector<ImplicitCastExpr const *>());
bool bRet = RecursiveASTVisitor::TraverseReturnStmt(stmt);
Expr const * expr = stmt->getRetValue();
if (expr != nullptr) {
ExprWithCleanups const * ec = dyn_cast<ExprWithCleanups>(expr);
if (ec != nullptr) {
expr = ec->getSubExpr();
}
expr = expr->IgnoreParens();
}
assert(!nested.empty());
for (auto i: nested.top()) {
if (i != expr || !bExternCIntFunctionDefinition) {
reportWarning(i);
}
}
nested.pop();
return bRet;
}
bool ImplicitBoolConversion::TraverseFunctionDecl(FunctionDecl * decl) {
bool bExt = false;
if (hasCLanguageLinkageType(decl) && decl->isThisDeclarationADefinition()) {
QualType t { decl->getReturnType() };
if (t->isSpecificBuiltinType(BuiltinType::Int)
|| t->isSpecificBuiltinType(BuiltinType::UInt))
{
bExt = true;
} else {
TypedefType const * t2 = t->getAs<TypedefType>();
// cf. rtl_locale_equals (and sal_Int32 can be long):
if (t2 != nullptr
&& t2->getDecl()->getNameAsString() == "sal_Int32")
{
bExt = true;
}
}
}
if (bExt) {
assert(!bExternCIntFunctionDefinition);
bExternCIntFunctionDefinition = true;
}
bool bRet = RecursiveASTVisitor::TraverseFunctionDecl(decl);
if (bExt) {
bExternCIntFunctionDefinition = false;
}
return bRet;
}
bool ImplicitBoolConversion::VisitImplicitCastExpr(
ImplicitCastExpr const * expr)
{
if (ignoreLocation(expr)) {
return true;
}
if (isBool(compat::getSubExprAsWritten(expr)) && !isBool(expr)) {
// Ignore NoOp from 'sal_Bool' (aka 'unsigned char') to 'const unsigned
// char' in makeAny(b) with b of type sal_Bool:
if (expr->getCastKind() != CK_NoOp) {
if (nested.empty()) {
reportWarning(expr);
} else {
nested.top().push_back(expr);
}
}
return true;
}
if (auto const sub = dyn_cast<ExplicitCastExpr>(
compat::getSubExprAsWritten(expr)))
{
auto const subsub = compat::getSubExprAsWritten(sub);
if (subsub->getType().IgnoreParens() == expr->getType().IgnoreParens()
&& isBool(subsub))
{
// Ignore "normalizing cast" bool(b) from sal_Bool b to bool, then
// implicitly cast back again to sal_Bool:
if (dyn_cast<CXXFunctionalCastExpr>(sub) != nullptr
&& sub->getType()->isBooleanType() && isSalBool(expr->getType())
&& isSalBool(subsub->getType()))
{
return true;
}
report(
DiagnosticsEngine::Warning,
("explicit conversion (%0) from %1 to %2 implicitly cast back"
" to %3"),
compat::getBeginLoc(expr))
<< sub->getCastKindName() << subsub->getType() << sub->getType()
<< expr->getType() << expr->getSourceRange();
return true;
}
}
if (expr->getType()->isBooleanType() && !isBoolExpr(expr->getSubExpr())
&& !calls.empty())
{
CallExpr const * call = calls.top();
if (std::any_of(
call->arg_begin(), call->arg_end(),
[expr](Expr const * e) { return expr == e->IgnoreParens(); }))
{
report(
DiagnosticsEngine::Warning,
"implicit conversion (%0) of call argument from %1 to %2",
compat::getBeginLoc(expr))
<< expr->getCastKindName() << expr->getSubExpr()->getType()
<< expr->getType() << expr->getSourceRange();
return true;
}
}
return true;
}
bool ImplicitBoolConversion::VisitMaterializeTemporaryExpr(
MaterializeTemporaryExpr const * expr)
{
if (ignoreLocation(expr)) {
return true;
}
if (auto const sub = dyn_cast<ExplicitCastExpr>(compat::getSubExpr(expr))) {
auto const subsub = compat::getSubExprAsWritten(sub);
if (subsub->getType().IgnoreParens() == expr->getType().IgnoreParens()
&& isBool(subsub))
{
report(
DiagnosticsEngine::Warning,
("explicit conversion (%0) from %1 to %2 implicitly converted"
" back to %3"),
compat::getBeginLoc(expr))
<< sub->getCastKindName() << subsub->getType() << sub->getType()
<< expr->getType() << expr->getSourceRange();
return true;
}
}
return true;
}
bool ImplicitBoolConversion::isExternCFunctionCall(
CallExpr const * expr, FunctionProtoType const ** functionType)
{
assert(functionType != nullptr);
*functionType = nullptr;
Decl const * d = expr->getCalleeDecl();
if (d != nullptr) {
FunctionDecl const * fd = dyn_cast<FunctionDecl>(d);
if (fd != nullptr) {
clang::PointerType const * pt = fd->getType()
->getAs<clang::PointerType>();
QualType t2(pt == nullptr ? fd->getType() : pt->getPointeeType());
*functionType = t2->getAs<FunctionProtoType>();
assert(
*functionType != nullptr || !compiler.getLangOpts().CPlusPlus
|| (fd->getBuiltinID() != Builtin::NotBuiltin
&& isa<FunctionNoProtoType>(t2)));
// __builtin_*s have no proto type?
return fd->isExternC()
|| compiler.getSourceManager().isInExternCSystemHeader(
fd->getLocation());
}
VarDecl const * vd = dyn_cast<VarDecl>(d);
if (vd != nullptr) {
clang::PointerType const * pt = vd->getType()
->getAs<clang::PointerType>();
*functionType
= ((pt == nullptr ? vd->getType() : pt->getPointeeType())
->getAs<FunctionProtoType>());
return vd->isExternC();
}
}
return false;
}
bool ImplicitBoolConversion::isExternCFunctionCallReturningInt(
Expr const * expr)
{
CallExpr const * e = dyn_cast<CallExpr>(expr->IgnoreParenImpCasts());
FunctionProtoType const * t;
return e != nullptr && e->getType()->isSpecificBuiltinType(BuiltinType::Int)
&& isExternCFunctionCall(e, &t);
}
void ImplicitBoolConversion::checkCXXConstructExpr(
CXXConstructExpr const * expr)
{
assert(!nested.empty());
for (auto i: nested.top()) {
auto j = std::find_if(
expr->arg_begin(), expr->arg_end(),
[&i](Expr const * e) {
return i == ignoreParenAndTemporaryMaterialization(e);
});
if (j != expr->arg_end()) {
TemplateSpecializationType const * t1 = expr->getType()->
getAs<TemplateSpecializationType>();
if (t1 == nullptr) {
//TODO:
if (i->getType()->isSpecificBuiltinType(clang::BuiltinType::UChar)) {
continue;
}
} else {
SubstTemplateTypeParmType const * t2 = nullptr;
CXXConstructorDecl const * d = expr->getConstructor();
if (d->getNumParams() == expr->getNumArgs()) { //TODO: better check
t2 = getAsSubstTemplateTypeParmType(
d->getParamDecl(j - expr->arg_begin())->getType()
.getNonReferenceType());
}
if (t2 != nullptr) {
TemplateDecl const * td
= t1->getTemplateName().getAsTemplateDecl();
if (td != nullptr) {
TemplateParameterList const * ps
= td->getTemplateParameters();
auto k = std::find(
ps->begin(), ps->end(),
t2->getReplacedParameter()->getDecl());
if (k != ps->end()) {
if (ps->size() == t1->getNumArgs()) { //TODO
TemplateArgument const & arg = t1->getArg(
k - ps->begin());
if (arg.getKind() == TemplateArgument::Type
&& (loplugin::TypeCheck(arg.getAsType())
.AnyBoolean()))
{
continue;
}
}
}
}
}
}
}
reportWarning(i);
}
}
void ImplicitBoolConversion::reportWarning(ImplicitCastExpr const * expr) {
if (compiler.getLangOpts().CPlusPlus) {
if (expr->getCastKind() == CK_ConstructorConversion) {
auto const t1 = expr->getType();
if (auto const t2 = t1->getAs<TemplateSpecializationType>()) {
assert(t2->getNumArgs() >= 1);
auto const a = t2->getArg(0);
if (a.getKind() == TemplateArgument::Type && a.getAsType()->isBooleanType()
&& (loplugin::TypeCheck(t1).TemplateSpecializationClass()
.ClassOrStruct("atomic").StdNamespace()))
{
return;
}
}
}
report(
DiagnosticsEngine::Warning,
"implicit conversion (%0) from %1 to %2", compat::getBeginLoc(expr))
<< expr->getCastKindName() << expr->getSubExprAsWritten()->getType()
<< expr->getType() << expr->getSourceRange();
}
}
loplugin::Plugin::Registration<ImplicitBoolConversion> X(
"implicitboolconversion");
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */