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/.
15 #include "clang/AST/Decl.h"
16 #include "clang/AST/DeclCXX.h"
17 #include "clang/AST/Expr.h"
18 #include "clang/AST/ExprCXX.h"
19 #include "clang/Basic/SourceManager.h"
20 #include "clang/Basic/Specifiers.h"
21 #include "clang/Frontend/CompilerInstance.h"
22 #include "clang/Lex/Lexer.h"
23 #include "llvm/ADT/StringRef.h"
24 #include "llvm/Support/Casting.h"
25 #include "llvm/Support/Compiler.h"
27 #include "config_clang.h"
29 // Compatibility wrapper to abstract over (trivial) changes in the Clang API:
32 // Copies code from LLVM's include/llvm/Support/Casting.h:
33 template<typename
... X
, typename Y
> LLVM_NODISCARD
inline bool isa_and_nonnull(Y
const & Val
) {
34 #if CLANG_VERSION >= 90000
35 return llvm::isa_and_nonnull
<X
...>(Val
);
40 return llvm::isa
<X
...>(Val
);
44 inline clang::SourceLocation
getBeginLoc(clang::Decl
const * decl
) {
45 #if CLANG_VERSION >= 80000
46 return decl
->getBeginLoc();
48 return decl
->getLocStart();
52 inline clang::SourceLocation
getEndLoc(clang::Decl
const * decl
) {
53 #if CLANG_VERSION >= 80000
54 return decl
->getEndLoc();
56 return decl
->getLocEnd();
60 inline clang::SourceLocation
getBeginLoc(clang::DeclarationNameInfo
const & info
) {
61 #if CLANG_VERSION >= 80000
62 return info
.getBeginLoc();
64 return info
.getLocStart();
68 inline clang::SourceLocation
getEndLoc(clang::DeclarationNameInfo
const & info
) {
69 #if CLANG_VERSION >= 80000
70 return info
.getEndLoc();
72 return info
.getLocEnd();
76 inline clang::SourceLocation
getBeginLoc(clang::Stmt
const * stmt
) {
77 #if CLANG_VERSION >= 80000
78 return stmt
->getBeginLoc();
80 return stmt
->getLocStart();
84 inline clang::SourceLocation
getEndLoc(clang::Stmt
const * stmt
) {
85 #if CLANG_VERSION >= 80000
86 return stmt
->getEndLoc();
88 return stmt
->getLocEnd();
92 inline clang::SourceLocation
getBeginLoc(clang::CXXBaseSpecifier
const * spec
) {
93 #if CLANG_VERSION >= 80000
94 return spec
->getBeginLoc();
96 return spec
->getLocStart();
100 inline clang::SourceLocation
getEndLoc(clang::CXXBaseSpecifier
const * spec
) {
101 #if CLANG_VERSION >= 80000
102 return spec
->getEndLoc();
104 return spec
->getLocEnd();
108 inline std::pair
<clang::SourceLocation
, clang::SourceLocation
> getImmediateExpansionRange(
109 clang::SourceManager
const & SM
, clang::SourceLocation Loc
)
111 #if CLANG_VERSION >= 70000
112 auto const csr
= SM
.getImmediateExpansionRange(Loc
);
113 if (csr
.isCharRange()) { /*TODO*/ }
114 return {csr
.getBegin(), csr
.getEnd()};
116 return SM
.getImmediateExpansionRange(Loc
);
120 inline bool isPointWithin(
121 clang::SourceManager
const & SM
, clang::SourceLocation Location
, clang::SourceLocation Start
,
122 clang::SourceLocation End
)
124 #if CLANG_VERSION >= 60000
125 return SM
.isPointWithin(Location
, Start
, End
);
128 Location
== Start
|| Location
== End
129 || (SM
.isBeforeInTranslationUnit(Start
, Location
)
130 && SM
.isBeforeInTranslationUnit(Location
, End
));
134 inline clang::Expr
const * IgnoreImplicit(clang::Expr
const * expr
) {
135 #if CLANG_VERSION >= 80000
136 return expr
->IgnoreImplicit();
138 using namespace clang
;
139 // Copy from Clang's lib/AST/Stmt.cpp, including <https://reviews.llvm.org/D50666> "Fix
140 // Stmt::ignoreImplicit":
141 Stmt
const *s
= expr
;
143 Stmt
const *lasts
= nullptr;
148 if (auto *ewc
= dyn_cast
<ExprWithCleanups
>(s
))
149 s
= ewc
->getSubExpr();
151 if (auto *mte
= dyn_cast
<MaterializeTemporaryExpr
>(s
))
152 s
= mte
->GetTemporaryExpr();
154 if (auto *bte
= dyn_cast
<CXXBindTemporaryExpr
>(s
))
155 s
= bte
->getSubExpr();
157 if (auto *ice
= dyn_cast
<ImplicitCastExpr
>(s
))
158 s
= ice
->getSubExpr();
161 return static_cast<Expr
const *>(s
);
165 inline bool CPlusPlus17(clang::LangOptions
const & opts
) {
166 #if CLANG_VERSION >= 60000
167 return opts
.CPlusPlus17
;
169 return opts
.CPlusPlus1z
;
173 #if CLANG_VERSION >= 130000
174 constexpr clang::ExprValueKind VK_PRValue
= clang::VK_PRValue
;
176 constexpr clang::ExprValueKind VK_PRValue
= clang::VK_RValue
;
179 inline bool EvaluateAsInt(clang::Expr
const * expr
, llvm::APSInt
& intRes
, const clang::ASTContext
& ctx
) {
180 #if CLANG_VERSION >= 80000
181 clang::Expr::EvalResult res
;
182 bool b
= expr
->EvaluateAsInt(res
, ctx
);
183 if (b
&& res
.Val
.isInt())
184 intRes
= res
.Val
.getInt();
187 return expr
->EvaluateAsInt(intRes
, ctx
);
191 inline llvm::Optional
<llvm::APSInt
> getIntegerConstantExpr(
192 clang::Expr
const * expr
, clang::ASTContext
const & context
)
194 #if CLANG_VERSION >= 120000
195 return expr
->getIntegerConstantExpr(context
);
198 return expr
->isIntegerConstantExpr(res
, context
) ? res
: llvm::Optional
<llvm::APSInt
>();
202 inline clang::Expr
* getSubExpr(clang::MaterializeTemporaryExpr
const * expr
) {
203 #if CLANG_VERSION >= 100000
204 return expr
->getSubExpr();
206 return expr
->GetTemporaryExpr();
210 #if CLANG_VERSION < 80000
211 inline clang::Expr
const * getSubExprAsWritten(clang::CastExpr
const * expr
)
212 { return expr
->getSubExprAsWritten(); }
214 // Work around CastExpr::getSubExprAsWritten firing
216 // include/llvm/Support/Casting.h:269: typename llvm::cast_retty<X, Y*>::ret_type llvm::cast(Y*)
217 // [with X = clang::CXXConstructExpr; Y = clang::Expr;
218 // typename llvm::cast_retty<X, Y*>::ret_type = clang::CXXConstructExpr*]: Assertion
219 // `isa<X>(Val) && "cast<Ty>() argument of incompatible type!"' failed.
221 // for CastExprs involving ConstantExpr (introduced with
222 // <https://github.com/llvm/llvm-project/commit/7c44da279e399d302a685c500e7f802f8adf9762> "Create
223 // ConstantExpr class" towards LLVM 8) like
225 // CXXFunctionalCastExpr 0xc01c4e8 'class rtl::OStringLiteral<9>':'class rtl::OStringLiteral<9>' functional cast to OStringLiteral <ConstructorConversion>
226 // `-ConstantExpr 0xc01c380 'class rtl::OStringLiteral<9>':'class rtl::OStringLiteral<9>'
228 // | |-fields: Int 1073741824, Int 8
229 // | `-field: Array size=9
230 // | |-elements: Int 46, Int 111, Int 115, Int 108
231 // | |-elements: Int 45, Int 116, Int 109, Int 112
232 // | `-element: Int 0
233 // `-CXXConstructExpr 0xc01c350 'class rtl::OStringLiteral<9>':'class rtl::OStringLiteral<9>' 'void (const char (&)[9])'
234 // `-StringLiteral 0xc019ad8 'const char [9]' lvalue ".osl-tmp"
236 // Copies code from Clang's lib/AST/Expr.cpp:
238 inline clang::Expr
*skipImplicitTemporary(clang::Expr
*expr
) {
239 // Skip through reference binding to temporary.
240 if (clang::MaterializeTemporaryExpr
*Materialize
241 = clang::dyn_cast
<clang::MaterializeTemporaryExpr
>(expr
))
242 expr
= compat::getSubExpr(Materialize
);
244 // Skip any temporary bindings; they're implicit.
245 if (clang::CXXBindTemporaryExpr
*Binder
= clang::dyn_cast
<clang::CXXBindTemporaryExpr
>(expr
))
246 expr
= Binder
->getSubExpr();
251 inline clang::Expr
*getSubExprAsWritten(clang::CastExpr
*This
) {
252 clang::Expr
*SubExpr
= nullptr;
253 clang::CastExpr
*E
= This
;
255 SubExpr
= detail::skipImplicitTemporary(E
->getSubExpr());
257 // Conversions by constructor and conversion functions have a
258 // subexpression describing the call; strip it off.
259 if (E
->getCastKind() == clang::CK_ConstructorConversion
)
261 detail::skipImplicitTemporary(clang::cast
<clang::CXXConstructExpr
>(SubExpr
->IgnoreImplicit())->getArg(0));
262 else if (E
->getCastKind() == clang::CK_UserDefinedConversion
) {
263 assert((clang::isa
<clang::CXXMemberCallExpr
>(SubExpr
) ||
264 clang::isa
<clang::BlockExpr
>(SubExpr
)) &&
265 "Unexpected SubExpr for CK_UserDefinedConversion.");
266 if (clang::isa
<clang::CXXMemberCallExpr
>(SubExpr
))
267 SubExpr
= clang::cast
<clang::CXXMemberCallExpr
>(SubExpr
)->getImplicitObjectArgument();
270 // If the subexpression we're left with is an implicit cast, look
271 // through that, too.
272 } while ((E
= clang::dyn_cast
<clang::ImplicitCastExpr
>(SubExpr
)));
276 inline const clang::Expr
*getSubExprAsWritten(const clang::CastExpr
*This
) {
277 return getSubExprAsWritten(const_cast<clang::CastExpr
*>(This
));
281 inline clang::QualType
getObjectType(clang::CXXMemberCallExpr
const * expr
) {
282 #if CLANG_VERSION >= 100000
283 return expr
->getObjectType();
285 // <https://github.com/llvm/llvm-project/commit/88559637641e993895337e1047a0bd787fecc647>
286 // "[OpenCL] Improve destructor support in C++ for OpenCL":
287 clang::QualType Ty
= expr
->getImplicitObjectArgument()->getType();
288 if (Ty
->isPointerType())
289 Ty
= Ty
->getPointeeType();
294 inline bool isExplicitSpecified(clang::CXXConstructorDecl
const * decl
) {
295 #if CLANG_VERSION >= 90000
296 return decl
->getExplicitSpecifier().isExplicit();
298 return decl
->isExplicitSpecified();
302 inline bool isExplicitSpecified(clang::CXXConversionDecl
const * decl
) {
303 #if CLANG_VERSION >= 90000
304 return decl
->getExplicitSpecifier().isExplicit();
306 return decl
->isExplicitSpecified();
310 inline clang::QualType
getDeclaredReturnType(clang::FunctionDecl
const * decl
) {
311 #if CLANG_VERSION >= 80000
312 return decl
->getDeclaredReturnType();
314 // <https://github.com/llvm/llvm-project/commit/4576a77b809649f5b8d0ff8c7a4be57eeee0ecf9>
315 // "PR33222: Require the declared return type not the actual return type to":
316 auto *TSI
= decl
->getTypeSourceInfo();
317 clang::QualType T
= TSI
? TSI
->getType() : decl
->getType();
318 return T
->castAs
<clang::FunctionType
>()->getReturnType();
322 // The isComparisonOp method on CXXOperatorCallExpr is not available yet for the clang we require
323 inline bool isComparisonOp(clang::CXXOperatorCallExpr
const * callExpr
)
325 using namespace clang
;
326 auto op
= callExpr
->getOperator();
327 return op
== OO_Less
|| op
== OO_Greater
|| op
== OO_LessEqual
|| op
== OO_GreaterEqual
328 || op
== OO_EqualEqual
|| op
== OO_ExclaimEqual
;
331 inline bool isPtrMemOp(clang::BinaryOperatorKind op
) {
332 #if CLANG_VERSION >= 80000
333 return clang::BinaryOperator::isPtrMemOp(op
);
335 return op
== clang::BO_PtrMemD
|| op
== clang::BO_PtrMemI
;
341 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */