//===-- ClangASTSource.h ----------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CLANG_CLANGASTSOURCE_H
#define LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CLANG_CLANGASTSOURCE_H

#include <set>

#include "Plugins/ExpressionParser/Clang/ClangASTImporter.h"
#include "Plugins/ExpressionParser/Clang/NameSearchContext.h"
#include "lldb/Symbol/CompilerType.h"
#include "lldb/Target/Target.h"
#include "clang/AST/ExternalASTSource.h"
#include "clang/Basic/IdentifierTable.h"

#include "llvm/ADT/SmallSet.h"

namespace lldb_private {

/// \class ClangASTSource ClangASTSource.h "lldb/Expression/ClangASTSource.h"
/// Provider for named objects defined in the debug info for Clang
///
/// As Clang parses an expression, it may encounter names that are not defined
/// inside the expression, including variables, functions, and types.  Clang
/// knows the name it is looking for, but nothing else. The ExternalSemaSource
/// class provides Decls (VarDecl, FunDecl, TypeDecl) to Clang for these
/// names, consulting the ClangExpressionDeclMap to do the actual lookups.
class ClangASTSource : public clang::ExternalASTSource,
                       public ClangASTImporter::MapCompleter {
public:
  /// Constructor
  ///
  /// Initializes class variables.
  ///
  /// \param[in] target
  ///     A reference to the target containing debug information to use.
  ///
  /// \param[in] importer
  ///     The ClangASTImporter to use.
  ClangASTSource(const lldb::TargetSP &target,
                 const std::shared_ptr<ClangASTImporter> &importer);

  /// Destructor
  ~ClangASTSource() override;

  /// Interface stubs.
  clang::Decl *GetExternalDecl(clang::GlobalDeclID) override { return nullptr; }
  clang::Stmt *GetExternalDeclStmt(uint64_t) override { return nullptr; }
  clang::Selector GetExternalSelector(uint32_t) override {
    return clang::Selector();
  }
  uint32_t GetNumExternalSelectors() override { return 0; }
  clang::CXXBaseSpecifier *
  GetExternalCXXBaseSpecifiers(uint64_t Offset) override {
    return nullptr;
  }
  void MaterializeVisibleDecls(const clang::DeclContext *DC) {}

  void InstallASTContext(TypeSystemClang &ast_context);

  //
  // APIs for ExternalASTSource
  //

  /// Look up all Decls that match a particular name.  Only handles
  /// Identifiers and DeclContexts that are either NamespaceDecls or
  /// TranslationUnitDecls.  Calls SetExternalVisibleDeclsForName with the
  /// result.
  ///
  /// The work for this function is done by
  /// void FindExternalVisibleDecls (NameSearchContext &);
  ///
  /// \param[in] DC
  ///     The DeclContext to register the found Decls in.
  ///
  /// \param[in] Name
  ///     The name to find entries for.
  ///
  /// \return
  ///     Whatever SetExternalVisibleDeclsForName returns.
  bool
  FindExternalVisibleDeclsByName(const clang::DeclContext *DC,
                                 clang::DeclarationName Name,
                                 const clang::DeclContext *OriginalDC) override;

  /// Enumerate all Decls in a given lexical context.
  ///
  /// \param[in] DC
  ///     The DeclContext being searched.
  ///
  /// \param[in] IsKindWeWant
  ///     A callback function that returns true given the
  ///     DeclKinds of desired Decls, and false otherwise.
  ///
  /// \param[in] Decls
  ///     A vector that is filled in with matching Decls.
  void FindExternalLexicalDecls(
      const clang::DeclContext *DC,
      llvm::function_ref<bool(clang::Decl::Kind)> IsKindWeWant,
      llvm::SmallVectorImpl<clang::Decl *> &Decls) override;

  /// Specify the layout of the contents of a RecordDecl.
  ///
  /// \param[in] Record
  ///     The record (in the parser's AST context) that needs to be
  ///     laid out.
  ///
  /// \param[out] Size
  ///     The total size of the record in bits.
  ///
  /// \param[out] Alignment
  ///     The alignment of the record in bits.
  ///
  /// \param[in] FieldOffsets
  ///     A map that must be populated with pairs of the record's
  ///     fields (in the parser's AST context) and their offsets
  ///     (measured in bits).
  ///
  /// \param[in] BaseOffsets
  ///     A map that must be populated with pairs of the record's
  ///     C++ concrete base classes (in the parser's AST context,
  ///     and only if the record is a CXXRecordDecl and has base
  ///     classes) and their offsets (measured in bytes).
  ///
  /// \param[in] VirtualBaseOffsets
  ///     A map that must be populated with pairs of the record's
  ///     C++ virtual base classes (in the parser's AST context,
  ///     and only if the record is a CXXRecordDecl and has base
  ///     classes) and their offsets (measured in bytes).
  ///
  /// \return
  ///     True <=> the layout is valid.
  bool layoutRecordType(
      const clang::RecordDecl *Record, uint64_t &Size, uint64_t &Alignment,
      llvm::DenseMap<const clang::FieldDecl *, uint64_t> &FieldOffsets,
      llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
          &BaseOffsets,
      llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
          &VirtualBaseOffsets) override;

  /// Complete a TagDecl.
  ///
  /// \param[in] Tag
  ///     The Decl to be completed in place.
  void CompleteType(clang::TagDecl *Tag) override;

  /// Complete an ObjCInterfaceDecl.
  ///
  /// \param[in] Class
  ///     The Decl to be completed in place.
  void CompleteType(clang::ObjCInterfaceDecl *Class) override;

  /// Called on entering a translation unit.  Tells Clang by calling
  /// setHasExternalVisibleStorage() and setHasExternalLexicalStorage() that
  /// this object has something to say about undefined names.
  ///
  /// \param[in] Consumer
  ///     Unused.
  void StartTranslationUnit(clang::ASTConsumer *Consumer) override;

  //
  // APIs for NamespaceMapCompleter
  //

  /// Look up the modules containing a given namespace and put the appropriate
  /// entries in the namespace map.
  ///
  /// \param[in] namespace_map
  ///     The map to be completed.
  ///
  /// \param[in] name
  ///     The name of the namespace to be found.
  ///
  /// \param[in] parent_map
  ///     The map for the namespace's parent namespace, if there is
  ///     one.
  void CompleteNamespaceMap(
      ClangASTImporter::NamespaceMapSP &namespace_map, ConstString name,
      ClangASTImporter::NamespaceMapSP &parent_map) const override;

  //
  // Helper APIs
  //

  clang::NamespaceDecl *AddNamespace(NameSearchContext &context);

  /// The worker function for FindExternalVisibleDeclsByName.
  ///
  /// \param[in] context
  ///     The NameSearchContext to use when filing results.
  virtual void FindExternalVisibleDecls(NameSearchContext &context);

  clang::Sema *getSema();

  void SetLookupsEnabled(bool lookups_enabled) {
    m_lookups_enabled = lookups_enabled;
  }
  bool GetLookupsEnabled() { return m_lookups_enabled; }

  /// \class ClangASTSourceProxy ClangASTSource.h
  /// "lldb/Expression/ClangASTSource.h" Proxy for ClangASTSource
  ///
  /// Clang AST contexts like to own their AST sources, so this is a state-
  /// free proxy object.
  class ClangASTSourceProxy : public clang::ExternalASTSource {
  public:
    ClangASTSourceProxy(ClangASTSource &original) : m_original(original) {}

    bool FindExternalVisibleDeclsByName(
        const clang::DeclContext *DC, clang::DeclarationName Name,
        const clang::DeclContext *OriginalDC) override {
      return m_original.FindExternalVisibleDeclsByName(DC, Name, OriginalDC);
    }

    void FindExternalLexicalDecls(
        const clang::DeclContext *DC,
        llvm::function_ref<bool(clang::Decl::Kind)> IsKindWeWant,
        llvm::SmallVectorImpl<clang::Decl *> &Decls) override {
      return m_original.FindExternalLexicalDecls(DC, IsKindWeWant, Decls);
    }

    void CompleteType(clang::TagDecl *Tag) override {
      return m_original.CompleteType(Tag);
    }

    void CompleteType(clang::ObjCInterfaceDecl *Class) override {
      return m_original.CompleteType(Class);
    }

    bool layoutRecordType(
        const clang::RecordDecl *Record, uint64_t &Size, uint64_t &Alignment,
        llvm::DenseMap<const clang::FieldDecl *, uint64_t> &FieldOffsets,
        llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
            &BaseOffsets,
        llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
            &VirtualBaseOffsets) override {
      return m_original.layoutRecordType(Record, Size, Alignment, FieldOffsets,
                                         BaseOffsets, VirtualBaseOffsets);
    }

    void StartTranslationUnit(clang::ASTConsumer *Consumer) override {
      return m_original.StartTranslationUnit(Consumer);
    }

  private:
    ClangASTSource &m_original;
  };

  llvm::IntrusiveRefCntPtr<clang::ExternalASTSource> CreateProxy() {
    return llvm::makeIntrusiveRefCnt<ClangASTSourceProxy>(*this);
  }

protected:
  /// Look for the complete version of an Objective-C interface, and return it
  /// if found.
  ///
  /// \param[in] interface_decl
  ///     An ObjCInterfaceDecl that may not be the complete one.
  ///
  /// \return
  ///     NULL if the complete interface couldn't be found;
  ///     the complete interface otherwise.
  clang::ObjCInterfaceDecl *
  GetCompleteObjCInterface(const clang::ObjCInterfaceDecl *interface_decl);

  /// Find all entities matching a given name in a given module, using a
  /// NameSearchContext to make Decls for them.
  ///
  /// \param[in] context
  ///     The NameSearchContext that can construct Decls for this name.
  ///
  /// \param[in] module
  ///     If non-NULL, the module to query.
  ///
  /// \param[in] namespace_decl
  ///     If valid and module is non-NULL, the parent namespace.
  void FindExternalVisibleDecls(NameSearchContext &context,
                                lldb::ModuleSP module,
                                CompilerDeclContext &namespace_decl);

  /// Find all Objective-C methods matching a given selector.
  ///
  /// \param[in] context
  ///     The NameSearchContext that can construct Decls for this name.
  ///     Its m_decl_name contains the selector and its m_decl_context
  ///     is the containing object.
  void FindObjCMethodDecls(NameSearchContext &context);

  /// Find all Objective-C properties and ivars with a given name.
  ///
  /// \param[in] context
  ///     The NameSearchContext that can construct Decls for this name.
  ///     Its m_decl_name contains the name and its m_decl_context
  ///     is the containing object.
  void FindObjCPropertyAndIvarDecls(NameSearchContext &context);

  /// Performs lookup into a namespace.
  ///
  /// \param context
  ///     The NameSearchContext for a lookup inside a namespace.
  void LookupInNamespace(NameSearchContext &context);

  /// A wrapper for TypeSystemClang::CopyType that sets a flag that
  /// indicates that we should not respond to queries during import.
  ///
  /// \param[in] src_type
  ///     The source type.
  ///
  /// \return
  ///     The imported type.
  CompilerType GuardedCopyType(const CompilerType &src_type);

  std::shared_ptr<ClangModulesDeclVendor> GetClangModulesDeclVendor();

public:
  /// Returns true if a name should be ignored by name lookup.
  ///
  /// \param[in] name
  ///     The name to be considered.
  ///
  /// \param[in] ignore_all_dollar_names
  ///     True if $-names of all sorts should be ignored.
  ///
  /// \return
  ///     True if the name is one of a class of names that are ignored by
  ///     global lookup for performance reasons.
  bool IgnoreName(const ConstString name, bool ignore_all_dollar_names);

  /// Copies a single Decl into the parser's AST context.
  ///
  /// \param[in] src_decl
  ///     The Decl to copy.
  ///
  /// \return
  ///     A copy of the Decl in m_ast_context, or NULL if the copy failed.
  clang::Decl *CopyDecl(clang::Decl *src_decl);

  /// Determined the origin of a single Decl, if it can be found.
  ///
  /// \param[in] decl
  ///     The Decl whose origin is to be found.
  ///
  /// \return
  ///     True if lookup succeeded; false otherwise.
  ClangASTImporter::DeclOrigin GetDeclOrigin(const clang::Decl *decl);

  /// Returns the TypeSystem that uses this ClangASTSource instance as it's
  /// ExternalASTSource.
  TypeSystemClang *GetTypeSystem() const { return m_clang_ast_context; }

private:
  bool FindObjCPropertyAndIvarDeclsWithOrigin(
      NameSearchContext &context,
      DeclFromUser<const clang::ObjCInterfaceDecl> &origin_iface_decl);

protected:
  bool FindObjCMethodDeclsWithOrigin(
      NameSearchContext &context,
      clang::ObjCInterfaceDecl *original_interface_decl, const char *log_info);

  void FindDeclInModules(NameSearchContext &context, ConstString name);
  void FindDeclInObjCRuntime(NameSearchContext &context, ConstString name);

  /// Fills the namespace map of the given NameSearchContext.
  ///
  /// \param context The NameSearchContext with the namespace map to fill.
  /// \param module_sp The module to search for namespaces or a nullptr if
  ///                  the current target should be searched.
  /// \param namespace_decl The DeclContext in which to search for namespaces.
  void FillNamespaceMap(NameSearchContext &context, lldb::ModuleSP module_sp,
                        const CompilerDeclContext &namespace_decl);

  clang::TagDecl *FindCompleteType(const clang::TagDecl *decl);

  friend struct NameSearchContext;

  bool m_lookups_enabled;

  /// The target to use in finding variables and types.
  const lldb::TargetSP m_target;
  /// The AST context requests are coming in for.
  clang::ASTContext *m_ast_context;
  /// The TypeSystemClang for m_ast_context.
  TypeSystemClang *m_clang_ast_context;
  /// The file manager paired with the AST context.
  clang::FileManager *m_file_manager;
  /// The target's AST importer.
  std::shared_ptr<ClangASTImporter> m_ast_importer_sp;
  std::set<const clang::Decl *> m_active_lexical_decls;
  std::set<const char *> m_active_lookups;
};

} // namespace lldb_private

#endif // LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CLANG_CLANGASTSOURCE_H
