Files
libreoffice/compilerplugins/clang/implicitboolconversion.cxx
Luboš Luňák b1c14c30ba fix various warnings in compilerplugins
These are triggered when using llvm-config --cxxflags for building,
and sometimes there's -Werror. The warnings were mostly unused
variables because of being used only in assert(), or default case
in switch that covers all enums (it's better to not handle default
to get warning if a case is not handled).

Change-Id: I0ecdd1f27390aadf033852b0d1ee0ca424ae3c37
Reviewed-on: https://gerrit.libreoffice.org/80317
Tested-by: Jenkins
Reviewed-by: Luboš Luňák <l.lunak@collabora.com>
2019-10-07 21:56:55 +02:00

1035 lines
36 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 "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 = e->GetTemporaryExpr();
}
}
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 TraverseBinLT(BinaryOperator * expr);
bool TraverseBinLE(BinaryOperator * expr);
bool TraverseBinGT(BinaryOperator * expr);
bool TraverseBinGE(BinaryOperator * expr);
bool TraverseBinEQ(BinaryOperator * expr);
bool TraverseBinNE(BinaryOperator * expr);
bool TraverseBinAssign(BinaryOperator * expr);
bool TraverseBinAndAssign(CompoundAssignOperator * expr);
bool TraverseBinOrAssign(CompoundAssignOperator * expr);
bool TraverseBinXorAssign(CompoundAssignOperator * expr);
bool TraverseCXXStdInitializerListExpr(CXXStdInitializerListExpr * 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::TraverseBinLT(BinaryOperator * expr) {
nested.push(std::vector<ImplicitCastExpr const *>());
bool bRet = RecursiveASTVisitor::TraverseBinLT(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;
}
bool ImplicitBoolConversion::TraverseBinLE(BinaryOperator * expr) {
nested.push(std::vector<ImplicitCastExpr const *>());
bool bRet = RecursiveASTVisitor::TraverseBinLE(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;
}
bool ImplicitBoolConversion::TraverseBinGT(BinaryOperator * expr) {
nested.push(std::vector<ImplicitCastExpr const *>());
bool bRet = RecursiveASTVisitor::TraverseBinGT(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;
}
bool ImplicitBoolConversion::TraverseBinGE(BinaryOperator * expr) {
nested.push(std::vector<ImplicitCastExpr const *>());
bool bRet = RecursiveASTVisitor::TraverseBinGE(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;
}
bool ImplicitBoolConversion::TraverseBinEQ(BinaryOperator * expr) {
nested.push(std::vector<ImplicitCastExpr const *>());
bool bRet = RecursiveASTVisitor::TraverseBinEQ(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;
}
bool ImplicitBoolConversion::TraverseBinNE(BinaryOperator * expr) {
nested.push(std::vector<ImplicitCastExpr const *>());
bool bRet = RecursiveASTVisitor::TraverseBinNE(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;
}
bool ImplicitBoolConversion::TraverseBinAssign(BinaryOperator * expr) {
nested.push(std::vector<ImplicitCastExpr const *>());
bool bRet = RecursiveASTVisitor::TraverseBinAssign(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;
}
bool ImplicitBoolConversion::TraverseBinAndAssign(CompoundAssignOperator * expr)
{
nested.push(std::vector<ImplicitCastExpr const *>());
bool bRet = RecursiveASTVisitor::TraverseBinAndAssign(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 &=",
compat::getBeginLoc(expr->getRHS()))
<< expr->getLHS()->getType()
<< expr->getRHS()->IgnoreParenImpCasts()->getType()
<< expr->getSourceRange();
}
return bRet;
}
bool ImplicitBoolConversion::TraverseBinOrAssign(CompoundAssignOperator * expr)
{
nested.push(std::vector<ImplicitCastExpr const *>());
bool bRet = RecursiveASTVisitor::TraverseBinOrAssign(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 |=",
compat::getBeginLoc(expr->getRHS()))
<< expr->getLHS()->getType()
<< expr->getRHS()->IgnoreParenImpCasts()->getType()
<< expr->getSourceRange();
}
return bRet;
}
bool ImplicitBoolConversion::TraverseBinXorAssign(CompoundAssignOperator * expr)
{
nested.push(std::vector<ImplicitCastExpr const *>());
bool bRet = RecursiveASTVisitor::TraverseBinXorAssign(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 ^=",
compat::getBeginLoc(expr->getRHS()))
<< expr->getLHS()->getType()
<< expr->getRHS()->IgnoreParenImpCasts()->getType()
<< expr->getSourceRange();
}
return bRet;
}
bool ImplicitBoolConversion::TraverseCXXStdInitializerListExpr(
CXXStdInitializerListExpr * expr)
{
// Must be some std::initializer_list<T>; check whether T is sal_Bool (i.e.,
// unsigned char) [TODO: check for real sal_Bool instead]:
auto t = expr->getType();
if (auto et = dyn_cast<ElaboratedType>(t)) {
t = et->desugar();
}
auto ts = t->getAs<TemplateSpecializationType>();
if (ts == nullptr
|| !ts->getArg(0).getAsType()->isSpecificBuiltinType(
clang::BuiltinType::UChar))
{
return RecursiveASTVisitor::TraverseCXXStdInitializerListExpr(expr);
}
// Avoid warnings for code like
//
// Sequence<sal_Bool> arBool({true, false, true});
//
auto e = dyn_cast<InitListExpr>(
ignoreParenAndTemporaryMaterialization(expr->getSubExpr()));
if (e == nullptr) {
return RecursiveASTVisitor::TraverseCXXStdInitializerListExpr(expr);
}
nested.push(std::vector<ImplicitCastExpr const *>());
bool ret = RecursiveASTVisitor::TraverseCXXStdInitializerListExpr(expr);
assert(!nested.empty());
for (auto i: nested.top()) {
if (std::find(e->begin(), e->end(), i) == e->end()) {
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(expr->getSubExprAsWritten()) && !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>(expr->GetTemporaryExpr())) {
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>();
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 (t1 != nullptr && t2 != nullptr) {
TemplateDecl const * td
= t1->getTemplateName().getAsTemplateDecl();
if (td != nullptr) {
TemplateParameterList const * ps
= td->getTemplateParameters();
auto i = std::find(
ps->begin(), ps->end(),
t2->getReplacedParameter()->getDecl());
if (i != ps->end()) {
if (ps->size() == t1->getNumArgs()) { //TODO
TemplateArgument const & arg = t1->getArg(
i - 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) {
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: */