//===--- FileUnit.h - The contents of a module ------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_AST_FILEUNIT_H
#define SWIFT_AST_FILEUNIT_H

#include "swift/AST/Module.h"
#include "swift/AST/RawComment.h"
#include "swift/Basic/BasicSourceInfo.h"
#include "swift/Basic/Debug.h"
#include "swift/Basic/Version.h"

#include "llvm/ADT/PointerIntPair.h"

namespace swift {
class SynthesizedFileUnit;

/// A container for module-scope declarations that itself provides a scope; the
/// smallest unit of code organization.
///
/// FileUnit is an abstract base class; its subclasses represent different
/// sorts of containers that can each provide a set of decls, e.g. a source
/// file. A module can contain several file-units.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
class FileUnit : public DeclContext, public ASTAllocated<FileUnit> {
#pragma clang diagnostic pop
  virtual void anchor();

  friend class DirectOperatorLookupRequest;
  friend class DirectPrecedenceGroupLookupRequest;

  /// The pointer is FileUnit insted of SynthesizedFileUnit to break
  /// circularity.
  llvm::PointerIntPair<FileUnit *, 3, FileUnitKind> SynthesizedFileAndKind;

protected:
  FileUnit(FileUnitKind kind, ModuleDecl &M)
    : DeclContext(DeclContextKind::FileUnit, &M),
      SynthesizedFileAndKind(nullptr, kind) {
  }

public:
  FileUnitKind getKind() const {
    return SynthesizedFileAndKind.getInt();
  }

  /// Returns the synthesized file for this source file, if it exists.
  SynthesizedFileUnit *getSynthesizedFile() const;

  /// Returns the synthesized file for this source file, creating one and
  /// inserting it into the module if it does not exist.
  SynthesizedFileUnit &getOrCreateSynthesizedFile();

  /// Look up a (possibly overloaded) value set at top-level scope
  /// (but with the specified access path, which may come from an import decl)
  /// within this file.
  ///
  /// This does a simple local lookup, not recursively looking through imports.
  virtual void lookupValue(DeclName name, NLKind lookupKind,
                           OptionSet<ModuleLookupFlags> Flags,
                           SmallVectorImpl<ValueDecl*> &result) const = 0;

  /// Look up a local type declaration by its mangled name.
  ///
  /// This does a simple local lookup, not recursively looking through imports.
  virtual TypeDecl *lookupLocalType(StringRef MangledName) const {
    return nullptr;
  }

  /// Look up an opaque return type by the mangled name of the declaration
  /// that defines it.
  virtual OpaqueTypeDecl *lookupOpaqueResultType(StringRef MangledName) {
    return nullptr;
  }

  /// Directly look for a nested type declared within this module inside the
  /// given nominal type (including any extensions).
  ///
  /// This is a fast-path hack to avoid circular dependencies in deserialization
  /// and the Clang importer.
  ///
  /// Private and fileprivate types should not be returned by this lookup.
  virtual TypeDecl *lookupNestedType(Identifier name,
                                     const NominalTypeDecl *parent) const {
    return nullptr;
  }

  /// Find ValueDecls in the module and pass them to the given consumer object.
  ///
  /// This does a simple local lookup, not recursively looking through imports.
  virtual void lookupVisibleDecls(ImportPath::Access accessPath,
                                  VisibleDeclConsumer &consumer,
                                  NLKind lookupKind) const {}

  /// Finds all class members defined in this file.
  ///
  /// This does a simple local lookup, not recursively looking through imports.
  virtual void lookupClassMembers(ImportPath::Access accessPath,
                                  VisibleDeclConsumer &consumer) const {}

  /// Finds class members defined in this file with the given name.
  ///
  /// This does a simple local lookup, not recursively looking through imports.
  virtual void lookupClassMember(ImportPath::Access accessPath,
                                 DeclName name,
                                 SmallVectorImpl<ValueDecl*> &results) const {}

  /// Find all Objective-C methods with the given selector.
  virtual void lookupObjCMethods(
                 ObjCSelector selector,
                 SmallVectorImpl<AbstractFunctionDecl *> &results) const = 0;

  /// Find all SPI names imported from \p importedModule by this module,
  /// collecting the identifiers in \p spiGroups.
  virtual void lookupImportedSPIGroups(
                            const ModuleDecl *importedModule,
                            llvm::SmallSetVector<Identifier, 4> &spiGroups) const {};

  /// Returns true if any import of \p importedModule has the `@preconcurrency`
  /// attribute.
  virtual bool
  isModuleImportedPreconcurrency(const ModuleDecl *importedModule) const {
    return false;
  };

  /// Find all availability domains defined in this module with the given
  /// identifier.
  ///
  /// This does a simple local lookup, not recursively looking through imports.
  virtual void lookupAvailabilityDomains(
      Identifier identifier,
      SmallVectorImpl<AvailabilityDomain> &results) const {};

  virtual std::optional<Fingerprint>
  loadFingerprint(const IterableDeclContext *IDC) const {
    return std::nullopt;
  }

protected:
  /// Look up an operator declaration. Do not call directly, use
  /// \c DirectOperatorLookupRequest instead.
  ///
  /// \param name The operator name ("+", ">>", etc.)
  ///
  /// \param fixity One of Prefix, Infix, or Postfix.
  virtual void
  lookupOperatorDirect(Identifier name, OperatorFixity fixity,
                       TinyPtrVector<OperatorDecl *> &results) const {}

  /// Look up a precedence group. Do not call directly, use
  /// \c DirectPrecedenceGroupLookupRequest instead.
  ///
  /// \param name The precedence group name.
  virtual void lookupPrecedenceGroupDirect(
      Identifier name, TinyPtrVector<PrecedenceGroupDecl *> &results) const {}

public:
  /// Returns the comment attached to the given declaration.
  ///
  /// This function is an implementation detail for comment serialization.
  /// If you just want to get a comment attached to a decl, use
  /// \c Decl::getRawComment() or \c Decl::getSemanticBriefComment().
  virtual std::optional<CommentInfo> getCommentForDecl(const Decl *D) const {
    return std::nullopt;
  }

  /// For a serialized AST file, returns \c true if an adjacent swiftdoc has been
  /// loaded. Otherwise, returns \c false.
  virtual bool hasLoadedSwiftDoc() const { return false; }

  virtual std::optional<StringRef> getGroupNameForDecl(const Decl *D) const {
    return std::nullopt;
  }

  virtual std::optional<StringRef>
  getSourceFileNameForDecl(const Decl *D) const {
    return std::nullopt;
  }

  virtual std::optional<unsigned> getSourceOrderForDecl(const Decl *D) const {
    return std::nullopt;
  }

  virtual std::optional<StringRef> getGroupNameByUSR(StringRef USR) const {
    return std::nullopt;
  }

  virtual std::optional<ExternalSourceLocs::RawLocs>
  getExternalRawLocsForDecl(const Decl *D) const {
    return std::nullopt;
  }

  virtual void collectAllGroups(SmallVectorImpl<StringRef> &Names) const {}

  /// Returns an implementation-defined "discriminator" for \p D, which
  /// distinguishes \p D from other declarations in the same module with the
  /// same name.
  ///
  /// Since this value is used in name mangling, it should be a valid ASCII-only
  /// identifier.
  virtual Identifier
  getDiscriminatorForPrivateDecl(const Decl *D) const = 0;

  /// Finds all top-level decls in this file.
  ///
  /// This does a simple local lookup, not recursively looking through imports.
  /// The order of the results is not guaranteed to be meaningful.
  virtual void getTopLevelDecls(SmallVectorImpl<Decl*> &results) const {}

  /// Finds all top-level decls in this file with their auxiliary decls such as
  /// macro expansions.
  ///
  /// This does a simple local lookup, not recursively looking through imports.
  /// The order of the results is not guaranteed to be meaningful.
  void getTopLevelDeclsWithAuxiliaryDecls(
      SmallVectorImpl<Decl*> &results) const;

  virtual void
  getExportedPrespecializations(SmallVectorImpl<Decl *> &results) const {}

  /// Finds top-level decls in this file filtered by their attributes.
  ///
  /// This does a simple local lookup, not recursively looking through imports.
  /// The order of the results is not guaranteed to be meaningful.
  ///
  /// \param Results Vector collecting the decls.
  ///
  /// \param matchAttributes Check on the attributes of a decl to keep only
  /// decls with matching attributes. The subclass SerializedASTFile checks the
  /// attributes first to only deserialize decls with accepted attributes,
  /// limiting deserialization work.
  virtual void
  getTopLevelDeclsWhereAttributesMatch(
              SmallVectorImpl<Decl*> &Results,
              llvm::function_ref<bool(DeclAttributes)> matchAttributes) const;

  /// Finds all operator decls in this file.
  ///
  /// This does a simple local lookup, not recursively looking through imports.
  /// The order of the results is not guaranteed to be meaningful.
  virtual void
  getOperatorDecls(SmallVectorImpl<OperatorDecl *> &results) const {}

  /// Finds all precedence group decls in this file.
  ///
  /// This does a simple local lookup, not recursively looking through imports.
  /// The order of the results is not guaranteed to be meaningful.
  virtual void
  getPrecedenceGroups(SmallVectorImpl<PrecedenceGroupDecl*> &Results) const {}

  /// Finds all local type decls in this file.
  ///
  /// This does a simple local lookup, not recursively looking through imports.
  /// The order of the results is not guaranteed to be meaningful.
  virtual void getLocalTypeDecls(SmallVectorImpl<TypeDecl*> &results) const {}

  virtual void
  getOpaqueReturnTypeDecls(SmallVectorImpl<OpaqueTypeDecl*> &results) const {}

  /// Adds all top-level decls to the given vector.
  ///
  /// This includes all decls that should be displayed to clients of the module.
  /// The order of the results is not guaranteed to be meaningful.
  ///
  /// This can differ from \c getTopLevelDecls, e.g. it returns decls from a
  /// shadowed clang module.
  virtual void getDisplayDecls(SmallVectorImpl<Decl*> &results, bool recursive = false) const {
    getTopLevelDecls(results);
  }

  /// Looks up which modules are imported by this file.
  ///
  /// \p filter controls whether public, private, or any imports are included
  /// in this list.
  virtual void
  getImportedModules(SmallVectorImpl<ImportedModule> &imports,
                     ModuleDecl::ImportFilter filter) const {}

  /// Looks up which external macros are defined by this file.
  virtual void
  getExternalMacros(SmallVectorImpl<ExternalMacroPlugin> &macros) const {}

  /// Lists modules that are not imported from this file and used in API.
  virtual void getImplicitImportsForModuleInterface(
      SmallVectorImpl<ImportedModule> &imports) const {}

  /// \see ModuleDecl::getImportedModulesForLookup
  virtual void getImportedModulesForLookup(
      SmallVectorImpl<ImportedModule> &imports) const {
    return getImportedModules(imports, ModuleDecl::ImportFilterKind::Exported);
  }

  /// Generates the list of libraries needed to link this file, based on its
  /// imports.
  virtual void
  collectLinkLibraries(ModuleDecl::LinkLibraryCallback callback) const {}

  /// Load extra dependencies of this module to satisfy a testable import.
  virtual void loadDependenciesForTestable(SourceLoc diagLoc) const {}

  /// Returns the path of the file or directory that defines the module
  /// represented by this \c FileUnit, or empty string if there is none.
  /// Cross-import overlay specifiers are found relative to this path.
  virtual StringRef getModuleDefiningPath() const {
    return "";
  }

  /// True if this file contains the main class for the module.
  bool hasMainClass() const {
    return getMainClass();
  }
  ClassDecl *getMainClass() const {
    return dyn_cast_or_null<ClassDecl>(getMainDecl());
  }
  bool hasMainDecl() const { return getMainDecl(); }
  virtual ValueDecl *getMainDecl() const { return nullptr; }
  FuncDecl *getMainFunc() const {
    return dyn_cast_or_null<FuncDecl>(getMainDecl());
  }
  virtual bool hasEntryPoint() const {
    return false;
  }

  virtual ModuleDecl *getUnderlyingModuleIfOverlay() const {
    return nullptr;
  }

  /// Returns the associated clang module if one exists.
  virtual const clang::Module *getUnderlyingClangModule() const {
    return nullptr;
  }

  /// Returns the real name of the enclosing module to use when referencing entities in this file.
  /// The 'real name' is the actual binary name of the module, which can be different from the 'name'
  /// if module aliasing was used (via -module-alias flag).
  ///
  /// This is usually the module real name which can be overriden by an
  /// `export_as` definition of a clang module, or `-export-as` flag on an
  /// imported Swift module. Swift modules built from source do not apply
  /// their own `-export-as` flag, this way the swiftinterface can be
  /// verified.
  virtual StringRef getExportedModuleName() const {
    return getParentModule()->getRealName().str();
  }

  /// Returns the public facing name of this module, only if it is set
  /// explicitly.
  virtual StringRef getPublicModuleName() const {
    return {};
  }

  /// Returns the version of the Swift compiler used to create generate
  /// .swiftinterface file if this file is produced from one.
  virtual version::Version getSwiftInterfaceCompilerVersion() const {
    return {};
  }

  SWIFT_DEBUG_DUMPER(dumpDisplayDecls());
  SWIFT_DEBUG_DUMPER(dumpTopLevelDecls());

  /// Traverse the decls within this file.
  ///
  /// \returns true if traversal was aborted, false if it completed
  /// successfully.
  virtual bool walk(ASTWalker &walker);

