//--------------------------------------------------------------------*- C++ -*-
// clad - the C++ Clang-based Automatic Differentiator
// author:  Alexander Penev <alexander_penev@yahoo.com>
//------------------------------------------------------------------------------

#ifndef CLAD_COMPATIBILITY
#define CLAD_COMPATIBILITY

#include "llvm/Config/llvm-config.h"

#include "clang/AST/ASTContext.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/Stmt.h"
#include "clang/Basic/Version.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Sema/Sema.h"
#if CLANG_VERSION_MAJOR > 18
#include "clang/Sema/SemaOpenMP.h"
#endif

namespace clad_compat {

using namespace clang;
using namespace llvm;

// clang-21
#if CLANG_VERSION_MAJOR < 21
#define CLAD_COMPAT_CLANG21_AtEndOfTUParam
#define CLAD_COMPAT_CLANG21_CSSExtendKWLocExtraParam(V) (V),
#define CLAD_COMPAT_CLANG21_StringLiteralParams(V) &(V)[0], (V).size()
#define CLAD_COMPAT_CLANG21_StringLiteralParamsRange , SourceRange()
#define CLAD_COMPAT_CLANG21_getTrailingRequiresClause(FD)                      \
  const_cast<FunctionDecl*>(FD)->getTrailingRequiresClause()
#define CLAD_COMPAT_CLANG21_getTrailingRequiresExpr(FD)                        \
  (FD)->getTrailingRequiresClause()
#define CLAD_COMPAT_CLANG21_UpdateTrailingRequiresClause(Trailing, V)          \
  (Trailing) = (V)
#define CLAD_COMPAT_CLANG21_TemplateKeywordParam , /*TemplateKeyword=*/false
#else
#define CLAD_COMPAT_CLANG21_AtEndOfTUParam , /*AtEndOfTU=*/true
#define CLAD_COMPAT_CLANG21_CSSExtendKWLocExtraParam(V)
#define CLAD_COMPAT_CLANG21_StringLiteralParams(V) (V)
#define CLAD_COMPAT_CLANG21_StringLiteralParamsRange
#define CLAD_COMPAT_CLANG21_getTrailingRequiresClause(FD)                      \
  (FD)->getTrailingRequiresClause()
#define CLAD_COMPAT_CLANG21_getTrailingRequiresExpr(FD)                        \
  (FD)->getTrailingRequiresClause().ConstraintExpr
#define CLAD_COMPAT_CLANG21_UpdateTrailingRequiresClause(Trailing, V)          \
  (Trailing).ConstraintExpr = (V)
#define CLAD_COMPAT_CLANG21_TemplateKeywordParam
#endif

// clang-21 OpenMPReductionClauseModifiers  got extra argument
#if CLANG_VERSION_MAJOR < 21
#define CLAD_COMPAT_CLANG21_getModifier(Clause) (Clause)->getModifier()
#else
#define CLAD_COMPAT_CLANG21_getModifier(Clause)                                \
  {(Clause)->getModifier(), (Clause)->getOriginalSharingModifier()}
#endif

// clang-20 Clause varlist typo
#if CLANG_VERSION_MAJOR < 20
#define CLAD_COMPAT_CLANG20_getvarlist(Clause) (Clause)->varlists()
#else
#define CLAD_COMPAT_CLANG20_getvarlist(Clause) (Clause)->varlist()
#endif

// clang-20 clang::Sema::AA_Casting became scoped
#if CLANG_VERSION_MAJOR < 20
#define CLAD_COMPAT_CLANG20_SemaAACasting clang::Sema::AA_Casting
#else
#define CLAD_COMPAT_CLANG20_SemaAACasting clang::AssignmentAction::Casting
#endif

// clang-19 SemaOpenMP was introduced
#if CLANG_VERSION_MAJOR < 19
#define CLAD_COMPAT_CLANG19_SemaOpenMP(Sema) (Sema)
#else
#define CLAD_COMPAT_CLANG19_SemaOpenMP(Sema) ((Sema).OpenMP())
#endif

// clang-18 CXXThisExpr got extra argument

#if CLANG_VERSION_MAJOR > 17
#define CLAD_COMPAT_CLANG17_CXXThisExpr_ExtraParam DEFINE_CREATE_EXPR(CXXThisExpr, (Ctx,
#else
#define CLAD_COMPAT_CLANG17_CXXThisExpr_ExtraParam DEFINE_CLONE_EXPR(CXXThisExpr, (
#endif

// clang-18 ActOnLambdaExpr got extra argument

#if CLANG_VERSION_MAJOR > 17
#define CLAD_COMPAT_CLANG17_ActOnLambdaExpr_getCurrentScope_ExtraParam(V) /**/
#else
#define CLAD_COMPAT_CLANG17_ActOnLambdaExpr_getCurrentScope_ExtraParam(V)      \
  , (V).getCurrentScope()
#endif

// Clang 18 ArrayType::Normal -> ArraySizeModifier::Normal

#if LLVM_VERSION_MAJOR < 18
const auto ArraySizeModifier_Normal = clang::ArrayType::Normal;
#else
const auto ArraySizeModifier_Normal = clang::ArraySizeModifier::Normal;
#endif

// Compatibility helper function for creation UnresolvedLookupExpr.
// Clang-18 extra argument knowndependent.
// FIXME: Knowndependent set to false temporarily until known value found for
// initialisation.

static inline Stmt* UnresolvedLookupExpr_Create(
    const ASTContext& Ctx, CXXRecordDecl* NamingClass,
    NestedNameSpecifierLoc QualifierLoc, SourceLocation TemplateKWLoc,
    const DeclarationNameInfo& NameInfo, bool RequiresADL,
    const TemplateArgumentListInfo* Args, UnresolvedSetIterator Begin,
    UnresolvedSetIterator End) {

#if CLANG_VERSION_MAJOR < 18
  return UnresolvedLookupExpr::Create(Ctx, NamingClass, QualifierLoc,
                                      TemplateKWLoc, NameInfo, RequiresADL,
                                      // They get copied again by
                                      // OverloadExpr, so we are safe.
                                      Args, Begin, End);

#elif CLANG_VERSION_MAJOR == 18
  bool KnownDependent = false;
  return UnresolvedLookupExpr::Create(Ctx, NamingClass, QualifierLoc,
                                      TemplateKWLoc, NameInfo, RequiresADL,
                                      // They get copied again by
                                      // OverloadExpr, so we are safe.
                                      Args, Begin, End, KnownDependent);
#else
  bool KnownDependent = false;
  bool KnownInstantiationDependent = false;
  return UnresolvedLookupExpr::Create(
      Ctx, NamingClass, QualifierLoc, TemplateKWLoc, NameInfo, RequiresADL,
      // They get copied again by
      // OverloadExpr, so we are safe.
      Args, Begin, End, KnownDependent, KnownInstantiationDependent);
#endif
}

// Clang 18 ETK_None -> ElaboratedTypeKeyword::None

#if LLVM_VERSION_MAJOR < 18
const auto ElaboratedTypeKeyword_None = ETK_None;
#else
const auto ElaboratedTypeKeyword_None = ElaboratedTypeKeyword::None;
#endif

// Clang 18 endswith->ends_with
// and starstwith->starts_with

#if LLVM_VERSION_MAJOR < 18
#define starts_with startswith
#define ends_with startswith
#endif

// Compatibility helper function for creation CompoundStmt.
// Clang 15 and above use a extra param FPFeatures in CompoundStmt::Create.

static inline bool SourceManager_isPointWithin(const SourceManager& SM,
                                               SourceLocation Loc,
                                               SourceLocation B,
                                               SourceLocation E) {
  return SM.isPointWithin(Loc, B, E);
}

#if CLANG_VERSION_MAJOR < 15
#define CLAD_COMPAT_CLANG15_CompoundStmt_Create_ExtraParam(Node) /**/
#define CLAD_COMPAT_CLANG15_CompoundStmt_Create_ExtraParam1(CS) /**/
#define CLAD_COMPAT_CLANG15_CompoundStmt_Create_ExtraParam2(FP) /**/
static inline CompoundStmt* CompoundStmt_Create(
        const ASTContext &Ctx, ArrayRef<Stmt *> Stmts,
        SourceLocation LB, SourceLocation RB)
{
   return CompoundStmt::Create(Ctx, Stmts, LB, RB);
}
#elif CLANG_VERSION_MAJOR >= 15
#define CLAD_COMPAT_CLANG15_CompoundStmt_Create_ExtraParam(Node) ,(Node)->getFPFeatures()
#define CLAD_COMPAT_CLANG15_CompoundStmt_Create_ExtraParam1(CS) ,(((CS)&&(CS)->hasStoredFPFeatures())?(CS)->getStoredFPFeatures():FPOptionsOverride())
#define CLAD_COMPAT_CLANG15_CompoundStmt_Create_ExtraParam2(FP) ,(FP)
static inline CompoundStmt* CompoundStmt_Create(
        const ASTContext &Ctx, ArrayRef<Stmt *> Stmts,
        FPOptionsOverride FPFeatures,
        SourceLocation LB, SourceLocation RB)
{
   return CompoundStmt::Create(Ctx, Stmts, FPFeatures, LB, RB);
}
#endif

#if CLANG_VERSION_MAJOR < 16
static inline NamespaceDecl*
NamespaceDecl_Create(ASTContext& C, DeclContext* DC, bool Inline,
                     SourceLocation StartLoc, SourceLocation IdLoc,
                     IdentifierInfo* Id, NamespaceDecl* PrevDecl) {
   return NamespaceDecl::Create(C, DC, Inline, StartLoc, IdLoc, Id, PrevDecl);
}
#else
static inline NamespaceDecl*
NamespaceDecl_Create(ASTContext& C, DeclContext* DC, bool Inline,
                     SourceLocation StartLoc, SourceLocation IdLoc,
                     IdentifierInfo* Id, NamespaceDecl* PrevDecl) {
   return NamespaceDecl::Create(C, DC, Inline, StartLoc, IdLoc, Id, PrevDecl,
                                /*Nested=*/false);
}
#endif

// Clang 12: bool Expr::EvaluateAsConstantExpr(EvalResult &Result,
// ConstExprUsage Usage, ASTContext &)
// => bool Expr::EvaluateAsConstantExpr(EvalResult &Result, ASTContext &)

static inline bool Expr_EvaluateAsConstantExpr(const Expr* E,
                                               Expr::EvalResult& res,
                                               const ASTContext& Ctx) {
#if CLANG_VERSION_MAJOR < 12
  return E->EvaluateAsConstantExpr(res, Expr::EvaluateForCodeGen, Ctx);
#else
  return E->EvaluateAsConstantExpr(res, Ctx);
#endif
}

// Compatibility helper function for creation IfStmt.
// Clang 12 and above use two extra params.

static inline IfStmt* IfStmt_Create(const ASTContext &Ctx,
   SourceLocation IL, bool IsConstexpr,
   Stmt *Init, VarDecl *Var, Expr *Cond,
   SourceLocation LPL, SourceLocation RPL,
   Stmt *Then, SourceLocation EL=SourceLocation(), Stmt *Else=nullptr)
{

#if CLANG_VERSION_MAJOR < 12
  return IfStmt::Create(Ctx, IL, IsConstexpr, Init, Var, Cond, Then, EL, Else);
#elif CLANG_VERSION_MAJOR < 14
  return IfStmt::Create(Ctx, IL, IsConstexpr, Init, Var, Cond, LPL, RPL, Then,
                        EL, Else);
#elif CLANG_VERSION_MAJOR >= 14
   IfStatementKind kind = IfStatementKind::Ordinary;
   if (IsConstexpr)
      kind = IfStatementKind::Constexpr;
   return IfStmt::Create(Ctx, IL, kind, Init, Var, Cond, LPL, RPL, Then, EL, Else);   
#endif
}

// Compatibility helper function for creation CallExpr and CUDAKernelCallExpr.
// Clang 12 and above use one extra param.

#if CLANG_VERSION_MAJOR < 12
static inline CallExpr* CallExpr_Create(const ASTContext &Ctx, Expr *Fn, ArrayRef< Expr *> Args,
   QualType Ty, ExprValueKind VK, SourceLocation RParenLoc,
   unsigned MinNumArgs = 0, CallExpr::ADLCallKind UsesADL = CallExpr::NotADL)
{
   return CallExpr::Create(Ctx, Fn, Args, Ty, VK, RParenLoc, MinNumArgs, UsesADL);
}

static inline CUDAKernelCallExpr*
CUDAKernelCallExpr_Create(const ASTContext& Ctx, Expr* Fn, CallExpr* Config,
                          ArrayRef<Expr*> Args, QualType Ty, ExprValueKind VK,
                          SourceLocation RParenLoc, unsigned MinNumArgs = 0,
                          CallExpr::ADLCallKind UsesADL = CallExpr::NotADL) {
  return CUDAKernelCallExpr::Create(Ctx, Fn, Config, Args, Ty, VK, RParenLoc,
                                    MinNumArgs);
}
#elif CLANG_VERSION_MAJOR >= 12
static inline CallExpr* CallExpr_Create(const ASTContext &Ctx, Expr *Fn, ArrayRef< Expr *> Args,
   QualType Ty, ExprValueKind VK, SourceLocation RParenLoc, FPOptionsOverride FPFeatures,
   unsigned MinNumArgs = 0, CallExpr::ADLCallKind UsesADL = CallExpr::NotADL)
{
   return CallExpr::Create(Ctx, Fn, Args, Ty, VK, RParenLoc, FPFeatures, MinNumArgs, UsesADL);
}

static inline CUDAKernelCallExpr*
CUDAKernelCallExpr_Create(const ASTContext& Ctx, Expr* Fn, CallExpr* Config,
                          ArrayRef<Expr*> Args, QualType Ty, ExprValueKind VK,
                          SourceLocation RParenLoc,
                          FPOptionsOverride FPFeatures, unsigned MinNumArgs = 0,
                          CallExpr::ADLCallKind UsesADL = CallExpr::NotADL) {
  return CUDAKernelCallExpr::Create(Ctx, Fn, Config, Args, Ty, VK, RParenLoc,
                                    FPFeatures, MinNumArgs);
}
#endif

// Clang 12 and above use one extra param.

#if CLANG_VERSION_MAJOR < 12
#define CLAD_COMPAT_CLANG8_CallExpr_ExtraParams                                \
  , Node->getNumArgs(), Node->getADLCallKind()
#elif CLANG_VERSION_MAJOR >= 12
   #define CLAD_COMPAT_CLANG8_CallExpr_ExtraParams ,Node->getFPFeatures(),Node->getNumArgs(),Node->getADLCallKind()
#endif

static inline void ExprSetDeps(Expr* result, Expr* Node) {
  struct ExprDependenceAccessor : public Expr {
    void setDependence(ExprDependence Deps) { Expr::setDependence(Deps); }
  };
   ((ExprDependenceAccessor*)result)->setDependence(Node->getDependence());
}

// Compatibility helper function for creation CXXMemberCallExpr.
// Clang 12 and above use two extra param.

#if CLANG_VERSION_MAJOR < 12
static inline CXXMemberCallExpr* CXXMemberCallExpr_Create(ASTContext &Ctx,
   Expr *Fn, ArrayRef<Expr *> Args, QualType Ty, ExprValueKind VK, SourceLocation RP)
{
   return CXXMemberCallExpr::Create(const_cast<ASTContext&>(Ctx), Fn, Args, Ty, VK, RP);
}
#elif CLANG_VERSION_MAJOR >= 12
static inline CXXMemberCallExpr* CXXMemberCallExpr_Create(ASTContext &Ctx,
   Expr *Fn, ArrayRef<Expr *> Args, QualType Ty, ExprValueKind VK, SourceLocation RP,
   FPOptionsOverride FPFeatures, unsigned MinNumArgs = 0)
{
   return CXXMemberCallExpr::Create(const_cast<ASTContext&>(Ctx), Fn, Args, Ty, VK, RP,
                                    FPFeatures, MinNumArgs);
}
#endif

// Compatibility helper function for creation SwitchStmt.
// Clang 12 and above use two extra params.

static inline SwitchStmt* SwitchStmt_Create(const ASTContext &Ctx,
   Stmt *Init, VarDecl *Var, Expr *Cond,
   SourceLocation LParenLoc, SourceLocation RParenLoc)
{

#if CLANG_VERSION_MAJOR < 12
  return SwitchStmt::Create(Ctx, Init, Var, Cond);
#elif CLANG_VERSION_MAJOR >= 12
   return SwitchStmt::Create(Ctx, Init, Var, Cond, LParenLoc, RParenLoc);
#endif
}

template<class T>
static inline T GetResult(ActionResult<T> Res)
{
   return Res.get();
}

// clang 18 clang::ArrayType::ArraySizeModifier became clang::ArraySizeModifier
#if CLANG_VERSION_MAJOR < 18
static inline QualType
getConstantArrayType(const ASTContext& Ctx, QualType EltTy,
                     const APInt& ArySize, const Expr* SizeExpr,
                     clang::ArrayType::ArraySizeModifier ASM,
                     unsigned IndexTypeQuals) {
  return Ctx.getConstantArrayType(EltTy, ArySize, SizeExpr, ASM,
                                  IndexTypeQuals);
}
#else
static inline QualType
getConstantArrayType(const ASTContext& Ctx, QualType EltTy,
                     const APInt& ArySize, const Expr* SizeExpr,
                     clang::ArraySizeModifier ASM, unsigned IndexTypeQuals) {
  return Ctx.getConstantArrayType(EltTy, ArySize, SizeExpr, ASM,
                                  IndexTypeQuals);
}
#endif

// Clang 12 rename DeclaratorContext::LambdaExprContext to DeclaratorContext::LambdaExpr.
// Clang 15 add one extra param to clang::Declarator() - const ParsedAttributesView & DeclarationAttrs

#if CLANG_VERSION_MAJOR < 12
   #define CLAD_COMPAT_CLANG12_Declarator_LambdaExpr clang::DeclaratorContext::LambdaExprContext
   #define CLAD_COMPAT_CLANG15_Declarator_DeclarationAttrs_ExtraParam /**/
#elif CLANG_VERSION_MAJOR < 15
   #define CLAD_COMPAT_CLANG12_Declarator_LambdaExpr clang::DeclaratorContext::LambdaExpr
   #define CLAD_COMPAT_CLANG15_Declarator_DeclarationAttrs_ExtraParam /**/
#elif CLANG_VERSION_MAJOR >= 15
   #define CLAD_COMPAT_CLANG12_Declarator_LambdaExpr clang::DeclaratorContext::LambdaExpr
   #define CLAD_COMPAT_CLANG15_Declarator_DeclarationAttrs_ExtraParam clang::ParsedAttributesView::none(),
#endif

// Clang 12 add one extra param (FPO) that we get from Node in Create method of:
// ImplicitCastExpr, CStyleCastExpr, CXXStaticCastExpr and CXXFunctionalCastExpr

#if CLANG_VERSION_MAJOR < 12
   #define CLAD_COMPAT_CLANG12_CastExpr_GetFPO(Node) /**/
#elif CLANG_VERSION_MAJOR >= 12
   #define CLAD_COMPAT_CLANG12_CastExpr_GetFPO(Node) ,Node->getFPFeatures()
#endif

// Clang 12 adds one extra param (FPO) in Create method of:
// ImplicitCastExpr, CStyleCastExpr, CXXStaticCastExpr and CXXFunctionalCastExpr

#if CLANG_VERSION_MAJOR < 12
#define CLAD_COMPAT_CLANG12_CastExpr_DefaultFPO /**/
#elif CLANG_VERSION_MAJOR >= 12
#define CLAD_COMPAT_CLANG12_CastExpr_DefaultFPO , FPOptionsOverride()
#endif

// Clang 12 add two extra param (Left and Right paren location) in Create method of:
// IfStat::Create

#if CLANG_VERSION_MAJOR < 12
   #define CLAD_COMPAT_CLANG12_LR_ExtraParams(Node) /**/
#elif CLANG_VERSION_MAJOR >= 12
   #define CLAD_COMPAT_CLANG12_LR_ExtraParams(Node) ,Node->getLParenLoc(),Node->getRParenLoc()
#endif

/// Clang >= 12 has more source locations parameters in `Sema::ActOnStartOfSwitchStmt`
static inline StmtResult
Sema_ActOnStartOfSwitchStmt(Sema& SemaRef, Stmt* initStmt,
                            Sema::ConditionResult Cond) {
  SourceLocation noLoc;
#if CLANG_VERSION_MAJOR >= 12
  return SemaRef.ActOnStartOfSwitchStmt(
      /*SwitchLoc=*/noLoc,
      /*LParenLoc=*/noLoc, initStmt, Cond,
      /*RParenLoc=*/noLoc);
#elif CLANG_VERSION_MAJOR < 12
  return SemaRef.ActOnStartOfSwitchStmt(/*SwitchLoc=*/noLoc, initStmt, Cond);
#endif
}

#if CLANG_VERSION_MAJOR < 13
#define CLAD_COMPAT_ExprValueKind_R_or_PR_Value ExprValueKind::VK_RValue
#elif CLANG_VERSION_MAJOR >= 13
#define CLAD_COMPAT_ExprValueKind_R_or_PR_Value ExprValueKind::VK_PRValue
#endif

#if LLVM_VERSION_MAJOR < 13
#define CLAD_COMPAT_llvm_sys_fs_Append llvm::sys::fs::F_Append
#elif LLVM_VERSION_MAJOR >= 13
#define CLAD_COMPAT_llvm_sys_fs_Append llvm::sys::fs::OF_Append
#endif

#if CLANG_VERSION_MAJOR < 19
#define CLAD_COMPAT_Sema_ForVisibleRedeclaration Sema::ForVisibleRedeclaration
#else
#define CLAD_COMPAT_Sema_ForVisibleRedeclaration                               \
  RedeclarationKind::ForVisibleRedeclaration
#endif

#if CLANG_VERSION_MAJOR <= 13
#define CLAD_COMPAT_FunctionDecl_UsesFPIntrin_Param(FD) /**/
#elif CLANG_VERSION_MAJOR > 13
#define CLAD_COMPAT_FunctionDecl_UsesFPIntrin_Param(FD) , FD->UsesFPIntrin()
#endif

#if CLANG_VERSION_MAJOR <= 13
#define CLAD_COMPAT_IfStmt_Create_IfStmtKind_Param(Node) Node->isConstexpr()
#elif CLANG_VERSION_MAJOR > 13
#define CLAD_COMPAT_IfStmt_Create_IfStmtKind_Param(Node) Node->getStatementKind()
#endif

#if CLANG_VERSION_MAJOR < 19
static inline MemberExpr* BuildMemberExpr(
    Sema& semaRef, Expr* base, bool isArrow, SourceLocation opLoc,
    const CXXScopeSpec* SS, SourceLocation templateKWLoc, ValueDecl* member,
    DeclAccessPair foundDecl, bool hadMultipleCandidates,
    const DeclarationNameInfo& memberNameInfo, QualType ty, ExprValueKind VK,
    ExprObjectKind OK, const TemplateArgumentListInfo* templateArgs = nullptr) {
  return semaRef.BuildMemberExpr(base, isArrow, opLoc, SS, templateKWLoc,
                                 member, foundDecl, hadMultipleCandidates,
                                 memberNameInfo, ty, VK, OK, templateArgs);
}
#else
static inline MemberExpr* BuildMemberExpr(
    Sema& semaRef, Expr* base, bool isArrow, SourceLocation opLoc,
    const CXXScopeSpec* SS, SourceLocation templateKWLoc, ValueDecl* member,
    DeclAccessPair foundDecl, bool hadMultipleCandidates,
    const DeclarationNameInfo& memberNameInfo, QualType ty, ExprValueKind VK,
    ExprObjectKind OK, const TemplateArgumentListInfo* templateArgs = nullptr) {
  NestedNameSpecifierLoc NNS =
      SS ? SS->getWithLocInContext(semaRef.getASTContext())
         : NestedNameSpecifierLoc();
  return semaRef.BuildMemberExpr(base, isArrow, opLoc, NNS, templateKWLoc,
                                 member, foundDecl, hadMultipleCandidates,
                                 memberNameInfo, ty, VK, OK, templateArgs);
}
#endif

static inline QualType
CXXMethodDecl_GetThisObjectType(Sema& semaRef, const CXXMethodDecl* MD) {
// clang-18 renamed getThisObjectType to getFunctionObjectParameterType
#if CLANG_VERSION_MAJOR < 18
  return MD->getThisObjectType();
#else
  return MD->getFunctionObjectParameterType();
#endif
}

#if CLANG_VERSION_MAJOR < 12
#define CLAD_COMPAT_SubstNonTypeTemplateParmExpr_isReferenceParameter_ExtraParam( \
    Node) /**/
#else
#define CLAD_COMPAT_SubstNonTypeTemplateParmExpr_isReferenceParameter_ExtraParam( \
    Node)                                                                         \
  Node->isReferenceParameter(),
#endif

#if CLANG_VERSION_MAJOR < 16
template <typename T>
llvm::ArrayRef<T> makeArrayRef(const T* data, size_t length) {
  return llvm::makeArrayRef(data, length);
}
template <typename T>
llvm::ArrayRef<T> makeArrayRef(const T* begin, const T* end) {
  return llvm::makeArrayRef(begin, end);
}
#else
template <typename T>
llvm::ArrayRef<T> makeArrayRef(const T* data, size_t length) {
  return llvm::ArrayRef(data, length);
}
template <typename T>
llvm::ArrayRef<T> makeArrayRef(const T* begin, const T* end) {
  return llvm::ArrayRef(begin, end);
}
#endif

#if CLANG_VERSION_MAJOR < 16
template <typename T> using llvm_Optional = llvm::Optional<T>;
#else
template <typename T> using llvm_Optional = std::optional<T>;
#endif

#if CLANG_VERSION_MAJOR < 16
template <typename T> T& llvm_Optional_GetValue(llvm::Optional<T>& opt) {
  return opt.getValue();
}
#else
template <typename T> T& llvm_Optional_GetValue(std::optional<T>& opt) {
  return opt.value();
}
#endif

#if CLANG_VERSION_MAJOR < 16
static inline const Expr*
ArraySize_GetValue(const llvm::Optional<const Expr*>& opt) {
  return opt.getValue();
}
#else
static inline const Expr*
ArraySize_GetValue(const std::optional<const Expr*>& opt) {
   return opt.value();
}
#endif

#if CLANG_VERSION_MAJOR < 16
#define CLAD_COMPAT_CLANG16_LangOptions_ExtraParams Ctx.getLangOpts()
#else
#define CLAD_COMPAT_CLANG16_LangOptions_ExtraParams /**/
#endif

#if CLANG_VERSION_MAJOR < 13
static inline bool IsPRValue(const Expr* E) { return E->isRValue(); }
#else
static inline bool IsPRValue(const Expr* E) { return E->isPRValue(); }
#endif

#if CLANG_VERSION_MAJOR >= 16
#define CLAD_COMPAT_CLANG16_CXXDefaultArgExpr_getRewrittenExpr_Param(Node)     \
  , Node->getRewrittenExpr()
#else
#define CLAD_COMPAT_CLANG16_CXXDefaultArgExpr_getRewrittenExpr_Param(Node) /**/
#endif

// Clang 15 renamed StringKind::Ascii to StringKind::Ordinary
// Clang 18 renamed clang::StringLiteral::StringKind::Ordinary became
// clang::StringLiteralKind::Ordinary;

#if CLANG_VERSION_MAJOR < 15
const auto StringLiteralKind_Ordinary = clang::StringLiteral::StringKind::Ascii;
#elif CLANG_VERSION_MAJOR >= 15
#if CLANG_VERSION_MAJOR < 18
const auto StringLiteralKind_Ordinary =
    clang::StringLiteral::StringKind::Ordinary;
#else
const auto StringLiteralKind_Ordinary = clang::StringLiteralKind::Ordinary;
#endif
#endif

// Clang 15 add one extra param to Sema::CheckFunctionDeclaration

#if CLANG_VERSION_MAJOR < 15
#define CLAD_COMPAT_CheckFunctionDeclaration_DeclIsDefn_ExtraParam(Fn) /**/
#elif CLANG_VERSION_MAJOR >= 15
#define CLAD_COMPAT_CheckFunctionDeclaration_DeclIsDefn_ExtraParam(Fn) \
  ,Fn->isThisDeclarationADefinition()
#endif


// Clang 17 change type of last param of ActOnStartOfLambdaDefinition
// from Scope* to 'const DeclSpec&'
#if CLANG_VERSION_MAJOR < 17
static inline Scope* Sema_ActOnStartOfLambdaDefinition_ScopeOrDeclSpec(Scope *CurScope, const DeclSpec &DS) {
  return CurScope;
}
#elif CLANG_VERSION_MAJOR >= 17
static inline const DeclSpec& Sema_ActOnStartOfLambdaDefinition_ScopeOrDeclSpec(Scope *CurScope, const DeclSpec &DS) {
  return DS;
}
#endif

// Clang 17 add one extra param to clang::PredefinedExpr::Create - isTransparent

#if CLANG_VERSION_MAJOR < 17
#define CLAD_COMPAT_CLANG17_IsTransparent(Node) /**/
#elif CLANG_VERSION_MAJOR >= 17
#define CLAD_COMPAT_CLANG17_IsTransparent(Node) \
  ,Node->isTransparent()
#endif

// Clang 19 renamed the enum representing template resolution results
#if CLANG_VERSION_MAJOR >= 19
#define CLAD_COMPAT_TemplateSuccess TemplateDeductionResult::Success
#else
#define CLAD_COMPAT_TemplateSuccess Sema::TDK_Success
#endif

} // namespace clad_compat
#endif //CLAD_COMPATIBILITY
