//===-- ClangASTContext.h ---------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#ifndef liblldb_ClangASTContext_h_
#define liblldb_ClangASTContext_h_

// C Includes
#include <stdint.h>

// C++ Includes
#include <functional>
#include <initializer_list>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>

// Other libraries and framework includes
#include "clang/AST/ASTContext.h"
#include "clang/AST/ExternalASTMerger.h"
#include "clang/AST/TemplateBase.h"
#include "llvm/ADT/SmallVector.h"

// Project includes
#include "Plugins/ExpressionParser/Clang/ClangPersistentVariables.h"
#include "lldb/Core/ClangForward.h"
#include "lldb/Symbol/CompilerType.h"
#include "lldb/Symbol/TypeSystem.h"
#include "lldb/Utility/ConstString.h"
#include "lldb/lldb-enumerations.h"

class DWARFASTParserClang;
class PDBASTParser;

namespace lldb_private {

class Declaration;

class ClangASTContext : public TypeSystem {
public:
  typedef void (*CompleteTagDeclCallback)(void *baton, clang::TagDecl *);
  typedef void (*CompleteObjCInterfaceDeclCallback)(void *baton,
                                                    clang::ObjCInterfaceDecl *);

  //------------------------------------------------------------------
  // llvm casting support
  //------------------------------------------------------------------
  static bool classof(const TypeSystem *ts) {
    return ts->getKind() == TypeSystem::eKindClang;
  }

  //------------------------------------------------------------------
  // Constructors and Destructors
  //------------------------------------------------------------------
  ClangASTContext(const char *triple = nullptr);

  ~ClangASTContext() override;

  void Finalize() override;

  //------------------------------------------------------------------
  // PluginInterface functions
  //------------------------------------------------------------------
  ConstString GetPluginName() override;

  uint32_t GetPluginVersion() override;

  static ConstString GetPluginNameStatic();

  static lldb::TypeSystemSP CreateInstance(lldb::LanguageType language,
                                           Module *module, Target *target);

  static void EnumerateSupportedLanguages(
      std::set<lldb::LanguageType> &languages_for_types,
      std::set<lldb::LanguageType> &languages_for_expressions);

  static void Initialize();

  static void Terminate();

  static ClangASTContext *GetASTContext(clang::ASTContext *ast_ctx);

  clang::ASTContext *getASTContext();

  void setASTContext(clang::ASTContext *ast_ctx);

  clang::Builtin::Context *getBuiltinContext();

  clang::IdentifierTable *getIdentifierTable();

  clang::LangOptions *getLanguageOptions();

  clang::SelectorTable *getSelectorTable();

  clang::FileManager *getFileManager();

  clang::SourceManager *getSourceManager();

  clang::DiagnosticsEngine *getDiagnosticsEngine();

  clang::DiagnosticConsumer *getDiagnosticConsumer();

  clang::MangleContext *getMangleContext();

  std::shared_ptr<clang::TargetOptions> &getTargetOptions();

  clang::TargetInfo *getTargetInfo();

  void Clear();

  const char *GetTargetTriple();

  void SetTargetTriple(const char *target_triple);

  void SetArchitecture(const ArchSpec &arch);

  bool HasExternalSource();

  void SetExternalSource(
      llvm::IntrusiveRefCntPtr<clang::ExternalASTSource> &ast_source_ap);

  void RemoveExternalSource();

  bool GetCompleteDecl(clang::Decl *decl) {
    return ClangASTContext::GetCompleteDecl(getASTContext(), decl);
  }

  static void DumpDeclHiearchy(clang::Decl *decl);

  static void DumpDeclContextHiearchy(clang::DeclContext *decl_ctx);

  static bool DeclsAreEquivalent(clang::Decl *lhs_decl, clang::Decl *rhs_decl);

  static bool GetCompleteDecl(clang::ASTContext *ast, clang::Decl *decl);

  void SetMetadataAsUserID(const void *object, lldb::user_id_t user_id);

  void SetMetadata(const void *object, ClangASTMetadata &meta_data) {
    SetMetadata(getASTContext(), object, meta_data);
  }

  static void SetMetadata(clang::ASTContext *ast, const void *object,
                          ClangASTMetadata &meta_data);

  ClangASTMetadata *GetMetadata(const void *object) {
    return GetMetadata(getASTContext(), object);
  }

  static ClangASTMetadata *GetMetadata(clang::ASTContext *ast,
                                       const void *object);

  //------------------------------------------------------------------
  // Basic Types
  //------------------------------------------------------------------
  CompilerType GetBuiltinTypeForEncodingAndBitSize(lldb::Encoding encoding,
                                                   size_t bit_size) override;

  static CompilerType GetBuiltinTypeForEncodingAndBitSize(
      clang::ASTContext *ast, lldb::Encoding encoding, uint32_t bit_size);

  CompilerType GetBasicType(lldb::BasicType type);

  static CompilerType GetBasicType(clang::ASTContext *ast,
                                   lldb::BasicType type);

  static CompilerType GetBasicType(clang::ASTContext *ast,
                                   const ConstString &name);

  static lldb::BasicType GetBasicTypeEnumeration(const ConstString &name);

  CompilerType GetBuiltinTypeForDWARFEncodingAndBitSize(const char *type_name,
                                                        uint32_t dw_ate,
                                                        uint32_t bit_size);

  CompilerType GetCStringType(bool is_const);

  static CompilerType GetUnknownAnyType(clang::ASTContext *ast);

  CompilerType GetUnknownAnyType() {
    return ClangASTContext::GetUnknownAnyType(getASTContext());
  }

  static clang::DeclContext *GetDeclContextForType(clang::QualType type);

  static clang::DeclContext *GetDeclContextForType(const CompilerType &type);

  uint32_t GetPointerByteSize() override;

  static clang::DeclContext *GetTranslationUnitDecl(clang::ASTContext *ast);

  clang::DeclContext *GetTranslationUnitDecl() {
    return GetTranslationUnitDecl(getASTContext());
  }

  static clang::Decl *CopyDecl(clang::ASTContext *dest_context,
                               clang::ASTContext *source_context,
                               clang::Decl *source_decl);

  static bool AreTypesSame(CompilerType type1, CompilerType type2,
                           bool ignore_qualifiers = false);

  static CompilerType GetTypeForDecl(clang::NamedDecl *decl);

  static CompilerType GetTypeForDecl(clang::TagDecl *decl);

  static CompilerType GetTypeForDecl(clang::ObjCInterfaceDecl *objc_decl);

  template <typename RecordDeclType>
  CompilerType GetTypeForIdentifier(const ConstString &type_name) {
    CompilerType compiler_type;

    if (type_name.GetLength()) {
      clang::ASTContext *ast = getASTContext();
      if (ast) {
        clang::IdentifierInfo &myIdent =
            ast->Idents.get(type_name.GetCString());
        clang::DeclarationName myName =
            ast->DeclarationNames.getIdentifier(&myIdent);

        clang::DeclContext::lookup_result result =
            ast->getTranslationUnitDecl()->lookup(myName);

        if (!result.empty()) {
          clang::NamedDecl *named_decl = result[0];
          if (const RecordDeclType *record_decl =
                  llvm::dyn_cast<RecordDeclType>(named_decl))
            compiler_type.SetCompilerType(
                ast, clang::QualType(record_decl->getTypeForDecl(), 0));
        }
      }
    }

    return compiler_type;
  }

  CompilerType CreateStructForIdentifier(
      const ConstString &type_name,
      const std::initializer_list<std::pair<const char *, CompilerType>>
          &type_fields,
      bool packed = false);

  CompilerType GetOrCreateStructForIdentifier(
      const ConstString &type_name,
      const std::initializer_list<std::pair<const char *, CompilerType>>
          &type_fields,
      bool packed = false);

  //------------------------------------------------------------------
  // Structure, Unions, Classes
  //------------------------------------------------------------------

  static clang::AccessSpecifier
  ConvertAccessTypeToAccessSpecifier(lldb::AccessType access);

  static clang::AccessSpecifier
  UnifyAccessSpecifiers(clang::AccessSpecifier lhs, clang::AccessSpecifier rhs);

  static uint32_t GetNumBaseClasses(const clang::CXXRecordDecl *cxx_record_decl,
                                    bool omit_empty_base_classes);

  CompilerType CreateRecordType(clang::DeclContext *decl_ctx,
                                lldb::AccessType access_type, const char *name,
                                int kind, lldb::LanguageType language,
                                ClangASTMetadata *metadata = nullptr);

  class TemplateParameterInfos {
  public:
    bool IsValid() const {
      if (args.empty())
        return false;
      return args.size() == names.size() &&
        ((bool)pack_name == (bool)packed_args) &&
        (!packed_args || !packed_args->packed_args);
    }

    llvm::SmallVector<const char *, 2> names;
    llvm::SmallVector<clang::TemplateArgument, 2> args;
    
    const char * pack_name = nullptr;
    std::unique_ptr<TemplateParameterInfos> packed_args;
  };

  clang::FunctionTemplateDecl *
  CreateFunctionTemplateDecl(clang::DeclContext *decl_ctx,
                             clang::FunctionDecl *func_decl, const char *name,
                             const TemplateParameterInfos &infos);

  void CreateFunctionTemplateSpecializationInfo(
      clang::FunctionDecl *func_decl, clang::FunctionTemplateDecl *Template,
      const TemplateParameterInfos &infos);

  clang::ClassTemplateDecl *
  CreateClassTemplateDecl(clang::DeclContext *decl_ctx,
                          lldb::AccessType access_type, const char *class_name,
                          int kind, const TemplateParameterInfos &infos);

  clang::ClassTemplateSpecializationDecl *CreateClassTemplateSpecializationDecl(
      clang::DeclContext *decl_ctx,
      clang::ClassTemplateDecl *class_template_decl, int kind,
      const TemplateParameterInfos &infos);

  CompilerType
  CreateClassTemplateSpecializationType(clang::ClassTemplateSpecializationDecl *
                                            class_template_specialization_decl);

  static clang::DeclContext *
  GetAsDeclContext(clang::CXXMethodDecl *cxx_method_decl);

  static clang::DeclContext *
  GetAsDeclContext(clang::ObjCMethodDecl *objc_method_decl);

  static bool CheckOverloadedOperatorKindParameterCount(
      bool is_method, clang::OverloadedOperatorKind op_kind,
      uint32_t num_params);

  bool FieldIsBitfield(clang::FieldDecl *field, uint32_t &bitfield_bit_size);

  static bool FieldIsBitfield(clang::ASTContext *ast, clang::FieldDecl *field,
                              uint32_t &bitfield_bit_size);

  static bool RecordHasFields(const clang::RecordDecl *record_decl);

  CompilerType CreateObjCClass(const char *name, clang::DeclContext *decl_ctx,
                               bool isForwardDecl, bool isInternal,
                               ClangASTMetadata *metadata = nullptr);

  bool SetTagTypeKind(clang::QualType type, int kind) const;

  bool SetDefaultAccessForRecordFields(clang::RecordDecl *record_decl,
                                       int default_accessibility,
                                       int *assigned_accessibilities,
                                       size_t num_assigned_accessibilities);

  // Returns a mask containing bits from the ClangASTContext::eTypeXXX
  // enumerations

  //------------------------------------------------------------------
  // Namespace Declarations
  //------------------------------------------------------------------

  clang::NamespaceDecl *
  GetUniqueNamespaceDeclaration(const char *name, clang::DeclContext *decl_ctx);

  static clang::NamespaceDecl *
  GetUniqueNamespaceDeclaration(clang::ASTContext *ast, const char *name,
                                clang::DeclContext *decl_ctx);

  //------------------------------------------------------------------
  // Function Types
  //------------------------------------------------------------------

  clang::FunctionDecl *
  CreateFunctionDeclaration(clang::DeclContext *decl_ctx, const char *name,
                            const CompilerType &function_Type, int storage,
                            bool is_inline);

  static CompilerType CreateFunctionType(clang::ASTContext *ast,
                                         const CompilerType &result_type,
                                         const CompilerType *args,
                                         unsigned num_args, bool is_variadic,
                                         unsigned type_quals);

  CompilerType CreateFunctionType(const CompilerType &result_type,
                                  const CompilerType *args, unsigned num_args,
                                  bool is_variadic, unsigned type_quals) {
    return ClangASTContext::CreateFunctionType(
        getASTContext(), result_type, args, num_args, is_variadic, type_quals);
  }

  clang::ParmVarDecl *CreateParameterDeclaration(const char *name,
                                                 const CompilerType &param_type,
                                                 int storage);

  void SetFunctionParameters(clang::FunctionDecl *function_decl,
                             clang::ParmVarDecl **params, unsigned num_params);

  CompilerType CreateBlockPointerType(const CompilerType &function_type);

  //------------------------------------------------------------------
  // Array Types
  //------------------------------------------------------------------

  CompilerType CreateArrayType(const CompilerType &element_type,
                               size_t element_count, bool is_vector);

  //------------------------------------------------------------------
  // Enumeration Types
  //------------------------------------------------------------------
  CompilerType CreateEnumerationType(const char *name,
                                     clang::DeclContext *decl_ctx,
                                     const Declaration &decl,
                                     const CompilerType &integer_qual_type,
                                     bool is_scoped);

  //------------------------------------------------------------------
  // Integer type functions
  //------------------------------------------------------------------

  static CompilerType GetIntTypeFromBitSize(clang::ASTContext *ast,
                                            size_t bit_size, bool is_signed);

  CompilerType GetPointerSizedIntType(bool is_signed) {
    return GetPointerSizedIntType(getASTContext(), is_signed);
  }

  static CompilerType GetPointerSizedIntType(clang::ASTContext *ast,
                                             bool is_signed);

  //------------------------------------------------------------------
  // Floating point functions
  //------------------------------------------------------------------

  static CompilerType GetFloatTypeFromBitSize(clang::ASTContext *ast,
                                              size_t bit_size);

  //------------------------------------------------------------------
  // TypeSystem methods
  //------------------------------------------------------------------
  DWARFASTParser *GetDWARFParser() override;
  PDBASTParser *GetPDBParser();

  //------------------------------------------------------------------
  // ClangASTContext callbacks for external source lookups.
  //------------------------------------------------------------------
  static void CompleteTagDecl(void *baton, clang::TagDecl *);

  static void CompleteObjCInterfaceDecl(void *baton,
                                        clang::ObjCInterfaceDecl *);

  static bool LayoutRecordType(
      void *baton, const clang::RecordDecl *record_decl, uint64_t &size,
      uint64_t &alignment,
      llvm::DenseMap<const clang::FieldDecl *, uint64_t> &field_offsets,
      llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
          &base_offsets,
      llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
          &vbase_offsets);

  //----------------------------------------------------------------------
  // CompilerDecl override functions
  //----------------------------------------------------------------------
  ConstString DeclGetName(void *opaque_decl) override;

  ConstString DeclGetMangledName(void *opaque_decl) override;

  CompilerDeclContext DeclGetDeclContext(void *opaque_decl) override;

  CompilerType DeclGetFunctionReturnType(void *opaque_decl) override;

  size_t DeclGetFunctionNumArguments(void *opaque_decl) override;

  CompilerType DeclGetFunctionArgumentType(void *opaque_decl,
                                           size_t arg_idx) override;

  //----------------------------------------------------------------------
  // CompilerDeclContext override functions
  //----------------------------------------------------------------------

  std::vector<CompilerDecl>
  DeclContextFindDeclByName(void *opaque_decl_ctx, ConstString name,
                            const bool ignore_using_decls) override;

  bool DeclContextIsStructUnionOrClass(void *opaque_decl_ctx) override;

  ConstString DeclContextGetName(void *opaque_decl_ctx) override;

  ConstString DeclContextGetScopeQualifiedName(void *opaque_decl_ctx) override;

  bool DeclContextIsClassMethod(void *opaque_decl_ctx,
                                lldb::LanguageType *language_ptr,
                                bool *is_instance_method_ptr,
                                ConstString *language_object_name_ptr) override;

  //----------------------------------------------------------------------
  // Clang specific clang::DeclContext functions
  //----------------------------------------------------------------------

  static clang::DeclContext *
  DeclContextGetAsDeclContext(const CompilerDeclContext &dc);

  static clang::ObjCMethodDecl *
  DeclContextGetAsObjCMethodDecl(const CompilerDeclContext &dc);

  static clang::CXXMethodDecl *
  DeclContextGetAsCXXMethodDecl(const CompilerDeclContext &dc);

  static clang::FunctionDecl *
  DeclContextGetAsFunctionDecl(const CompilerDeclContext &dc);

  static clang::NamespaceDecl *
  DeclContextGetAsNamespaceDecl(const CompilerDeclContext &dc);

  static ClangASTMetadata *DeclContextGetMetaData(const CompilerDeclContext &dc,
                                                  const void *object);

  static clang::ASTContext *
  DeclContextGetClangASTContext(const CompilerDeclContext &dc);

  //----------------------------------------------------------------------
  // Tests
  //----------------------------------------------------------------------

  bool IsArrayType(lldb::opaque_compiler_type_t type,
                   CompilerType *element_type, uint64_t *size,
                   bool *is_incomplete) override;

  bool IsVectorType(lldb::opaque_compiler_type_t type,
                    CompilerType *element_type, uint64_t *size) override;

  bool IsAggregateType(lldb::opaque_compiler_type_t type) override;

  bool IsAnonymousType(lldb::opaque_compiler_type_t type) override;

  bool IsBeingDefined(lldb::opaque_compiler_type_t type) override;

  bool IsCharType(lldb::opaque_compiler_type_t type) override;

  bool IsCompleteType(lldb::opaque_compiler_type_t type) override;

  bool IsConst(lldb::opaque_compiler_type_t type) override;

  bool IsCStringType(lldb::opaque_compiler_type_t type,
                     uint32_t &length) override;

  static bool IsCXXClassType(const CompilerType &type);

  bool IsDefined(lldb::opaque_compiler_type_t type) override;

  bool IsFloatingPointType(lldb::opaque_compiler_type_t type, uint32_t &count,
                           bool &is_complex) override;

  bool IsFunctionType(lldb::opaque_compiler_type_t type,
                      bool *is_variadic_ptr) override;

  uint32_t IsHomogeneousAggregate(lldb::opaque_compiler_type_t type,
                                  CompilerType *base_type_ptr) override;

  size_t
  GetNumberOfFunctionArguments(lldb::opaque_compiler_type_t type) override;

  CompilerType GetFunctionArgumentAtIndex(lldb::opaque_compiler_type_t type,
                                          const size_t index) override;

  bool IsFunctionPointerType(lldb::opaque_compiler_type_t type) override;

  bool IsBlockPointerType(lldb::opaque_compiler_type_t type,
                          CompilerType *function_pointer_type_ptr) override;

  bool IsIntegerType(lldb::opaque_compiler_type_t type,
                     bool &is_signed) override;

  bool IsEnumerationType(lldb::opaque_compiler_type_t type,
                         bool &is_signed) override;

  static bool IsObjCClassType(const CompilerType &type);

  static bool IsObjCClassTypeAndHasIVars(const CompilerType &type,
                                         bool check_superclass);

  static bool IsObjCObjectOrInterfaceType(const CompilerType &type);

  static bool IsObjCObjectPointerType(const CompilerType &type,
                                      CompilerType *target_type = nullptr);

  bool IsPolymorphicClass(lldb::opaque_compiler_type_t type) override;

  static bool IsClassType(lldb::opaque_compiler_type_t type);

  static bool IsEnumType(lldb::opaque_compiler_type_t type);

  bool IsPossibleDynamicType(lldb::opaque_compiler_type_t type,
                             CompilerType *target_type, // Can pass nullptr
                             bool check_cplusplus, bool check_objc) override;

  bool IsRuntimeGeneratedType(lldb::opaque_compiler_type_t type) override;

  bool IsPointerType(lldb::opaque_compiler_type_t type,
                     CompilerType *pointee_type) override;

  bool IsPointerOrReferenceType(lldb::opaque_compiler_type_t type,
                                CompilerType *pointee_type) override;

  bool IsReferenceType(lldb::opaque_compiler_type_t type,
                       CompilerType *pointee_type, bool *is_rvalue) override;

  bool IsScalarType(lldb::opaque_compiler_type_t type) override;

  bool IsTypedefType(lldb::opaque_compiler_type_t type) override;

  bool IsVoidType(lldb::opaque_compiler_type_t type) override;

  bool SupportsLanguage(lldb::LanguageType language) override;

  static bool GetCXXClassName(const CompilerType &type,
                              std::string &class_name);

  static bool GetObjCClassName(const CompilerType &type,
                               std::string &class_name);

  //----------------------------------------------------------------------
  // Type Completion
  //----------------------------------------------------------------------

  bool GetCompleteType(lldb::opaque_compiler_type_t type) override;

  //----------------------------------------------------------------------
  // Accessors
  //----------------------------------------------------------------------

  ConstString GetTypeName(lldb::opaque_compiler_type_t type) override;

  uint32_t GetTypeInfo(lldb::opaque_compiler_type_t type,
                       CompilerType *pointee_or_element_compiler_type) override;

  lldb::LanguageType
  GetMinimumLanguage(lldb::opaque_compiler_type_t type) override;

  lldb::TypeClass GetTypeClass(lldb::opaque_compiler_type_t type) override;

  unsigned GetTypeQualifiers(lldb::opaque_compiler_type_t type) override;

  //----------------------------------------------------------------------
  // Creating related types
  //----------------------------------------------------------------------

  // Using the current type, create a new typedef to that type using
  // "typedef_name"
  // as the name and "decl_ctx" as the decl context.
  static CompilerType
  CreateTypedefType(const CompilerType &type, const char *typedef_name,
                    const CompilerDeclContext &compiler_decl_ctx);

  CompilerType GetArrayElementType(lldb::opaque_compiler_type_t type,
                                   uint64_t *stride) override;

  CompilerType GetArrayType(lldb::opaque_compiler_type_t type,
                            uint64_t size) override;

  CompilerType GetCanonicalType(lldb::opaque_compiler_type_t type) override;

  CompilerType
  GetFullyUnqualifiedType(lldb::opaque_compiler_type_t type) override;

  // Returns -1 if this isn't a function of if the function doesn't have a
  // prototype
  // Returns a value >= 0 if there is a prototype.
  int GetFunctionArgumentCount(lldb::opaque_compiler_type_t type) override;

  CompilerType GetFunctionArgumentTypeAtIndex(lldb::opaque_compiler_type_t type,
                                              size_t idx) override;

  CompilerType
  GetFunctionReturnType(lldb::opaque_compiler_type_t type) override;

  size_t GetNumMemberFunctions(lldb::opaque_compiler_type_t type) override;

  TypeMemberFunctionImpl
  GetMemberFunctionAtIndex(lldb::opaque_compiler_type_t type,
                           size_t idx) override;

  CompilerType GetNonReferenceType(lldb::opaque_compiler_type_t type) override;

  CompilerType GetPointeeType(lldb::opaque_compiler_type_t type) override;

  CompilerType GetPointerType(lldb::opaque_compiler_type_t type) override;

  CompilerType
  GetLValueReferenceType(lldb::opaque_compiler_type_t type) override;

  CompilerType
  GetRValueReferenceType(lldb::opaque_compiler_type_t type) override;

  CompilerType AddConstModifier(lldb::opaque_compiler_type_t type) override;

  CompilerType AddVolatileModifier(lldb::opaque_compiler_type_t type) override;

  CompilerType AddRestrictModifier(lldb::opaque_compiler_type_t type) override;

  CompilerType CreateTypedef(lldb::opaque_compiler_type_t type,
                             const char *name,
                             const CompilerDeclContext &decl_ctx) override;

  // If the current object represents a typedef type, get the underlying type
  CompilerType GetTypedefedType(lldb::opaque_compiler_type_t type) override;

  //----------------------------------------------------------------------
  // Create related types using the current type's AST
  //----------------------------------------------------------------------
  CompilerType GetBasicTypeFromAST(lldb::BasicType basic_type) override;

  //----------------------------------------------------------------------
  // Exploring the type
  //----------------------------------------------------------------------

  uint64_t GetByteSize(lldb::opaque_compiler_type_t type,
                       ExecutionContextScope *exe_scope) {
    return (GetBitSize(type, exe_scope) + 7) / 8;
  }

  uint64_t GetBitSize(lldb::opaque_compiler_type_t type,
                      ExecutionContextScope *exe_scope) override;

  lldb::Encoding GetEncoding(lldb::opaque_compiler_type_t type,
                             uint64_t &count) override;

  lldb::Format GetFormat(lldb::opaque_compiler_type_t type) override;

  size_t GetTypeBitAlign(lldb::opaque_compiler_type_t type) override;

  uint32_t GetNumChildren(lldb::opaque_compiler_type_t type,
                          bool omit_empty_base_classes) override;

  CompilerType GetBuiltinTypeByName(const ConstString &name) override;

  lldb::BasicType
  GetBasicTypeEnumeration(lldb::opaque_compiler_type_t type) override;

  static lldb::BasicType
  GetBasicTypeEnumeration(lldb::opaque_compiler_type_t type,
                          const ConstString &name);

  void ForEachEnumerator(
      lldb::opaque_compiler_type_t type,
      std::function<bool(const CompilerType &integer_type,
                         const ConstString &name,
                         const llvm::APSInt &value)> const &callback) override;

  uint32_t GetNumFields(lldb::opaque_compiler_type_t type) override;

  CompilerType GetFieldAtIndex(lldb::opaque_compiler_type_t type, size_t idx,
                               std::string &name, uint64_t *bit_offset_ptr,
                               uint32_t *bitfield_bit_size_ptr,
                               bool *is_bitfield_ptr) override;

  uint32_t GetNumDirectBaseClasses(lldb::opaque_compiler_type_t type) override;

  uint32_t GetNumVirtualBaseClasses(lldb::opaque_compiler_type_t type) override;

  CompilerType GetDirectBaseClassAtIndex(lldb::opaque_compiler_type_t type,
                                         size_t idx,
                                         uint32_t *bit_offset_ptr) override;

  CompilerType GetVirtualBaseClassAtIndex(lldb::opaque_compiler_type_t type,
                                          size_t idx,
                                          uint32_t *bit_offset_ptr) override;

  static uint32_t GetNumPointeeChildren(clang::QualType type);

  CompilerType GetChildCompilerTypeAtIndex(
      lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, size_t idx,
      bool transparent_pointers, bool omit_empty_base_classes,
      bool ignore_array_bounds, std::string &child_name,
      uint32_t &child_byte_size, int32_t &child_byte_offset,
      uint32_t &child_bitfield_bit_size, uint32_t &child_bitfield_bit_offset,
      bool &child_is_base_class, bool &child_is_deref_of_parent,
      ValueObject *valobj, uint64_t &language_flags) override;

  // Lookup a child given a name. This function will match base class names
  // and member member names in "clang_type" only, not descendants.
  uint32_t GetIndexOfChildWithName(lldb::opaque_compiler_type_t type,
                                   const char *name,
                                   bool omit_empty_base_classes) override;

  // Lookup a child member given a name. This function will match member names
  // only and will descend into "clang_type" children in search for the first
  // member in this class, or any base class that matches "name".
  // TODO: Return all matches for a given name by returning a
  // vector<vector<uint32_t>>
  // so we catch all names that match a given child name, not just the first.
  size_t
  GetIndexOfChildMemberWithName(lldb::opaque_compiler_type_t type,
                                const char *name, bool omit_empty_base_classes,
                                std::vector<uint32_t> &child_indexes) override;

  size_t GetNumTemplateArguments(lldb::opaque_compiler_type_t type) override;

  lldb::TemplateArgumentKind
  GetTemplateArgumentKind(lldb::opaque_compiler_type_t type,
                          size_t idx) override;
  CompilerType GetTypeTemplateArgument(lldb::opaque_compiler_type_t type,
                                       size_t idx) override;
  llvm::Optional<CompilerType::IntegralTemplateArgument>
  GetIntegralTemplateArgument(lldb::opaque_compiler_type_t type,
                              size_t idx) override;

  CompilerType GetTypeForFormatters(void *type) override;

#define LLDB_INVALID_DECL_LEVEL UINT32_MAX
  // LLDB_INVALID_DECL_LEVEL is returned by CountDeclLevels if
  // child_decl_ctx could not be found in decl_ctx.
  uint32_t CountDeclLevels(clang::DeclContext *frame_decl_ctx,
                           clang::DeclContext *child_decl_ctx,
                           ConstString *child_name = nullptr,
                           CompilerType *child_type = nullptr);

  //----------------------------------------------------------------------
  // Modifying RecordType
  //----------------------------------------------------------------------
  static clang::FieldDecl *AddFieldToRecordType(const CompilerType &type,
                                                const char *name,
                                                const CompilerType &field_type,
                                                lldb::AccessType access,
                                                uint32_t bitfield_bit_size);

  static void BuildIndirectFields(const CompilerType &type);

  static void SetIsPacked(const CompilerType &type);

  static clang::VarDecl *AddVariableToRecordType(const CompilerType &type,
                                                 const char *name,
                                                 const CompilerType &var_type,
                                                 lldb::AccessType access);

  clang::CXXMethodDecl *
  AddMethodToCXXRecordType(lldb::opaque_compiler_type_t type, const char *name,
                           const CompilerType &method_type,
                           lldb::AccessType access, bool is_virtual,
                           bool is_static, bool is_inline, bool is_explicit,
                           bool is_attr_used, bool is_artificial);

  // C++ Base Classes
  clang::CXXBaseSpecifier *
  CreateBaseClassSpecifier(lldb::opaque_compiler_type_t type,
                           lldb::AccessType access, bool is_virtual,
                           bool base_of_class);

  static void DeleteBaseClassSpecifiers(clang::CXXBaseSpecifier **base_classes,
                                        unsigned num_base_classes);

  bool
  SetBaseClassesForClassType(lldb::opaque_compiler_type_t type,
                             clang::CXXBaseSpecifier const *const *base_classes,
                             unsigned num_base_classes);

  static bool SetObjCSuperClass(const CompilerType &type,
                                const CompilerType &superclass_compiler_type);

  static bool AddObjCClassProperty(const CompilerType &type,
                                   const char *property_name,
                                   const CompilerType &property_compiler_type,
                                   clang::ObjCIvarDecl *ivar_decl,
                                   const char *property_setter_name,
                                   const char *property_getter_name,
                                   uint32_t property_attributes,
                                   ClangASTMetadata *metadata);

  static clang::ObjCMethodDecl *AddMethodToObjCObjectType(
      const CompilerType &type,
      const char *name, // the full symbol name as seen in the symbol table
                        // (lldb::opaque_compiler_type_t type, "-[NString
                        // stringWithCString:]")
      const CompilerType &method_compiler_type, lldb::AccessType access,
      bool is_artificial, bool is_variadic);

  static bool SetHasExternalStorage(lldb::opaque_compiler_type_t type,
                                    bool has_extern);

  static bool GetHasExternalStorage(const CompilerType &type);
  //------------------------------------------------------------------
  // Tag Declarations
  //------------------------------------------------------------------
  static bool StartTagDeclarationDefinition(const CompilerType &type);

  static bool CompleteTagDeclarationDefinition(const CompilerType &type);

  //----------------------------------------------------------------------
  // Modifying Enumeration types
  //----------------------------------------------------------------------
  bool AddEnumerationValueToEnumerationType(
      lldb::opaque_compiler_type_t type,
      const CompilerType &enumerator_qual_type, const Declaration &decl,
      const char *name, int64_t enum_value, uint32_t enum_value_bit_size);

  CompilerType GetEnumerationIntegerType(lldb::opaque_compiler_type_t type);

  //------------------------------------------------------------------
  // Pointers & References
  //------------------------------------------------------------------

  // Call this function using the class type when you want to make a
  // member pointer type to pointee_type.
  static CompilerType CreateMemberPointerType(const CompilerType &type,
                                              const CompilerType &pointee_type);

  // Converts "s" to a floating point value and place resulting floating
  // point bytes in the "dst" buffer.
  size_t ConvertStringToFloatValue(lldb::opaque_compiler_type_t type,
                                   const char *s, uint8_t *dst,
                                   size_t dst_size) override;

  //----------------------------------------------------------------------
  // Dumping types
  //----------------------------------------------------------------------
  void DumpValue(lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx,
                 Stream *s, lldb::Format format, const DataExtractor &data,
                 lldb::offset_t data_offset, size_t data_byte_size,
                 uint32_t bitfield_bit_size, uint32_t bitfield_bit_offset,
                 bool show_types, bool show_summary, bool verbose,
                 uint32_t depth) override;

  bool DumpTypeValue(lldb::opaque_compiler_type_t type, Stream *s,
                     lldb::Format format, const DataExtractor &data,
                     lldb::offset_t data_offset, size_t data_byte_size,
                     uint32_t bitfield_bit_size, uint32_t bitfield_bit_offset,
                     ExecutionContextScope *exe_scope) override;

  void DumpSummary(lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx,
                   Stream *s, const DataExtractor &data,
                   lldb::offset_t data_offset, size_t data_byte_size) override;

  void DumpTypeDescription(
      lldb::opaque_compiler_type_t type) override; // Dump to stdout

  void DumpTypeDescription(lldb::opaque_compiler_type_t type,
                           Stream *s) override;

  static void DumpTypeName(const CompilerType &type);

  static clang::EnumDecl *GetAsEnumDecl(const CompilerType &type);

  static clang::RecordDecl *GetAsRecordDecl(const CompilerType &type);

  static clang::TagDecl *GetAsTagDecl(const CompilerType &type);

  clang::CXXRecordDecl *GetAsCXXRecordDecl(lldb::opaque_compiler_type_t type);

  static clang::ObjCInterfaceDecl *
  GetAsObjCInterfaceDecl(const CompilerType &type);

  clang::ClassTemplateDecl *ParseClassTemplateDecl(
      clang::DeclContext *decl_ctx, lldb::AccessType access_type,
      const char *parent_name, int tag_decl_kind,
      const ClangASTContext::TemplateParameterInfos &template_param_infos);

  clang::BlockDecl *CreateBlockDeclaration(clang::DeclContext *ctx);

  clang::UsingDirectiveDecl *
  CreateUsingDirectiveDeclaration(clang::DeclContext *decl_ctx,
                                  clang::NamespaceDecl *ns_decl);

  clang::UsingDecl *CreateUsingDeclaration(clang::DeclContext *current_decl_ctx,
                                           clang::NamedDecl *target);

  clang::VarDecl *CreateVariableDeclaration(clang::DeclContext *decl_context,
                                            const char *name,
                                            clang::QualType type);

  static lldb::opaque_compiler_type_t
  GetOpaqueCompilerType(clang::ASTContext *ast, lldb::BasicType basic_type);

  static clang::QualType GetQualType(lldb::opaque_compiler_type_t type) {
    if (type)
      return clang::QualType::getFromOpaquePtr(type);
    return clang::QualType();
  }

  static clang::QualType
  GetCanonicalQualType(lldb::opaque_compiler_type_t type) {
    if (type)
      return clang::QualType::getFromOpaquePtr(type).getCanonicalType();
    return clang::QualType();
  }

  clang::DeclarationName
  GetDeclarationName(const char *name, const CompilerType &function_clang_type);
  
  virtual const clang::ExternalASTMerger::OriginMap &GetOriginMap() {
    return m_origins;
  }
protected:
  const clang::ClassTemplateSpecializationDecl *
  GetAsTemplateSpecialization(lldb::opaque_compiler_type_t type);

  //------------------------------------------------------------------
  // Classes that inherit from ClangASTContext can see and modify these
  //------------------------------------------------------------------
  // clang-format off
    std::string                                     m_target_triple;
    std::unique_ptr<clang::ASTContext>              m_ast_ap;
    std::unique_ptr<clang::LangOptions>             m_language_options_ap;
    std::unique_ptr<clang::FileManager>             m_file_manager_ap;
    std::unique_ptr<clang::FileSystemOptions>       m_file_system_options_ap;
    std::unique_ptr<clang::SourceManager>           m_source_manager_ap;
    std::unique_ptr<clang::DiagnosticsEngine>       m_diagnostics_engine_ap;
    std::unique_ptr<clang::DiagnosticConsumer>      m_diagnostic_consumer_ap;
    std::shared_ptr<clang::TargetOptions>           m_target_options_rp;
    std::unique_ptr<clang::TargetInfo>              m_target_info_ap;
    std::unique_ptr<clang::IdentifierTable>         m_identifier_table_ap;
    std::unique_ptr<clang::SelectorTable>           m_selector_table_ap;
    std::unique_ptr<clang::Builtin::Context>        m_builtins_ap;
    std::unique_ptr<DWARFASTParserClang>            m_dwarf_ast_parser_ap;
    std::unique_ptr<PDBASTParser>                   m_pdb_ast_parser_ap;
    std::unique_ptr<ClangASTSource>                 m_scratch_ast_source_ap;
    std::unique_ptr<clang::MangleContext>           m_mangle_ctx_ap;
    CompleteTagDeclCallback                         m_callback_tag_decl;
    CompleteObjCInterfaceDeclCallback               m_callback_objc_decl;
    void *                                          m_callback_baton;
    clang::ExternalASTMerger::OriginMap             m_origins;
    uint32_t                                        m_pointer_byte_size;
    bool                                            m_ast_owned;
    bool                                            m_can_evaluate_expressions;
  // clang-format on
private:
  //------------------------------------------------------------------
  // For ClangASTContext only
  //------------------------------------------------------------------
  ClangASTContext(const ClangASTContext &);
  const ClangASTContext &operator=(const ClangASTContext &);
};

class ClangASTContextForExpressions : public ClangASTContext {
public:
  ClangASTContextForExpressions(Target &target);

  ~ClangASTContextForExpressions() override = default;

  UserExpression *
  GetUserExpression(llvm::StringRef expr, llvm::StringRef prefix,
                    lldb::LanguageType language,
                    Expression::ResultType desired_type,
                    const EvaluateExpressionOptions &options) override;

  FunctionCaller *GetFunctionCaller(const CompilerType &return_type,
                                    const Address &function_address,
                                    const ValueList &arg_value_list,
                                    const char *name) override;

  UtilityFunction *GetUtilityFunction(const char *text,
                                      const char *name) override;

  PersistentExpressionState *GetPersistentExpressionState() override;
  
  clang::ExternalASTMerger &GetMergerUnchecked();
  
  const clang::ExternalASTMerger::OriginMap &GetOriginMap() override {
    return GetMergerUnchecked().GetOrigins();
  }
private:
  lldb::TargetWP m_target_wp;
  lldb::ClangPersistentVariablesUP m_persistent_variables; ///< These are the
                                                           ///persistent
                                                           ///variables
                                                           ///associated with
                                                           ///this process for
                                                           ///the expression
                                                           ///parser.
};

} // namespace lldb_private

#endif // liblldb_ClangASTContext_h_
