text: //===--- Utils.cpp - Misc utilities ---------------------------------------===//
  //
  // 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
  //
  //===----------------------------------------------------------------------===//
  
  #include "swift/IDE/Utils.h"
  #include "swift/Basic/Edit.h"
  #include "swift/Basic/SourceManager.h"
  #include "swift/Basic/Platform.h"
  #include "swift/ClangImporter/ClangModule.h"
  #include "swift/Driver/FrontendUtil.h"
  #include "swift/Frontend/Frontend.h"
  #include "swift/Parse/Parser.h"
  #include "swift/Subsystems.h"
  #include "clang/AST/ASTContext.h"
  #include "clang/AST/DeclObjC.h"
  #include "clang/Basic/TargetInfo.h"
  #include "clang/CodeGen/ObjectFilePCHContainerOperations.h"
  #include "clang/Frontend/CompilerInstance.h"
  #include "clang/Frontend/TextDiagnosticBuffer.h"
  #include "clang/Lex/PreprocessorOptions.h"
  #include "clang/Rewrite/Core/RewriteBuffer.h"
  #include "clang/Serialization/ASTReader.h"
  #include "llvm/ADT/STLExtras.h"
  #include "llvm/Support/Compiler.h"
  #include "llvm/Support/MemoryBuffer.h"
  #include "llvm/Support/Path.h"
  
  using namespace swift;
  using namespace ide;
  
  static const char *skipStringInCode(const char *p, const char *End);
  
  static const char *skipParenExpression(const char *p, const char *End) {
    const char *e = p;
    if (*e == '(') {
      uint32_t ParenCount = 1;
      bool done = false;
      for (++e; e < End; ++e) {
        switch (*e) {
        case ')':
          done = --ParenCount == 0;
          break;
  
        case '(':
          ++ParenCount;
          break;
  
        case '"':
          e = skipStringInCode (e, End);
          break;
  
        default:
          break;
        }
        // If "done" is true make sure we don't increment "e"
        if (done)
          break;
      }
    }
    if (e >= End)
      return End;
    return e;
  }
  
  static const char *skipStringInCode(const char *p, const char *End) {
    const char *e = p;
    if (*e == '"') {
      bool done = false;
      for (++e; e < End; ++e) {
        switch (*e) {
        case '"':
          done = true;
          break;
  
        case '\\':
          ++e;
          if (e >= End)
            done = true;
          else if (*e == '(')
            e = skipParenExpression (e, End);
          break;
  
        default:
          break;
        }
        // If "done" is true make sure we don't increment "e"
        if (done)
            break;
      }
    }
    if (e >= End)
      return End;
    return e;
  }
  
  SourceCompleteResult
  ide::isSourceInputComplete(std::unique_ptr<llvm::MemoryBuffer> MemBuf,
                             SourceFileKind SFKind) {
    SourceManager SM;
    auto BufferID = SM.addNewSourceBuffer(std::move(MemBuf));
    ParserUnit Parse(SM, SFKind, BufferID);
    Parse.parse();
    SourceCompleteResult SCR;
    SCR.IsComplete = !Parse.getParser().isInputIncomplete();
  
    // Use the same code that was in the REPL code to track the indent level
    // for now. In the future we should get this from the Parser if possible.
  
    CharSourceRange entireRange = SM.getRangeForBuffer(BufferID);
    StringRef Buffer = SM.extractText(entireRange);
    const char *SourceStart = Buffer.data();
    const char *SourceEnd = Buffer.data() + Buffer.size();
    const char *LineStart = SourceStart;
    const char *LineSourceStart = nullptr;
    uint32_t LineIndent = 0;
    struct IndentInfo {
      StringRef Prefix;
      uint32_t Indent;
      IndentInfo(const char *s, size_t n, uint32_t i) :
        Prefix(s, n),
        Indent(i) {}
    };
    SmallVector<IndentInfo, 4> IndentInfos;
    for (const char *p = SourceStart; p<SourceEnd; ++p) {
      switch (*p) {
      case '\r':
      case '\n':
        LineIndent = 0;
        LineSourceStart = nullptr;
        LineStart = p + 1;
        break;
  
      case '"':
        p = skipStringInCode (p, SourceEnd);
        break;
  
      case '{':
      case '(':
      case '[':
        ++LineIndent;
        if (LineSourceStart == nullptr)
          IndentInfos.push_back(IndentInfo(LineStart,
                                           p - LineStart,
                                           LineIndent));
        else
          IndentInfos.push_back(IndentInfo(LineStart,
                                           LineSourceStart - LineStart,
                                           LineIndent));
        break;
  
      case '}':
      case ')':
      case ']':
        if (LineIndent > 0)
          --LineIndent;
        if (!IndentInfos.empty())
          IndentInfos.pop_back();
        break;
  
      default:
        if (LineSourceStart == nullptr && !isspace(*p))
          LineSourceStart = p;
        break;
      }
      if (*p == '\0')
        break;
    }
    if (!IndentInfos.empty()) {
      SCR.IndentPrefix = IndentInfos.back().Prefix.str();
      // Trim off anything that follows a non-space character
      const size_t pos = SCR.IndentPrefix.find_first_not_of(" \t");
      if (pos != std::string::npos)
          SCR.IndentPrefix.erase(pos);
      SCR.IndentLevel = IndentInfos.back().Indent;
    }
    return SCR;
  }
  
  SourceCompleteResult
  ide::isSourceInputComplete(StringRef Text,SourceFileKind SFKind) {
    return ide::isSourceInputComplete(llvm::MemoryBuffer::getMemBufferCopy(Text),
                                      SFKind);
  }
  
  static FrontendInputsAndOutputs resolveSymbolicLinksInInputs(
      FrontendInputsAndOutputs &inputsAndOutputs, StringRef UnresolvedPrimaryFile,
      llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
      std::string &Error) {
    assert(FileSystem);
  
    llvm::SmallString<128> PrimaryFile;
    if (auto err = FileSystem->getRealPath(UnresolvedPrimaryFile, PrimaryFile))
      PrimaryFile = UnresolvedPrimaryFile;
  
    unsigned primaryCount = 0;
    // FIXME: The frontend should be dealing with symlinks, maybe similar to
    // clang's FileManager ?
    FrontendInputsAndOutputs replacementInputsAndOutputs;
    for (const InputFile &input : inputsAndOutputs.getAllInputs()) {
      llvm::SmallString<128> newFilename;
      if (auto err = FileSystem->getRealPath(input.getFileName(), newFilename))
        newFilename = input.getFileName();
      llvm::sys::path::native(newFilename);
      bool newIsPrimary = input.isPrimary() ||
                          (!PrimaryFile.empty() && PrimaryFile == newFilename);
      if (newIsPrimary) {
        ++primaryCount;
      }
      assert(primaryCount < 2 && "cannot handle multiple primaries");
  
      replacementInputsAndOutputs.addInput(
          InputFile(newFilename.str(), newIsPrimary, input.getBuffer()));
    }
  
    if (PrimaryFile.empty() || primaryCount == 1) {
      return replacementInputsAndOutputs;
    }
  
    llvm::SmallString<64> Err;
    llvm::raw_svector_ostream OS(Err);
    OS << "'" << PrimaryFile << "' is not part of the input files";
    Error = std::string(OS.str());
    return replacementInputsAndOutputs;
  }
  
  static void disableExpensiveSILOptions(SILOptions &Opts) {
    // Disable the sanitizers.
    Opts.Sanitizers = {};
  
    // Disable PGO and code coverage.
    Opts.GenerateProfile = false;
    Opts.EmitProfileCoverageMapping = false;
    Opts.UseProfile = "";
  }
  
  namespace {
  class StreamDiagConsumer : public DiagnosticConsumer {
    llvm::raw_ostream &OS;
  
  public:
    StreamDiagConsumer(llvm::raw_ostream &OS) : OS(OS) {}
  
    void handleDiagnostic(SourceManager &SM,
                          const DiagnosticInfo &Info) override {
      // FIXME: Print location info if available.
      switch (Info.Kind) {
      case DiagnosticKind::Error:
        OS << "error: ";
        break;
      case DiagnosticKind::Warning:
        OS << "warning: ";
        break;
      case DiagnosticKind::Note:
        OS << "note: ";
        break;
      case DiagnosticKind::Remark:
        OS << "remark: ";
        break;
      }
      DiagnosticEngine::formatDiagnosticText(OS, Info.FormatString,
                                             Info.FormatArgs);
    }
  };
  } // end anonymous namespace
  
  bool ide::initCompilerInvocation(
      CompilerInvocation &Invocation, ArrayRef<const char *> OrigArgs,
      DiagnosticEngine &Diags, StringRef UnresolvedPrimaryFile,
      llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
      const std::string &runtimeResourcePath,
      const std::string &diagnosticDocumentationPath,
      bool shouldOptimizeForIDE, time_t sessionTimestamp, std::string &Error) {
    SmallVector<const char *, 16> Args;
    // Make sure to put '-resource-dir' and '-diagnostic-documentation-path' at
    // the top to allow overriding them with the passed in arguments.
    Args.push_back("-resource-dir");
    Args.push_back(runtimeResourcePath.c_str());
    Args.push_back("-Xfrontend");
    Args.push_back("-diagnostic-documentation-path");
    Args.push_back("-Xfrontend");
    Args.push_back(diagnosticDocumentationPath.c_str());
    Args.append(OrigArgs.begin(), OrigArgs.end());
  
    SmallString<32> ErrStr;
    llvm::raw_svector_ostream ErrOS(ErrStr);
    StreamDiagConsumer DiagConsumer(ErrOS);
    Diags.addConsumer(DiagConsumer);
  
    bool HadError = driver::getSingleFrontendInvocationFromDriverArguments(
        Args, Diags, [&](ArrayRef<const char *> FrontendArgs) {
      return Invocation.parseArgs(FrontendArgs, Diags);
    }, /*ForceNoOutputs=*/true);
  
    // Remove the StreamDiagConsumer as it's no longer needed.
    Diags.removeConsumer(DiagConsumer);
  
    if (HadError) {
      Error = std::string(ErrOS.str());
      return true;
    }
  
    Invocation.getFrontendOptions().InputsAndOutputs =
        resolveSymbolicLinksInInputs(
            Invocation.getFrontendOptions().InputsAndOutputs,
            UnresolvedPrimaryFile, FileSystem, Error);
  
    // SourceKit functionalities want to proceed even if there are missing inputs.
    Invocation.getFrontendOptions().InputsAndOutputs
        .setShouldRecoverMissingInputs();
  
    if (!Error.empty())
      return true;
  
    ClangImporterOptions &ImporterOpts = Invocation.getClangImporterOptions();
    ImporterOpts.DetailedPreprocessingRecord = true;
  
    assert(!Invocation.getModuleName().empty());
  
    auto &LangOpts = Invocation.getLangOptions();
    LangOpts.AttachCommentsToDecls = true;
    LangOpts.DiagnosticsEditorMode = true;
    LangOpts.CollectParsedToken = true;
    if (LangOpts.PlaygroundTransform) {
      // The playground instrumenter changes the AST in ways that disrupt the
      // SourceKit functionality. Since we don't need the instrumenter, and all we
      // actually need is the playground semantics visible to the user, like
      // silencing the "expression resolves to an unused l-value" error, disable it.
      LangOpts.PlaygroundTransform = false;
    }
  
    // Disable the index-store functionality for the sourcekitd requests.
    auto &FrontendOpts = Invocation.getFrontendOptions();
    FrontendOpts.IndexStorePath.clear();
    ImporterOpts.IndexStorePath.clear();
  
    // Force the action type to be -typecheck. This affects importing the
    // SwiftONoneSupport module.
    FrontendOpts.RequestedAction = FrontendOptions::ActionType::Typecheck;
  
    // We don't care about LLVMArgs
    FrontendOpts.LLVMArgs.clear();
  
    // SwiftSourceInfo files provide source location information for decls coming
    // from loaded modules. For most IDE use cases it either has an undesirable
    // impact on performance with no benefit (code completion), results in stale
    // locations being used instead of more up-to-date indexer locations (cursor
    // info), or has no observable effect (diagnostics, which are filtered to just
    // those with a location in the primary file, and everything else).
    if (shouldOptimizeForIDE)
      FrontendOpts.IgnoreSwiftSourceInfo = true;
  
    // To save the time for module validation, consider the lifetime of ASTManager
    // as a single build session.
    // NOTE: Do this only if '-disable-modules-validate-system-headers' is *not*
    //       explicitly enabled.
    auto &SearchPathOpts = Invocation.getSearchPathOptions();
    if (!SearchPathOpts.DisableModulesValidateSystemDependencies) {
      // NOTE: 'SessionTimestamp - 1' because clang compares it with '<=' that may
      //       cause unnecessary validations if they happens within one second
      //       from the SourceKit startup.
      ImporterOpts.ExtraArgs.push_back("-fbuild-session-timestamp=" +
                                       std::to_string(sessionTimestamp - 1));
      ImporterOpts.ExtraArgs.push_back(
          "-fmodules-validate-once-per-build-session");
  
      SearchPathOpts.DisableModulesValidateSystemDependencies = true;
    }
  
    // Disable expensive SIL options to reduce time spent in SILGen.
    disableExpensiveSILOptions(Invocation.getSILOptions());
  
    return false;
  }
  
  // Adjust the cc1 triple string we got from clang, to make sure it will be
  // accepted when it goes through the swift clang importer.
  static std::string adjustClangTriple(StringRef TripleStr) {
    std::string Result;
    llvm::raw_string_ostream OS(Result);
  
    llvm::Triple Triple(TripleStr);
    switch (Triple.getSubArch()) {
    case llvm::Triple::SubArchType::ARMSubArch_v7:
      OS << "armv7"; break;
    case llvm::Triple::SubArchType::ARMSubArch_v7s:
      OS << "armv7s"; break;
    case llvm::Triple::SubArchType::ARMSubArch_v7k:
      OS << "armv7k"; break;
    case llvm::Triple::SubArchType::ARMSubArch_v6:
      OS << "armv6"; break;
    case llvm::Triple::SubArchType::ARMSubArch_v6m:
      OS << "armv6m"; break;
    case llvm::Triple::SubArchType::ARMSubArch_v6k:
      OS << "armv6k"; break;
    case llvm::Triple::SubArchType::ARMSubArch_v6t2:
      OS << "armv6t2"; break;
    default:
      // Adjust i386-macosx to x86_64 because there is no Swift stdlib for i386.
      if ((Triple.getOS() == llvm::Triple::MacOSX ||
        Triple.getOS() == llvm::Triple::Darwin) && Triple.getArch() == llvm::Triple::x86) {
        OS << "x86_64";
      } else {
        OS << Triple.getArchName();
      }
      break;
    }
    OS << '-' << Triple.getVendorName() << '-' <<
        Triple.getOSAndEnvironmentName();
    OS.flush();
    return Result;
  }
  
  bool ide::initInvocationByClangArguments(ArrayRef<const char *> ArgList,
                                           CompilerInvocation &Invok,
                                           std::string &Error) {
    llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts{
      new clang::DiagnosticOptions()
    };
  
    clang::TextDiagnosticBuffer DiagBuf;
    llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> ClangDiags =
      clang::CompilerInstance::createDiagnostics(DiagOpts.get(),
                                                 &DiagBuf,
                                                 /*ShouldOwnClient=*/false);
  
    // Clang expects this to be like an actual command line. So we need to pass in
    // "clang" for argv[0].
    std::vector<const char *> ClangArgList;
    ClangArgList.push_back("clang");
    ClangArgList.insert(ClangArgList.end(), ArgList.begin(), ArgList.end());
  
    // Create a new Clang compiler invocation.
    std::unique_ptr<clang::CompilerInvocation> ClangInvok =
        clang::createInvocationFromCommandLine(ClangArgList, ClangDiags);
    if (!ClangInvok || ClangDiags->hasErrorOccurred()) {
      for (auto I = DiagBuf.err_begin(), E = DiagBuf.err_end(); I != E; ++I) {
        Error += I->second;
        Error += " ";
      }
      return true;
    }
  
    auto &PPOpts = ClangInvok->getPreprocessorOpts();
    auto &HSOpts = ClangInvok->getHeaderSearchOpts();
  
    Invok.setTargetTriple(adjustClangTriple(ClangInvok->getTargetOpts().Triple));
    if (!HSOpts.Sysroot.empty())
      Invok.setSDKPath(HSOpts.Sysroot);
    if (!HSOpts.ModuleCachePath.empty())
      Invok.setClangModuleCachePath(HSOpts.ModuleCachePath);
  
    auto &CCArgs = Invok.getClangImporterOptions().ExtraArgs;
    for (auto MacroEntry : PPOpts.Macros) {
      std::string MacroFlag;
      if (MacroEntry.second)
        MacroFlag += "-U";
      else
        MacroFlag += "-D";
      MacroFlag += MacroEntry.first;
      CCArgs.push_back(MacroFlag);
    }
  
    for (auto &Entry : HSOpts.UserEntries) {
      switch (Entry.Group) {
        case clang::frontend::Quoted:
          CCArgs.push_back("-iquote");
          CCArgs.push_back(Entry.Path);
          break;
        case clang::frontend::IndexHeaderMap:
          CCArgs.push_back("-index-header-map");
          LLVM_FALLTHROUGH;
        case clang::frontend::Angled: {
          std::string Flag;
          if (Entry.IsFramework)
            Flag += "-F";
          else
            Flag += "-I";
          Flag += Entry.Path;
          CCArgs.push_back(Flag);
          break;
        }
        case clang::frontend::System:
          if (Entry.IsFramework)
            CCArgs.push_back("-iframework");
          else
            CCArgs.push_back("-isystem");
          CCArgs.push_back(Entry.Path);
          break;
        case clang::frontend::ExternCSystem:
        case clang::frontend::CSystem:
        case clang::frontend::CXXSystem:
        case clang::frontend::ObjCSystem:
        case clang::frontend::ObjCXXSystem:
        case clang::frontend::After:
          break;
      }
    }
  
    if (!PPOpts.ImplicitPCHInclude.empty()) {
      clang::FileSystemOptions FileSysOpts;
      clang::FileManager FileMgr(FileSysOpts);
      auto PCHContainerOperations =
          std::make_shared<clang::PCHContainerOperations>();
      std::string HeaderFile = clang::ASTReader::getOriginalSourceFile(
          PPOpts.ImplicitPCHInclude, FileMgr,
          PCHContainerOperations->getRawReader(), *ClangDiags);
      if (!HeaderFile.empty()) {
        CCArgs.push_back("-include");
        CCArgs.push_back(std::move(HeaderFile));
      }
    }
    for (auto &Header : PPOpts.Includes) {
      CCArgs.push_back("-include");
      CCArgs.push_back(Header);
    }
  
    for (auto &Entry : HSOpts.ModulesIgnoreMacros) {
      std::string Flag = "-fmodules-ignore-macro=";
      Flag += Entry;
      CCArgs.push_back(Flag);
    }
  
    for (auto &Entry : HSOpts.VFSOverlayFiles) {
      CCArgs.push_back("-ivfsoverlay");
      CCArgs.push_back(Entry);
    }
  
    if (!ClangInvok->getLangOpts()->isCompilingModule()) {
      CCArgs.push_back("-Xclang");
      llvm::SmallString<64> Str;
      Str += "-fmodule-name=";
      Str += ClangInvok->getLangOpts()->CurrentModule;
      CCArgs.push_back(std::string(Str.str()));
    }
  
    if (PPOpts.DetailedRecord) {
      Invok.getClangImporterOptions().DetailedPreprocessingRecord = true;
    }
  
    if (!ClangInvok->getFrontendOpts().Inputs.empty()) {
      Invok.getFrontendOptions().ImplicitObjCHeaderPath =
          ClangInvok->getFrontendOpts().Inputs[0].getFile().str();
    }
  
    return false;
  }
  
  template <typename FnTy>
  static void walkOverriddenClangDecls(const clang::NamedDecl *D, const FnTy &Fn){
    SmallVector<const clang::NamedDecl *, 8> OverDecls;
    D->getASTContext().getOverriddenMethods(D, OverDecls);
    for (auto Over : OverDecls)
      Fn(Over);
    for (auto Over : OverDecls)
      walkOverriddenClangDecls(Over, Fn);
  }
  
  void
  ide::walkOverriddenDecls(const ValueDecl *VD,
                           llvm::function_ref<void(llvm::PointerUnion<
                               const ValueDecl*, const clang::NamedDecl*>)> Fn) {
    for (auto CurrOver = VD; CurrOver; CurrOver = CurrOver->getOverriddenDecl()) {
      if (CurrOver != VD)
        Fn(CurrOver);
      if (auto ClangD =
          dyn_cast_or_null<clang::NamedDecl>(CurrOver->getClangDecl())) {
        walkOverriddenClangDecls(ClangD, Fn);
        return;
      }
      for (auto Conf: CurrOver->getSatisfiedProtocolRequirements())
        Fn(Conf);
    }
  }
  
  /// \returns true if a placeholder was found.
  static bool findPlaceholder(StringRef Input, PlaceholderOccurrence &Occur) {
    while (true) {
      size_t Pos = Input.find("<#");
      if (Pos == StringRef::npos)
        return false;
  
      const char *Begin = Input.begin() + Pos;
      const char *Ptr = Begin + 2;
      const char *End = Input.end();
      for (; Ptr < End-1; ++Ptr) {
        if (*Ptr == '\n')
          break;
        if (Ptr[0] == '<' && Ptr[1] == '#')
          break;
        if (Ptr[0] == '#' && Ptr[1] == '>') {
          // Found it.
          Occur.FullPlaceholder = Input.substr(Pos, Ptr-Begin + 2);
          Occur.PlaceholderContent =
            Occur.FullPlaceholder.drop_front(2).drop_back(2);
          return true;
        }
      }
  
      // Try again.
      Input = Input.substr(Ptr - Input.begin());
    }
  }
  
  std::unique_ptr<llvm::MemoryBuffer>
  ide::replacePlaceholders(std::unique_ptr<llvm::MemoryBuffer> InputBuf,
               llvm::function_ref<void(const PlaceholderOccurrence &)> Callback) {
    StringRef Input = InputBuf->getBuffer();
    PlaceholderOccurrence Occur;
    bool Found = findPlaceholder(Input, Occur);
    if (!Found)
      return InputBuf;
  
    std::unique_ptr<llvm::MemoryBuffer> NewBuf =
      llvm::MemoryBuffer::getMemBufferCopy(InputBuf->getBuffer(),
                                           InputBuf->getBufferIdentifier());
  
    unsigned Counter = 0;
    auto replacePlaceholder = [&](PlaceholderOccurrence &Occur) {
      llvm::SmallString<10> Id;
      Id = "$_";
      llvm::raw_svector_ostream(Id) << (Counter++);
      assert(Occur.FullPlaceholder.size() >= 2);
      if (Id.size() > Occur.FullPlaceholder.size()) {
        // The identifier+counter exceeds placeholder size; replace it without
        // using the counter.
        Id = "$";
        Id.append(Occur.FullPlaceholder.size()-1, '_');
      } else {
        Id.append(Occur.FullPlaceholder.size()-Id.size(), '_');
      }
      assert(Id.size() == Occur.FullPlaceholder.size());
  
      unsigned Offset = Occur.FullPlaceholder.data() - InputBuf->getBufferStart();
      char *Ptr = const_cast<char *>(NewBuf->getBufferStart()) + Offset;
      std::copy(Id.begin(), Id.end(), Ptr);
  
      Occur.IdentifierReplacement = Id.str();
      Callback(Occur);
    };
  
    while (true) {
      replacePlaceholder(Occur);
      unsigned Offset = Occur.FullPlaceholder.data() - InputBuf->getBufferStart();
      Input = InputBuf->getBuffer().substr(Offset+Occur.FullPlaceholder.size());
  
      bool Found = findPlaceholder(Input, Occur);
      if (!Found)
        break;
    }
  
    return NewBuf;
  }
  
  std::unique_ptr<llvm::MemoryBuffer>
  ide::replacePlaceholders(std::unique_ptr<llvm::MemoryBuffer> InputBuf,
                           bool *HadPlaceholder) {
    if (HadPlaceholder)
      *HadPlaceholder = false;
    return replacePlaceholders(std::move(InputBuf),
                               [&](const PlaceholderOccurrence &){
      if (HadPlaceholder)
        *HadPlaceholder = true;
    });
  }
  
  // Modules failing to load are commented-out.
  static const char *OSXModuleList[] = {
    "AGL",
    "AVFoundation",
    "AVKit",
    "Accelerate",
    "Accounts",
    "AddressBook",
    "AppKit",
    "AppKitScripting",
    "AppleScriptKit",
    "AppleScriptObjC",
    "ApplicationServices",
    "AudioToolbox",
    "AudioUnit",
    "AudioVideoBridging",
    "Automator",
    "CFNetwork",
    // "CalendarStore",
    "Carbon",
    "CloudKit",
    "Cocoa",
    "Collaboration",
    "Contacts",
    "CoreAudio",
    "CoreAudioKit",
    "CoreBluetooth",
    "CoreData",
    "CoreFoundation",
    "CoreGraphics",
    "CoreImage",
    "CoreLocation",
    "CoreMIDI",
    //  "CoreMIDIServer",
    "CoreMedia",
    "CoreMediaIO",
    "CoreServices",
    "CoreTelephony",
    "CoreText",
    "CoreVideo",
    "CoreWLAN",
    //  "CryptoTokenKit",
    //  "DVComponentGlue",
    "DVDPlayback",
    "Darwin",
    "DirectoryService",
    "DiscRecording",
    "DiscRecordingUI",
    "DiskArbitration",
    "Dispatch",
    //  "DrawSprocket",
    "EventKit",
    "ExceptionHandling",
    "FWAUserLib",
    "FinderSync",
    "ForceFeedback",
    "Foundation",
    "GLKit",
    "GLUT",
    "GSS",
    "GameController",
    "GameKit",
    "GameplayKit",
    "Hypervisor",
    //  "ICADevices",
    "IMServicePlugIn",
    "IOBluetooth",
    "IOBluetoothUI",
    "IOKit",
    "IOSurface",
    "ImageCaptureCore",
    "ImageIO",
    "InputMethodKit",
    // "InstallerPlugins",
    // "InstantMessage",
    // "JavaFrameEmbedding",
    "JavaScriptCore",
    // "JavaVM",
    // "Kerberos",
    // "LDAP",
    "LatentSemanticMapping",
    "LocalAuthentication",
    "MachO",
    "MapKit",
    "MediaAccessibility",
    "MediaLibrary",
    "MediaToolbox",
    //  "Message",
    "Metal",
    "MetalKit",
    "ModelIO",
    "MultipeerConnectivity",
    "NetFS",
    //  "NetworkExtension",
    "NotificationCenter",
    "OSAKit",
    "ObjectiveC",
    "OpenAL",
    "OpenCL",
    "OpenDirectory",
    "OpenGL",
    // "PCSC",
    "PreferencePanes",
    "PubSub",
    "Python",
    //  "QTKit", QTKit is unavailable on Swift.
    "Quartz",
    "QuartzCore",
    "QuickLook",
    "QuickTime",
    //  "Ruby",
    "SceneKit",
    "ScreenSaver",
    "Scripting",
    "ScriptingBridge",
    "Security",
    "SecurityFoundation",
    "SecurityInterface",
    // "ServiceManagement",
    "Social",
    "SpriteKit",
    "StoreKit",
    // "SyncServices",
    "SystemConfiguration",
    "TWAIN",
    "Tcl",
    // "VideoDecodeAcceleration",
    "VideoToolbox",
    "WebKit",
    "XPC",
    "libkern",
    "os",
    //  "vecLib",
    "vmnet",
  };
  
  // Modules failing to load are commented-out.
  static const char *iOSModuleList[] = {
    "AVFoundation",
    "AVKit",
    "Accelerate",
    "Accounts",
    "AdSupport",
    "AddressBook",
    "AddressBookUI",
    "AssetsLibrary",
    "AudioToolbox",
    "AudioUnit",
    "CFNetwork",
    "CloudKit",
    "Contacts",
    "ContactsUI",
    "CoreAudio",
    "CoreAudioKit",
    "CoreBluetooth",
    "CoreData",
    "CoreFoundation",
    "CoreGraphics",
    "CoreImage",
    "CoreLocation",
    "CoreMIDI",
    "CoreMedia",
    "CoreMotion",
    "CoreSpotlight",
    "CoreTelephony",
    "CoreText",
    "CoreVideo",
    "Darwin",
    "Dispatch",
    "EventKit",
    "EventKitUI",
    "ExternalAccessory",
    "Foundation",
    "GLKit",
    "GSS",
    "GameController",
    "GameFoundation",
    "GameKit",
    "GameplayKit",
    "HealthKit",
    "HomeKit",
    "IMCommonCore",
    // "IOKit",
    "ImageIO",
    "JavaScriptCore",
    "LocalAuthentication",
    "MachO",
    "MapKit",
    "MediaAccessibility",
    "MediaPlayer",
    "MediaToolbox",
    "MessageUI",
    "MobileCoreServices",
    "ModelIO",
    "MultipeerConnectivity",
    "NetworkExtension",
    "NewsstandKit",
    "NotificationCenter",
    "ObjectiveC",
    "OpenAL",
    "OpenGLES",
    "PassKit",
    "Photos",
    "PhotosUI",
    "PushKit",
    "QuartzCore",
    "QuickLook",
    "SafariServices",
    "SceneKit",
    "ScreenRecorder",
    "Security",
    "Social",
    "SpriteKit",
    "StoreKit",
    "SystemConfiguration",
    "Twitter",
    "UIKit",
    "UIKit.UIGestureRecognizerSubclass",
    "VideoToolbox",
    "WatchConnectivity",
    "WatchKit",
    "WebKit",
    "iAd",
    "libkern",
    "os",
  };
  
  static const char *DeviceOnlyModuleList[] = {
    "Metal",
    "MetalKit",
    "MetalShaders",
  };
  
  
  static ArrayRef<const char *> getOSXModuleList() {
    return OSXModuleList;
  }
  
  static ArrayRef<const char *> getiOSModuleList() {
    return iOSModuleList;
  }
  
  static ArrayRef<const char *> getDeviceOnlyModuleList() {
    return DeviceOnlyModuleList;
  }
  
  void ide::collectModuleNames(StringRef SDKPath,
                                 std::vector<std::string> &Modules) {
    std::string SDKName = getSDKName(SDKPath);
    std::string lowerSDKName = StringRef(SDKName).lower();
    bool isOSXSDK = StringRef(lowerSDKName).contains("macosx");
    bool isDeviceOnly = StringRef(lowerSDKName).contains("iphoneos");
    auto Mods = isOSXSDK ? getOSXModuleList() : getiOSModuleList();
    Modules.insert(Modules.end(), Mods.begin(), Mods.end());
    if (isDeviceOnly) {
      Mods = getDeviceOnlyModuleList();
      Modules.insert(Modules.end(), Mods.begin(), Mods.end());
    }
  }
  
  DeclNameViewer::DeclNameViewer(StringRef Text): IsValid(true), HasParen(false) {
    auto ArgStart = Text.find_first_of('(');
    if (ArgStart == StringRef::npos) {
      BaseName = Text;
      return;
    }
    HasParen = true;
    BaseName = Text.substr(0, ArgStart);
    auto ArgEnd = Text.find_last_of(')');
    if (ArgEnd == StringRef::npos) {
      IsValid = false;
      return;
    }
    StringRef AllArgs = Text.substr(ArgStart + 1, ArgEnd - ArgStart - 1);
    AllArgs.split(Labels, ":");
    if (Labels.empty())
      return;
    if ((IsValid = Labels.back().empty())) {
      Labels.pop_back();
      llvm::transform(Labels, Labels.begin(), [](StringRef Label) {
        return Label == "_" ? StringRef() : Label;
      });
    }
  }
  
  unsigned DeclNameViewer::commonPartsCount(DeclNameViewer &Other) const {
    if (base() != Other.base())
      return 0;
    unsigned Result = 1;
    unsigned Len = std::min(args().size(), Other.args().size());
    for (unsigned I = 0; I < Len; ++ I) {
      if (args()[I] == Other.args()[I])
        ++Result;
      else
        return Result;
    }
    return Result;
  }
  
  void swift::ide::SourceEditConsumer::
  accept(SourceManager &SM, SourceLoc Loc, StringRef Text,
         ArrayRef<NoteRegion> SubRegions) {
    accept(SM, CharSourceRange(Loc, 0), Text, SubRegions);
  }
  
  void swift::ide::SourceEditConsumer::
  accept(SourceManager &SM, CharSourceRange Range, StringRef Text,
         ArrayRef<NoteRegion> SubRegions) {
    accept(SM, RegionType::ActiveCode, {{Range, Text, SubRegions}});
  }
  
  void swift::ide::SourceEditConsumer::
  insertAfter(SourceManager &SM, SourceLoc Loc, StringRef Text,
              ArrayRef<NoteRegion> SubRegions) {
    accept(SM, Lexer::getLocForEndOfToken(SM, Loc), Text, SubRegions);
  }
  
  void swift::ide::SourceEditConsumer::
  remove(SourceManager &SM, CharSourceRange Range) {
    accept(SM, Range, "");
  }
  
  struct swift::ide::SourceEditJsonConsumer::Implementation {
    llvm::raw_ostream &OS;
    std::vector<SingleEdit> AllEdits;
    Implementation(llvm::raw_ostream &OS) : OS(OS) {}
    ~Implementation() {
      writeEditsInJson(AllEdits, OS);
    }
    void accept(SourceManager &SM, CharSourceRange Range,
                llvm::StringRef Text) {
      AllEdits.push_back({SM, Range, Text.str()});
    }
  };
  
  swift::ide::SourceEditJsonConsumer::SourceEditJsonConsumer(llvm::raw_ostream &OS) :
    Impl(*new Implementation(OS)) {}
  
  swift::ide::SourceEditJsonConsumer::~SourceEditJsonConsumer() { delete &Impl; }
  
  void swift::ide::SourceEditJsonConsumer::
  accept(SourceManager &SM, RegionType Type, ArrayRef<Replacement> Replacements) {
    for (const auto &Replacement: Replacements) {
      Impl.accept(SM, Replacement.Range, Replacement.Text);
    }
  }
  
  swift::ide::SourceEditTextConsumer::
  SourceEditTextConsumer(llvm::raw_ostream &OS) : OS(OS) { }
  
  void swift::ide::SourceEditTextConsumer::
  accept(SourceManager &SM, RegionType Type, ArrayRef<Replacement> Replacements) {
    for (const auto &Replacement: Replacements) {
      CharSourceRange Range = Replacement.Range;
      unsigned BufID = SM.findBufferContainingLoc(Range.getStart());
      auto Path(SM.getIdentifierForBuffer(BufID));
      auto Start = SM.getLineAndColumnInBuffer(Range.getStart());
      auto End = SM.getLineAndColumnInBuffer(Range.getEnd());
  
      OS << "// " << Path.str() << " ";
      OS << Start.first << ":" << Start.second << " -> ";
      OS << End.first << ":" << End.second << "\n";
      OS << Replacement.Text << "\n";
    }
  }
  
  namespace {
  class ClangFileRewriterHelper {
    unsigned InterestedId;
    clang::RewriteBuffer RewriteBuf;
    bool HasChange;
    llvm::raw_ostream &OS;
  
    void removeCommentLines(clang::RewriteBuffer &Buffer, StringRef Input,
                            StringRef LineHeader) {
      size_t Pos = 0;
      while (true) {
        Pos = Input.find(LineHeader, Pos);
        if (Pos == StringRef::npos)
          break;
        Pos = Input.substr(0, Pos).rfind("//");
        assert(Pos != StringRef::npos);
        size_t EndLine = Input.find('\n', Pos);
        assert(EndLine != StringRef::npos);
        ++EndLine;
        Buffer.RemoveText(Pos, EndLine-Pos);
        Pos = EndLine;
      }
    }
  
  public:
    ClangFileRewriterHelper(SourceManager &SM, unsigned InterestedId,
                            llvm::raw_ostream &OS)
    : InterestedId(InterestedId), HasChange(false), OS(OS) {
      StringRef Input(SM.getLLVMSourceMgr().getMemoryBuffer(InterestedId)->
                      getBuffer());
      RewriteBuf.Initialize(Input);
      removeCommentLines(RewriteBuf, Input, "RUN");
      removeCommentLines(RewriteBuf, Input, "CHECK");
    }
  
    void replaceText(SourceManager &SM, CharSourceRange Range, StringRef Text) {
      auto BufferId = SM.findBufferContainingLoc(Range.getStart());
      if (BufferId == InterestedId) {
        HasChange = true;
        auto StartLoc = SM.getLocOffsetInBuffer(Range.getStart(), BufferId);
        if (!Range.getByteLength())
            RewriteBuf.InsertText(StartLoc, Text);
        else
            RewriteBuf.ReplaceText(StartLoc, Range.str().size(), Text);
      }
    }
  
    ~ClangFileRewriterHelper() {
      if (HasChange)
        RewriteBuf.write(OS);
    }
  };
  } // end anonymous namespace
  struct swift::ide::SourceEditOutputConsumer::Implementation {
    ClangFileRewriterHelper Rewriter;
    Implementation(SourceManager &SM, unsigned BufferId, llvm::raw_ostream &OS)
    : Rewriter(SM, BufferId, OS) {}
    void accept(SourceManager &SM, CharSourceRange Range, StringRef Text) {
      Rewriter.replaceText(SM, Range, Text);
    }
  };
  
  swift::ide::SourceEditOutputConsumer::
  SourceEditOutputConsumer(SourceManager &SM, unsigned BufferId,
    llvm::raw_ostream &OS) : Impl(*new Implementation(SM, BufferId, OS)) {}
  
  swift::ide::SourceEditOutputConsumer::~SourceEditOutputConsumer() { delete &Impl; }
  
  void swift::ide::SourceEditOutputConsumer::
  accept(SourceManager &SM, RegionType RegionType,
         ArrayRef<Replacement> Replacements) {
    // ignore mismatched or
    if (RegionType == RegionType::Unmatched || RegionType == RegionType::Mismatch)
      return;
  
    for (const auto &Replacement : Replacements) {
      Impl.accept(SM, Replacement.Range, Replacement.Text);
    }
  }
  
  bool swift::ide::isFromClang(const Decl *D) {
    if (getEffectiveClangNode(D))
      return true;
    if (auto *Ext = dyn_cast<ExtensionDecl>(D))
      return static_cast<bool>(extensionGetClangNode(Ext));
    return false;
  }
  
  ClangNode swift::ide::getEffectiveClangNode(const Decl *decl) {
    auto &ctx = decl->getASTContext();
    auto *importer = static_cast<ClangImporter *>(ctx.getClangModuleLoader());
    return importer->getEffectiveClangNode(decl);
  }
  
  /// Retrieve the Clang node for the given extension, if it has one.
  ClangNode swift::ide::extensionGetClangNode(const ExtensionDecl *ext) {
    // If it has a Clang node (directly),
    if (ext->hasClangNode()) return ext->getClangNode();
  
    // Check whether it was syntheszed into a module-scope context.
    if (!isa<ClangModuleUnit>(ext->getModuleScopeContext()))
      return ClangNode();
  
    // It may have a global imported as a member.
    for (auto member : ext->getMembers()) {
      if (auto clangNode = getEffectiveClangNode(member))
        return clangNode;
    }
  
    return ClangNode();
  }
  
  std::pair<Type, ConcreteDeclRef> swift::ide::getReferencedDecl(Expr *expr) {
    auto exprTy = expr->getType();
  
    // Look through unbound instance member accesses.
    if (auto *dotSyntaxExpr = dyn_cast<DotSyntaxBaseIgnoredExpr>(expr))
      expr = dotSyntaxExpr->getRHS();
  
    // Look through the 'self' application.
    if (auto *selfApplyExpr = dyn_cast<SelfApplyExpr>(expr))
      expr = selfApplyExpr->getFn();
  
    // Look through curry thunks.
    if (auto *closure = dyn_cast<AutoClosureExpr>(expr))
      if (auto *unwrappedThunk = closure->getUnwrappedCurryThunkExpr())
        expr = unwrappedThunk;
  
    // If this is an IUO result, unwrap the optional type.
    auto refDecl = expr->getReferencedDecl();
    if (!refDecl) {
      if (auto *applyExpr = dyn_cast<ApplyExpr>(expr)) {
        auto fnDecl = applyExpr->getFn()->getReferencedDecl();
        if (auto *func = fnDecl.getDecl()) {
          if (func->isImplicitlyUnwrappedOptional()) {
            if (auto objectTy = exprTy->getOptionalObjectType())
              exprTy = objectTy;
          }
        }
      }
    }
  
    return std::make_pair(exprTy, refDecl);
  }
  