//===--- Frontend.h - frontend utility methods ------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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
//
//===----------------------------------------------------------------------===//
//
// This file contains declarations of utility methods for parsing and
// performing semantic on modules.
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_FRONTEND_H
#define SWIFT_FRONTEND_H

#include "swift/AST/DiagnosticConsumer.h"
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/IRGenOptions.h"
#include "swift/AST/LinkLibrary.h"
#include "swift/AST/Module.h"
#include "swift/AST/SILOptions.h"
#include "swift/AST/SearchPathOptions.h"
#include "swift/Basic/DiagnosticOptions.h"
#include "swift/Basic/LangOptions.h"
#include "swift/Basic/SourceManager.h"
#include "swift/ClangImporter/ClangImporter.h"
#include "swift/ClangImporter/ClangImporterOptions.h"
#include "swift/Frontend/FrontendOptions.h"
#include "swift/Migrator/MigratorOptions.h"
#include "swift/Parse/CodeCompletionCallbacks.h"
#include "swift/Parse/Parser.h"
#include "swift/SIL/SILModule.h"
#include "swift/Sema/SourceLoader.h"
#include "swift/Serialization/Validation.h"
#include "swift/Subsystems.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/MemoryBuffer.h"

#include <memory>

namespace swift {

class SerializedModuleLoader;

/// The abstract configuration of the compiler, including:
///   - options for all stages of translation,
///   - information about the build environment,
///   - information about the job being performed, and
///   - lists of inputs.
///
/// A CompilerInvocation can be built from a frontend command line
/// using parseArgs.  It can then be used to build a CompilerInstance,
/// which manages the actual compiler execution.
class CompilerInvocation {
  LangOptions LangOpts;
  FrontendOptions FrontendOpts;
  ClangImporterOptions ClangImporterOpts;
  SearchPathOptions SearchPathOpts;
  DiagnosticOptions DiagnosticOpts;
  MigratorOptions MigratorOpts;
  SILOptions SILOpts;
  IRGenOptions IRGenOpts;

  llvm::MemoryBuffer *CodeCompletionBuffer = nullptr;

  /// \brief Code completion offset in bytes from the beginning of the main
  /// source file.  Valid only if \c isCodeCompletion() == true.
  unsigned CodeCompletionOffset = ~0U;

  CodeCompletionCallbacksFactory *CodeCompletionFactory = nullptr;

public:
  CompilerInvocation();

  /// Initializes the compiler invocation for the list of arguments.
  ///
  /// All parsing should be additive, i.e. options should not be reset to their
  /// default values given the /absence/ of a flag. This is because \c parseArgs
  /// may be used to modify an already partially configured invocation.
  ///
  /// If non-empty, relative search paths are resolved relative to
  /// \p workingDirectory.
  ///
  /// \returns true if there was an error, false on success.
  bool parseArgs(ArrayRef<const char *> Args, DiagnosticEngine &Diags,
                 StringRef workingDirectory = {});

  /// Sets specific options based on the given serialized Swift binary data.
  ///
  /// This is additive, i.e. options are not reset to their default values given
  /// the /absence/ of a flag. However, flags that only have a single value may
  /// (and should) be overwritten by this method.
  ///
  /// Invoking this on more than one serialized AST is likely to result in
  /// one or both of them failing to load. Please pick one AST to provide base
  /// flags for the entire ASTContext and let the others succeed or fail the
  /// normal way. (Some additive flags, like search paths, will be handled
  /// properly during normal module loading.)
  ///
  /// \returns Status::Valid on success, one of the Status issues on error.
  serialization::Status loadFromSerializedAST(StringRef data);

  /// Serialize the command line arguments for emitting them
  /// to DWARF and inject SDKPath if necessary.
  static void buildDWARFDebugFlags(std::string &Output,
                                   const ArrayRef<const char*> &Args,
                                   StringRef SDKPath,
                                   StringRef ResourceDir);

  void setTargetTriple(StringRef Triple);

  StringRef getTargetTriple() const {
    return LangOpts.Target.str();
  }

  void setClangModuleCachePath(StringRef Path) {
    ClangImporterOpts.ModuleCachePath = Path.str();
  }

  StringRef getClangModuleCachePath() const {
    return ClangImporterOpts.ModuleCachePath;
  }

  void setImportSearchPaths(const std::vector<std::string> &Paths) {
    SearchPathOpts.ImportSearchPaths = Paths;
  }

  ArrayRef<std::string> getImportSearchPaths() const {
    return SearchPathOpts.ImportSearchPaths;
  }

  void setFrameworkSearchPaths(
             const std::vector<SearchPathOptions::FrameworkSearchPath> &Paths) {
    SearchPathOpts.FrameworkSearchPaths = Paths;
  }

  ArrayRef<SearchPathOptions::FrameworkSearchPath> getFrameworkSearchPaths() const {
    return SearchPathOpts.FrameworkSearchPaths;
  }

  void setExtraClangArgs(const std::vector<std::string> &Args) {
    ClangImporterOpts.ExtraArgs = Args;
  }

  ArrayRef<std::string> getExtraClangArgs() const {
    return ClangImporterOpts.ExtraArgs;
  }

  void addLinkLibrary(StringRef name, LibraryKind kind) {
    IRGenOpts.LinkLibraries.push_back({name, kind});
  }

  ArrayRef<LinkLibrary> getLinkLibraries() const {
    return IRGenOpts.LinkLibraries;
  }

  void setMainExecutablePath(StringRef Path);

  void setRuntimeResourcePath(StringRef Path);

  void setSDKPath(const std::string &Path) {
    SearchPathOpts.SDKPath = Path;
  }

  StringRef getSDKPath() const {
    return SearchPathOpts.SDKPath;
  }

  void setSerializedDiagnosticsPath(StringRef Path) {
    FrontendOpts.SerializedDiagnosticsPath = Path;
  }
  StringRef getSerializedDiagnosticsPath() const {
    return FrontendOpts.SerializedDiagnosticsPath;
  }

  LangOptions &getLangOptions() {
    return LangOpts;
  }
  const LangOptions &getLangOptions() const {
    return LangOpts;
  }

  FrontendOptions &getFrontendOptions() { return FrontendOpts; }
  const FrontendOptions &getFrontendOptions() const { return FrontendOpts; }

  ClangImporterOptions &getClangImporterOptions() { return ClangImporterOpts; }
  const ClangImporterOptions &getClangImporterOptions() const {
    return ClangImporterOpts;
  }

  SearchPathOptions &getSearchPathOptions() { return SearchPathOpts; }
  const SearchPathOptions &getSearchPathOptions() const {
    return SearchPathOpts;
  }

  DiagnosticOptions &getDiagnosticOptions() { return DiagnosticOpts; }
  const DiagnosticOptions &getDiagnosticOptions() const {
    return DiagnosticOpts;
  }

  const MigratorOptions &getMigratorOptions() const {
    return MigratorOpts;
  }

  SILOptions &getSILOptions() { return SILOpts; }
  const SILOptions &getSILOptions() const { return SILOpts; }

  IRGenOptions &getIRGenOptions() { return IRGenOpts; }
  const IRGenOptions &getIRGenOptions() const { return IRGenOpts; }

  void setParseStdlib() {
    FrontendOpts.ParseStdlib = true;
  }

  bool getParseStdlib() const {
    return FrontendOpts.ParseStdlib;
  }

  void setInputKind(InputFileKind K) {
    FrontendOpts.InputKind = K;
  }

  InputFileKind getInputKind() const {
    return FrontendOpts.InputKind;
  }

  SourceFileKind getSourceFileKind() const;

  void setModuleName(StringRef Name) {
    FrontendOpts.ModuleName = Name.str();
    IRGenOpts.ModuleName = Name.str();
  }

  StringRef getModuleName() const {
    return FrontendOpts.ModuleName;
  }

  void addInputFilename(StringRef Filename) {
    FrontendOpts.Inputs.addInputFilename(Filename);
  }

  /// Does not take ownership of \p Buf.
  void addInputBuffer(llvm::MemoryBuffer *Buf) {
    FrontendOpts.Inputs.addInputBuffer(Buf);
  }

  void setPrimaryInput(SelectedInput pi) {
    FrontendOpts.Inputs.setPrimaryInput(pi);
  }

  void clearInputs() { FrontendOpts.Inputs.clearInputs(); }

  StringRef getOutputFilename() const {
    return FrontendOpts.getSingleOutputFilename();
  }

  void setCodeCompletionPoint(llvm::MemoryBuffer *Buf, unsigned Offset) {
    assert(Buf);
    CodeCompletionBuffer = Buf;
    CodeCompletionOffset = Offset;
    // We don't need typo-correction for code-completion.
    // FIXME: This isn't really true, but is a performance issue.
    LangOpts.TypoCorrectionLimit = 0;
  }

  std::pair<llvm::MemoryBuffer *, unsigned> getCodeCompletionPoint() const {
    return std::make_pair(CodeCompletionBuffer, CodeCompletionOffset);
  }

  /// \returns true if we are doing code completion.
  bool isCodeCompletion() const {
    return CodeCompletionOffset != ~0U;
  }

  void setCodeCompletionFactory(CodeCompletionCallbacksFactory *Factory) {
    CodeCompletionFactory = Factory;
  }

  CodeCompletionCallbacksFactory *getCodeCompletionFactory() const {
    return CodeCompletionFactory;
  }

  /// Retrieve a module hash string that is suitable for uniquely
  /// identifying the conditions under which the module was built, for use
  /// in generating a cached PCH file for the bridging header.
  std::string getPCHHash() const;

  SourceFile::ImplicitModuleImportKind getImplicitModuleImportKind() {
    if (getInputKind() == InputFileKind::IFK_SIL) {
      return SourceFile::ImplicitModuleImportKind::None;
    }
    if (getParseStdlib()) {
      return SourceFile::ImplicitModuleImportKind::Builtin;
    }
    return SourceFile::ImplicitModuleImportKind::Stdlib;
  }

  /// Performs input setup common to these tools:
  /// sil-opt, sil-func-extractor, sil-llvm-gen, and sil-nm.
  /// Return value includes the buffer so caller can keep it alive.
  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
  setUpInputForSILTool(StringRef inputFilename, StringRef moduleNameArg,
                       bool alwaysSetModuleToMain,
                       serialization::ExtendedValidationInfo &extendedInfo);
  bool hasSerializedAST() {
    return FrontendOpts.InputKind == InputFileKind::IFK_Swift_Library;
  }
};

/// A class which manages the state and execution of the compiler.
/// This owns the primary compiler singletons, such as the ASTContext,
/// as well as various build products such as the SILModule.
///
/// Before a CompilerInstance can be used, it must be configured by
/// calling \a setup.  If successful, this will create an ASTContext
/// and set up the basic compiler invariants.  Calling \a setup multiple
/// times on a single CompilerInstance is not permitted.
class CompilerInstance {
  CompilerInvocation Invocation;
  SourceManager SourceMgr;
  DiagnosticEngine Diagnostics{SourceMgr};
  std::unique_ptr<ASTContext> Context;
  std::unique_ptr<SILModule> TheSILModule;

  DependencyTracker *DepTracker = nullptr;
  ReferencedNameTracker *NameTracker = nullptr;

  ModuleDecl *MainModule = nullptr;
  SerializedModuleLoader *SML = nullptr;

  /// Contains buffer IDs for input source code files.
  std::vector<unsigned> InputSourceCodeBufferIDs;

  struct PartialModuleInputs {
    std::unique_ptr<llvm::MemoryBuffer> ModuleBuffer;
    std::unique_ptr<llvm::MemoryBuffer> ModuleDocBuffer;
  };

  /// Contains \c MemoryBuffers for partial serialized module files and
  /// corresponding partial serialized module documentation files.
  std::vector<PartialModuleInputs> PartialModules;

  enum : unsigned { NO_SUCH_BUFFER = ~0U };
  unsigned MainBufferID = NO_SUCH_BUFFER;

  /// PrimaryBufferID corresponds to PrimaryInput.
  unsigned PrimaryBufferID = NO_SUCH_BUFFER;
  bool isWholeModuleCompilation() { return PrimaryBufferID == NO_SUCH_BUFFER; }

  SourceFile *PrimarySourceFile = nullptr;

  void createSILModule();
  void setPrimarySourceFile(SourceFile *SF);

  bool setUpForFileAt(unsigned i);

public:
  SourceManager &getSourceMgr() { return SourceMgr; }

  DiagnosticEngine &getDiags() { return Diagnostics; }

  ASTContext &getASTContext() {
    return *Context;
  }
  bool hasASTContext() const { return Context != nullptr; }

  SILOptions &getSILOptions() { return Invocation.getSILOptions(); }
  const SILOptions &getSILOptions() const { return Invocation.getSILOptions(); }

  void addDiagnosticConsumer(DiagnosticConsumer *DC) {
    Diagnostics.addConsumer(*DC);
  }

  void setDependencyTracker(DependencyTracker *DT) {
    assert(!Context && "must be called before setup()");
    DepTracker = DT;
  }
  DependencyTracker *getDependencyTracker() {
    return DepTracker;
  }

  void setReferencedNameTracker(ReferencedNameTracker *tracker) {
    assert(!PrimarySourceFile && "must be called before performSema()");
    NameTracker = tracker;
  }
  ReferencedNameTracker *getReferencedNameTracker() {
    return NameTracker;
  }

  /// Set the SIL module for this compilation instance.
  ///
  /// The CompilerInstance takes ownership of the given SILModule object.
  void setSILModule(std::unique_ptr<SILModule> M) {
    TheSILModule = std::move(M);
  }

  SILModule *getSILModule() {
    return TheSILModule.get();
  }

  std::unique_ptr<SILModule> takeSILModule() {
    return std::move(TheSILModule);
  }

  bool hasSILModule() {
    return static_cast<bool>(TheSILModule);
  }

  ModuleDecl *getMainModule();

  SerializedModuleLoader *getSerializedModuleLoader() const { return SML; }

  ArrayRef<unsigned> getInputBufferIDs() const {
    return InputSourceCodeBufferIDs;
  }

  ArrayRef<LinkLibrary> getLinkLibraries() const {
    return Invocation.getLinkLibraries();
  }

  bool hasSourceImport() const {
    return Invocation.getFrontendOptions().EnableSourceImport;
  }

  /// Gets the SourceFile which is the primary input for this CompilerInstance.
  /// \returns the primary SourceFile, or nullptr if there is no primary input
  SourceFile *getPrimarySourceFile() { return PrimarySourceFile; }

  /// \brief Returns true if there was an error during setup.
  bool setup(const CompilerInvocation &Invocation);

  /// Parses and type-checks all input files.
  void performSema();

  /// Parses the input file but does no type-checking or module imports.
  /// Note that this only supports parsing an invocation with a single file.
  ///
  ///
  void performParseOnly(bool EvaluateConditionals = false);

  /// Frees up the ASTContext and SILModule objects that this instance is
  /// holding on.
  void freeContextAndSIL();

private:
  /// Load stdlib & return true if should continue, i.e. no error
  bool loadStdlib();
  ModuleDecl *importUnderlyingModule();
  ModuleDecl *importBridgingHeader();

  void
  getImplicitlyImportedModules(SmallVectorImpl<ModuleDecl *> &importModules);

public: // for static functions in Frontend.cpp
  struct ImplicitImports {
    SourceFile::ImplicitModuleImportKind kind;
    ModuleDecl *objCModuleUnderlyingMixedFramework;
    ModuleDecl *headerModule;
    SmallVector<ModuleDecl *, 4> modules;

    explicit ImplicitImports(CompilerInstance &compiler);
  };

private:
  void createREPLFile(const ImplicitImports &implicitImports) const;
  std::unique_ptr<DelayedParsingCallbacks>
  computeDelayedParsingCallback(bool isPrimary);

  void addMainFileToModule(const ImplicitImports &implicitImports);

  void parseAndCheckTypes(const ImplicitImports &implicitImports);

  void parseLibraryFile(unsigned BufferID,
                        const ImplicitImports &implicitImports,
                        PersistentParserState &PersistentState,
                        DelayedParsingCallbacks *PrimaryDelayedCB,
                        DelayedParsingCallbacks *SecondaryDelayedCB);

  /// Return true if had load error
  bool
  parsePartialModulesAndLibraryFiles(const ImplicitImports &implicitImports,
                                     PersistentParserState &PersistentState,
                                     DelayedParsingCallbacks *PrimaryDelayedCB,
                                     DelayedParsingCallbacks *SecondaryDelayedCB);

  OptionSet<TypeCheckingFlags> computeTypeCheckingOptions();

  void forEachFileToTypeCheck(llvm::function_ref<void(SourceFile &)> fn);

  void parseAndTypeCheckMainFile(PersistentParserState &PersistentState,
                                 DelayedParsingCallbacks *DelayedParseCB,
                                 OptionSet<TypeCheckingFlags> TypeCheckOptions);

  void finishTypeChecking(OptionSet<TypeCheckingFlags> TypeCheckOptions);
};

} // namespace swift

#endif
