Noel Grandin cda88ec7a4 loplugin:simplifybool a little more aggressive
with expressions like !(a && b)

Change-Id: Id2acec2a8d0eaaa8e5e37dbd2cae7281be36572e
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/100040
Tested-by: Jenkins
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
2020-08-04 11:21:36 +02:00

317 lines
10 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/.
*/
#ifndef INCLUDED_COMPILERPLUGINS_CLANG_COMPAT_HXX
#define INCLUDED_COMPILERPLUGINS_CLANG_COMPAT_HXX
#include <cstddef>
#include <utility>
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Lexer.h"
#include "llvm/ADT/StringRef.h"
#include "config_clang.h"
// Compatibility wrapper to abstract over (trivial) changes in the Clang API:
namespace compat {
inline clang::SourceLocation getBeginLoc(clang::Decl const * decl) {
#if CLANG_VERSION >= 80000
return decl->getBeginLoc();
#else
return decl->getLocStart();
#endif
}
inline clang::SourceLocation getEndLoc(clang::Decl const * decl) {
#if CLANG_VERSION >= 80000
return decl->getEndLoc();
#else
return decl->getLocEnd();
#endif
}
inline clang::SourceLocation getBeginLoc(clang::DeclarationNameInfo const & info) {
#if CLANG_VERSION >= 80000
return info.getBeginLoc();
#else
return info.getLocStart();
#endif
}
inline clang::SourceLocation getEndLoc(clang::DeclarationNameInfo const & info) {
#if CLANG_VERSION >= 80000
return info.getEndLoc();
#else
return info.getLocEnd();
#endif
}
inline clang::SourceLocation getBeginLoc(clang::Stmt const * stmt) {
#if CLANG_VERSION >= 80000
return stmt->getBeginLoc();
#else
return stmt->getLocStart();
#endif
}
inline clang::SourceLocation getEndLoc(clang::Stmt const * stmt) {
#if CLANG_VERSION >= 80000
return stmt->getEndLoc();
#else
return stmt->getLocEnd();
#endif
}
inline clang::SourceLocation getBeginLoc(clang::CXXBaseSpecifier const * spec) {
#if CLANG_VERSION >= 80000
return spec->getBeginLoc();
#else
return spec->getLocStart();
#endif
}
inline clang::SourceLocation getEndLoc(clang::CXXBaseSpecifier const * spec) {
#if CLANG_VERSION >= 80000
return spec->getEndLoc();
#else
return spec->getLocEnd();
#endif
}
inline std::pair<clang::SourceLocation, clang::SourceLocation> getImmediateExpansionRange(
clang::SourceManager const & SM, clang::SourceLocation Loc)
{
#if CLANG_VERSION >= 70000
auto const csr = SM.getImmediateExpansionRange(Loc);
if (csr.isCharRange()) { /*TODO*/ }
return {csr.getBegin(), csr.getEnd()};
#else
return SM.getImmediateExpansionRange(Loc);
#endif
}
inline bool isPointWithin(
clang::SourceManager const & SM, clang::SourceLocation Location, clang::SourceLocation Start,
clang::SourceLocation End)
{
#if CLANG_VERSION >= 60000
return SM.isPointWithin(Location, Start, End);
#else
return
Location == Start || Location == End
|| (SM.isBeforeInTranslationUnit(Start, Location)
&& SM.isBeforeInTranslationUnit(Location, End));
#endif
}
inline clang::Expr const * IgnoreImplicit(clang::Expr const * expr) {
#if CLANG_VERSION >= 80000
return expr->IgnoreImplicit();
#else
using namespace clang;
// Copy from Clang's lib/AST/Stmt.cpp, including <https://reviews.llvm.org/D50666> "Fix
// Stmt::ignoreImplicit":
Stmt const *s = expr;
Stmt const *lasts = nullptr;
while (s != lasts) {
lasts = s;
if (auto *ewc = dyn_cast<ExprWithCleanups>(s))
s = ewc->getSubExpr();
if (auto *mte = dyn_cast<MaterializeTemporaryExpr>(s))
s = mte->GetTemporaryExpr();
if (auto *bte = dyn_cast<CXXBindTemporaryExpr>(s))
s = bte->getSubExpr();
if (auto *ice = dyn_cast<ImplicitCastExpr>(s))
s = ice->getSubExpr();
}
return static_cast<Expr const *>(s);
#endif
}
inline bool CPlusPlus17(clang::LangOptions const & opts) {
#if CLANG_VERSION >= 60000
return opts.CPlusPlus17;
#else
return opts.CPlusPlus1z;
#endif
}
inline bool EvaluateAsInt(clang::Expr const * expr, llvm::APSInt& intRes, const clang::ASTContext& ctx) {
#if CLANG_VERSION >= 80000
clang::Expr::EvalResult res;
bool b = expr->EvaluateAsInt(res, ctx);
if (b && res.Val.isInt())
intRes = res.Val.getInt();
return b;
#else
return expr->EvaluateAsInt(intRes, ctx);
#endif
}
inline llvm::Optional<llvm::APSInt> getIntegerConstantExpr(
clang::Expr const * expr, clang::ASTContext const & context)
{
#if CLANG_VERSION >= 120000
return expr->getIntegerConstantExpr(context);
#else
llvm::APSInt res;
return expr->isIntegerConstantExpr(res, context) ? res : llvm::Optional<llvm::APSInt>();
#endif
}
inline clang::Expr * getSubExpr(clang::MaterializeTemporaryExpr const * expr) {
#if CLANG_VERSION >= 100000
return expr->getSubExpr();
#else
return expr->GetTemporaryExpr();
#endif
}
// Work around <http://reviews.llvm.org/D22128>:
//
// SfxErrorHandler::GetClassString (svtools/source/misc/ehdl.cxx):
//
// ErrorResource_Impl aEr(aId, (sal_uInt16)lClassId);
// if(aEr)
// {
// rStr = static_cast<ResString>(aEr).GetString();
// }
//
// expr->dump():
// CXXStaticCastExpr 0x2b74e8e657b8 'class ResString' static_cast<class ResString> <ConstructorConversion>
// `-CXXBindTemporaryExpr 0x2b74e8e65798 'class ResString' (CXXTemporary 0x2b74e8e65790)
// `-CXXConstructExpr 0x2b74e8e65758 'class ResString' 'void (class ResString &&) noexcept(false)' elidable
// `-MaterializeTemporaryExpr 0x2b74e8e65740 'class ResString' xvalue
// `-CXXBindTemporaryExpr 0x2b74e8e65720 'class ResString' (CXXTemporary 0x2b74e8e65718)
// `-ImplicitCastExpr 0x2b74e8e65700 'class ResString' <UserDefinedConversion>
// `-CXXMemberCallExpr 0x2b74e8e656d8 'class ResString'
// `-MemberExpr 0x2b74e8e656a0 '<bound member function type>' .operator ResString 0x2b74e8dc1f00
// `-DeclRefExpr 0x2b74e8e65648 'struct ErrorResource_Impl' lvalue Var 0x2b74e8e653b0 'aEr' 'struct ErrorResource_Impl'
// expr->getSubExprAsWritten()->dump():
// MaterializeTemporaryExpr 0x2b74e8e65740 'class ResString' xvalue
// `-CXXBindTemporaryExpr 0x2b74e8e65720 'class ResString' (CXXTemporary 0x2b74e8e65718)
// `-ImplicitCastExpr 0x2b74e8e65700 'class ResString' <UserDefinedConversion>
// `-CXXMemberCallExpr 0x2b74e8e656d8 'class ResString'
// `-MemberExpr 0x2b74e8e656a0 '<bound member function type>' .operator ResString 0x2b74e8dc1f00
// `-DeclRefExpr 0x2b74e8e65648 'struct ErrorResource_Impl' lvalue Var 0x2b74e8e653b0 'aEr' 'struct ErrorResource_Impl'
//
// Copies code from Clang's lib/AST/Expr.cpp:
namespace detail {
inline clang::Expr *skipImplicitTemporary(clang::Expr *expr) {
// Skip through reference binding to temporary.
if (clang::MaterializeTemporaryExpr *Materialize
= clang::dyn_cast<clang::MaterializeTemporaryExpr>(expr))
expr = compat::getSubExpr(Materialize);
// Skip any temporary bindings; they're implicit.
if (clang::CXXBindTemporaryExpr *Binder = clang::dyn_cast<clang::CXXBindTemporaryExpr>(expr))
expr = Binder->getSubExpr();
return expr;
}
}
inline clang::Expr *getSubExprAsWritten(clang::CastExpr *This) {
clang::Expr *SubExpr = nullptr;
clang::CastExpr *E = This;
do {
SubExpr = detail::skipImplicitTemporary(E->getSubExpr());
// Conversions by constructor and conversion functions have a
// subexpression describing the call; strip it off.
if (E->getCastKind() == clang::CK_ConstructorConversion)
SubExpr =
detail::skipImplicitTemporary(clang::cast<clang::CXXConstructExpr>(SubExpr)->getArg(0));
else if (E->getCastKind() == clang::CK_UserDefinedConversion) {
assert((clang::isa<clang::CXXMemberCallExpr>(SubExpr) ||
clang::isa<clang::BlockExpr>(SubExpr)) &&
"Unexpected SubExpr for CK_UserDefinedConversion.");
if (clang::isa<clang::CXXMemberCallExpr>(SubExpr))
SubExpr = clang::cast<clang::CXXMemberCallExpr>(SubExpr)->getImplicitObjectArgument();
}
// If the subexpression we're left with is an implicit cast, look
// through that, too.
} while ((E = clang::dyn_cast<clang::ImplicitCastExpr>(SubExpr)));
return SubExpr;
}
inline const clang::Expr *getSubExprAsWritten(const clang::CastExpr *This) {
return getSubExprAsWritten(const_cast<clang::CastExpr *>(This));
}
inline clang::QualType getObjectType(clang::CXXMemberCallExpr const * expr) {
#if CLANG_VERSION >= 100000
return expr->getObjectType();
#else
// <https://github.com/llvm/llvm-project/commit/88559637641e993895337e1047a0bd787fecc647>
// "[OpenCL] Improve destructor support in C++ for OpenCL":
clang::QualType Ty = expr->getImplicitObjectArgument()->getType();
if (Ty->isPointerType())
Ty = Ty->getPointeeType();
return Ty;
#endif
}
inline bool isExplicitSpecified(clang::CXXConstructorDecl const * decl) {
#if CLANG_VERSION >= 90000
return decl->getExplicitSpecifier().isExplicit();
#else
return decl->isExplicitSpecified();
#endif
}
inline bool isExplicitSpecified(clang::CXXConversionDecl const * decl) {
#if CLANG_VERSION >= 90000
return decl->getExplicitSpecifier().isExplicit();
#else
return decl->isExplicitSpecified();
#endif
}
inline clang::QualType getDeclaredReturnType(clang::FunctionDecl const * decl) {
#if CLANG_VERSION >= 80000
return decl->getDeclaredReturnType();
#else
// <https://github.com/llvm/llvm-project/commit/4576a77b809649f5b8d0ff8c7a4be57eeee0ecf9>
// "PR33222: Require the declared return type not the actual return type to":
auto *TSI = decl->getTypeSourceInfo();
clang::QualType T = TSI ? TSI->getType() : decl->getType();
return T->castAs<clang::FunctionType>()->getReturnType();
#endif
}
// The isComparisonOp method on CXXOperatorCallExpr is not available yet for the clang we require
inline bool isComparisonOp(clang::CXXOperatorCallExpr const * callExpr)
{
using namespace clang;
auto op = callExpr->getOperator();
return op == OO_Less || op == OO_Greater || op == OO_LessEqual || op == OO_GreaterEqual
|| op == OO_EqualEqual || op == OO_ExclaimEqual;
}
}
#endif
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */