diff options
Diffstat (limited to '')
-rw-r--r-- | compilerplugins/clang/compat.hxx | 308 |
1 files changed, 308 insertions, 0 deletions
diff --git a/compilerplugins/clang/compat.hxx b/compilerplugins/clang/compat.hxx new file mode 100644 index 000000000..76f076621 --- /dev/null +++ b/compilerplugins/clang/compat.hxx @@ -0,0 +1,308 @@ +/* -*- 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" + +#if CLANG_VERSION >= 110000 +namespace clang { class DynTypedNodeList; } +#else +#include "clang/AST/ASTContext.h" +#endif + +// 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 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 +} + +#if CLANG_VERSION >= 110000 +using DynTypedNodeList = clang::DynTypedNodeList; +#else +using DynTypedNodeList = clang::ASTContext::DynTypedNodeList; +#endif + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |