#include "version.h"

#if CLANG_VERSION_NUMBER == VERSION_NUMBER_11_0_0
#elif CLANG_VERSION_NUMBER == VERSION_NUMBER_11_0_1
#elif CLANG_VERSION_NUMBER == VERSION_NUMBER_11_1_0
#elif CLANG_VERSION_NUMBER == VERSION_NUMBER_12_0_1
#elif CLANG_VERSION_NUMBER == VERSION_NUMBER_14_0_0
#elif CLANG_VERSION_NUMBER == VERSION_NUMBER_14_0_6
#elif CLANG_VERSION_NUMBER == VERSION_NUMBER_18_1_3
#elif CLANG_VERSION_NUMBER == VERSION_NUMBER_18_1_8
#elif CLANG_VERSION_NUMBER == VERSION_NUMBER_19_1_6
#elif CLANG_VERSION_NUMBER == VERSION_NUMBER_20_1_2
#elif CLANG_VERSION_NUMBER == VERSION_NUMBER_21_1_6
#elif CLANG_VERSION_NUMBER == VERSION_NUMBER_21_1_8
#else
#warning "Unsupported clang version used!"
#endif

#include "ClangIntroSema.h"
#include "ClangWeaverBase.h"
using namespace clang;

ClangIntroSema *intro_sema; // global set by Transformer

// first define some helper macros to avoid code duplication

#ifdef __APPLE__
#define WRAP(x) x
#else
#define WRAP(x) __wrap_##x
#endif

#define GEN_SEMA_WRAPPER_WITH_RESULT(result_type, func_name, mangled, param_list, ...) \
  extern "C" result_type WRAP(mangled) param_list; \
  result_type WRAP(mangled) param_list { \
    return intro_sema->func_name(__VA_ARGS__); \
  } \
  extern "C" result_type __real_##mangled param_list; \
  result_type Real##func_name param_list { \
    return __real_##mangled(that, __VA_ARGS__); \
  }

#define GEN_SEMA_WRAPPER_NO_RESULT(func_name, mangled, param_list, ...) \
  extern "C" void WRAP(mangled) param_list; \
  void WRAP(mangled) param_list { \
    intro_sema->func_name(__VA_ARGS__); \
  } \
  extern "C" void __real_##mangled param_list; \
  void Real##func_name param_list { \
    __real_##mangled(that, __VA_ARGS__); \
  }

#define GEN_SEMA_WRAPPER_NO_RESULT_NO_ARG(func_name, mangled) \
  extern "C" void WRAP(mangled) (Sema &that); \
  void WRAP(mangled) (Sema &that) { \
    intro_sema->func_name(); \
  } \
  extern "C" void __real_##mangled (Sema &that); \
  void Real##func_name (Sema &that) { \
    __real_##mangled(that); \
  }

// wrapper for Sema::canSkipFunctionBody
GEN_SEMA_WRAPPER_WITH_RESULT(bool, canSkipFunctionBody, _ZN5clang4Sema19canSkipFunctionBodyEPNS_4DeclE, \
    (clang::Sema &that, clang::Decl *D), D)

#if CLANG_VERSION_NUMBER >= VERSION_NUMBER_18_1_3
// wrapper for Sema::ProcessStmtAttributes
GEN_SEMA_WRAPPER_NO_RESULT(ProcessStmtAttributes, \
    _ZN5clang4Sema21ProcessStmtAttributesEPNS_4StmtERKNS_16ParsedAttributesERN4llvm15SmallVectorImplIPKNS_4AttrEEE, \
    (clang::Sema &that, clang::Stmt *S, const ParsedAttributes &InAttrs, SmallVectorImpl<const Attr *> &OutAttrs), S, InAttrs, OutAttrs)
#elif CLANG_VERSION_NUMBER >= VERSION_NUMBER_14_0_0
// wrapper for Sema::ProcessStmtAttributes
GEN_SEMA_WRAPPER_NO_RESULT(ProcessStmtAttributes, \
    _ZN5clang4Sema21ProcessStmtAttributesEPNS_4StmtERKNS_25ParsedAttributesWithRangeERN4llvm15SmallVectorImplIPKNS_4AttrEEE, \
    (clang::Sema &that, clang::Stmt *S, const ParsedAttributes &InAttrs, SmallVectorImpl<const Attr *> &OutAttrs), S, InAttrs, OutAttrs)
#else
// wrapper for Sema::ProcessStmtAttributes
GEN_SEMA_WRAPPER_WITH_RESULT(clang::StmtResult, ProcessStmtAttributes, _ZN5clang4Sema21ProcessStmtAttributesEPNS_4StmtERKNS_20ParsedAttributesViewENS_11SourceRangeE, \
    (clang::Sema &that, clang::Stmt *S, const clang::ParsedAttributesView &AttrList, clang::SourceRange Range), S, AttrList, Range)
#endif
