LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / compilerplugins / clang / compat.hxx
blob54f0d701a0236af393490ed24d244eec13388491
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #pragma once
12 #include <cstddef>
13 #include <string>
14 #include <utility>
16 #include "clang/AST/Decl.h"
17 #include "clang/AST/DeclCXX.h"
18 #include "clang/AST/Expr.h"
19 #include "clang/AST/ExprCXX.h"
20 #include "clang/Basic/SourceManager.h"
21 #include "clang/Basic/Specifiers.h"
22 #include "clang/Frontend/CompilerInstance.h"
23 #include "clang/Lex/Lexer.h"
24 #include "llvm/ADT/StringExtras.h"
25 #include "llvm/ADT/StringRef.h"
26 #include "llvm/Support/Casting.h"
27 #include "llvm/Support/Compiler.h"
29 #include "config_clang.h"
31 // Compatibility wrapper to abstract over (trivial) changes in the Clang API:
32 namespace compat {
34 // Copies code from LLVM's include/llvm/Support/Casting.h:
35 template<typename... X, typename Y> LLVM_NODISCARD inline bool isa_and_nonnull(Y const & Val) {
36 #if CLANG_VERSION >= 90000
37 return llvm::isa_and_nonnull<X...>(Val);
38 #else
39 if (!Val) {
40 return false;
42 return llvm::isa<X...>(Val);
43 #endif
46 inline std::string toString(llvm::APSInt const & i, unsigned radix) {
47 #if CLANG_VERSION >= 130000
48 return llvm::toString(i, radix);
49 #else
50 return i.toString(radix);
51 #endif
54 inline clang::SourceLocation getBeginLoc(clang::Decl const * decl) {
55 #if CLANG_VERSION >= 80000
56 return decl->getBeginLoc();
57 #else
58 return decl->getLocStart();
59 #endif
62 inline clang::SourceLocation getEndLoc(clang::Decl const * decl) {
63 #if CLANG_VERSION >= 80000
64 return decl->getEndLoc();
65 #else
66 return decl->getLocEnd();
67 #endif
70 inline clang::SourceLocation getBeginLoc(clang::DeclarationNameInfo const & info) {
71 #if CLANG_VERSION >= 80000
72 return info.getBeginLoc();
73 #else
74 return info.getLocStart();
75 #endif
78 inline clang::SourceLocation getEndLoc(clang::DeclarationNameInfo const & info) {
79 #if CLANG_VERSION >= 80000
80 return info.getEndLoc();
81 #else
82 return info.getLocEnd();
83 #endif
86 inline clang::SourceLocation getBeginLoc(clang::Stmt const * stmt) {
87 #if CLANG_VERSION >= 80000
88 return stmt->getBeginLoc();
89 #else
90 return stmt->getLocStart();
91 #endif
94 inline clang::SourceLocation getEndLoc(clang::Stmt const * stmt) {
95 #if CLANG_VERSION >= 80000
96 return stmt->getEndLoc();
97 #else
98 return stmt->getLocEnd();
99 #endif
102 inline clang::SourceLocation getBeginLoc(clang::CXXBaseSpecifier const * spec) {
103 #if CLANG_VERSION >= 80000
104 return spec->getBeginLoc();
105 #else
106 return spec->getLocStart();
107 #endif
110 inline clang::SourceLocation getEndLoc(clang::CXXBaseSpecifier const * spec) {
111 #if CLANG_VERSION >= 80000
112 return spec->getEndLoc();
113 #else
114 return spec->getLocEnd();
115 #endif
118 inline std::pair<clang::SourceLocation, clang::SourceLocation> getImmediateExpansionRange(
119 clang::SourceManager const & SM, clang::SourceLocation Loc)
121 #if CLANG_VERSION >= 70000
122 auto const csr = SM.getImmediateExpansionRange(Loc);
123 if (csr.isCharRange()) { /*TODO*/ }
124 return {csr.getBegin(), csr.getEnd()};
125 #else
126 return SM.getImmediateExpansionRange(Loc);
127 #endif
130 inline bool isPointWithin(
131 clang::SourceManager const & SM, clang::SourceLocation Location, clang::SourceLocation Start,
132 clang::SourceLocation End)
134 #if CLANG_VERSION >= 60000
135 return SM.isPointWithin(Location, Start, End);
136 #else
137 return
138 Location == Start || Location == End
139 || (SM.isBeforeInTranslationUnit(Start, Location)
140 && SM.isBeforeInTranslationUnit(Location, End));
141 #endif
144 inline clang::Expr const * IgnoreImplicit(clang::Expr const * expr) {
145 #if CLANG_VERSION >= 80000
146 return expr->IgnoreImplicit();
147 #else
148 using namespace clang;
149 // Copy from Clang's lib/AST/Stmt.cpp, including <https://reviews.llvm.org/D50666> "Fix
150 // Stmt::ignoreImplicit":
151 Stmt const *s = expr;
153 Stmt const *lasts = nullptr;
155 while (s != lasts) {
156 lasts = s;
158 if (auto *ewc = dyn_cast<ExprWithCleanups>(s))
159 s = ewc->getSubExpr();
161 if (auto *mte = dyn_cast<MaterializeTemporaryExpr>(s))
162 s = mte->GetTemporaryExpr();
164 if (auto *bte = dyn_cast<CXXBindTemporaryExpr>(s))
165 s = bte->getSubExpr();
167 if (auto *ice = dyn_cast<ImplicitCastExpr>(s))
168 s = ice->getSubExpr();
171 return static_cast<Expr const *>(s);
172 #endif
175 /// Utility method
176 inline clang::Expr const * IgnoreParenImplicit(clang::Expr const * expr) {
177 return compat::IgnoreImplicit(compat::IgnoreImplicit(expr)->IgnoreParens());
180 inline bool CPlusPlus17(clang::LangOptions const & opts) {
181 #if CLANG_VERSION >= 60000
182 return opts.CPlusPlus17;
183 #else
184 return opts.CPlusPlus1z;
185 #endif
188 #if CLANG_VERSION >= 130000
189 constexpr clang::ExprValueKind VK_PRValue = clang::VK_PRValue;
190 #else
191 constexpr clang::ExprValueKind VK_PRValue = clang::VK_RValue;
192 #endif
194 inline bool EvaluateAsInt(clang::Expr const * expr, llvm::APSInt& intRes, const clang::ASTContext& ctx) {
195 #if CLANG_VERSION >= 80000
196 clang::Expr::EvalResult res;
197 bool b = expr->EvaluateAsInt(res, ctx);
198 if (b && res.Val.isInt())
199 intRes = res.Val.getInt();
200 return b;
201 #else
202 return expr->EvaluateAsInt(intRes, ctx);
203 #endif
206 inline llvm::Optional<llvm::APSInt> getIntegerConstantExpr(
207 clang::Expr const * expr, clang::ASTContext const & context)
209 #if CLANG_VERSION >= 120000
210 return expr->getIntegerConstantExpr(context);
211 #else
212 llvm::APSInt res;
213 return expr->isIntegerConstantExpr(res, context) ? res : llvm::Optional<llvm::APSInt>();
214 #endif
217 inline clang::Expr * getSubExpr(clang::MaterializeTemporaryExpr const * expr) {
218 #if CLANG_VERSION >= 100000
219 return expr->getSubExpr();
220 #else
221 return expr->GetTemporaryExpr();
222 #endif
225 #if CLANG_VERSION < 80000
226 inline clang::Expr const * getSubExprAsWritten(clang::CastExpr const * expr)
227 { return expr->getSubExprAsWritten(); }
228 #else
229 // Work around CastExpr::getSubExprAsWritten firing
231 // include/llvm/Support/Casting.h:269: typename llvm::cast_retty<X, Y*>::ret_type llvm::cast(Y*)
232 // [with X = clang::CXXConstructExpr; Y = clang::Expr;
233 // typename llvm::cast_retty<X, Y*>::ret_type = clang::CXXConstructExpr*]: Assertion
234 // `isa<X>(Val) && "cast<Ty>() argument of incompatible type!"' failed.
236 // for CastExprs involving ConstantExpr (introduced with
237 // <https://github.com/llvm/llvm-project/commit/7c44da279e399d302a685c500e7f802f8adf9762> "Create
238 // ConstantExpr class" towards LLVM 8) like
240 // CXXFunctionalCastExpr 0xc01c4e8 'class rtl::OStringLiteral<9>':'class rtl::OStringLiteral<9>' functional cast to OStringLiteral <ConstructorConversion>
241 // `-ConstantExpr 0xc01c380 'class rtl::OStringLiteral<9>':'class rtl::OStringLiteral<9>'
242 // |-value: Struct
243 // | |-fields: Int 1073741824, Int 8
244 // | `-field: Array size=9
245 // | |-elements: Int 46, Int 111, Int 115, Int 108
246 // | |-elements: Int 45, Int 116, Int 109, Int 112
247 // | `-element: Int 0
248 // `-CXXConstructExpr 0xc01c350 'class rtl::OStringLiteral<9>':'class rtl::OStringLiteral<9>' 'void (const char (&)[9])'
249 // `-StringLiteral 0xc019ad8 'const char [9]' lvalue ".osl-tmp"
251 // Copies code from Clang's lib/AST/Expr.cpp:
252 namespace detail {
253 inline clang::Expr *skipImplicitTemporary(clang::Expr *expr) {
254 // Skip through reference binding to temporary.
255 if (clang::MaterializeTemporaryExpr *Materialize
256 = clang::dyn_cast<clang::MaterializeTemporaryExpr>(expr))
257 expr = compat::getSubExpr(Materialize);
259 // Skip any temporary bindings; they're implicit.
260 if (clang::CXXBindTemporaryExpr *Binder = clang::dyn_cast<clang::CXXBindTemporaryExpr>(expr))
261 expr = Binder->getSubExpr();
263 return expr;
266 inline clang::Expr *getSubExprAsWritten(clang::CastExpr *This) {
267 clang::Expr *SubExpr = nullptr;
268 clang::CastExpr *E = This;
269 do {
270 SubExpr = detail::skipImplicitTemporary(E->getSubExpr());
272 // Conversions by constructor and conversion functions have a
273 // subexpression describing the call; strip it off.
274 if (E->getCastKind() == clang::CK_ConstructorConversion)
275 SubExpr =
276 detail::skipImplicitTemporary(clang::cast<clang::CXXConstructExpr>(SubExpr->IgnoreImplicit())->getArg(0));
277 else if (E->getCastKind() == clang::CK_UserDefinedConversion) {
278 assert((clang::isa<clang::CXXMemberCallExpr>(SubExpr) ||
279 clang::isa<clang::BlockExpr>(SubExpr)) &&
280 "Unexpected SubExpr for CK_UserDefinedConversion.");
281 if (clang::isa<clang::CXXMemberCallExpr>(SubExpr))
282 SubExpr = clang::cast<clang::CXXMemberCallExpr>(SubExpr)->getImplicitObjectArgument();
285 // If the subexpression we're left with is an implicit cast, look
286 // through that, too.
287 } while ((E = clang::dyn_cast<clang::ImplicitCastExpr>(SubExpr)));
289 return SubExpr;
291 inline const clang::Expr *getSubExprAsWritten(const clang::CastExpr *This) {
292 return getSubExprAsWritten(const_cast<clang::CastExpr *>(This));
294 #endif
296 inline clang::QualType getObjectType(clang::CXXMemberCallExpr const * expr) {
297 #if CLANG_VERSION >= 100000
298 return expr->getObjectType();
299 #else
300 // <https://github.com/llvm/llvm-project/commit/88559637641e993895337e1047a0bd787fecc647>
301 // "[OpenCL] Improve destructor support in C++ for OpenCL":
302 clang::QualType Ty = expr->getImplicitObjectArgument()->getType();
303 if (Ty->isPointerType())
304 Ty = Ty->getPointeeType();
305 return Ty;
306 #endif
309 inline bool isExplicitSpecified(clang::CXXConstructorDecl const * decl) {
310 #if CLANG_VERSION >= 90000
311 return decl->getExplicitSpecifier().isExplicit();
312 #else
313 return decl->isExplicitSpecified();
314 #endif
317 inline bool isExplicitSpecified(clang::CXXConversionDecl const * decl) {
318 #if CLANG_VERSION >= 90000
319 return decl->getExplicitSpecifier().isExplicit();
320 #else
321 return decl->isExplicitSpecified();
322 #endif
325 inline clang::QualType getDeclaredReturnType(clang::FunctionDecl const * decl) {
326 #if CLANG_VERSION >= 80000
327 return decl->getDeclaredReturnType();
328 #else
329 // <https://github.com/llvm/llvm-project/commit/4576a77b809649f5b8d0ff8c7a4be57eeee0ecf9>
330 // "PR33222: Require the declared return type not the actual return type to":
331 auto *TSI = decl->getTypeSourceInfo();
332 clang::QualType T = TSI ? TSI->getType() : decl->getType();
333 return T->castAs<clang::FunctionType>()->getReturnType();
334 #endif
337 // The isComparisonOp method on CXXOperatorCallExpr is not available yet for the clang we require
338 inline bool isComparisonOp(clang::CXXOperatorCallExpr const * callExpr)
340 using namespace clang;
341 auto op = callExpr->getOperator();
342 return op == OO_Less || op == OO_Greater || op == OO_LessEqual || op == OO_GreaterEqual
343 || op == OO_EqualEqual || op == OO_ExclaimEqual;
346 inline bool isPtrMemOp(clang::BinaryOperatorKind op) {
347 #if CLANG_VERSION >= 80000
348 return clang::BinaryOperator::isPtrMemOp(op);
349 #else
350 return op == clang::BO_PtrMemD || op == clang::BO_PtrMemI;
351 #endif
354 #if CLANG_VERSION >= 70000
355 constexpr llvm::sys::fs::OpenFlags OF_None = llvm::sys::fs::OF_None;
356 #else
357 constexpr llvm::sys::fs::OpenFlags OF_None = llvm::sys::fs::F_None;
358 #endif
362 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */