1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
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:
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
);
42 return llvm::isa
<X
...>(Val
);
46 inline std::string
toString(llvm::APSInt
const & i
, unsigned radix
) {
47 #if CLANG_VERSION >= 130000
48 return llvm::toString(i
, radix
);
50 return i
.toString(radix
);
54 inline clang::SourceLocation
getBeginLoc(clang::Decl
const * decl
) {
55 #if CLANG_VERSION >= 80000
56 return decl
->getBeginLoc();
58 return decl
->getLocStart();
62 inline clang::SourceLocation
getEndLoc(clang::Decl
const * decl
) {
63 #if CLANG_VERSION >= 80000
64 return decl
->getEndLoc();
66 return decl
->getLocEnd();
70 inline clang::SourceLocation
getBeginLoc(clang::DeclarationNameInfo
const & info
) {
71 #if CLANG_VERSION >= 80000
72 return info
.getBeginLoc();
74 return info
.getLocStart();
78 inline clang::SourceLocation
getEndLoc(clang::DeclarationNameInfo
const & info
) {
79 #if CLANG_VERSION >= 80000
80 return info
.getEndLoc();
82 return info
.getLocEnd();
86 inline clang::SourceLocation
getBeginLoc(clang::Stmt
const * stmt
) {
87 #if CLANG_VERSION >= 80000
88 return stmt
->getBeginLoc();
90 return stmt
->getLocStart();
94 inline clang::SourceLocation
getEndLoc(clang::Stmt
const * stmt
) {
95 #if CLANG_VERSION >= 80000
96 return stmt
->getEndLoc();
98 return stmt
->getLocEnd();
102 inline clang::SourceLocation
getBeginLoc(clang::CXXBaseSpecifier
const * spec
) {
103 #if CLANG_VERSION >= 80000
104 return spec
->getBeginLoc();
106 return spec
->getLocStart();
110 inline clang::SourceLocation
getEndLoc(clang::CXXBaseSpecifier
const * spec
) {
111 #if CLANG_VERSION >= 80000
112 return spec
->getEndLoc();
114 return spec
->getLocEnd();
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()};
126 return SM
.getImmediateExpansionRange(Loc
);
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
);
138 Location
== Start
|| Location
== End
139 || (SM
.isBeforeInTranslationUnit(Start
, Location
)
140 && SM
.isBeforeInTranslationUnit(Location
, End
));
144 inline clang::Expr
const * IgnoreImplicit(clang::Expr
const * expr
) {
145 #if CLANG_VERSION >= 80000
146 return expr
->IgnoreImplicit();
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;
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
);
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
;
184 return opts
.CPlusPlus1z
;
188 #if CLANG_VERSION >= 130000
189 constexpr clang::ExprValueKind VK_PRValue
= clang::VK_PRValue
;
191 constexpr clang::ExprValueKind VK_PRValue
= clang::VK_RValue
;
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();
202 return expr
->EvaluateAsInt(intRes
, ctx
);
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
);
213 return expr
->isIntegerConstantExpr(res
, context
) ? res
: llvm::Optional
<llvm::APSInt
>();
217 inline clang::Expr
* getSubExpr(clang::MaterializeTemporaryExpr
const * expr
) {
218 #if CLANG_VERSION >= 100000
219 return expr
->getSubExpr();
221 return expr
->GetTemporaryExpr();
225 #if CLANG_VERSION < 80000
226 inline clang::Expr
const * getSubExprAsWritten(clang::CastExpr
const * expr
)
227 { return expr
->getSubExprAsWritten(); }
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>'
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:
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();
266 inline clang::Expr
*getSubExprAsWritten(clang::CastExpr
*This
) {
267 clang::Expr
*SubExpr
= nullptr;
268 clang::CastExpr
*E
= This
;
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
)
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
)));
291 inline const clang::Expr
*getSubExprAsWritten(const clang::CastExpr
*This
) {
292 return getSubExprAsWritten(const_cast<clang::CastExpr
*>(This
));
296 inline clang::QualType
getObjectType(clang::CXXMemberCallExpr
const * expr
) {
297 #if CLANG_VERSION >= 100000
298 return expr
->getObjectType();
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();
309 inline bool isExplicitSpecified(clang::CXXConstructorDecl
const * decl
) {
310 #if CLANG_VERSION >= 90000
311 return decl
->getExplicitSpecifier().isExplicit();
313 return decl
->isExplicitSpecified();
317 inline bool isExplicitSpecified(clang::CXXConversionDecl
const * decl
) {
318 #if CLANG_VERSION >= 90000
319 return decl
->getExplicitSpecifier().isExplicit();
321 return decl
->isExplicitSpecified();
325 inline clang::QualType
getDeclaredReturnType(clang::FunctionDecl
const * decl
) {
326 #if CLANG_VERSION >= 80000
327 return decl
->getDeclaredReturnType();
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();
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
);
350 return op
== clang::BO_PtrMemD
|| op
== clang::BO_PtrMemI
;
354 #if CLANG_VERSION >= 70000
355 constexpr llvm::sys::fs::OpenFlags OF_None
= llvm::sys::fs::OF_None
;
357 constexpr llvm::sys::fs::OpenFlags OF_None
= llvm::sys::fs::F_None
;
362 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */