#include "clang/Basic/Version.h"

#if CLANG_VERSION_NUMBER == VERSION_NUMBER_11_0_0
#else
#warning "Invalid clang version used! Only 11.0.0 is supported by this code."
#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)

// 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)
