d1a2b80b9d
...as discussed in the mail thread starting at <https://lists.freedesktop.org/archives/libreoffice/2020-November/086234.html> "Bump --enable-compiler-plugins Clang baseline?" (and now picked up again at <https://lists.freedesktop.org/archives/libreoffice/2022-February/088459.html> "Re: Bump --enable-compiler-plugins Clang baseline?"), and clean up compilerplugins/clang/ accordingly Change-Id: I5e81c6fdcc363aeefd6227606225b526fdf7ac16 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/129989 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
1333 lines
53 KiB
C++
1333 lines
53 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 <cassert>
|
|
|
|
#include "plugin.hxx"
|
|
#include "clang/AST/CXXInheritance.h"
|
|
|
|
namespace {
|
|
|
|
// Like clang::Stmt::IgnoreImplicit (lib/AST/Stmt.cpp), but also looking through implicit
|
|
// UserDefinedConversion's member function call:
|
|
Expr const * ignoreAllImplicit(Expr const * expr) {
|
|
if (auto const e = dyn_cast<ExprWithCleanups>(expr)) {
|
|
expr = e->getSubExpr();
|
|
}
|
|
if (auto const e = dyn_cast<MaterializeTemporaryExpr>(expr)) {
|
|
expr = e->getSubExpr();
|
|
}
|
|
if (auto const e = dyn_cast<CXXBindTemporaryExpr>(expr)) {
|
|
expr = e->getSubExpr();
|
|
}
|
|
while (auto const e = dyn_cast<ImplicitCastExpr>(expr)) {
|
|
expr = e->getSubExpr();
|
|
if (e->getCastKind() == CK_UserDefinedConversion) {
|
|
auto const ce = cast<CXXMemberCallExpr>(expr);
|
|
assert(ce->getNumArgs() == 0);
|
|
expr = ce->getImplicitObjectArgument();
|
|
}
|
|
}
|
|
return expr;
|
|
}
|
|
|
|
Expr const * ignoreParenImpCastAndComma(Expr const * expr) {
|
|
for (;;) {
|
|
expr = expr->IgnoreParenImpCasts();
|
|
auto e = dyn_cast<BinaryOperator>(expr);
|
|
if (e == nullptr || e->getOpcode() != BO_Comma) {
|
|
return expr;
|
|
}
|
|
expr = e->getRHS();
|
|
}
|
|
}
|
|
|
|
Expr const * getSubExprOfLogicalNegation(Expr const * expr) {
|
|
auto e = dyn_cast<UnaryOperator>(ignoreParenImpCastAndComma(expr));
|
|
return e == nullptr || e->getOpcode() != UO_LNot
|
|
? nullptr : e->getSubExpr();
|
|
}
|
|
|
|
clang::Type const * stripConstRef(clang::Type const * type) {
|
|
auto lvalueType = dyn_cast<LValueReferenceType>(type);
|
|
if (!lvalueType)
|
|
return type;
|
|
return lvalueType->getPointeeType()->getUnqualifiedDesugaredType();
|
|
}
|
|
|
|
bool isCompatibleTypeForOperator(clang::Type const * paramType, CXXRecordDecl const * argRecordDecl) {
|
|
paramType = stripConstRef(paramType);
|
|
auto paramRecordType = dyn_cast<RecordType>(paramType);
|
|
if (!paramRecordType)
|
|
return false;
|
|
CXXRecordDecl const * paramRecordDecl = dyn_cast<CXXRecordDecl>(paramRecordType->getDecl());
|
|
if (!paramRecordDecl)
|
|
return false;
|
|
return argRecordDecl == paramRecordDecl || argRecordDecl->isDerivedFrom(paramRecordDecl);
|
|
}
|
|
|
|
FunctionDecl const * findMemberOperator(CXXRecordDecl const * recordDecl, OverloadedOperatorKind ooOpcode, CXXRecordDecl const * rhs) {
|
|
for (auto it = recordDecl->method_begin(); it != recordDecl->method_end(); ++it) {
|
|
if (it->getOverloadedOperator() == ooOpcode) {
|
|
if (it->getNumParams() == 1 && isCompatibleTypeForOperator(it->getParamDecl(0)->getType().getTypePtr(), rhs))
|
|
return *it;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
// Magic value to indicate we assume this operator exists
|
|
static FunctionDecl const * const ASSUME_OPERATOR_EXISTS = reinterpret_cast<FunctionDecl const *>(-1);
|
|
|
|
// Search for an operator with matching parameter types; while this may miss some operators with
|
|
// odd parameter types that would actually be used by the compiler, it is overall better to have too
|
|
// many false negatives (i.e., miss valid loplugin:simplifybool warnings) than false positives here:
|
|
FunctionDecl const * findOperator(CompilerInstance& compiler, BinaryOperator::Opcode opcode, clang::Type const * lhsType, clang::Type const * rhsType) {
|
|
auto lhsRecordType = dyn_cast<RecordType>(lhsType);
|
|
if (!lhsRecordType)
|
|
return nullptr;
|
|
auto rhsRecordType = dyn_cast<RecordType>(rhsType);
|
|
if (!rhsRecordType)
|
|
return nullptr;
|
|
CXXRecordDecl const * lhsRecordDecl = dyn_cast<CXXRecordDecl>(lhsRecordType->getDecl());
|
|
if (!lhsRecordDecl)
|
|
return nullptr;
|
|
CXXRecordDecl const * rhsRecordDecl = dyn_cast<CXXRecordDecl>(rhsRecordType->getDecl());
|
|
if (!rhsRecordDecl)
|
|
return nullptr;
|
|
|
|
auto ctx = lhsRecordDecl->getCanonicalDecl()->getDeclContext();
|
|
|
|
/*
|
|
It looks the clang Sema::LookupOverloadedOperatorName is the chunk of functionality I need,
|
|
but I have no idea how to call it from here.
|
|
Actually finding the right standard library operators requires doing conversions and other funky stuff.
|
|
For now, just assume that standard library operators are well-behaved, and have negated operators.
|
|
*/
|
|
if (ctx->isStdNamespace())
|
|
return ASSUME_OPERATOR_EXISTS;
|
|
if (auto namespaceDecl = dyn_cast<NamespaceDecl>(ctx)) {
|
|
// because, of course, half the standard library is not "in the standard namespace"
|
|
if (namespaceDecl->getName() == "__gnu_debug")
|
|
return ASSUME_OPERATOR_EXISTS;
|
|
}
|
|
|
|
// search for member overloads
|
|
// (using the hard way here because DeclContext::lookup does not work for member operators)
|
|
auto ooOpcode = BinaryOperator::getOverloadedOperator(opcode);
|
|
FunctionDecl const * foundFunction = findMemberOperator(lhsRecordDecl, ooOpcode, rhsRecordDecl);
|
|
if (foundFunction)
|
|
return foundFunction;
|
|
auto ForallBasesCallback = [&](const CXXRecordDecl *baseCXXRecordDecl)
|
|
{
|
|
if (baseCXXRecordDecl->isInvalidDecl())
|
|
return false;
|
|
foundFunction = findMemberOperator(baseCXXRecordDecl, ooOpcode, rhsRecordDecl);
|
|
return false;
|
|
};
|
|
|
|
lhsRecordDecl->forallBases(ForallBasesCallback);
|
|
if (foundFunction)
|
|
return foundFunction;
|
|
|
|
// search for free function overloads
|
|
if (ctx->getDeclKind() == Decl::LinkageSpec) {
|
|
ctx = ctx->getParent();
|
|
}
|
|
auto operatorDeclName = compiler.getASTContext().DeclarationNames.getCXXOperatorName(ooOpcode);
|
|
auto res = ctx->lookup(operatorDeclName);
|
|
for (auto d = res.begin(); d != res.end(); ++d) {
|
|
FunctionDecl const * f = dyn_cast<FunctionDecl>(*d);
|
|
if (!f || f->getNumParams() != 2)
|
|
continue;
|
|
if (!isCompatibleTypeForOperator(f->getParamDecl(0)->getType().getTypePtr(), lhsRecordDecl))
|
|
continue;
|
|
if (!isCompatibleTypeForOperator(f->getParamDecl(1)->getType().getTypePtr(), rhsRecordDecl))
|
|
continue;
|
|
return f;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
enum class Value { Unknown, False, True };
|
|
|
|
Value getValue(Expr const * expr) {
|
|
expr = ignoreParenImpCastAndComma(expr);
|
|
if (expr->getType()->isBooleanType()) {
|
|
// Instead going via Expr::isCXX11ConstantExpr would turn up exactly one
|
|
// additional place in svx/source/dialog/framelinkarray.cxx
|
|
//
|
|
// const bool DIAG_DBL_CLIP_DEFAULT = false;
|
|
// ...
|
|
// ... = mxImpl.get() ? mxImpl->mbDiagDblClip : DIAG_DBL_CLIP_DEFAULT;
|
|
//
|
|
// where it is unclear whether it is not actually better to consider
|
|
// DIAG_DBL_CLIP_DEFAULT a tunable parameter (and thus not to simplify):
|
|
auto lit = dyn_cast<CXXBoolLiteralExpr>(expr);
|
|
if (lit != nullptr) {
|
|
return lit->getValue() ? Value::True : Value::False;
|
|
}
|
|
}
|
|
return Value::Unknown;
|
|
}
|
|
|
|
class SimplifyBool:
|
|
public loplugin::FilteringPlugin<SimplifyBool>
|
|
{
|
|
public:
|
|
explicit SimplifyBool(loplugin::InstantiationData const & data):
|
|
FilteringPlugin(data) {}
|
|
|
|
void run() override;
|
|
|
|
bool VisitUnaryOperator(UnaryOperator const * expr);
|
|
|
|
bool VisitBinaryOperator(BinaryOperator const * expr);
|
|
|
|
bool VisitConditionalOperator(ConditionalOperator const * expr);
|
|
|
|
bool TraverseFunctionDecl(FunctionDecl *);
|
|
|
|
bool TraverseCXXMethodDecl(CXXMethodDecl *);
|
|
|
|
private:
|
|
bool visitBinLT(BinaryOperator const * expr);
|
|
|
|
bool visitBinGT(BinaryOperator const * expr);
|
|
|
|
bool visitBinLE(BinaryOperator const * expr);
|
|
|
|
bool visitBinGE(BinaryOperator const * expr);
|
|
|
|
bool visitBinEQ(BinaryOperator const * expr);
|
|
|
|
bool visitBinNE(BinaryOperator const * expr);
|
|
|
|
FunctionDecl* m_insideFunctionDecl = nullptr;
|
|
};
|
|
|
|
void SimplifyBool::run() {
|
|
if (compiler.getLangOpts().CPlusPlus) {
|
|
TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
|
|
}
|
|
}
|
|
|
|
bool SimplifyBool::VisitUnaryOperator(UnaryOperator const * expr) {
|
|
if (expr->getOpcode() != UO_LNot) {
|
|
return true;
|
|
}
|
|
if (ignoreLocation(expr)) {
|
|
return true;
|
|
}
|
|
auto e = getSubExprOfLogicalNegation(expr->getSubExpr());
|
|
if (e) {
|
|
// Ignore macros, otherwise
|
|
// OSL_ENSURE(!b, ...);
|
|
// triggers.
|
|
if (e->getBeginLoc().isMacroID())
|
|
return true;
|
|
// double logical not of an int is an idiom to convert to bool
|
|
auto const sub = ignoreAllImplicit(e);
|
|
if (!sub->getType()->isBooleanType())
|
|
return true;
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("double logical negation expression of the form '!!A' (with A of type"
|
|
" %0) can %select{logically|literally}1 be simplified as 'A'"),
|
|
expr->getBeginLoc())
|
|
<< sub->getType()
|
|
<< sub->getType()->isBooleanType()
|
|
<< expr->getSourceRange();
|
|
return true;
|
|
}
|
|
auto sub = expr->getSubExpr()->IgnoreParenImpCasts();
|
|
auto reversed = false;
|
|
if (auto const rewritten = dyn_cast<CXXRewrittenBinaryOperator>(sub)) {
|
|
if (rewritten->isReversed()) {
|
|
if (rewritten->getOperator() == BO_EQ) {
|
|
auto const sem = rewritten->getSemanticForm();
|
|
bool match;
|
|
if (auto const op1 = dyn_cast<BinaryOperator>(sem)) {
|
|
match = op1->getOpcode() == BO_EQ;
|
|
} else if (auto const op2 = dyn_cast<CXXOperatorCallExpr>(sem)) {
|
|
match = op2->getOperator() == OO_EqualEqual;
|
|
} else {
|
|
match = false;
|
|
}
|
|
if (match) {
|
|
sub = sem;
|
|
reversed = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (auto binaryOp = dyn_cast<BinaryOperator>(sub)) {
|
|
// Ignore macros, otherwise
|
|
// OSL_ENSURE(!b, ...);
|
|
// triggers.
|
|
if (binaryOp->getBeginLoc().isMacroID())
|
|
return true;
|
|
if (binaryOp->isComparisonOp())
|
|
{
|
|
auto t = binaryOp->getLHS()->IgnoreImpCasts()->getType()->getUnqualifiedDesugaredType();
|
|
if (t->isTemplateTypeParmType() || t->isDependentType() || t->isRecordType())
|
|
return true;
|
|
// for floating point (with NaN) !(x<y) need not be equivalent to x>=y
|
|
if (t->isFloatingType() ||
|
|
binaryOp->getRHS()->IgnoreImpCasts()->getType()->getUnqualifiedDesugaredType()->isFloatingType())
|
|
return true;
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("logical negation of comparison operator, can be simplified by inverting operator"),
|
|
expr->getBeginLoc())
|
|
<< expr->getSourceRange();
|
|
}
|
|
else if (binaryOp->isLogicalOp())
|
|
{
|
|
// if we find a negation condition inside, it is definitely better
|
|
// to expand it out
|
|
bool foundLNot = false;
|
|
auto containsNegationOrComparison = [&](Expr const * expr) {
|
|
expr = ignoreParenImpCastAndComma(expr);
|
|
if (auto unaryOp = dyn_cast<UnaryOperator>(expr))
|
|
if (unaryOp->getOpcode() == UO_LNot)
|
|
{
|
|
foundLNot = true;
|
|
return expr;
|
|
}
|
|
if (auto binaryOp = dyn_cast<BinaryOperator>(expr))
|
|
if (binaryOp->isComparisonOp())
|
|
return expr;
|
|
if (auto cxxOpCall = dyn_cast<CXXOperatorCallExpr>(expr))
|
|
if (cxxOpCall->isComparisonOp())
|
|
return expr;
|
|
return (Expr const*)nullptr;
|
|
};
|
|
auto lhs = containsNegationOrComparison(binaryOp->getLHS());
|
|
auto rhs = containsNegationOrComparison(binaryOp->getRHS());
|
|
if (foundLNot || (lhs && rhs))
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("logical negation of logical op containing negation, can be simplified"),
|
|
binaryOp->getBeginLoc())
|
|
<< binaryOp->getSourceRange();
|
|
}
|
|
}
|
|
if (auto binaryOp = dyn_cast<CXXOperatorCallExpr>(sub)) {
|
|
// Ignore macros, otherwise
|
|
// OSL_ENSURE(!b, ...);
|
|
// triggers.
|
|
if (binaryOp->getBeginLoc().isMacroID())
|
|
return true;
|
|
auto op = binaryOp->getOperator();
|
|
// Negating things like > and >= would probably not be wise, there is no guarantee the negation holds for operator overloaded types.
|
|
// However, == and != are normally considered ok.
|
|
if (!(op == OO_EqualEqual || op == OO_ExclaimEqual))
|
|
return true;
|
|
BinaryOperator::Opcode negatedOpcode = BinaryOperator::negateComparisonOp(BinaryOperator::getOverloadedOpcode(op));
|
|
auto lhs = binaryOp->getArg(reversed ? 1 : 0)->IgnoreImpCasts()->getType()->getUnqualifiedDesugaredType();
|
|
auto rhs = binaryOp->getArg(reversed ? 0 : 1)->IgnoreImpCasts()->getType()->getUnqualifiedDesugaredType();
|
|
auto const negOp = findOperator(compiler, negatedOpcode, lhs, rhs);
|
|
if (!negOp)
|
|
return true;
|
|
// if we are inside a similar operator, ignore, eg. operator!= is often defined by calling !operator==
|
|
if (m_insideFunctionDecl && m_insideFunctionDecl->getNumParams() >= 1) {
|
|
auto t = stripConstRef(m_insideFunctionDecl->getParamDecl(0)->getType().getTypePtr());
|
|
if (t == lhs)
|
|
return true;
|
|
}
|
|
// QA code
|
|
StringRef fn(handler.getMainFileName());
|
|
if (loplugin::isSamePathname(fn, SRCDIR "/testtools/source/bridgetest/bridgetest.cxx"))
|
|
return true;
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("logical negation of comparison operator, can be simplified by inverting operator"),
|
|
expr->getBeginLoc())
|
|
<< expr->getSourceRange();
|
|
if (negOp != ASSUME_OPERATOR_EXISTS)
|
|
report(
|
|
DiagnosticsEngine::Note, "the presumed corresponding negated operator for %0 and %1 is declared here",
|
|
negOp->getLocation())
|
|
<< binaryOp->getArg(reversed ? 1 : 0)->IgnoreImpCasts()->getType()
|
|
<< binaryOp->getArg(reversed ? 0 : 1)->IgnoreImpCasts()->getType()
|
|
<< negOp->getSourceRange();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SimplifyBool::VisitBinaryOperator(BinaryOperator const * expr) {
|
|
switch (expr->getOpcode()) {
|
|
case BO_LT:
|
|
return visitBinLT(expr);
|
|
case BO_GT:
|
|
return visitBinGT(expr);
|
|
case BO_LE:
|
|
return visitBinLE(expr);
|
|
case BO_GE:
|
|
return visitBinGE(expr);
|
|
case BO_EQ:
|
|
return visitBinEQ(expr);
|
|
case BO_NE:
|
|
return visitBinNE(expr);
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool SimplifyBool::visitBinLT(BinaryOperator const * expr) {
|
|
if (ignoreLocation(expr)) {
|
|
return true;
|
|
}
|
|
if (!(expr->getLHS()->IgnoreImpCasts()->getType()->isBooleanType()
|
|
&& expr->getRHS()->IgnoreImpCasts()->getType()->isBooleanType()))
|
|
{
|
|
return true;
|
|
}
|
|
auto v1 = getValue(expr->getLHS());
|
|
auto v2 = getValue(expr->getRHS());
|
|
switch (v1) {
|
|
case Value::Unknown:
|
|
switch (v2) {
|
|
case Value::Unknown:
|
|
break;
|
|
case Value::False:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("less-than expression of the form 'A < false' (with A of type"
|
|
" %0) can logically be simplified as 'false'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getLHS()->IgnoreImpCasts()->getType()
|
|
<< expr->getSourceRange();
|
|
break;
|
|
case Value::True:
|
|
{
|
|
auto e = getSubExprOfLogicalNegation(expr->getLHS());
|
|
if (e == nullptr) {
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("less-than expression of the form 'A < true' (with A"
|
|
" of type %0) can %select{logically|literally}1 be"
|
|
" simplified as '!A'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getLHS()->IgnoreImpCasts()->getType()
|
|
<< (expr->getLHS()->IgnoreImpCasts()->getType()
|
|
->isBooleanType())
|
|
<< expr->getSourceRange();
|
|
} else {
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("less-than expression of the form '!A < true' (with A"
|
|
" of type %0) can %select{logically|literally}1 be"
|
|
" simplified as 'A'"),
|
|
expr->getBeginLoc())
|
|
<< e->IgnoreImpCasts()->getType()
|
|
<< e->IgnoreImpCasts()->getType()->isBooleanType()
|
|
<< expr->getSourceRange();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case Value::False:
|
|
switch (v2) {
|
|
case Value::Unknown:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("less-than expression of the form 'false < A' (with A of type"
|
|
" %0) can %select{logically|literally}1 be simplified as 'A'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getRHS()->IgnoreImpCasts()->getType()
|
|
<< expr->getRHS()->IgnoreImpCasts()->getType()->isBooleanType()
|
|
<< expr->getSourceRange();
|
|
break;
|
|
case Value::False:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("less-than expression of the form 'false < false' can"
|
|
" literally be simplified as 'false'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getSourceRange();
|
|
break;
|
|
case Value::True:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("less-than expression of the form 'false < true' can"
|
|
" literally be simplified as 'true'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getSourceRange();
|
|
break;
|
|
}
|
|
break;
|
|
case Value::True:
|
|
switch (v2) {
|
|
case Value::Unknown:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("less-than expression of the form 'true < A' (with A of type"
|
|
" %0) can logically be simplified as 'false'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getRHS()->IgnoreImpCasts()->getType()
|
|
<< expr->getSourceRange();
|
|
break;
|
|
case Value::False:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("less-than expression of the form 'true < false' can"
|
|
" literally be simplified as 'false'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getSourceRange();
|
|
break;
|
|
case Value::True:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("less-than expression of the form 'true < true' can"
|
|
" literally be simplified as 'false'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getSourceRange();
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SimplifyBool::visitBinGT(BinaryOperator const * expr) {
|
|
if (ignoreLocation(expr)) {
|
|
return true;
|
|
}
|
|
if (!(expr->getLHS()->IgnoreImpCasts()->getType()->isBooleanType()
|
|
&& expr->getRHS()->IgnoreImpCasts()->getType()->isBooleanType()))
|
|
{
|
|
return true;
|
|
}
|
|
auto v1 = getValue(expr->getLHS());
|
|
auto v2 = getValue(expr->getRHS());
|
|
switch (v1) {
|
|
case Value::Unknown:
|
|
switch (v2) {
|
|
case Value::Unknown:
|
|
break;
|
|
case Value::False:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("greater-than expression of the form 'A > false' (with A of"
|
|
" type %0) can %select{logically|literally}1 be simplified as"
|
|
" 'A'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getLHS()->IgnoreImpCasts()->getType()
|
|
<< expr->getLHS()->IgnoreImpCasts()->getType()->isBooleanType()
|
|
<< expr->getSourceRange();
|
|
break;
|
|
case Value::True:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("greater-than expression of the form 'A > true' (with A of"
|
|
" type %0) can logically be simplified as 'false'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getLHS()->IgnoreImpCasts()->getType()
|
|
<< expr->getSourceRange();
|
|
break;
|
|
}
|
|
break;
|
|
case Value::False:
|
|
switch (v2) {
|
|
case Value::Unknown:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("greater-than expression of the form 'false > A' (with A of"
|
|
" type %0) can logically be simplified as 'false'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getRHS()->IgnoreImpCasts()->getType()
|
|
<< expr->getSourceRange();
|
|
break;
|
|
case Value::False:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("greater-than expression of the form 'false > false' can"
|
|
" literally be simplified as 'false'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getSourceRange();
|
|
break;
|
|
case Value::True:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("greater-than expression of the form 'false > true' can"
|
|
" literally be simplified as 'false'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getSourceRange();
|
|
break;
|
|
}
|
|
break;
|
|
case Value::True:
|
|
switch (v2) {
|
|
case Value::Unknown:
|
|
{
|
|
auto e = getSubExprOfLogicalNegation(expr->getRHS());
|
|
if (e == nullptr) {
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("greater-than expression of the form 'true > A' (with"
|
|
" A of type %0) can %select{logically|literally}1 be"
|
|
" simplified as '!A'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getRHS()->IgnoreImpCasts()->getType()
|
|
<< (expr->getRHS()->IgnoreImpCasts()->getType()
|
|
->isBooleanType())
|
|
<< expr->getSourceRange();
|
|
} else {
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("greater-than expression of the form 'true > !A' (with"
|
|
" A of type %0) can %select{logically|literally}1 be"
|
|
" simplified as 'A'"),
|
|
expr->getBeginLoc())
|
|
<< e->IgnoreImpCasts()->getType()
|
|
<< e->IgnoreImpCasts()->getType()->isBooleanType()
|
|
<< expr->getSourceRange();
|
|
}
|
|
break;
|
|
}
|
|
case Value::False:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("greater-than expression of the form 'true > false' can"
|
|
" literally be simplified as 'true'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getSourceRange();
|
|
break;
|
|
case Value::True:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("greater-than expression of the form 'true > true' can"
|
|
" literally be simplified as 'false'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getSourceRange();
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SimplifyBool::visitBinLE(BinaryOperator const * expr) {
|
|
if (ignoreLocation(expr)) {
|
|
return true;
|
|
}
|
|
if (!(expr->getLHS()->IgnoreImpCasts()->getType()->isBooleanType()
|
|
&& expr->getRHS()->IgnoreImpCasts()->getType()->isBooleanType()))
|
|
{
|
|
return true;
|
|
}
|
|
auto v1 = getValue(expr->getLHS());
|
|
auto v2 = getValue(expr->getRHS());
|
|
switch (v1) {
|
|
case Value::Unknown:
|
|
switch (v2) {
|
|
case Value::Unknown:
|
|
break;
|
|
case Value::False:
|
|
{
|
|
auto e = getSubExprOfLogicalNegation(expr->getLHS());
|
|
if (e == nullptr) {
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("less-than-or-equal-to expression of the form 'A <="
|
|
" false' (with A of type %0) can"
|
|
" %select{logically|literally}1 be simplified as"
|
|
" '!A'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getLHS()->IgnoreImpCasts()->getType()
|
|
<< (expr->getLHS()->IgnoreImpCasts()->getType()
|
|
->isBooleanType())
|
|
<< expr->getSourceRange();
|
|
} else {
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("less-than-or-equal-to expression of the form '!A <="
|
|
" false' (with A of type %0) can"
|
|
" %select{logically|literally}1 be simplified as 'A'"),
|
|
expr->getBeginLoc())
|
|
<< e->IgnoreImpCasts()->getType()
|
|
<< e->IgnoreImpCasts()->getType()->isBooleanType()
|
|
<< expr->getSourceRange();
|
|
}
|
|
break;
|
|
}
|
|
case Value::True:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("less-than-or-equal-to expression of the form 'A <= true'"
|
|
" (with A of type %0) can logically be simplified as 'true'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getLHS()->IgnoreImpCasts()->getType()
|
|
<< expr->getSourceRange();
|
|
break;
|
|
}
|
|
break;
|
|
case Value::False:
|
|
switch (v2) {
|
|
case Value::Unknown:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("less-than-or-equal-to expression of the form 'false <= A'"
|
|
" (with A of type %0) can logically be simplified as 'true'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getRHS()->IgnoreImpCasts()->getType()
|
|
<< expr->getSourceRange();
|
|
break;
|
|
case Value::False:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("less-than-or-equal-to expression of the form 'false <= false'"
|
|
" can literally be simplified as 'true'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getSourceRange();
|
|
break;
|
|
case Value::True:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("less-than-or-equal-to expression of the form 'false <= true'"
|
|
" can literally be simplified as 'true'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getSourceRange();
|
|
break;
|
|
}
|
|
break;
|
|
case Value::True:
|
|
switch (v2) {
|
|
case Value::Unknown:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("less-than-or-equal-to expression of the form 'true <= A'"
|
|
" (with A of type %0) can %select{logically|literally}1 be"
|
|
" simplified as 'A'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getRHS()->IgnoreImpCasts()->getType()
|
|
<< expr->getRHS()->IgnoreImpCasts()->getType()->isBooleanType()
|
|
<< expr->getSourceRange();
|
|
break;
|
|
case Value::False:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("less-than-or-equal-to expression of the form 'true <= false'"
|
|
" can literally be simplified as 'false'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getSourceRange();
|
|
break;
|
|
case Value::True:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("less-than-or-equal-to expression of the form 'true <= true'"
|
|
" can literally be simplified as 'true'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getSourceRange();
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SimplifyBool::visitBinGE(BinaryOperator const * expr) {
|
|
if (ignoreLocation(expr)) {
|
|
return true;
|
|
}
|
|
if (!(expr->getLHS()->IgnoreImpCasts()->getType()->isBooleanType()
|
|
&& expr->getRHS()->IgnoreImpCasts()->getType()->isBooleanType()))
|
|
{
|
|
return true;
|
|
}
|
|
auto v1 = getValue(expr->getLHS());
|
|
auto v2 = getValue(expr->getRHS());
|
|
switch (v1) {
|
|
case Value::Unknown:
|
|
switch (v2) {
|
|
case Value::Unknown:
|
|
break;
|
|
case Value::False:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("greater-than-or-equal-to expression of the form 'A >= false'"
|
|
" (with A of type %0) can logically be simplified as 'true'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getLHS()->IgnoreImpCasts()->getType()
|
|
<< expr->getSourceRange();
|
|
break;
|
|
case Value::True:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("greater-than-or-equal-to expression of the form 'A >= true'"
|
|
" (with A of type %0) can %select{logically|literally}1 be"
|
|
" simplified as 'A'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getLHS()->IgnoreImpCasts()->getType()
|
|
<< expr->getLHS()->IgnoreImpCasts()->getType()->isBooleanType()
|
|
<< expr->getSourceRange();
|
|
break;
|
|
}
|
|
break;
|
|
case Value::False:
|
|
switch (v2) {
|
|
case Value::Unknown:
|
|
{
|
|
auto e = getSubExprOfLogicalNegation(expr->getRHS());
|
|
if (e == nullptr) {
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("greater-than-or-equal-to expression of the form"
|
|
" 'false >= A' (with A of type %0) can"
|
|
" %select{logically|literally}1 be simplified as"
|
|
" '!A'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getRHS()->IgnoreImpCasts()->getType()
|
|
<< (expr->getRHS()->IgnoreImpCasts()->getType()
|
|
->isBooleanType())
|
|
<< expr->getSourceRange();
|
|
} else {
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("greater-than-or-equal-to expression of the form"
|
|
" 'false >= !A' (with A of type %0) can"
|
|
" %select{logically|literally}1 be simplified as 'A'"),
|
|
expr->getBeginLoc())
|
|
<< e->IgnoreImpCasts()->getType()
|
|
<< e->IgnoreImpCasts()->getType()->isBooleanType()
|
|
<< expr->getSourceRange();
|
|
}
|
|
break;
|
|
}
|
|
case Value::False:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("greater-than-or-equal-to expression of the form 'false >="
|
|
" false' can literally be simplified as 'true'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getSourceRange();
|
|
break;
|
|
case Value::True:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("greater-than-or-equal-to expression of the form 'false >="
|
|
" true' can literally be simplified as 'false'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getSourceRange();
|
|
break;
|
|
}
|
|
break;
|
|
case Value::True:
|
|
switch (v2) {
|
|
case Value::Unknown:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("greater-than-or-equal-to expression of the form 'true >= A'"
|
|
" (with A of type %0) can logically be simplified as 'true'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getRHS()->IgnoreImpCasts()->getType()
|
|
<< expr->getSourceRange();
|
|
break;
|
|
case Value::False:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("greater-than-or-equal-to expression of the form 'true >="
|
|
" false' can literally be simplified as 'true'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getSourceRange();
|
|
break;
|
|
case Value::True:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("greater-than-or-equal-to expression of the form 'true >="
|
|
" true' can literally be simplified as 'true'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getSourceRange();
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SimplifyBool::visitBinEQ(BinaryOperator const * expr) {
|
|
if (ignoreLocation(expr)) {
|
|
return true;
|
|
}
|
|
if (!(expr->getLHS()->IgnoreImpCasts()->getType()->isBooleanType()
|
|
&& expr->getRHS()->IgnoreImpCasts()->getType()->isBooleanType()))
|
|
{
|
|
return true;
|
|
}
|
|
auto v1 = getValue(expr->getLHS());
|
|
auto v2 = getValue(expr->getRHS());
|
|
switch (v1) {
|
|
case Value::Unknown:
|
|
switch (v2) {
|
|
case Value::Unknown:
|
|
break;
|
|
case Value::False:
|
|
{
|
|
auto e = getSubExprOfLogicalNegation(expr->getLHS());
|
|
if (e == nullptr) {
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("equal-to expression of the form 'A == false' (with A"
|
|
" of type %0) can %select{logically|literally}1 be"
|
|
" simplified as '!A'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getLHS()->IgnoreImpCasts()->getType()
|
|
<< (expr->getLHS()->IgnoreImpCasts()->getType()
|
|
->isBooleanType())
|
|
<< expr->getSourceRange();
|
|
} else {
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("equal-to expression of the form '!A == false' (with A"
|
|
" of type %0) can %select{logically|literally}1 be"
|
|
" simplified as 'A'"),
|
|
expr->getBeginLoc())
|
|
<< e->IgnoreImpCasts()->getType()
|
|
<< e->IgnoreImpCasts()->getType()->isBooleanType()
|
|
<< expr->getSourceRange();
|
|
}
|
|
break;
|
|
}
|
|
case Value::True:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("equal-to expression of the form 'A == true' (with A of type"
|
|
" %0) can %select{logically|literally}1 be simplified as 'A'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getLHS()->IgnoreImpCasts()->getType()
|
|
<< expr->getLHS()->IgnoreImpCasts()->getType()->isBooleanType()
|
|
<< expr->getSourceRange();
|
|
break;
|
|
}
|
|
break;
|
|
case Value::False:
|
|
switch (v2) {
|
|
case Value::Unknown:
|
|
{
|
|
auto e = getSubExprOfLogicalNegation(expr->getRHS());
|
|
if (e == nullptr) {
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("equal-to expression of the form 'false == A' (with A"
|
|
" of type %0) can %select{logically|literally}1 be"
|
|
" simplified as '!A'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getRHS()->IgnoreImpCasts()->getType()
|
|
<< (expr->getRHS()->IgnoreImpCasts()->getType()
|
|
->isBooleanType())
|
|
<< expr->getSourceRange();
|
|
} else {
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("equal-to expression of the form 'false == !A' (with A"
|
|
" of type %0) can %select{logically|literally}1 be"
|
|
" simplified as 'A'"),
|
|
expr->getBeginLoc())
|
|
<< e->IgnoreImpCasts()->getType()
|
|
<< e->IgnoreImpCasts()->getType()->isBooleanType()
|
|
<< expr->getSourceRange();
|
|
}
|
|
break;
|
|
}
|
|
case Value::False:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("equal-to expression of the form 'false == false' can"
|
|
" literally be simplified as 'true'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getSourceRange();
|
|
break;
|
|
case Value::True:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("equal-to expression of the form 'false == true' can"
|
|
" literally be simplified as 'false'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getSourceRange();
|
|
break;
|
|
}
|
|
break;
|
|
case Value::True:
|
|
switch (v2) {
|
|
case Value::Unknown:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("equal-to expression of the form 'true == A' (with A of type"
|
|
" %0) can %select{logically|literally}1 be simplified as 'A'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getRHS()->IgnoreImpCasts()->getType()
|
|
<< expr->getRHS()->IgnoreImpCasts()->getType()->isBooleanType()
|
|
<< expr->getSourceRange();
|
|
break;
|
|
case Value::False:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("equal-to expression of the form 'true == false' can"
|
|
" literally be simplified as 'false'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getSourceRange();
|
|
break;
|
|
case Value::True:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("equal-to expression of the form 'true == true' can"
|
|
" literally be simplified as 'true'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getSourceRange();
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SimplifyBool::visitBinNE(BinaryOperator const * expr) {
|
|
if (ignoreLocation(expr)) {
|
|
return true;
|
|
}
|
|
if (!(expr->getLHS()->IgnoreImpCasts()->getType()->isBooleanType()
|
|
&& expr->getRHS()->IgnoreImpCasts()->getType()->isBooleanType()))
|
|
{
|
|
return true;
|
|
}
|
|
auto v1 = getValue(expr->getLHS());
|
|
auto v2 = getValue(expr->getRHS());
|
|
switch (v1) {
|
|
case Value::Unknown:
|
|
switch (v2) {
|
|
case Value::Unknown:
|
|
break;
|
|
case Value::False:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("not-equal-to expression of the form 'A != false' (with A of"
|
|
" type %0) can %select{logically|literally}1 be simplified as"
|
|
" 'A'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getLHS()->IgnoreImpCasts()->getType()
|
|
<< expr->getLHS()->IgnoreImpCasts()->getType()->isBooleanType()
|
|
<< expr->getSourceRange();
|
|
break;
|
|
case Value::True:
|
|
{
|
|
auto e = getSubExprOfLogicalNegation(expr->getLHS());
|
|
if (e == nullptr) {
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("not-equal-to expression of the form 'A != true' (with"
|
|
" A of type %0) can %select{logically|literally}1 be"
|
|
" simplified as '!A'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getLHS()->IgnoreImpCasts()->getType()
|
|
<< (expr->getLHS()->IgnoreImpCasts()->getType()
|
|
->isBooleanType())
|
|
<< expr->getSourceRange();
|
|
} else {
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("not-equal-to expression of the form '!A != true'"
|
|
" (with A of type %0) can"
|
|
" %select{logically|literally}1 be simplified as 'A'"),
|
|
expr->getBeginLoc())
|
|
<< e->IgnoreImpCasts()->getType()
|
|
<< e->IgnoreImpCasts()->getType()->isBooleanType()
|
|
<< expr->getSourceRange();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case Value::False:
|
|
switch (v2) {
|
|
case Value::Unknown:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("not-equal-to expression of the form 'false != A' (with A of"
|
|
" type %0) can %select{logically|literally}1 be simplified as"
|
|
" 'A'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getRHS()->IgnoreImpCasts()->getType()
|
|
<< expr->getRHS()->IgnoreImpCasts()->getType()->isBooleanType()
|
|
<< expr->getSourceRange();
|
|
break;
|
|
case Value::False:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("not-equal-to expression of the form 'false != false' can"
|
|
" literally be simplified as 'false'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getSourceRange();
|
|
break;
|
|
case Value::True:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("not-equal-to expression of the form 'false != true' can"
|
|
" literally be simplified as 'true'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getSourceRange();
|
|
break;
|
|
}
|
|
break;
|
|
case Value::True:
|
|
switch (v2) {
|
|
case Value::Unknown:
|
|
{
|
|
auto e = getSubExprOfLogicalNegation(expr->getRHS());
|
|
if (e == nullptr) {
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("not-equal-to expression of the form 'true != A' (with"
|
|
" A of type %0) can %select{logically|literally}1 be"
|
|
" simplified as '!A'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getRHS()->IgnoreImpCasts()->getType()
|
|
<< (expr->getRHS()->IgnoreImpCasts()->getType()
|
|
->isBooleanType())
|
|
<< expr->getSourceRange();
|
|
} else {
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("not-equal-to expression of the form 'true != !A'"
|
|
" (with A of type %0) can"
|
|
" %select{logically|literally}1 be simplified as 'A'"),
|
|
expr->getBeginLoc())
|
|
<< e->IgnoreImpCasts()->getType()
|
|
<< e->IgnoreImpCasts()->getType()->isBooleanType()
|
|
<< expr->getSourceRange();
|
|
}
|
|
break;
|
|
}
|
|
case Value::False:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("not-equal-to expression of the form 'true != false' can"
|
|
" literally be simplified as 'true'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getSourceRange();
|
|
break;
|
|
case Value::True:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("not-equal-to expression of the form 'true != true' can"
|
|
" literally be simplified as 'false'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getSourceRange();
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SimplifyBool::VisitConditionalOperator(ConditionalOperator const * expr) {
|
|
if (ignoreLocation(expr)) {
|
|
return true;
|
|
}
|
|
auto v1 = getValue(expr->getTrueExpr());
|
|
auto v2 = getValue(expr->getFalseExpr());
|
|
switch (v1) {
|
|
case Value::Unknown:
|
|
switch (v2) {
|
|
case Value::Unknown:
|
|
break;
|
|
case Value::False:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("conditional expression of the form 'A ? B : false' (with A of"
|
|
" type %0 and B of type %1) can %select{logically|literally}2"
|
|
" be simplified as 'A && B'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getCond()->IgnoreImpCasts()->getType()
|
|
<< expr->getTrueExpr()->IgnoreImpCasts()->getType()
|
|
<< ((expr->getCond()->IgnoreImpCasts()->getType()
|
|
->isBooleanType())
|
|
&& (expr->getTrueExpr()->IgnoreImpCasts()->getType()
|
|
->isBooleanType()))
|
|
<< expr->getSourceRange();
|
|
break;
|
|
case Value::True:
|
|
{
|
|
auto e = getSubExprOfLogicalNegation(expr->getCond());
|
|
if (e == nullptr) {
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("conditional expression of the form 'A ? B : true'"
|
|
" (with A of type %0 and B of type %1) can"
|
|
" %select{logically|literally}2 be simplified as '!A"
|
|
" || B'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getCond()->IgnoreImpCasts()->getType()
|
|
<< expr->getTrueExpr()->IgnoreImpCasts()->getType()
|
|
<< ((expr->getCond()->IgnoreImpCasts()->getType()
|
|
->isBooleanType())
|
|
&& (expr->getTrueExpr()->IgnoreImpCasts()->getType()
|
|
->isBooleanType()))
|
|
<< expr->getSourceRange();
|
|
} else {
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("conditional expression of the form '!A ? B : true'"
|
|
" (with A of type %0 and B of type %1) can"
|
|
" %select{logically|literally}2 be simplified as 'A ||"
|
|
" B'"),
|
|
expr->getBeginLoc())
|
|
<< e->IgnoreImpCasts()->getType()
|
|
<< expr->getTrueExpr()->IgnoreImpCasts()->getType()
|
|
<< (e->IgnoreImpCasts()->getType()->isBooleanType()
|
|
&& (expr->getTrueExpr()->IgnoreImpCasts()
|
|
->getType()->isBooleanType()))
|
|
<< expr->getSourceRange();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case Value::False:
|
|
switch (v2) {
|
|
case Value::Unknown:
|
|
{
|
|
auto e = getSubExprOfLogicalNegation(expr->getCond());
|
|
if (e == nullptr) {
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("conditional expression of the form 'A ? false : B'"
|
|
" (with A of type %0 and B of type %1) can"
|
|
" %select{logically|literally}2 be simplified as '!A"
|
|
" && B'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getCond()->IgnoreImpCasts()->getType()
|
|
<< expr->getFalseExpr()->IgnoreImpCasts()->getType()
|
|
<< ((expr->getCond()->IgnoreImpCasts()->getType()
|
|
->isBooleanType())
|
|
&& (expr->getFalseExpr()->IgnoreImpCasts()
|
|
->getType()->isBooleanType()))
|
|
<< expr->getSourceRange();
|
|
} else {
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("conditional expression of the form '!A ? false : B'"
|
|
" (with A of type %0 and B of type %1) can"
|
|
" %select{logically|literally}2 be simplified as 'A &&"
|
|
" B'"),
|
|
expr->getBeginLoc())
|
|
<< e->IgnoreImpCasts()->getType()
|
|
<< expr->getFalseExpr()->IgnoreImpCasts()->getType()
|
|
<< (e->IgnoreImpCasts()->getType()->isBooleanType()
|
|
&& (expr->getFalseExpr()->IgnoreImpCasts()
|
|
->getType()->isBooleanType()))
|
|
<< expr->getSourceRange();
|
|
}
|
|
break;
|
|
}
|
|
case Value::False:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("conditional expression of the form 'A ? false : false' (with"
|
|
" A of type %0) can logically be simplified as 'false'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getCond()->IgnoreImpCasts()->getType()
|
|
<< expr->getSourceRange();
|
|
break;
|
|
case Value::True:
|
|
{
|
|
auto e = getSubExprOfLogicalNegation(expr->getCond());
|
|
if (e == nullptr) {
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("conditional expression of the form 'A ? false : true'"
|
|
" (with A of type %0) can"
|
|
" %select{logically|literally}1 be simplified as"
|
|
" '!A'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getCond()->IgnoreImpCasts()->getType()
|
|
<< (expr->getCond()->IgnoreImpCasts()->getType()
|
|
->isBooleanType())
|
|
<< expr->getSourceRange();
|
|
} else {
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("conditional expression of the form '!A ? false :"
|
|
" true' (with A of type %0) can"
|
|
" %select{logically|literally}1 be simplified as 'A'"),
|
|
expr->getBeginLoc())
|
|
<< e->IgnoreImpCasts()->getType()
|
|
<< e->IgnoreImpCasts()->getType()->isBooleanType()
|
|
<< expr->getSourceRange();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case Value::True:
|
|
switch (v2) {
|
|
case Value::Unknown:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("conditional expression of the form 'A ? true : B' (with A of"
|
|
" type %0 and B of type %1) can %select{logically|literally}2"
|
|
" be simplified as 'A || B'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getCond()->IgnoreImpCasts()->getType()
|
|
<< expr->getFalseExpr()->IgnoreImpCasts()->getType()
|
|
<< ((expr->getCond()->IgnoreImpCasts()->getType()
|
|
->isBooleanType())
|
|
&& (expr->getFalseExpr()->IgnoreImpCasts()->getType()
|
|
->isBooleanType()))
|
|
<< expr->getSourceRange();
|
|
break;
|
|
case Value::False:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("conditional expression of the form 'A ? true : false' (with A"
|
|
" of type %0) can %select{logically|literally}1 be simplified"
|
|
" as 'A'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getCond()->IgnoreImpCasts()->getType()
|
|
<< expr->getCond()->IgnoreImpCasts()->getType()->isBooleanType()
|
|
<< expr->getSourceRange();
|
|
break;
|
|
case Value::True:
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
("conditional expression of the form 'A ? true : true' (with A"
|
|
" of type %0) can logically be simplified as 'true'"),
|
|
expr->getBeginLoc())
|
|
<< expr->getCond()->IgnoreImpCasts()->getType()
|
|
<< expr->getSourceRange();
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SimplifyBool::TraverseFunctionDecl(FunctionDecl * functionDecl) {
|
|
auto copy = m_insideFunctionDecl;
|
|
m_insideFunctionDecl = functionDecl;
|
|
bool ret = RecursiveASTVisitor::TraverseFunctionDecl(functionDecl);
|
|
m_insideFunctionDecl = copy;
|
|
return ret;
|
|
}
|
|
|
|
bool SimplifyBool::TraverseCXXMethodDecl(CXXMethodDecl * functionDecl) {
|
|
auto copy = m_insideFunctionDecl;
|
|
m_insideFunctionDecl = functionDecl;
|
|
bool ret = RecursiveASTVisitor::TraverseCXXMethodDecl(functionDecl);
|
|
m_insideFunctionDecl = copy;
|
|
return ret;
|
|
}
|
|
|
|
loplugin::Plugin::Registration<SimplifyBool> X("simplifybool");
|
|
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|