//===-- 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 <vector>
#include <utility>

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

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

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;

    //------------------------------------------------------------------
    // 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
    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();
        }

        size_t
        GetSize () const
        {
            if (IsValid())
                return args.size();
            return 0;
        }

        llvm::SmallVector<const char *, 2> names;
        llvm::SmallVector<clang::TemplateArgument, 2> 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 (uint32_t 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);

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

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

    //------------------------------------------------------------------
    // 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
    //----------------------------------------------------------------------
    lldb::VariableSP
    DeclGetVariable (void *opaque_decl) override;

    void
    DeclLinkToObject (void *opaque_decl, std::shared_ptr<void> object) override;
    
    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) 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 CompilerType predicates
    //----------------------------------------------------------------------
    
    static bool
    IsClangType (const CompilerType &ct)
    {
        return llvm::dyn_cast_or_null<ClangASTContext>(ct.GetTypeSystem()) != nullptr && ct.GetOpaqueQualType() != nullptr;
    }

    //----------------------------------------------------------------------
    // 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
    IsIntegerType (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;
    
    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
    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;

    static CompilerType
    RemoveFastQualifiers (const CompilerType& type);
    
    //----------------------------------------------------------------------
    // 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;
    
    CompilerType
    GetTemplateArgument (lldb::opaque_compiler_type_t type,
                         size_t idx,
                         lldb::TemplateArgumentKind &kind) 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);
    
    static bool
    SetHasExternalStorage (lldb::opaque_compiler_type_t type, bool has_extern);
    

    static bool
    CanImport (const CompilerType &type, lldb_private::ClangASTImporter &importer);

    static bool
    Import (const CompilerType &type, lldb_private::ClangASTImporter &importer);

    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);
    
    static clang::QualType
    GetQualType (const CompilerType& type)
    {
        // Make sure we have a clang type before making a clang::QualType
        if (type.GetOpaqueQualType())
        {
            ClangASTContext *ast = llvm::dyn_cast_or_null<ClangASTContext>(type.GetTypeSystem());
            if (ast)
                return clang::QualType::getFromOpaquePtr(type.GetOpaqueQualType());
        }
        return clang::QualType();
    }

    static clang::QualType
    GetCanonicalQualType (const CompilerType& type)
    {
        // Make sure we have a clang type before making a clang::QualType
        ClangASTContext *ast = llvm::dyn_cast_or_null<ClangASTContext>(type.GetTypeSystem());
        if (ast)
            return clang::QualType::getFromOpaquePtr(type.GetOpaqueQualType()).getCanonicalType();
        return clang::QualType();
    }

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

protected:
    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();
    }

    //------------------------------------------------------------------
    // Classes that inherit from ClangASTContext can see and modify these
    //------------------------------------------------------------------
    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<DWARFASTParser>                 m_dwarf_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;
    uint32_t                                        m_pointer_byte_size;
    bool                                            m_ast_owned;
    bool                                            m_can_evaluate_expressions;
    std::map<void *, std::shared_ptr<void>>         m_decl_objects;

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 (const char *expr,
                       const char *expr_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;
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_