  // Efficiency override for DeclContext::getParentModule().
  ModuleDecl *getParentModule() const {
    return const_cast<ModuleDecl *>(cast<ModuleDecl>(getParent()));
  }

  static bool classof(const DeclContext *DC) {
    return DC->getContextKind() == DeclContextKind::FileUnit;
  }

  using ASTAllocated<FileUnit>::operator new;
  using ASTAllocated<FileUnit>::operator delete;
};

/// This represents the compiler's implicitly generated declarations in the
/// Builtin module.
class BuiltinUnit final : public FileUnit {
public:
  class LookupCache;

private:
  std::unique_ptr<LookupCache> Cache;
  LookupCache &getCache() const;

  friend ASTContext;
  ~BuiltinUnit() = default;

public:
  explicit BuiltinUnit(ModuleDecl &M);

  virtual void lookupValue(DeclName name, NLKind lookupKind,
                           OptionSet<ModuleLookupFlags> Flags,
                           SmallVectorImpl<ValueDecl*> &result) const override;

  /// Find all Objective-C methods with the given selector.
  void lookupObjCMethods(
         ObjCSelector selector,
         SmallVectorImpl<AbstractFunctionDecl *> &results) const override;

  Identifier
  getDiscriminatorForPrivateDecl(const Decl *D) const override {
    llvm_unreachable("no private values in the Builtin module");
  }

  static bool classof(const FileUnit *file) {
    return file->getKind() == FileUnitKind::Builtin;
  }

  static bool classof(const DeclContext *DC) {
    return isa<FileUnit>(DC) && classof(cast<FileUnit>(DC));
  }
};

/// Represents an externally-loaded file of some kind.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
class LoadedFile : public FileUnit {
#pragma clang diagnostic pop
protected:
  LoadedFile(FileUnitKind Kind, ModuleDecl &M) noexcept
    : FileUnit(Kind, M) {
    assert(classof(this) && "invalid kind");
  }
public:
  /// Returns the language version that was used to compile the contents of this
  /// file. An empty `Version` is returned if the information is not available.
  virtual version::Version getLanguageVersionBuiltWith() const = 0;

  /// Returns an arbitrary string representing the storage backing this file.
  ///
  /// This is usually a filesystem path.
  virtual StringRef getFilename() const;

  /// Get the path to the file loaded by the compiler. Usually the binary
  /// swiftmodule file or a pcm in the cache. Returns an empty string if not
  /// applicable.
  virtual StringRef getLoadedFilename() const { return StringRef(); }

  /// Returns the path of the file for the module represented by this
  /// \c FileUnit, or an empty string if there is none. For modules either
  /// built by or adjacent to a module interface, returns the module
  /// interface instead.
  virtual StringRef getSourceFilename() const {
    return getModuleDefiningPath();
  }

  virtual StringRef getFilenameForPrivateDecl(const Decl *decl) const {
    return StringRef();
  }

  /// Returns the Swift module that overlays a Clang module.
  virtual ModuleDecl *getOverlayModule() const { return nullptr; }

  virtual bool isSystemModule() const { return false; }

  /// Checks whether an error was encountered while loading the file.
  virtual bool hadLoadError() const { return false; }

  /// Retrieve the set of generic signatures stored within this module.
  ///
  /// \returns \c true if this module file supports retrieving all of the
  /// generic signatures, \c false otherwise.
  virtual bool getAllGenericSignatures(
                 SmallVectorImpl<GenericSignature> &genericSignatures) {
    return false;
  }

  virtual void collectBasicSourceFileInfo(
      llvm::function_ref<void(const BasicSourceFileInfo &)> callback) const {}

  virtual void collectSerializedSearchPath(
      llvm::function_ref<void(StringRef)> callback) const {}
  static bool classof(const FileUnit *file) {
    return file->getKind() == FileUnitKind::SerializedAST ||
           file->getKind() == FileUnitKind::ClangModule ||
           file->getKind() == FileUnitKind::DWARFModule;
  }
  static bool classof(const DeclContext *DC) {
    return isa<FileUnit>(DC) && classof(cast<FileUnit>(DC));
  }
};

void simple_display(llvm::raw_ostream &out, const FileUnit *file);

inline FileUnit &ModuleDecl::getMainFile(FileUnitKind expectedKind) const {
  assert(expectedKind != FileUnitKind::Source &&
         "must use specific source kind; see getMainSourceFile");

  auto files = getFiles();
  assert(!files.empty() && "No files in module");
  assert(files.front()->getKind() == expectedKind);
  return *files.front();
}

} // end namespace swift

#endif
