//===--- sourcekitdAPI-Common.cpp -----------------------------------------===//
//
// 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 "DictionaryKeys.h"
#include "sourcekitd/Internal.h"
#include "sourcekitd/Logging.h"
#include "sourcekitd/RequestResponsePrinterBase.h"
#include "SourceKit/Support/Logging.h"
#include "SourceKit/Support/UIdent.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Mutex.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/Threading.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/YAMLParser.h"
#include <mutex>

using namespace SourceKit;
using namespace sourcekitd;
using llvm::ArrayRef;
using llvm::StringRef;
using llvm::raw_ostream;

#define KEY(NAME, CONTENT) UIdent sourcekitd::Key##NAME(CONTENT);
#include "SourceKit/Core/ProtocolUIDs.def"

/// Order for the keys to use when emitting the debug description of
/// dictionaries.
static UIdent *OrderedKeys[] = {
#define KEY(NAME, CONTENT) &Key##NAME,
#include "SourceKit/Core/ProtocolUIDs.def"
};

static unsigned findPrintOrderForDictKey(UIdent Key) {
  unsigned Order = 1;
  for (auto UIDPtr : OrderedKeys) {
    if (*UIDPtr == Key)
      return Order;
    ++Order;
  }
  return 10000;
}

bool sourcekitd::compareDictKeys(UIdent LHS, UIdent RHS) {
  if (LHS == RHS)
    return false;
  unsigned LHSOrder = findPrintOrderForDictKey(LHS);
  unsigned RHSOrder = findPrintOrderForDictKey(RHS);
  if (LHSOrder == RHSOrder)
    return LHS.getName() < RHS.getName();
  return LHSOrder < RHSOrder;
}


namespace {
template <typename ImplClass,
          typename RetTy = void>
class VariantVisitor {
public:
  typedef std::vector<std::pair<UIdent, sourcekitd_variant_t>> DictMap;

  static bool compKeys(const std::pair<UIdent, sourcekitd_variant_t> &LHS,
                       const std::pair<UIdent, sourcekitd_variant_t> &RHS) {
    return sourcekitd::compareDictKeys(LHS.first, RHS.first);
  }

  RetTy visit(sourcekitd_variant_t Obj) {
    switch (sourcekitd_variant_get_type(Obj)) {
    case SOURCEKITD_VARIANT_TYPE_NULL:
      return static_cast<ImplClass*>(this)->visitNull();
    case SOURCEKITD_VARIANT_TYPE_DICTIONARY: {
      DictMap Dict;
      DictMap &DictRef = Dict;
      sourcekitd_variant_dictionary_apply_impl(
          Obj,
          [&](sourcekitd_uid_t key, sourcekitd_variant_t value) {
            DictRef.push_back({UIdentFromSKDUID(key), value});
            return true;
          });
      std::sort(Dict.begin(), Dict.end(), compKeys);
      return static_cast<ImplClass*>(this)->visitDictionary(Dict);
    }
    case SOURCEKITD_VARIANT_TYPE_ARRAY: {
      std::vector<sourcekitd_variant_t> Vec;
      for (size_t i = 0, e = sourcekitd_variant_array_get_count(Obj);
           i != e; ++i)
        Vec.push_back(sourcekitd_variant_array_get_value(Obj, i));
      return static_cast<ImplClass*>(this)->visitArray(Vec);
    }
    case SOURCEKITD_VARIANT_TYPE_INT64:
      return static_cast<ImplClass*>(this)->visitInt64(
                                       sourcekitd_variant_int64_get_value(Obj));
    case SOURCEKITD_VARIANT_TYPE_BOOL:
      return static_cast<ImplClass*>(this)->visitBool(
                                       sourcekitd_variant_bool_get_value(Obj));
    case SOURCEKITD_VARIANT_TYPE_STRING: {
      size_t Len = sourcekitd_variant_string_get_length(Obj);
      const char *Ptr = sourcekitd_variant_string_get_ptr(Obj);
      return static_cast<ImplClass*>(this)->visitString(StringRef(Ptr, Len));
    }
    case SOURCEKITD_VARIANT_TYPE_UID: {
      sourcekitd_uid_t UID = sourcekitd_variant_uid_get_value(Obj);
      size_t Len = sourcekitd_uid_get_length(UID);
      const char *Ptr = sourcekitd_uid_get_string_ptr(UID);
      return static_cast<ImplClass*>(this)->visitUID(StringRef(Ptr, Len));
    }
    case SOURCEKITD_VARIANT_TYPE_DATA: {
      const void *Data = sourcekitd_variant_data_get_ptr(Obj);
      size_t Size = sourcekitd_variant_data_get_size(Obj);
      return static_cast<ImplClass*>(this)->visitData(Data, Size);
    }
    }
  }
};

class VariantPrinter : public VariantVisitor<VariantPrinter>,
                       public RequestResponsePrinterBase<VariantPrinter,
                                                         sourcekitd_variant_t> {
public:
  VariantPrinter(raw_ostream &OS, unsigned Indent = 0, bool PrintAsJSON = false)
    : RequestResponsePrinterBase(OS, Indent, PrintAsJSON) { }
};
} // end anonymous namespace

void sourcekitd::writeEscaped(llvm::StringRef Str, llvm::raw_ostream &OS) {
  for (unsigned i = 0, e = Str.size(); i != e; ++i) {
    unsigned char c = Str[i];

    switch (c) {
    case '\\':
      OS << '\\' << '\\';
      break;
    case '\t':
      OS << '\\' << 't';
      break;
    case '\n':
      OS << '\\' << 'n';
      break;
    case '"':
      OS << '\\' << '"';
      break;
    default:
      OS << c;
      break;
    }
  }
}

static void printError(sourcekitd_response_t Err, raw_ostream &OS) {
  OS << "error response (";
  switch (sourcekitd_response_error_get_kind(Err)) {
  case SOURCEKITD_ERROR_CONNECTION_INTERRUPTED:
    OS << "Connection Interrupted"; break;
  case SOURCEKITD_ERROR_REQUEST_INVALID:
    OS << "Request Invalid"; break;
  case SOURCEKITD_ERROR_REQUEST_FAILED:
    OS << "Request Failed"; break;
  case SOURCEKITD_ERROR_REQUEST_CANCELLED:
    OS << "Request Cancelled"; break;
  }
  OS << "): ";
  OS << sourcekitd_response_error_get_description(Err);
}

static void printVariant(sourcekitd_variant_t obj, raw_ostream &OS) {
  VariantPrinter(OS).visit(obj);
}

void sourcekitd::printResponse(sourcekitd_response_t Resp, raw_ostream &OS) {
  if (!Resp) {
    OS << "<<NULL>>";
    return;
  }

  if (sourcekitd_response_is_error(Resp))
    printError(Resp, OS);
  else
    printVariant(sourcekitd_response_get_value(Resp), OS);
}

static void fatal_error_handler(void *user_data, const std::string& reason,
                                bool gen_crash_diag) {
  // Write the result out to stderr avoiding errs() because raw_ostreams can
  // call report_fatal_error.
  // FIXME: Put the error message in the crash report.
  fprintf(stderr, "SOURCEKITD FATAL ERROR: %s\n", reason.c_str());
  ::abort();
}

void sourcekitd::enableLogging(StringRef LoggerName) {
  Logger::Level LogLevel = Logger::Level::Warning;
  const char *EnvOpt = ::getenv("SOURCEKIT_LOGGING");
  if (EnvOpt) {
    int Val;
    bool Err = StringRef(EnvOpt).getAsInteger(10, Val);
    if (!Err) {
      if (Val > 2)
        LogLevel = Logger::Level::InfoLowPrio;
      else if (Val == 2)
        LogLevel = Logger::Level::InfoMediumPrio;
      else if (Val == 1)
        LogLevel = Logger::Level::InfoHighPrio;
    }
  }

  Logger::enableLogging(LoggerName, LogLevel);
}

//===----------------------------------------------------------------------===//
// Public API
//===----------------------------------------------------------------------===//

static llvm::sys::Mutex GlobalInitMtx;
static unsigned gInitRefCount = 0;

bool sourcekitd::initializeClient() {
  llvm::sys::ScopedLock L(GlobalInitMtx);
  ++gInitRefCount;
  if (gInitRefCount > 1)
    return false;

  static std::once_flag flag;
  std::call_once(flag, []() {
    llvm::install_fatal_error_handler(fatal_error_handler, 0);
    sourcekitd::enableLogging("sourcekit");
  });

  return true;
}

bool sourcekitd::shutdownClient() {
  llvm::sys::ScopedLock L(GlobalInitMtx);
  --gInitRefCount;
  if (gInitRefCount > 0)
    return false;
  return true;
}

void
sourcekitd_response_description_dump(sourcekitd_response_t resp) {
  // Avoid colors here, we don't properly detect that the debug window inside
  // Xcode doesn't support colors.
  llvm::SmallString<128> Desc;
  llvm::raw_svector_ostream OS(Desc);
  printResponse(resp, OS);
  llvm::errs() << OS.str() << '\n';
}

void
sourcekitd_response_description_dump_filedesc(sourcekitd_response_t resp,
                                              int fd) {
  llvm::raw_fd_ostream OS(fd, /*shouldClose=*/false);
  printResponse(resp, OS);
  OS << '\n';
}

char *
sourcekitd_response_description_copy(sourcekitd_response_t resp) {
  llvm::SmallString<128> Desc;
  llvm::raw_svector_ostream OS(Desc);
  printResponse(resp, OS);
  return strdup(Desc.c_str());
}


sourcekitd_uid_t
sourcekitd_uid_get_from_cstr(const char *string) {
  return SKDUIDFromUIdent(UIdent(string));
}

sourcekitd_uid_t
sourcekitd_uid_get_from_buf(const char *buf, size_t length) {
  return SKDUIDFromUIdent(UIdent(llvm::StringRef(buf, length)));
}

size_t
sourcekitd_uid_get_length(sourcekitd_uid_t uid) {
  UIdent UID = UIdentFromSKDUID(uid);
  return UID.getName().size();
}

const char *
sourcekitd_uid_get_string_ptr(sourcekitd_uid_t uid) {
  UIdent UID = UIdentFromSKDUID(uid);
  return UID.getName().begin();
}

void
sourcekitd_request_description_dump(sourcekitd_object_t obj) {
  // Avoid colors here, we don't properly detect that the debug window inside
  // Xcode doesn't support colors.
  llvm::SmallString<128> Desc;
  llvm::raw_svector_ostream OS(Desc);
  printRequestObject(obj, OS);
  llvm::errs() << OS.str() << '\n';
}

char *
sourcekitd_request_description_copy(sourcekitd_object_t obj) {
  llvm::SmallString<128> Desc;
  llvm::raw_svector_ostream OS(Desc);
  printRequestObject(obj, OS);
  return strdup(Desc.c_str());
}

//===----------------------------------------------------------------------===//
// Variant API
//===----------------------------------------------------------------------===//

#define VAR_FN(var, name) ((var).data[0] ? \
                            ((VariantFunctions*)(var).data[0])->name : nullptr)

sourcekitd_variant_type_t
sourcekitd_variant_get_type(sourcekitd_variant_t var) {
  if (auto fn = VAR_FN(var, get_type))
    return fn(var);

  // Default implementation:
  // Assume the variant encapsulates a basic type.
  static_assert(SOURCEKITD_VARIANT_TYPE_NULL == 0,
                "NULL type does not match null variant");
  return (sourcekitd_variant_type_t)var.data[2];
}

sourcekitd_variant_t
sourcekitd_variant_dictionary_get_value(sourcekitd_variant_t dict,
                                        sourcekitd_uid_t key) {
  if (auto fn = VAR_FN(dict, dictionary_get_value))
    return fn(dict, key);

  // Default implementation:
  // Linear search for the key/value pair via sourcekitd_variant_dictionary_apply.
  sourcekitd_variant_t result = makeNullVariant();
  sourcekitd_variant_dictionary_apply_impl(
      dict, [&](sourcekitd_uid_t curr_key, sourcekitd_variant_t curr_value) {
        if (curr_key == key) {
          result = curr_value;
          return false;
        }
        return true;
      });

  return result;
}

const char *
sourcekitd_variant_dictionary_get_string(sourcekitd_variant_t dict,
                                         sourcekitd_uid_t key) {
  if (auto fn = VAR_FN(dict, dictionary_get_string))
    return fn(dict, key);

  // Default implementation:
  // Get the value via sourcekitd_variant_dictionary_get_value.
  return sourcekitd_variant_string_get_ptr(
             sourcekitd_variant_dictionary_get_value(dict, key));
}

int64_t
sourcekitd_variant_dictionary_get_int64(sourcekitd_variant_t dict,
                                        sourcekitd_uid_t key) {
  if (auto fn = VAR_FN(dict, dictionary_get_int64))
    return fn(dict, key);

  // Default implementation:
  // Get the value via sourcekitd_variant_dictionary_get_value.
  return sourcekitd_variant_int64_get_value(
             sourcekitd_variant_dictionary_get_value(dict, key));
}

bool
sourcekitd_variant_dictionary_get_bool(sourcekitd_variant_t dict,
                                       sourcekitd_uid_t key) {
  if (auto fn = VAR_FN(dict, dictionary_get_bool))
    return fn(dict, key);

  // Default implementation:
  // Get the value via sourcekitd_variant_dictionary_get_value.
  return sourcekitd_variant_bool_get_value(
             sourcekitd_variant_dictionary_get_value(dict, key));
}

sourcekitd_uid_t
sourcekitd_variant_dictionary_get_uid(sourcekitd_variant_t dict,
                                      sourcekitd_uid_t key) {
  if (auto fn = VAR_FN(dict, dictionary_get_uid))
    return fn(dict, key);

  // Default implementation:
  // Get the value via sourcekitd_variant_dictionary_get_value.
  return sourcekitd_variant_uid_get_value(
             sourcekitd_variant_dictionary_get_value(dict, key));
}

#if SOURCEKITD_HAS_BLOCKS
bool
sourcekitd_variant_dictionary_apply(sourcekitd_variant_t dict,
                              sourcekitd_variant_dictionary_applier_t applier) {
  return sourcekitd_variant_dictionary_apply_impl(dict, applier);
}
#endif

bool
sourcekitd_variant_dictionary_apply_impl(
  sourcekitd_variant_t dict,
  llvm::function_ref<bool(sourcekitd_uid_t, sourcekitd_variant_t)> applier) {
  if (auto fn = VAR_FN(dict, dictionary_apply))
    return fn(dict, applier);

  // Default implementation:
  // Treat as empty container.
  return true;
}

bool
sourcekitd_variant_dictionary_apply_f(sourcekitd_variant_t dict,
                              sourcekitd_variant_dictionary_applier_f_t applier,
                              void *context) {
  return sourcekitd_variant_dictionary_apply_impl(
      dict,
      [&](sourcekitd_uid_t key, sourcekitd_variant_t value) {
          return applier(key, value, context);
  });
}

size_t
sourcekitd_variant_array_get_count(sourcekitd_variant_t array) {
  if (auto fn = VAR_FN(array, array_get_count))
    return fn(array);

  // Default implementation:
  // Treat as empty container.
  return 0;
}

sourcekitd_variant_t
sourcekitd_variant_array_get_value(sourcekitd_variant_t array, size_t index) {
  if (auto fn = VAR_FN(array, array_get_value))
    return fn(array, index);

  llvm::report_fatal_error("Trying to index an empty array.");
}

const char *
sourcekitd_variant_array_get_string(sourcekitd_variant_t array, size_t index) {
  if (auto fn = VAR_FN(array, array_get_string))
    return fn(array, index);

  // Default implementation:
  // Get the value via sourcekitd_variant_array_get_value.
  return sourcekitd_variant_string_get_ptr(
             sourcekitd_variant_array_get_value(array, index));
}

int64_t
sourcekitd_variant_array_get_int64(sourcekitd_variant_t array, size_t index) {
  if (auto fn = VAR_FN(array, array_get_int64))
    return fn(array, index);

  // Default implementation:
  // Get the value via sourcekitd_variant_array_get_value.
  return sourcekitd_variant_int64_get_value(
             sourcekitd_variant_array_get_value(array, index));
}

bool
sourcekitd_variant_array_get_bool(sourcekitd_variant_t array, size_t index) {
  if (auto fn = VAR_FN(array, array_get_bool))
    return fn(array, index);

  // Default implementation:
  // Get the value via sourcekitd_variant_array_get_value.
  return sourcekitd_variant_bool_get_value(
             sourcekitd_variant_array_get_value(array, index));
}

sourcekitd_uid_t
sourcekitd_variant_array_get_uid(sourcekitd_variant_t array, size_t index) {
  if (auto fn = VAR_FN(array, array_get_uid))
    return fn(array, index);

  // Default implementation:
  // Get the value via sourcekitd_variant_array_get_value.
  return sourcekitd_variant_uid_get_value(
             sourcekitd_variant_array_get_value(array, index));
}

#if SOURCEKITD_HAS_BLOCKS
bool
sourcekitd_variant_array_apply(sourcekitd_variant_t array,
                               sourcekitd_variant_array_applier_t applier) {
  return sourcekitd_variant_array_apply_impl(array, applier);
}
#endif

bool sourcekitd_variant_array_apply_impl(
    sourcekitd_variant_t array,
    llvm::function_ref<bool(size_t, sourcekitd_variant_t)> applier) {
  if (auto fn = VAR_FN(array, array_apply))
    return fn(array, applier);

  // Default implementation:
  // Iterate over elements via a for-loop.
  for (size_t i = 0, e = sourcekitd_variant_array_get_count(array); i != e; ++i) {
    bool Continue = applier(i, sourcekitd_variant_array_get_value(array, i));
    if (!Continue)
      return false;
  }
  return true;
}

bool sourcekitd_variant_array_apply_f(
    sourcekitd_variant_t array, sourcekitd_variant_array_applier_f_t applier,
    void *context) {
  return sourcekitd_variant_array_apply_impl(
      array, [&](size_t index, sourcekitd_variant_t value) {
        return applier(index, value, context);
      });
}

int64_t
sourcekitd_variant_int64_get_value(sourcekitd_variant_t obj) {
  if (auto fn = VAR_FN(obj, int64_get_value))
    return fn(obj);

  // Default implementation:
  // Assume this is a variant encapsulating the basic type.
  return obj.data[1];
}

bool
sourcekitd_variant_bool_get_value(sourcekitd_variant_t obj) {
  if (auto fn = VAR_FN(obj, bool_get_value))
    return fn(obj);

  // Default implementation:
  // Assume this is a variant encapsulating the basic type.
  return obj.data[1];
}

size_t
sourcekitd_variant_string_get_length(sourcekitd_variant_t obj) {
  if (auto fn = VAR_FN(obj, string_get_length))
    return fn(obj);

  // Default implementation:
  // Use strlen.
  return strlen(sourcekitd_variant_string_get_ptr(obj));
}

const char *
sourcekitd_variant_string_get_ptr(sourcekitd_variant_t obj) {
  if (auto fn = VAR_FN(obj, string_get_ptr))
    return fn(obj);

  // Default implementation:
  // Assume this is a variant encapsulating the basic type.
  return (const char *)obj.data[1];
}

