2014-01-17 17:12:36 +01:00
|
|
|
/* -*- 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>
|
|
|
|
|
2019-11-18 13:32:06 +01:00
|
|
|
#include "clang/Basic/Builtins.h"
|
|
|
|
|
2020-07-19 14:53:29 +02:00
|
|
|
#include "config_clang.h"
|
|
|
|
|
2016-06-28 18:54:31 +02:00
|
|
|
#include "check.hxx"
|
2014-01-31 09:37:27 +01:00
|
|
|
#include "compat.hxx"
|
2014-01-17 17:12:36 +01:00
|
|
|
#include "plugin.hxx"
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2015-04-17 15:07:50 +02:00
|
|
|
Expr const * ignoreParenAndTemporaryMaterialization(Expr const * expr) {
|
|
|
|
for (;;) {
|
|
|
|
expr = expr->IgnoreParens();
|
|
|
|
auto e = dyn_cast<MaterializeTemporaryExpr>(expr);
|
|
|
|
if (e == nullptr) {
|
|
|
|
return expr;
|
|
|
|
}
|
2019-11-25 13:04:02 +01:00
|
|
|
expr = compat::getSubExpr(e);
|
2015-04-17 15:07:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-14 17:32:38 +01:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2015-05-08 09:48:38 +02:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2015-04-17 15:07:50 +02:00
|
|
|
bool isBool(Expr const * expr, bool allowTypedefs = true) {
|
2016-06-19 21:29:43 +02:00
|
|
|
auto t = expr->getType();
|
|
|
|
return allowTypedefs
|
|
|
|
? bool(loplugin::TypeCheck(t).AnyBoolean()) : t->isBooleanType();
|
2014-01-17 17:12:36 +01:00
|
|
|
}
|
|
|
|
|
2015-05-08 09:48:38 +02:00
|
|
|
bool isMatchingBool(Expr const * expr, Expr const * comparisonExpr) {
|
|
|
|
return isBool(expr, false)
|
|
|
|
|| areSameTypedef(expr->getType(), comparisonExpr->getType());
|
|
|
|
}
|
|
|
|
|
2016-04-20 16:45:17 +02:00
|
|
|
bool isSalBool(QualType type) {
|
|
|
|
auto t = type->getAs<TypedefType>();
|
|
|
|
return t != nullptr && t->getDecl()->getName() == "sal_Bool";
|
|
|
|
}
|
|
|
|
|
2014-01-17 17:12:36 +01:00
|
|
|
bool isBoolExpr(Expr const * expr) {
|
|
|
|
if (isBool(expr)) {
|
|
|
|
return true;
|
|
|
|
}
|
2015-04-17 15:07:50 +02:00
|
|
|
expr = ignoreParenImpCastAndComma(expr);
|
2014-01-17 17:12:36 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2015-04-17 15:07:50 +02:00
|
|
|
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;
|
|
|
|
}
|
2017-12-14 17:32:38 +01:00
|
|
|
ty = reconstructTemplateArgumentType(td, t, t2);
|
|
|
|
if (ty.isNull()) {
|
|
|
|
auto const canon = cast<TemplateDecl>(td->getCanonicalDecl());
|
|
|
|
if (canon != td) {
|
|
|
|
ty = reconstructTemplateArgumentType(canon, t, t2);
|
|
|
|
}
|
2015-04-17 15:07:50 +02:00
|
|
|
}
|
2017-12-14 17:32:38 +01:00
|
|
|
if (ty.isNull()) {
|
2015-04-17 15:07:50 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
CXXOperatorCallExpr const * op
|
|
|
|
= dyn_cast<CXXOperatorCallExpr>(stack.top());
|
|
|
|
assert(op != nullptr);
|
2019-10-06 14:22:59 +02:00
|
|
|
(void)op;
|
2015-04-17 15:07:50 +02:00
|
|
|
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()) {
|
2016-06-19 21:29:43 +02:00
|
|
|
if (loplugin::TypeCheck(ty).AnyBoolean()) {
|
2015-04-17 15:07:50 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
t = ty->getAs<TemplateSpecializationType>();
|
|
|
|
}
|
|
|
|
}
|
2014-01-17 17:12:36 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-01-20 14:31:54 +01:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2014-01-17 17:12:36 +01:00
|
|
|
class ImplicitBoolConversion:
|
2018-08-13 17:24:26 +02:00
|
|
|
public loplugin::FilteringPlugin<ImplicitBoolConversion>
|
2014-01-17 17:12:36 +01:00
|
|
|
{
|
|
|
|
public:
|
2017-11-07 11:50:47 +01:00
|
|
|
explicit ImplicitBoolConversion(loplugin::InstantiationData const & data):
|
2018-08-13 17:24:26 +02:00
|
|
|
FilteringPlugin(data) {}
|
2014-01-17 17:12:36 +01:00
|
|
|
|
|
|
|
virtual void run() override
|
|
|
|
{ TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
|
|
|
|
|
|
|
|
bool TraverseCallExpr(CallExpr * expr);
|
|
|
|
|
2015-04-17 15:07:50 +02:00
|
|
|
bool TraverseCXXMemberCallExpr(CXXMemberCallExpr * expr);
|
|
|
|
|
|
|
|
bool TraverseCXXConstructExpr(CXXConstructExpr * expr);
|
|
|
|
|
|
|
|
bool TraverseCXXTemporaryObjectExpr(CXXTemporaryObjectExpr * expr);
|
|
|
|
|
2014-01-17 17:12:36 +01:00
|
|
|
bool TraverseCStyleCastExpr(CStyleCastExpr * expr);
|
|
|
|
|
|
|
|
bool TraverseCXXStaticCastExpr(CXXStaticCastExpr * expr);
|
|
|
|
|
|
|
|
bool TraverseCXXFunctionalCastExpr(CXXFunctionalCastExpr * expr);
|
|
|
|
|
|
|
|
bool TraverseConditionalOperator(ConditionalOperator * expr);
|
|
|
|
|
2020-07-19 14:53:29 +02:00
|
|
|
bool TraverseBinaryOperator(BinaryOperator * expr);
|
|
|
|
|
2020-10-15 08:43:36 +02:00
|
|
|
#if CLANG_VERSION < 110000
|
2020-07-19 14:53:29 +02:00
|
|
|
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);
|
|
|
|
|
2020-10-15 08:43:36 +02:00
|
|
|
#if CLANG_VERSION < 110000
|
2020-07-19 14:53:29 +02:00
|
|
|
bool TraverseBinAndAssign(CompoundAssignOperator * expr)
|
|
|
|
{ return TraverseCompoundAssignOperator(expr); }
|
|
|
|
bool TraverseBinOrAssign(CompoundAssignOperator * expr)
|
|
|
|
{ return TraverseCompoundAssignOperator(expr); }
|
|
|
|
bool TraverseBinXorAssign(CompoundAssignOperator * expr)
|
|
|
|
{ return TraverseCompoundAssignOperator(expr); }
|
|
|
|
#endif
|
2014-01-17 17:12:36 +01:00
|
|
|
|
2016-04-21 17:27:43 +02:00
|
|
|
bool TraverseCXXStdInitializerListExpr(CXXStdInitializerListExpr * expr);
|
|
|
|
|
2014-01-17 17:12:36 +01:00
|
|
|
bool TraverseReturnStmt(ReturnStmt * stmt);
|
|
|
|
|
|
|
|
bool TraverseFunctionDecl(FunctionDecl * decl);
|
|
|
|
|
|
|
|
bool VisitImplicitCastExpr(ImplicitCastExpr const * expr);
|
|
|
|
|
2017-10-26 21:04:40 +02:00
|
|
|
bool VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr const * expr);
|
|
|
|
|
2014-01-17 17:12:36 +01:00
|
|
|
private:
|
2015-04-23 17:18:32 +02:00
|
|
|
bool isExternCFunctionCall(
|
|
|
|
CallExpr const * expr, FunctionProtoType const ** functionType);
|
|
|
|
|
|
|
|
bool isExternCFunctionCallReturningInt(Expr const * expr);
|
|
|
|
|
2015-04-17 15:07:50 +02:00
|
|
|
void checkCXXConstructExpr(CXXConstructExpr const * expr);
|
|
|
|
|
2014-01-17 17:12:36 +01:00
|
|
|
void reportWarning(ImplicitCastExpr const * expr);
|
|
|
|
|
|
|
|
std::stack<std::vector<ImplicitCastExpr const *>> nested;
|
2014-02-24 17:25:05 +01:00
|
|
|
std::stack<CallExpr const *> calls;
|
2015-09-25 11:41:53 +01:00
|
|
|
bool bExternCIntFunctionDefinition = false;
|
2014-01-17 17:12:36 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
bool ImplicitBoolConversion::TraverseCallExpr(CallExpr * expr) {
|
|
|
|
nested.push(std::vector<ImplicitCastExpr const *>());
|
2014-02-24 17:25:05 +01:00
|
|
|
calls.push(expr);
|
2015-09-25 11:41:53 +01:00
|
|
|
bool bRet = RecursiveASTVisitor::TraverseCallExpr(expr);
|
2015-04-23 17:18:32 +02:00
|
|
|
FunctionProtoType const * t;
|
2015-09-25 11:41:53 +01:00
|
|
|
bool bExt = isExternCFunctionCall(expr, &t);
|
2014-01-17 17:12:36 +01:00
|
|
|
assert(!nested.empty());
|
|
|
|
for (auto i: nested.top()) {
|
2015-04-17 15:07:50 +02:00
|
|
|
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);
|
2015-05-08 09:48:38 +02:00
|
|
|
if (t != nullptr
|
2017-12-15 14:20:38 +01:00
|
|
|
&& static_cast<std::size_t>(n) >= t->getNumParams())
|
2015-05-08 09:48:38 +02:00
|
|
|
{
|
|
|
|
assert(t->isVariadic());
|
|
|
|
// ignore bool to int promotions of variadic arguments
|
2015-09-25 11:41:53 +01:00
|
|
|
} else if (bExt) {
|
2015-04-17 15:07:50 +02:00
|
|
|
if (t != nullptr) {
|
|
|
|
assert(
|
2017-12-15 14:20:38 +01:00
|
|
|
static_cast<std::size_t>(n) < t->getNumParams());
|
|
|
|
if (!(t->getParamType(n)->isSpecificBuiltinType(
|
2015-05-08 09:48:38 +02:00
|
|
|
BuiltinType::Int)
|
2017-12-15 14:20:38 +01:00
|
|
|
|| t->getParamType(n)->isSpecificBuiltinType(
|
2015-05-08 09:48:38 +02:00
|
|
|
BuiltinType::UInt)
|
2017-12-15 14:20:38 +01:00
|
|
|
|| t->getParamType(n)->isSpecificBuiltinType(
|
2015-05-08 09:48:38 +02:00
|
|
|
BuiltinType::Long)))
|
2015-04-17 15:07:50 +02:00
|
|
|
{
|
|
|
|
reportWarning(i);
|
|
|
|
}
|
|
|
|
} else {
|
2014-01-17 17:12:36 +01:00
|
|
|
reportWarning(i);
|
|
|
|
}
|
2015-04-17 15:07:50 +02:00
|
|
|
} 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:
|
2016-01-05 09:51:29 +01:00
|
|
|
if (dr->getNumTemplateArgs() == 1) {
|
|
|
|
auto const ta = dr->getTemplateArgs();
|
|
|
|
if ((ta[0].getArgument().getKind()
|
|
|
|
== TemplateArgument::Type)
|
2016-06-19 21:29:43 +02:00
|
|
|
&& (loplugin::TypeCheck(
|
|
|
|
ta[0].getTypeSourceInfo()
|
|
|
|
->getType())
|
|
|
|
.AnyBoolean()))
|
2016-01-05 09:51:29 +01:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2015-04-17 15:07:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
reportWarning(i);
|
2014-01-17 17:12:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-02-24 17:25:05 +01:00
|
|
|
calls.pop();
|
2014-01-17 17:12:36 +01:00
|
|
|
nested.pop();
|
2015-09-25 11:41:53 +01:00
|
|
|
return bRet;
|
2014-01-17 17:12:36 +01:00
|
|
|
}
|
|
|
|
|
2015-04-17 15:07:50 +02:00
|
|
|
bool ImplicitBoolConversion::TraverseCXXMemberCallExpr(CXXMemberCallExpr * expr)
|
|
|
|
{
|
|
|
|
nested.push(std::vector<ImplicitCastExpr const *>());
|
2015-09-25 11:41:53 +01:00
|
|
|
bool bRet = RecursiveASTVisitor::TraverseCXXMemberCallExpr(expr);
|
2015-04-17 15:07:50 +02:00
|
|
|
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);
|
2015-05-08 09:48:38 +02:00
|
|
|
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;
|
|
|
|
}
|
2015-04-17 15:07:50 +02:00
|
|
|
QualType ty
|
|
|
|
= ignoreParenImpCastAndComma(expr->getImplicitObjectArgument())
|
|
|
|
->getType();
|
|
|
|
if (dyn_cast<MemberExpr>(expr->getCallee())->isArrow()) {
|
2017-09-11 10:48:12 +02:00
|
|
|
ty = ty->getAs<clang::PointerType>()->getPointeeType();
|
2015-04-17 15:07:50 +02:00
|
|
|
}
|
|
|
|
TemplateSpecializationType const * ct
|
|
|
|
= ty->getAs<TemplateSpecializationType>();
|
2015-05-08 09:48:38 +02:00
|
|
|
if (ct != nullptr) {
|
2015-04-17 15:07:50 +02:00
|
|
|
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
|
2016-06-19 21:29:43 +02:00
|
|
|
&& (loplugin::TypeCheck(ct->getArg(0).getAsType())
|
|
|
|
.AnyBoolean()))
|
2015-04-17 15:07:50 +02:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
reportWarning(i);
|
|
|
|
}
|
|
|
|
nested.pop();
|
2015-09-25 11:41:53 +01:00
|
|
|
return bRet;
|
2015-04-17 15:07:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ImplicitBoolConversion::TraverseCXXConstructExpr(CXXConstructExpr * expr) {
|
|
|
|
nested.push(std::vector<ImplicitCastExpr const *>());
|
2015-09-25 11:41:53 +01:00
|
|
|
bool bRet = RecursiveASTVisitor::TraverseCXXConstructExpr(expr);
|
2015-04-17 15:07:50 +02:00
|
|
|
checkCXXConstructExpr(expr);
|
|
|
|
nested.pop();
|
2015-09-25 11:41:53 +01:00
|
|
|
return bRet;
|
2015-04-17 15:07:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ImplicitBoolConversion::TraverseCXXTemporaryObjectExpr(
|
|
|
|
CXXTemporaryObjectExpr * expr)
|
|
|
|
{
|
|
|
|
nested.push(std::vector<ImplicitCastExpr const *>());
|
2015-09-25 11:41:53 +01:00
|
|
|
bool bRet = RecursiveASTVisitor::TraverseCXXTemporaryObjectExpr(expr);
|
2015-04-17 15:07:50 +02:00
|
|
|
checkCXXConstructExpr(expr);
|
|
|
|
nested.pop();
|
2015-09-25 11:41:53 +01:00
|
|
|
return bRet;
|
2015-04-17 15:07:50 +02:00
|
|
|
}
|
|
|
|
|
2014-01-17 17:12:36 +01:00
|
|
|
bool ImplicitBoolConversion::TraverseCStyleCastExpr(CStyleCastExpr * expr) {
|
|
|
|
nested.push(std::vector<ImplicitCastExpr const *>());
|
2015-09-25 11:41:53 +01:00
|
|
|
bool bRet = RecursiveASTVisitor::TraverseCStyleCastExpr(expr);
|
2014-01-17 17:12:36 +01:00
|
|
|
assert(!nested.empty());
|
|
|
|
for (auto i: nested.top()) {
|
|
|
|
if (i != expr->getSubExpr()->IgnoreParens()) {
|
|
|
|
reportWarning(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
nested.pop();
|
2015-09-25 11:41:53 +01:00
|
|
|
return bRet;
|
2014-01-17 17:12:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ImplicitBoolConversion::TraverseCXXStaticCastExpr(CXXStaticCastExpr * expr)
|
|
|
|
{
|
|
|
|
nested.push(std::vector<ImplicitCastExpr const *>());
|
2015-09-25 11:41:53 +01:00
|
|
|
bool bRet = RecursiveASTVisitor::TraverseCXXStaticCastExpr(expr);
|
2014-01-17 17:12:36 +01:00
|
|
|
assert(!nested.empty());
|
|
|
|
for (auto i: nested.top()) {
|
|
|
|
if (i != expr->getSubExpr()->IgnoreParens()) {
|
|
|
|
reportWarning(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
nested.pop();
|
2015-09-25 11:41:53 +01:00
|
|
|
return bRet;
|
2014-01-17 17:12:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ImplicitBoolConversion::TraverseCXXFunctionalCastExpr(
|
|
|
|
CXXFunctionalCastExpr * expr)
|
|
|
|
{
|
|
|
|
nested.push(std::vector<ImplicitCastExpr const *>());
|
2015-09-25 11:41:53 +01:00
|
|
|
bool bRet = RecursiveASTVisitor::TraverseCXXFunctionalCastExpr(expr);
|
2014-01-17 17:12:36 +01:00
|
|
|
assert(!nested.empty());
|
|
|
|
for (auto i: nested.top()) {
|
|
|
|
if (i != expr->getSubExpr()->IgnoreParens()) {
|
|
|
|
reportWarning(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
nested.pop();
|
2015-09-25 11:41:53 +01:00
|
|
|
return bRet;
|
2014-01-17 17:12:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ImplicitBoolConversion::TraverseConditionalOperator(
|
|
|
|
ConditionalOperator * expr)
|
|
|
|
{
|
|
|
|
nested.push(std::vector<ImplicitCastExpr const *>());
|
2015-09-25 11:41:53 +01:00
|
|
|
bool bRet = RecursiveASTVisitor::TraverseConditionalOperator(expr);
|
2014-01-17 17:12:36 +01:00
|
|
|
assert(!nested.empty());
|
|
|
|
for (auto i: nested.top()) {
|
|
|
|
if (!((i == expr->getTrueExpr()->IgnoreParens()
|
2015-04-23 17:18:32 +02:00
|
|
|
&& (isBoolExpr(expr->getFalseExpr()->IgnoreParenImpCasts())
|
|
|
|
|| isExternCFunctionCallReturningInt(expr->getFalseExpr())))
|
2014-01-17 17:12:36 +01:00
|
|
|
|| (i == expr->getFalseExpr()->IgnoreParens()
|
2015-04-23 17:18:32 +02:00
|
|
|
&& (isBoolExpr(expr->getTrueExpr()->IgnoreParenImpCasts())
|
|
|
|
|| isExternCFunctionCallReturningInt(
|
2015-05-08 09:48:38 +02:00
|
|
|
expr->getTrueExpr())))
|
|
|
|
|| (!compiler.getLangOpts().CPlusPlus
|
|
|
|
&& i == expr->getCond()->IgnoreParens())))
|
2014-01-17 17:12:36 +01:00
|
|
|
{
|
|
|
|
reportWarning(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
nested.pop();
|
2015-09-25 11:41:53 +01:00
|
|
|
return bRet;
|
2014-01-17 17:12:36 +01:00
|
|
|
}
|
|
|
|
|
2020-07-19 14:53:29 +02:00
|
|
|
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:
|
2014-01-17 17:12:36 +01:00
|
|
|
{
|
2020-07-19 14:53:29 +02:00
|
|
|
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;
|
2014-01-17 17:12:36 +01:00
|
|
|
}
|
2020-07-19 14:53:29 +02:00
|
|
|
case BO_Assign:
|
2015-04-17 15:07:50 +02:00
|
|
|
{
|
2020-07-19 14:53:29 +02:00
|
|
|
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;
|
2014-01-17 17:12:36 +01:00
|
|
|
}
|
2020-07-19 14:53:29 +02:00
|
|
|
default:
|
|
|
|
return RecursiveASTVisitor::TraverseBinaryOperator(expr);
|
2014-01-17 17:12:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-19 14:53:29 +02:00
|
|
|
bool ImplicitBoolConversion::TraverseCompoundAssignOperator(CompoundAssignOperator * expr) {
|
|
|
|
switch (expr->getOpcode()) {
|
|
|
|
case BO_AndAssign:
|
|
|
|
case BO_OrAssign:
|
|
|
|
case BO_XorAssign:
|
2014-01-17 17:12:36 +01:00
|
|
|
{
|
2020-07-19 14:53:29 +02:00
|
|
|
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;
|
2014-01-17 17:12:36 +01:00
|
|
|
}
|
2020-07-19 14:53:29 +02:00
|
|
|
default:
|
|
|
|
return RecursiveASTVisitor::TraverseCompoundAssignOperator(expr);
|
2014-01-17 17:12:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-21 17:27:43 +02:00
|
|
|
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()) {
|
2016-04-26 15:50:12 +02:00
|
|
|
if (std::find(e->begin(), e->end(), i) == e->end()) {
|
2016-04-21 17:27:43 +02:00
|
|
|
reportWarning(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
nested.pop();
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-01-17 17:12:36 +01:00
|
|
|
bool ImplicitBoolConversion::TraverseReturnStmt(ReturnStmt * stmt) {
|
|
|
|
nested.push(std::vector<ImplicitCastExpr const *>());
|
2015-09-25 11:41:53 +01:00
|
|
|
bool bRet = RecursiveASTVisitor::TraverseReturnStmt(stmt);
|
2014-01-17 17:12:36 +01:00
|
|
|
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()) {
|
2015-09-25 11:41:53 +01:00
|
|
|
if (i != expr || !bExternCIntFunctionDefinition) {
|
2014-01-17 17:12:36 +01:00
|
|
|
reportWarning(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
nested.pop();
|
2015-09-25 11:41:53 +01:00
|
|
|
return bRet;
|
2014-01-17 17:12:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ImplicitBoolConversion::TraverseFunctionDecl(FunctionDecl * decl) {
|
2015-09-25 11:41:53 +01:00
|
|
|
bool bExt = false;
|
2014-02-13 10:49:01 +01:00
|
|
|
if (hasCLanguageLinkageType(decl) && decl->isThisDeclarationADefinition()) {
|
2017-12-15 14:20:38 +01:00
|
|
|
QualType t { decl->getReturnType() };
|
2014-02-13 10:49:01 +01:00
|
|
|
if (t->isSpecificBuiltinType(BuiltinType::Int)
|
|
|
|
|| t->isSpecificBuiltinType(BuiltinType::UInt))
|
|
|
|
{
|
2015-09-25 11:41:53 +01:00
|
|
|
bExt = true;
|
2014-02-13 10:49:01 +01:00
|
|
|
} 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")
|
|
|
|
{
|
2015-09-25 11:41:53 +01:00
|
|
|
bExt = true;
|
2014-02-13 10:49:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-09-25 11:41:53 +01:00
|
|
|
if (bExt) {
|
|
|
|
assert(!bExternCIntFunctionDefinition);
|
|
|
|
bExternCIntFunctionDefinition = true;
|
2014-01-17 17:12:36 +01:00
|
|
|
}
|
2015-09-25 11:41:53 +01:00
|
|
|
bool bRet = RecursiveASTVisitor::TraverseFunctionDecl(decl);
|
|
|
|
if (bExt) {
|
|
|
|
bExternCIntFunctionDefinition = false;
|
2014-01-17 17:12:36 +01:00
|
|
|
}
|
2015-09-25 11:41:53 +01:00
|
|
|
return bRet;
|
2014-01-17 17:12:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ImplicitBoolConversion::VisitImplicitCastExpr(
|
|
|
|
ImplicitCastExpr const * expr)
|
|
|
|
{
|
|
|
|
if (ignoreLocation(expr)) {
|
|
|
|
return true;
|
|
|
|
}
|
Turn OUStringLiteral into a consteval'ed, static-refcound rtl_uString
...from which an OUString can cheaply be instantiated. This is the OUString
equivalent of 4b9e440c51be3e40326bc90c33ae69885bfb51e4 "Turn OStringLiteral into
a consteval'ed, static-refcound rtl_String". Most remarks about that commit
apply here too (this commit is just substantially bigger and a bit more
complicated because there were so much more uses of OUStringLiteral than of
OStringLiteral):
The one downside is that OUStringLiteral now needs to be a template abstracting
over the string length. But any uses for which that is a problem (e.g., as the
element type of a container that would no longer be homogeneous, or in the
signature of a function that shall not be turned into a template for one reason
or another) can be replaced with std::u16string_view, without loss of efficiency
compared to the original OUStringLiteral, and without loss of expressivity.
The new OUStringLiteral ctor code would probably not be very efficient if it
were ever executed at runtime, but it is intended to be only executed at compile
time. Where available, C++20 "consteval" is used to statically ensure that.
The intended use of the new OUStringLiteral is in all cases where an
object that shall itself not be an OUString (e.g., because it shall be a
global static variable for which the OUString ctor/dtor would be detrimental at
library load/unload) must be converted to an OUString instance in at least one
place. Other string literal abstractions could use std::u16string_view (or just
plain char16_t const[N]), but interestingly OUStringLiteral might be more
efficient than constexpr std::u16string_view even for such cases, as it should
not need any relocations at library load time. For now, no existing uses of
OUStringLiteral have been changed to some other abstraction (unless technically
necessary as discussed above), and no additional places that would benefit from
OUStringLiteral have been changed to use it.
Global constexpr OUStringLiteral variables defined in an included file would be
somewhat suboptimal, as each translation unit that uses them would create its
own, unshared instance. The envisioned solution is to turn them into static
data members of some class (and there may be a loplugin coming to find and fix
affected places). Another approach that has been taken here in a few cases
where such variables were only used in one .cxx anyway is to move their
definitions from the .hxx into that one .cxx (in turn causing some files to
become empty and get removed completely)---which also silenced some GCC
-Werror=unused-variable if a variable from a .hxx was not used in some .cxx
including it.
To keep individual commits reasonably manageable, some consumers of
OUStringLiteral in rtl/ustrbuf.hxx and rtl/ustring.hxx are left in a somewhat
odd state for now, where they don't take advantage of OUStringLiteral's
equivalence to rtl_uString, but just keep extracting its contents and copy it
elsewhere. In follow-up commits, those consumers should be changed
appropriately, making them treat OUStringLiteral like an rtl_uString or
dropping the OUStringLiteral overload in favor of an existing (and cheap to use
now) OUString overload, etc.
In a similar vein, comparison operators between OUString and std::u16string_view
have been added to the existing plethora of comparison operator overloads. It
would be nice to eventually consolidate them, esp. with the overloads taking
OUStringLiteral and/or char16_t const[N] string literals, but that appears
tricky to get right without introducing new ambiguities. Also, a handful of
places across the code base use comparisons between OUString and OUStringNumber,
which are now ambiguous (converting the OUStringNumber to either OUString or
std::u16string_view). For simplicity, those few places have manually been fixed
for now by adding explicit conversion to std::u16string_view.
Also some compilerplugins code needed to be adapted, and some of the
compilerplugins/test cases have become irrelevant (and have been removed), as
the tested code would no longer compile in the first place.
sal/qa/rtl/strings/test_oustring_concat.cxx documents a workaround for GCC bug
<https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96878> "Failed class template
argument deduction in unevaluated, parenthesized context". That place, as well
as uses of OUStringLiteral in extensions/source/abpilot/fieldmappingimpl.cxx and
i18npool/source/localedata/localedata.cxx, which have been replaced with
OUString::Concat (and which is arguably a better choice, anyway), also caused
failures with at least Clang 5.0.2 (but would not have caused failures with at
least recent Clang 12 trunk, so appear to be bugs in Clang that have meanwhile
been fixed).
Change-Id: I34174462a28f2000cfeb2d219ffd533a767920b8
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/102222
Tested-by: Jenkins
Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2020-09-08 09:48:17 +02:00
|
|
|
if (isBool(compat::getSubExprAsWritten(expr)) && !isBool(expr)) {
|
2015-05-08 09:48:38 +02:00
|
|
|
// 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);
|
|
|
|
}
|
2014-01-17 17:12:36 +01:00
|
|
|
}
|
2014-02-24 17:25:05 +01:00
|
|
|
return true;
|
|
|
|
}
|
2017-10-26 21:04:03 +02:00
|
|
|
if (auto const sub = dyn_cast<ExplicitCastExpr>(
|
|
|
|
compat::getSubExprAsWritten(expr)))
|
2014-02-24 17:25:05 +01:00
|
|
|
{
|
2017-10-26 21:04:03 +02:00
|
|
|
auto const subsub = compat::getSubExprAsWritten(sub);
|
|
|
|
if (subsub->getType().IgnoreParens() == expr->getType().IgnoreParens()
|
|
|
|
&& isBool(subsub))
|
2016-04-20 16:45:17 +02:00
|
|
|
{
|
2017-10-26 21:04:03 +02:00
|
|
|
// 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"),
|
2018-08-10 12:35:21 +02:00
|
|
|
compat::getBeginLoc(expr))
|
2017-10-26 21:04:03 +02:00
|
|
|
<< sub->getCastKindName() << subsub->getType() << sub->getType()
|
|
|
|
<< expr->getType() << expr->getSourceRange();
|
2016-04-20 16:45:17 +02:00
|
|
|
return true;
|
|
|
|
}
|
2014-02-24 17:25:05 +01:00
|
|
|
}
|
2015-04-17 15:07:50 +02:00
|
|
|
if (expr->getType()->isBooleanType() && !isBoolExpr(expr->getSubExpr())
|
2014-02-24 17:25:05 +01:00
|
|
|
&& !calls.empty())
|
|
|
|
{
|
|
|
|
CallExpr const * call = calls.top();
|
2018-09-08 09:50:18 +03:00
|
|
|
if (std::any_of(
|
2014-02-24 17:25:05 +01:00
|
|
|
call->arg_begin(), call->arg_end(),
|
2018-09-08 09:50:18 +03:00
|
|
|
[expr](Expr const * e) { return expr == e->IgnoreParens(); }))
|
2014-01-29 15:42:04 +01:00
|
|
|
{
|
|
|
|
report(
|
|
|
|
DiagnosticsEngine::Warning,
|
2014-02-24 17:25:05 +01:00
|
|
|
"implicit conversion (%0) of call argument from %1 to %2",
|
2018-08-10 12:35:21 +02:00
|
|
|
compat::getBeginLoc(expr))
|
2014-02-24 17:25:05 +01:00
|
|
|
<< expr->getCastKindName() << expr->getSubExpr()->getType()
|
|
|
|
<< expr->getType() << expr->getSourceRange();
|
|
|
|
return true;
|
2014-01-29 15:42:04 +01:00
|
|
|
}
|
2014-01-17 17:12:36 +01:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-10-26 21:04:40 +02:00
|
|
|
bool ImplicitBoolConversion::VisitMaterializeTemporaryExpr(
|
|
|
|
MaterializeTemporaryExpr const * expr)
|
|
|
|
{
|
|
|
|
if (ignoreLocation(expr)) {
|
|
|
|
return true;
|
|
|
|
}
|
2019-11-25 13:04:02 +01:00
|
|
|
if (auto const sub = dyn_cast<ExplicitCastExpr>(compat::getSubExpr(expr))) {
|
2017-10-26 21:04:40 +02:00
|
|
|
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"),
|
2018-08-10 12:35:21 +02:00
|
|
|
compat::getBeginLoc(expr))
|
2017-10-26 21:04:40 +02:00
|
|
|
<< sub->getCastKindName() << subsub->getType() << sub->getType()
|
|
|
|
<< expr->getType() << expr->getSourceRange();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-04-23 17:18:32 +02:00
|
|
|
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);
|
2015-05-08 09:48:38 +02:00
|
|
|
if (fd != nullptr) {
|
2017-09-11 10:48:12 +02:00
|
|
|
clang::PointerType const * pt = fd->getType()
|
|
|
|
->getAs<clang::PointerType>();
|
2015-04-23 17:18:32 +02:00
|
|
|
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?
|
2015-05-08 09:48:38 +02:00
|
|
|
return fd->isExternC()
|
|
|
|
|| compiler.getSourceManager().isInExternCSystemHeader(
|
|
|
|
fd->getLocation());
|
2015-04-23 17:18:32 +02:00
|
|
|
}
|
|
|
|
VarDecl const * vd = dyn_cast<VarDecl>(d);
|
2015-05-08 09:48:38 +02:00
|
|
|
if (vd != nullptr) {
|
2017-09-11 10:48:12 +02:00
|
|
|
clang::PointerType const * pt = vd->getType()
|
|
|
|
->getAs<clang::PointerType>();
|
2015-04-23 17:18:32 +02:00
|
|
|
*functionType
|
|
|
|
= ((pt == nullptr ? vd->getType() : pt->getPointeeType())
|
2015-05-08 09:48:38 +02:00
|
|
|
->getAs<FunctionProtoType>());
|
|
|
|
return vd->isExternC();
|
2015-04-23 17:18:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-04-17 15:07:50 +02:00
|
|
|
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
|
2016-06-19 21:29:43 +02:00
|
|
|
&& (loplugin::TypeCheck(arg.getAsType())
|
|
|
|
.AnyBoolean()))
|
2015-04-17 15:07:50 +02:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
reportWarning(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-17 17:12:36 +01:00
|
|
|
void ImplicitBoolConversion::reportWarning(ImplicitCastExpr const * expr) {
|
2016-12-22 08:54:04 +01:00
|
|
|
if (compiler.getLangOpts().CPlusPlus) {
|
2019-11-25 11:17:19 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-05-11 13:28:35 +02:00
|
|
|
|
|
|
|
// ignore template magic
|
|
|
|
StringRef aFileName = getFilenameOfLocation(compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(expr)));
|
|
|
|
if (loplugin::hasPathnamePrefix(aFileName, SRCDIR "/include/o3tl/hash_combine.hxx"))
|
|
|
|
return;
|
|
|
|
|
2015-05-11 09:17:45 +02:00
|
|
|
report(
|
|
|
|
DiagnosticsEngine::Warning,
|
2018-08-10 12:35:21 +02:00
|
|
|
"implicit conversion (%0) from %1 to %2", compat::getBeginLoc(expr))
|
2015-05-11 09:17:45 +02:00
|
|
|
<< expr->getCastKindName() << expr->getSubExprAsWritten()->getType()
|
|
|
|
<< expr->getType() << expr->getSourceRange();
|
|
|
|
}
|
2014-01-17 17:12:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
loplugin::Plugin::Registration<ImplicitBoolConversion> X(
|
|
|
|
"implicitboolconversion");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|