size_t
sourcekitd_variant_data_get_size(sourcekitd_variant_t obj) {
  if (auto fn = VAR_FN(obj, data_get_size))
    return fn(obj);

  // Default implementation:
  // We store the byte's length in data[2] and its data in data[1]
  return obj.data[2];
}

const void *
sourcekitd_variant_data_get_ptr(sourcekitd_variant_t obj) {
  if (auto fn = VAR_FN(obj, data_get_ptr))
    return fn(obj);

  // Default implementation:
  // We store the byte's length in data[2] and its data in data[1]
  return reinterpret_cast<void *>(obj.data[1]);
}

sourcekitd_uid_t
sourcekitd_variant_uid_get_value(sourcekitd_variant_t obj) {
  if (auto fn = VAR_FN(obj, uid_get_value))
    return fn(obj);

  // Default implementation:
  // Assume this is a variant encapsulating the basic type.
  return (sourcekitd_uid_t)obj.data[1];
}

void
sourcekitd_variant_description_dump(sourcekitd_variant_t obj) {
  // Avoid colors here, we don't properly detect that the debug window inside
  // Xcode doesn't support colors.
  llvm::SmallString<128> Desc;
  llvm::raw_svector_ostream OS(Desc);
  printVariant(obj, OS);
  llvm::errs() << OS.str() << '\n';
}

void
sourcekitd_variant_description_dump_filedesc(sourcekitd_variant_t obj, int fd) {
  llvm::raw_fd_ostream OS(fd, /*shouldClose=*/false);
  printVariant(obj, OS);
  OS << '\n';
}

char *
sourcekitd_variant_description_copy(sourcekitd_variant_t obj) {
  llvm::SmallString<128> Desc;
  llvm::raw_svector_ostream OS(Desc);
  printVariant(obj, OS);
  return strdup(Desc.c_str());
}

char *
sourcekitd_variant_json_description_copy(sourcekitd_variant_t obj) {
  llvm::SmallString<128> Desc;
  {
    llvm::raw_svector_ostream OS(Desc);
    VariantPrinter(OS, /*Indent=*/0, /*PrintAsJSON=*/true).visit(obj);
  }
  return strdup(Desc.c_str());
}

namespace {
class YAMLRequestParser {
public:
  sourcekitd_object_t parse(StringRef YAMLStr, std::string &Error);

private:
  sourcekitd_object_t createObjFromNode(llvm::yaml::Node *Value,
                                        std::string &Error);
  bool parseDict(sourcekitd_object_t Dict, llvm::yaml::MappingNode *Node,
                  std::string &Error);
  bool parseArray(sourcekitd_object_t Array, llvm::yaml::SequenceNode *Node,
                   std::string &Error);
  void initError(StringRef Desc, llvm::yaml::Node *Node, std::string &Error);
  sourcekitd_object_t withError(StringRef Desc, llvm::yaml::Node *Node,
                                std::string &Error) {
    initError(Desc, Node, Error);
    return nullptr;
  }
  bool withBoolError(StringRef Desc, llvm::yaml::Node *Node,
                     std::string &Error) {
    initError(Desc, Node, Error);
    return true;
  }
};
} // anonymous namespace

sourcekitd_object_t
sourcekitd_request_create_from_yaml(const char *yaml, char **error) {
  std::string Error;
  sourcekitd_object_t Result = YAMLRequestParser().parse(yaml, Error);
  if (Result)
    return Result;
  if (error)
    *error = strdup(Error.c_str());
  return nullptr;
}

sourcekitd_object_t YAMLRequestParser::parse(StringRef YAMLStr,
                                             std::string &Error) {
  llvm::SourceMgr SM;
  llvm::yaml::Stream YAMLStream(YAMLStr, SM);
  llvm::yaml::document_iterator I = YAMLStream.begin();
  if (I == YAMLStream.end()) {
    Error = "Error while parsing";
    return nullptr;
  }
  llvm::yaml::Node *Root = I->getRoot();
  if (Root == nullptr) {
    Error = "Error while parsing";
    return nullptr;
  }

  return createObjFromNode(Root, Error);
}

sourcekitd_object_t YAMLRequestParser::createObjFromNode(
                                  llvm::yaml::Node *Value, std::string &Error) {
  if (auto Array = dyn_cast<llvm::yaml::SequenceNode>(Value)) {
    sourcekitd_object_t Val = sourcekitd_request_array_create(nullptr, 0);
    if (parseArray(Val, Array, Error)) {
      sourcekitd_request_release(Val);
      return nullptr;
    }
    return Val;
  }

  if (auto Map = dyn_cast<llvm::yaml::MappingNode>(Value)) {
    sourcekitd_object_t Val =
        sourcekitd_request_dictionary_create(nullptr, nullptr, 0);
    if (parseDict(Val, Map, Error)) {
      sourcekitd_request_release(Val);
      return nullptr;
    }
    return Val;
  }

  if (auto ValueString = dyn_cast<llvm::yaml::ScalarNode>(Value)) {
    StringRef Raw = ValueString->getRawValue().trim();
    SmallString<32> ValueStorage;
    if (Raw[0] == '\"') {
      SmallString<32> Str(ValueString->getValue(ValueStorage));
      return sourcekitd_request_string_create(Str.c_str());
    }

    long long val;
    if (!Raw.getAsInteger(10, val))
      return sourcekitd_request_int64_create(val);

    if (Raw.contains(' '))
      return withError("Found space in non-string value", Value, Error);

    return sourcekitd_request_uid_create(
        sourcekitd_uid_get_from_buf(Raw.data(), Raw.size()));
  }

  return withError("Expected value as array, dictionary, or scalar", Value,
                   Error);
}


bool YAMLRequestParser::parseDict(sourcekitd_object_t Dict,
                                  llvm::yaml::MappingNode *Node,
                                  std::string &Error) {

  for (llvm::yaml::MappingNode::iterator KVI = Node->begin(),
                                         KVE = Node->end();
       KVI != KVE; ++KVI) {
    llvm::yaml::Node *Value = (*KVI).getValue();
    if (Value == nullptr || isa<llvm::yaml::NullNode>(Value))
      return withBoolError("Expected value", (*KVI).getKey(), Error);

    sourcekitd_object_t Val = createObjFromNode(Value, Error);
    if (!Val)
      return true;

    auto KeyString = dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey());
    if (KeyString == nullptr) {
      sourcekitd_request_release(Val);
      return withBoolError("Expected string as key", (*KVI).getKey(), Error);
    }

    SmallString<32> KeyStorage;
    StringRef Key = KeyString->getValue(KeyStorage);
    sourcekitd_uid_t uid_key =
        sourcekitd_uid_get_from_buf(Key.data(), Key.size());

    sourcekitd_request_dictionary_set_value(Dict, uid_key, Val);
  }

  return false;
}

bool YAMLRequestParser::parseArray(sourcekitd_object_t Array,
                                   llvm::yaml::SequenceNode *Node,
                                   std::string &Error) {
  for (llvm::yaml::SequenceNode::iterator AI = Node->begin(),
                                          AE = Node->end();
       AI != AE; ++AI) {
    sourcekitd_object_t Val = createObjFromNode(&*AI, Error);
    if (!Val)
      return true;

    sourcekitd_request_array_set_value(Array, SOURCEKITD_ARRAY_APPEND, Val);
  }

  return false;
}

void YAMLRequestParser::initError(StringRef Desc, llvm::yaml::Node *Node,
                                  std::string &Error) {
  Error = Desc.str();
  Error += " at: ";
  llvm::SMRange Range = Node->getSourceRange();
  StringRef Text(Range.Start.getPointer(),
                 Range.End.getPointer() - Range.Start.getPointer());
  Error.append(Text.begin(), Text.end());
}
