mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
1609 lines
59 KiB
C++
1609 lines
59 KiB
C++
//===--- sourcekitd-test.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 "sourcekitd/sourcekitd.h"
|
|
|
|
#include "TestOptions.h"
|
|
#include "SourceKit/Support/Concurrency.h"
|
|
#include "clang/Rewrite/Core/RewriteBuffer.h"
|
|
#include "swift/Demangling/ManglingMacros.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/Optional.h"
|
|
#include "llvm/ADT/StringSwitch.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
#include "llvm/Support/Regex.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include "llvm/Support/Signals.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include <fstream>
|
|
#include <unistd.h>
|
|
#include <sys/param.h>
|
|
|
|
// FIXME: Platform compatibility.
|
|
#include <dispatch/dispatch.h>
|
|
|
|
using namespace llvm;
|
|
|
|
using namespace sourcekitd_test;
|
|
|
|
static int handleTestInvocation(ArrayRef<const char *> Args, TestOptions &InitOpts);
|
|
static bool handleResponse(sourcekitd_response_t Resp, const TestOptions &Opts,
|
|
const std::string &SourceFile,
|
|
std::unique_ptr<llvm::MemoryBuffer> SourceBuf,
|
|
TestOptions *InitOpts);
|
|
static void printCursorInfo(sourcekitd_variant_t Info, StringRef Filename,
|
|
llvm::raw_ostream &OS);
|
|
static void printNameTranslationInfo(sourcekitd_variant_t Info, llvm::raw_ostream &OS);
|
|
static void printRangeInfo(sourcekitd_variant_t Info, StringRef Filename,
|
|
llvm::raw_ostream &OS);
|
|
static void printDocInfo(sourcekitd_variant_t Info, StringRef Filename);
|
|
static void printInterfaceGen(sourcekitd_variant_t Info, bool CheckASCII);
|
|
static void printSemanticInfo();
|
|
static void printRelatedIdents(sourcekitd_variant_t Info, StringRef Filename,
|
|
llvm::raw_ostream &OS);
|
|
static void printFoundInterface(sourcekitd_variant_t Info,
|
|
llvm::raw_ostream &OS);
|
|
static void printFoundUSR(sourcekitd_variant_t Info,
|
|
llvm::MemoryBuffer *SourceBuf,
|
|
llvm::raw_ostream &OS);
|
|
static void printNormalizedDocComment(sourcekitd_variant_t Info);
|
|
static void expandPlaceholders(llvm::MemoryBuffer *SourceBuf,
|
|
llvm::raw_ostream &OS);
|
|
|
|
static void printModuleGroupNames(sourcekitd_variant_t Info,
|
|
llvm::raw_ostream &OS);
|
|
|
|
static void prepareDemangleRequest(sourcekitd_object_t Req,
|
|
const TestOptions &Opts);
|
|
static void printDemangleResults(sourcekitd_variant_t Info, raw_ostream &OS);
|
|
static void prepareMangleRequest(sourcekitd_object_t Req,
|
|
const TestOptions &Opts);
|
|
static void printMangleResults(sourcekitd_variant_t Info, raw_ostream &OS);
|
|
|
|
static unsigned resolveFromLineCol(unsigned Line, unsigned Col,
|
|
StringRef Filename);
|
|
static unsigned resolveFromLineCol(unsigned Line, unsigned Col,
|
|
llvm::MemoryBuffer *InputBuf);
|
|
static std::pair<unsigned, unsigned> resolveToLineCol(unsigned Offset,
|
|
StringRef Filename);
|
|
static std::pair<unsigned, unsigned> resolveToLineCol(unsigned Offset,
|
|
llvm::MemoryBuffer *InputBuf);
|
|
static std::pair<unsigned, unsigned> resolveToLineColFromBuf(unsigned Offset,
|
|
const char *Buf);
|
|
static llvm::MemoryBuffer *getBufferForFilename(StringRef Filename);
|
|
|
|
static void notification_receiver(sourcekitd_response_t resp);
|
|
|
|
static SourceKitRequest ActiveRequest = SourceKitRequest::None;
|
|
|
|
#define KEY(NAME, CONTENT) static sourcekitd_uid_t Key##NAME;
|
|
#define REQUEST(NAME, CONTENT) static sourcekitd_uid_t Request##NAME;
|
|
#define KIND(NAME, CONTENT) static sourcekitd_uid_t Kind##NAME;
|
|
#include "SourceKit/Core/ProtocolUIDs.def"
|
|
|
|
static sourcekitd_uid_t SemaDiagnosticStage;
|
|
|
|
static sourcekitd_uid_t NoteDocUpdate;
|
|
|
|
static SourceKit::Semaphore semaSemaphore(0);
|
|
static sourcekitd_response_t semaResponse;
|
|
static const char *semaName;
|
|
|
|
namespace {
|
|
struct AsyncResponseInfo {
|
|
SourceKit::Semaphore semaphore{0};
|
|
sourcekitd_response_t response = nullptr;
|
|
TestOptions options;
|
|
std::string sourceFilename;
|
|
std::unique_ptr<llvm::MemoryBuffer> sourceBuffer;
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
static std::vector<AsyncResponseInfo> asyncResponses;
|
|
|
|
static int skt_main(int argc, const char **argv);
|
|
|
|
int main(int argc, const char **argv) {
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
|
|
int ret = skt_main(argc, argv);
|
|
exit(ret);
|
|
});
|
|
|
|
dispatch_main();
|
|
}
|
|
|
|
static int skt_main(int argc, const char **argv) {
|
|
llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
|
|
|
|
sourcekitd_initialize();
|
|
|
|
sourcekitd_set_notification_handler(^(sourcekitd_response_t resp) {
|
|
notification_receiver(resp);
|
|
});
|
|
|
|
#define KEY(NAME, CONTENT) Key##NAME = sourcekitd_uid_get_from_cstr(CONTENT);
|
|
#include "SourceKit/Core/ProtocolUIDs.def"
|
|
|
|
SemaDiagnosticStage = sourcekitd_uid_get_from_cstr("source.diagnostic.stage.swift.sema");
|
|
|
|
NoteDocUpdate = sourcekitd_uid_get_from_cstr("source.notification.editor.documentupdate");
|
|
|
|
#define REQUEST(NAME, CONTENT) Request##NAME = sourcekitd_uid_get_from_cstr(CONTENT);
|
|
#define KIND(NAME, CONTENT) Kind##NAME = sourcekitd_uid_get_from_cstr(CONTENT);
|
|
#include "SourceKit/Core/ProtocolUIDs.def"
|
|
|
|
// A test invocation may initialize the options to be used for subsequent
|
|
// invocations.
|
|
TestOptions InitOpts;
|
|
auto Args = llvm::makeArrayRef(argv+1, argc-1);
|
|
while (1) {
|
|
unsigned i = 0;
|
|
for (auto Arg: Args) {
|
|
if (StringRef(Arg) == "==")
|
|
break;
|
|
++i;
|
|
}
|
|
if (i == Args.size())
|
|
break;
|
|
if (int ret = handleTestInvocation(Args.slice(0, i), InitOpts)) {
|
|
sourcekitd_shutdown();
|
|
return ret;
|
|
}
|
|
Args = Args.slice(i+1);
|
|
}
|
|
|
|
if (int ret = handleTestInvocation(Args, InitOpts)) {
|
|
sourcekitd_shutdown();
|
|
return ret;
|
|
}
|
|
|
|
for (auto &info : asyncResponses) {
|
|
if (info.semaphore.wait(60 * 1000)) {
|
|
llvm::report_fatal_error("async request timed out");
|
|
}
|
|
|
|
if (handleResponse(info.response, info.options, info.sourceFilename,
|
|
std::move(info.sourceBuffer), nullptr)) {
|
|
sourcekitd_shutdown();
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
sourcekitd_shutdown();
|
|
return 0;
|
|
}
|
|
|
|
static inline const char *getInterfaceGenDocumentName() {
|
|
// Absolute "path" so that handleTestInvocation doesn't try to make it
|
|
// absolute.
|
|
return "/<interface-gen>";
|
|
}
|
|
|
|
static int printAnnotations();
|
|
static int printDiags();
|
|
|
|
static void getSemanticInfo(sourcekitd_variant_t Info, StringRef Filename);
|
|
|
|
static void addCodeCompleteOptions(sourcekitd_object_t Req, TestOptions &Opts) {
|
|
if (!Opts.RequestOptions.empty()) {
|
|
sourcekitd_object_t CCOpts =
|
|
sourcekitd_request_dictionary_create(nullptr, nullptr, 0);
|
|
for (auto &Opt : Opts.RequestOptions) {
|
|
auto KeyValue = StringRef(Opt).split('=');
|
|
std::string KeyStr("key.codecomplete.");
|
|
KeyStr.append(KeyValue.first);
|
|
sourcekitd_uid_t Key = sourcekitd_uid_get_from_cstr(KeyStr.c_str());
|
|
|
|
// FIXME: more robust way to determine the option type.
|
|
if (KeyValue.first == "filtertext") {
|
|
sourcekitd_request_dictionary_set_stringbuf(
|
|
CCOpts, Key, KeyValue.second.data(), KeyValue.second.size());
|
|
} else {
|
|
int64_t Value = 0;
|
|
KeyValue.second.getAsInteger(0, Value);
|
|
sourcekitd_request_dictionary_set_int64(CCOpts, Key, Value);
|
|
}
|
|
}
|
|
sourcekitd_request_dictionary_set_value(Req, KeyCodeCompleteOptions,
|
|
CCOpts);
|
|
sourcekitd_request_release(CCOpts);
|
|
}
|
|
}
|
|
|
|
static bool readPopularAPIList(StringRef filename,
|
|
std::vector<std::string> &result) {
|
|
std::ifstream in(filename);
|
|
if (!in.is_open()) {
|
|
llvm::errs() << "error opening '" << filename << "'\n";
|
|
return true;
|
|
}
|
|
|
|
std::string line;
|
|
while (std::getline(in, line)) {
|
|
result.emplace_back();
|
|
std::swap(result.back(), line);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static int handleJsonRequestPath(StringRef QueryPath) {
|
|
auto Buffer = getBufferForFilename(QueryPath)->getBuffer();
|
|
char *Err = nullptr;
|
|
auto Req = sourcekitd_request_create_from_yaml(Buffer.data(), &Err);
|
|
if (!Req) {
|
|
assert(Err);
|
|
llvm::errs() << Err;
|
|
free(Err);
|
|
return 1;
|
|
}
|
|
sourcekitd_request_description_dump(Req);
|
|
sourcekitd_response_t Resp = sourcekitd_send_request_sync(Req);
|
|
auto Error = sourcekitd_response_is_error(Resp);
|
|
sourcekitd_response_description_dump_filedesc(Resp, STDOUT_FILENO);
|
|
return Error ? 1 : 0;
|
|
}
|
|
|
|
static int handleTestInvocation(ArrayRef<const char *> Args,
|
|
TestOptions &InitOpts) {
|
|
|
|
unsigned Optargc = 0;
|
|
for (auto Arg: Args) {
|
|
if (StringRef(Arg) == "--")
|
|
break;
|
|
++Optargc;
|
|
}
|
|
|
|
TestOptions Opts = InitOpts;
|
|
if (Opts.parseArgs(Args.slice(0, Optargc)))
|
|
return 1;
|
|
|
|
if (!Opts.JsonRequestPath.empty())
|
|
return handleJsonRequestPath(Opts.JsonRequestPath);
|
|
|
|
if (Optargc < Args.size())
|
|
Opts.CompilerArgs = Args.slice(Optargc+1);
|
|
|
|
if (Opts.Request == SourceKitRequest::DemangleNames ||
|
|
Opts.Request == SourceKitRequest::MangleSimpleClasses)
|
|
Opts.SourceFile.clear();
|
|
|
|
std::string SourceFile = Opts.SourceFile;
|
|
if (!SourceFile.empty()) {
|
|
llvm::SmallString<64> AbsSourceFile;
|
|
AbsSourceFile += SourceFile;
|
|
llvm::sys::fs::make_absolute(AbsSourceFile);
|
|
SourceFile = AbsSourceFile.str();
|
|
}
|
|
|
|
if (!Opts.TextInputFile.empty()) {
|
|
auto Buf = getBufferForFilename(Opts.TextInputFile);
|
|
Opts.SourceText = Buf->getBuffer();
|
|
}
|
|
|
|
std::unique_ptr<llvm::MemoryBuffer> SourceBuf;
|
|
if (Opts.SourceText.hasValue()) {
|
|
SourceBuf = llvm::MemoryBuffer::getMemBuffer(*Opts.SourceText, Opts.SourceFile);
|
|
} else if (!SourceFile.empty()) {
|
|
SourceBuf = llvm::MemoryBuffer::getMemBuffer(
|
|
getBufferForFilename(SourceFile)->getBuffer(), SourceFile);
|
|
}
|
|
|
|
// FIXME: we should detect if offset is required but not set.
|
|
unsigned ByteOffset = Opts.Offset;
|
|
if (Opts.Line != 0) {
|
|
ByteOffset = resolveFromLineCol(Opts.Line, Opts.Col, SourceBuf.get());
|
|
}
|
|
|
|
sourcekitd_object_t Req = sourcekitd_request_dictionary_create(nullptr,
|
|
nullptr, 0);
|
|
ActiveRequest = Opts.Request;
|
|
switch (Opts.Request) {
|
|
case SourceKitRequest::None:
|
|
llvm::errs() << "request is not set\n";
|
|
// FIXME: This non-zero return value is not propagated as an exit code.
|
|
// In other words, despite returning 1 here, the program still exits
|
|
// with a zero (successful) exit code.
|
|
return 1;
|
|
|
|
case SourceKitRequest::ProtocolVersion:
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestProtocolVersion);
|
|
break;
|
|
|
|
case SourceKitRequest::DemangleNames:
|
|
prepareDemangleRequest(Req, Opts);
|
|
break;
|
|
|
|
case SourceKitRequest::MangleSimpleClasses:
|
|
prepareMangleRequest(Req, Opts);
|
|
break;
|
|
|
|
case SourceKitRequest::Index:
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestIndex);
|
|
break;
|
|
|
|
case SourceKitRequest::CodeComplete:
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestCodeComplete);
|
|
sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset);
|
|
break;
|
|
|
|
case SourceKitRequest::CodeCompleteOpen:
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest,
|
|
RequestCodeCompleteOpen);
|
|
sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset);
|
|
sourcekitd_request_dictionary_set_string(Req, KeyName, SourceFile.c_str());
|
|
addCodeCompleteOptions(Req, Opts);
|
|
break;
|
|
|
|
case SourceKitRequest::CodeCompleteClose:
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest,
|
|
RequestCodeCompleteClose);
|
|
sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset);
|
|
sourcekitd_request_dictionary_set_string(Req, KeyName, SourceFile.c_str());
|
|
break;
|
|
|
|
case SourceKitRequest::CodeCompleteUpdate:
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest,
|
|
RequestCodeCompleteUpdate);
|
|
sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset);
|
|
sourcekitd_request_dictionary_set_string(Req, KeyName, SourceFile.c_str());
|
|
addCodeCompleteOptions(Req, Opts);
|
|
break;
|
|
|
|
case SourceKitRequest::CodeCompleteCacheOnDisk:
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest,
|
|
RequestCodeCompleteCacheOnDisk);
|
|
sourcekitd_request_dictionary_set_string(Req, KeyName,
|
|
Opts.CachePath.c_str());
|
|
break;
|
|
|
|
case SourceKitRequest::CodeCompleteSetPopularAPI: {
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest,
|
|
RequestCodeCompleteSetPopularAPI);
|
|
|
|
auto addPopularList = [&Req](StringRef filename, sourcekitd_uid_t key) {
|
|
std::vector<std::string> names;
|
|
if (readPopularAPIList(filename, names))
|
|
return true;
|
|
|
|
sourcekitd_object_t popular = sourcekitd_request_array_create(nullptr, 0);
|
|
for (auto name : names)
|
|
sourcekitd_request_array_set_string(popular, SOURCEKITD_ARRAY_APPEND,
|
|
name.c_str());
|
|
sourcekitd_request_dictionary_set_value(Req, key, popular);
|
|
return false;
|
|
};
|
|
|
|
for (auto &Opt : Opts.RequestOptions) {
|
|
auto KeyValue = StringRef(Opt).split('=');
|
|
auto key = llvm::StringSwitch<sourcekitd_uid_t>(KeyValue.first)
|
|
.Case("popular", KeyPopular)
|
|
.Case("unpopular", KeyUnpopular)
|
|
.Default(nullptr);
|
|
if (!key) {
|
|
llvm::errs() << "invalid key '" << KeyValue.first << "' in -req-opts\n";
|
|
return 1;
|
|
}
|
|
|
|
if (addPopularList(KeyValue.second, key))
|
|
return 1;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case SourceKitRequest::CursorInfo:
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestCursorInfo);
|
|
if (!Opts.USR.empty()) {
|
|
sourcekitd_request_dictionary_set_string(Req, KeyUSR, Opts.USR.c_str());
|
|
} else {
|
|
sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset);
|
|
}
|
|
break;
|
|
case SourceKitRequest::RangeInfo: {
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestRangeInfo);
|
|
sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset);
|
|
auto Length = Opts.Length;
|
|
if (Opts.Length == 0 && Opts.EndLine > 0) {
|
|
auto EndOff = resolveFromLineCol(Opts.EndLine, Opts.EndCol, SourceFile);
|
|
Length = EndOff - ByteOffset;
|
|
}
|
|
sourcekitd_request_dictionary_set_int64(Req, KeyLength, Length);
|
|
break;
|
|
}
|
|
case SourceKitRequest::MarkupToXML: {
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestMarkupToXML);
|
|
break;
|
|
}
|
|
case SourceKitRequest::NameTranslation: {
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestNameTranslation);
|
|
sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset);
|
|
StringRef BaseName;
|
|
llvm::SmallVector<StringRef, 4> ArgPieces;
|
|
sourcekitd_uid_t ArgName;
|
|
if (!Opts.SwiftName.empty()) {
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyNameKind, KindNameSwift);
|
|
ArgName = KeyArgNames;
|
|
StringRef Text(Opts.SwiftName);
|
|
auto ArgStart = Text.find_first_of('(');
|
|
if (ArgStart == StringRef::npos) {
|
|
BaseName = Text;
|
|
} else {
|
|
BaseName = Text.substr(0, ArgStart);
|
|
auto ArgEnd = Text.find_last_of(')');
|
|
if (ArgEnd == StringRef::npos) {
|
|
llvm::errs() << "Swift name is malformed.\n";
|
|
return 1;
|
|
}
|
|
StringRef AllArgs = Text.substr(ArgStart + 1, ArgEnd - ArgStart - 1);
|
|
AllArgs.split(ArgPieces, ':');
|
|
if (!Args.empty()) {
|
|
if (!ArgPieces.back().empty()) {
|
|
llvm::errs() << "Swift name is malformed.\n";
|
|
return 1;
|
|
}
|
|
ArgPieces.pop_back();
|
|
}
|
|
}
|
|
} else if (!Opts.ObjCName.empty()) {
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyNameKind, KindNameObjc);
|
|
BaseName = Opts.ObjCName;
|
|
ArgName = KeySelectorPieces;
|
|
} else if (!Opts.ObjCSelector.empty()) {
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyNameKind, KindNameObjc);
|
|
StringRef Name(Opts.ObjCSelector);
|
|
Name.split(ArgPieces, ':');
|
|
if (ArgPieces.back().empty())
|
|
ArgPieces.pop_back();
|
|
ArgName = KeySelectorPieces;
|
|
} else {
|
|
llvm::errs() << "must specify either -swift-name or -objc-name or -objc-selector\n";
|
|
return 1;
|
|
}
|
|
if (!BaseName.empty()) {
|
|
std::string S = BaseName;
|
|
sourcekitd_request_dictionary_set_string(Req, KeyBaseName, S.c_str());
|
|
}
|
|
if (!ArgPieces.empty()) {
|
|
sourcekitd_object_t Arr = sourcekitd_request_array_create(nullptr, 0);
|
|
for (StringRef A : ArgPieces) {
|
|
std::string S = A;
|
|
sourcekitd_request_array_set_string(Arr, SOURCEKITD_ARRAY_APPEND,
|
|
S.c_str());
|
|
}
|
|
sourcekitd_request_dictionary_set_value(Req, ArgName, Arr);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SourceKitRequest::RelatedIdents:
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestRelatedIdents);
|
|
sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset);
|
|
break;
|
|
|
|
case SourceKitRequest::SyntaxMap:
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestEditorOpen);
|
|
sourcekitd_request_dictionary_set_string(Req, KeyName, SourceFile.c_str());
|
|
sourcekitd_request_dictionary_set_int64(Req, KeyEnableSyntaxMap, true);
|
|
sourcekitd_request_dictionary_set_int64(Req, KeyEnableStructure, false);
|
|
sourcekitd_request_dictionary_set_int64(Req, KeySyntacticOnly, !Opts.UsedSema);
|
|
break;
|
|
|
|
case SourceKitRequest::Structure:
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestEditorOpen);
|
|
sourcekitd_request_dictionary_set_string(Req, KeyName, SourceFile.c_str());
|
|
sourcekitd_request_dictionary_set_int64(Req, KeyEnableSyntaxMap, false);
|
|
sourcekitd_request_dictionary_set_int64(Req, KeyEnableStructure, true);
|
|
sourcekitd_request_dictionary_set_int64(Req, KeySyntacticOnly, !Opts.UsedSema);
|
|
break;
|
|
|
|
case SourceKitRequest::Format:
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestEditorOpen);
|
|
sourcekitd_request_dictionary_set_string(Req, KeyName, SourceFile.c_str());
|
|
sourcekitd_request_dictionary_set_int64(Req, KeyEnableSyntaxMap, false);
|
|
sourcekitd_request_dictionary_set_int64(Req, KeyEnableStructure, false);
|
|
sourcekitd_request_dictionary_set_int64(Req, KeySyntacticOnly, !Opts.UsedSema);
|
|
break;
|
|
|
|
case SourceKitRequest::ExpandPlaceholder:
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestEditorOpen);
|
|
sourcekitd_request_dictionary_set_string(Req, KeyName, SourceFile.c_str());
|
|
sourcekitd_request_dictionary_set_int64(Req, KeyEnableSyntaxMap, false);
|
|
sourcekitd_request_dictionary_set_int64(Req, KeyEnableStructure, false);
|
|
sourcekitd_request_dictionary_set_int64(Req, KeySyntacticOnly, !Opts.UsedSema);
|
|
break;
|
|
|
|
case SourceKitRequest::DocInfo:
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestDocInfo);
|
|
break;
|
|
|
|
case SourceKitRequest::SemanticInfo:
|
|
InitOpts.UsedSema = true;
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestEditorOpen);
|
|
sourcekitd_request_dictionary_set_string(Req, KeyName, SourceFile.c_str());
|
|
break;
|
|
|
|
case SourceKitRequest::Open:
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestEditorOpen);
|
|
sourcekitd_request_dictionary_set_string(Req, KeyName, SourceFile.c_str());
|
|
break;
|
|
|
|
case SourceKitRequest::Edit:
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest,
|
|
RequestEditorReplaceText);
|
|
sourcekitd_request_dictionary_set_string(Req, KeyName, SourceFile.c_str());
|
|
sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset);
|
|
sourcekitd_request_dictionary_set_int64(Req, KeyLength, Opts.Length);
|
|
sourcekitd_request_dictionary_set_string(Req, KeySourceText,
|
|
Opts.ReplaceText.getValue().c_str());
|
|
break;
|
|
|
|
case SourceKitRequest::PrintAnnotations:
|
|
return printAnnotations();
|
|
case SourceKitRequest::PrintDiags:
|
|
return printDiags();
|
|
case SourceKitRequest::ExtractComment:
|
|
if (Opts.SourceFile.empty()) {
|
|
llvm::errs() << "Missing '<source-file>' \n";
|
|
return 1;
|
|
}
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest,
|
|
RequestEditorExtractTextFromComment);
|
|
break;
|
|
|
|
case SourceKitRequest::InterfaceGen:
|
|
case SourceKitRequest::InterfaceGenOpen:
|
|
sourcekitd_request_dictionary_set_int64(Req, KeySynthesizedExtension,
|
|
Opts.SynthesizedExtensions);
|
|
if (Opts.ModuleName.empty() && Opts.HeaderPath.empty() &&
|
|
Opts.SourceFile.empty() && Opts.USR.empty()) {
|
|
llvm::errs() << "Missing '-module <module name>' or '-header <path>'" <<
|
|
"or '<source-file>' or '-usr <USR>' \n";
|
|
return 1;
|
|
}
|
|
if (!Opts.ModuleName.empty()) {
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest,
|
|
RequestEditorOpenInterface);
|
|
} else if (!Opts.USR.empty()) {
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest,
|
|
RequestEditorOpenSwiftTypeInterface);
|
|
} else if (!Opts.SourceFile.empty()) {
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest,
|
|
RequestEditorOpenSwiftSourceInterface);
|
|
} else {
|
|
if (Opts.AreSwiftArgs)
|
|
sourcekitd_request_dictionary_set_int64(Req, KeyAreSwiftArgs, true);
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest,
|
|
RequestEditorOpenHeaderInterface);
|
|
}
|
|
|
|
sourcekitd_request_dictionary_set_string(Req, KeyName, getInterfaceGenDocumentName());
|
|
if (!Opts.ModuleGroupName.empty())
|
|
sourcekitd_request_dictionary_set_string(Req, KeyGroupName,
|
|
Opts.ModuleGroupName.c_str());
|
|
if (!Opts.InterestedUSR.empty())
|
|
sourcekitd_request_dictionary_set_string(Req, KeyInterestedUSR,
|
|
Opts.InterestedUSR.c_str());
|
|
if (!Opts.USR.empty())
|
|
sourcekitd_request_dictionary_set_string(Req, KeyUSR, Opts.USR.c_str());
|
|
break;
|
|
|
|
case SourceKitRequest::FindInterfaceDoc:
|
|
if (Opts.ModuleName.empty()) {
|
|
llvm::errs() << "Missing '-module <module name>'\n";
|
|
return 1;
|
|
}
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestEditorFindInterfaceDoc);
|
|
break;
|
|
|
|
case SourceKitRequest::FindUSR:
|
|
if (Opts.USR.empty()) {
|
|
llvm::errs() << "Missing '-usr <USR string>'\n";
|
|
return 1;
|
|
}
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestEditorFindUSR);
|
|
sourcekitd_request_dictionary_set_string(Req, KeyUSR, Opts.USR.c_str());
|
|
break;
|
|
|
|
case SourceKitRequest::ModuleGroups:
|
|
if (Opts.ModuleName.empty()) {
|
|
llvm::errs() << "Missing '-module <module name>'\n";
|
|
return 1;
|
|
}
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestModuleGroups);
|
|
break;
|
|
}
|
|
|
|
if (!SourceFile.empty()) {
|
|
if (Opts.PassAsSourceText) {
|
|
auto Buf = getBufferForFilename(SourceFile);
|
|
sourcekitd_request_dictionary_set_string(Req, KeySourceText,
|
|
Buf->getBufferStart());
|
|
}
|
|
sourcekitd_request_dictionary_set_string(Req, KeySourceFile,
|
|
SourceFile.c_str());
|
|
}
|
|
|
|
if (Opts.SourceText) {
|
|
sourcekitd_request_dictionary_set_string(Req, KeySourceText,
|
|
Opts.SourceText->c_str());
|
|
}
|
|
|
|
if (!Opts.CompilerArgs.empty()) {
|
|
sourcekitd_object_t Args = sourcekitd_request_array_create(nullptr, 0);
|
|
for (auto Arg : Opts.CompilerArgs)
|
|
sourcekitd_request_array_set_string(Args, SOURCEKITD_ARRAY_APPEND, Arg);
|
|
sourcekitd_request_dictionary_set_value(Req, KeyCompilerArgs, Args);
|
|
sourcekitd_request_release(Args);
|
|
}
|
|
|
|
if (!Opts.ModuleName.empty()) {
|
|
sourcekitd_request_dictionary_set_string(Req, KeyModuleName,
|
|
Opts.ModuleName.c_str());
|
|
}
|
|
if (!Opts.HeaderPath.empty()) {
|
|
sourcekitd_request_dictionary_set_string(Req, KeyFilePath,
|
|
Opts.HeaderPath.c_str());
|
|
}
|
|
if (Opts.CancelOnSubsequentRequest.hasValue()) {
|
|
sourcekitd_request_dictionary_set_int64(Req, KeyCancelOnSubsequentRequest,
|
|
*Opts.CancelOnSubsequentRequest);
|
|
}
|
|
|
|
if (Opts.SwiftVersion.hasValue()) {
|
|
sourcekitd_request_dictionary_set_int64(Req, KeySwiftVersion,
|
|
Opts.SwiftVersion.getValue());
|
|
}
|
|
|
|
if (Opts.PrintRequest)
|
|
sourcekitd_request_description_dump(Req);
|
|
|
|
if (!Opts.isAsyncRequest) {
|
|
sourcekitd_response_t Resp = sourcekitd_send_request_sync(Req);
|
|
sourcekitd_request_release(Req);
|
|
return handleResponse(Resp, Opts, SourceFile, std::move(SourceBuf),
|
|
&InitOpts)
|
|
? 1
|
|
: 0;
|
|
} else {
|
|
#if SOURCEKITD_HAS_BLOCKS
|
|
AsyncResponseInfo info;
|
|
info.options = Opts;
|
|
info.sourceFilename = std::move(SourceFile);
|
|
info.sourceBuffer = std::move(SourceBuf);
|
|
unsigned respIndex = asyncResponses.size();
|
|
asyncResponses.push_back(std::move(info));
|
|
|
|
sourcekitd_send_request(Req, nullptr, ^(sourcekitd_response_t resp) {
|
|
auto &info = asyncResponses[respIndex];
|
|
info.response = resp;
|
|
info.semaphore.signal(); // Ready to be handled!
|
|
});
|
|
|
|
#else
|
|
llvm::report_fatal_error(
|
|
"-async not supported when sourcekitd is built without blocks support");
|
|
#endif
|
|
|
|
sourcekitd_request_release(Req);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static bool handleResponse(sourcekitd_response_t Resp, const TestOptions &Opts,
|
|
const std::string &SourceFile,
|
|
std::unique_ptr<llvm::MemoryBuffer> SourceBuf,
|
|
TestOptions *InitOpts) {
|
|
bool KeepResponseAlive = false;
|
|
bool IsError = sourcekitd_response_is_error(Resp);
|
|
if (IsError) {
|
|
sourcekitd_response_description_dump(Resp);
|
|
|
|
} else if (Opts.PrintResponseAsJSON) {
|
|
sourcekitd_variant_t Info = sourcekitd_response_get_value(Resp);
|
|
char *json = sourcekitd_variant_json_description_copy(Info);
|
|
llvm::outs() << json << '\n';
|
|
free(json);
|
|
|
|
} else if (Opts.PrintRawResponse) {
|
|
sourcekitd_response_description_dump_filedesc(Resp, STDOUT_FILENO);
|
|
|
|
} else {
|
|
sourcekitd_variant_t Info = sourcekitd_response_get_value(Resp);
|
|
switch (Opts.Request) {
|
|
case SourceKitRequest::None:
|
|
llvm_unreachable("request should be set");
|
|
case SourceKitRequest::PrintAnnotations:
|
|
case SourceKitRequest::PrintDiags:
|
|
llvm_unreachable("print-annotations/print-diags is handled elsewhere");
|
|
|
|
case SourceKitRequest::Open:
|
|
case SourceKitRequest::Edit:
|
|
getSemanticInfo(Info, SourceFile);
|
|
KeepResponseAlive = true;
|
|
break;
|
|
|
|
case SourceKitRequest::DemangleNames:
|
|
printDemangleResults(sourcekitd_response_get_value(Resp), outs());
|
|
break;
|
|
|
|
case SourceKitRequest::MangleSimpleClasses:
|
|
printMangleResults(sourcekitd_response_get_value(Resp), outs());
|
|
break;
|
|
|
|
case SourceKitRequest::ProtocolVersion:
|
|
case SourceKitRequest::Index:
|
|
case SourceKitRequest::CodeComplete:
|
|
case SourceKitRequest::CodeCompleteOpen:
|
|
case SourceKitRequest::CodeCompleteClose:
|
|
case SourceKitRequest::CodeCompleteUpdate:
|
|
case SourceKitRequest::CodeCompleteCacheOnDisk:
|
|
case SourceKitRequest::CodeCompleteSetPopularAPI:
|
|
sourcekitd_response_description_dump_filedesc(Resp, STDOUT_FILENO);
|
|
break;
|
|
|
|
case SourceKitRequest::RelatedIdents:
|
|
printRelatedIdents(Info, SourceFile, llvm::outs());
|
|
break;
|
|
|
|
case SourceKitRequest::CursorInfo:
|
|
printCursorInfo(Info, SourceFile, llvm::outs());
|
|
break;
|
|
|
|
case SourceKitRequest::NameTranslation:
|
|
printNameTranslationInfo(Info, llvm::outs());
|
|
break;
|
|
|
|
case SourceKitRequest::RangeInfo:
|
|
printRangeInfo(Info, SourceFile, llvm::outs());
|
|
break;
|
|
|
|
case SourceKitRequest::DocInfo:
|
|
printDocInfo(Info, SourceFile);
|
|
break;
|
|
|
|
case SourceKitRequest::SemanticInfo:
|
|
getSemanticInfo(Info, SourceFile);
|
|
printSemanticInfo();
|
|
break;
|
|
|
|
case SourceKitRequest::InterfaceGen:
|
|
printInterfaceGen(Info, Opts.CheckInterfaceIsASCII);
|
|
break;
|
|
|
|
case SourceKitRequest::ExtractComment:
|
|
case SourceKitRequest::MarkupToXML:
|
|
printNormalizedDocComment(Info);
|
|
break;
|
|
|
|
case SourceKitRequest::InterfaceGenOpen:
|
|
// Just initialize the options for the subsequent request.
|
|
assert(!Opts.isAsyncRequest && InitOpts &&
|
|
"async interface-gen-open is not supported");
|
|
InitOpts->SourceFile = getInterfaceGenDocumentName();
|
|
InitOpts->SourceText =
|
|
sourcekitd_variant_dictionary_get_string(Info, KeySourceText);
|
|
break;
|
|
|
|
case SourceKitRequest::FindInterfaceDoc:
|
|
printFoundInterface(Info, llvm::outs());
|
|
break;
|
|
|
|
case SourceKitRequest::FindUSR:
|
|
printFoundUSR(Info, SourceBuf.get(), llvm::outs());
|
|
break;
|
|
|
|
case SourceKitRequest::SyntaxMap:
|
|
case SourceKitRequest::Structure:
|
|
sourcekitd_response_description_dump_filedesc(Resp, STDOUT_FILENO);
|
|
if (Opts.ReplaceText.hasValue()) {
|
|
unsigned Offset = resolveFromLineCol(Opts.Line, Opts.Col, SourceFile);
|
|
unsigned Length = Opts.Length;
|
|
sourcekitd_object_t EdReq = sourcekitd_request_dictionary_create(nullptr,
|
|
nullptr, 0);
|
|
sourcekitd_request_dictionary_set_uid(EdReq, KeyRequest,
|
|
RequestEditorReplaceText);
|
|
sourcekitd_request_dictionary_set_string(EdReq, KeyName,
|
|
SourceFile.c_str());
|
|
sourcekitd_request_dictionary_set_int64(EdReq, KeyOffset, Offset);
|
|
sourcekitd_request_dictionary_set_int64(EdReq, KeyLength, Length);
|
|
sourcekitd_request_dictionary_set_string(EdReq, KeySourceText,
|
|
Opts.ReplaceText.getValue().c_str());
|
|
bool EnableSyntaxMax = Opts.Request == SourceKitRequest::SyntaxMap;
|
|
bool EnableSubStructure = Opts.Request == SourceKitRequest::Structure;
|
|
sourcekitd_request_dictionary_set_int64(EdReq, KeyEnableSyntaxMap,
|
|
EnableSyntaxMax);
|
|
sourcekitd_request_dictionary_set_int64(EdReq, KeyEnableStructure,
|
|
EnableSubStructure);
|
|
sourcekitd_request_dictionary_set_int64(EdReq, KeySyntacticOnly, !Opts.UsedSema);
|
|
|
|
sourcekitd_response_t EdResp = sourcekitd_send_request_sync(EdReq);
|
|
sourcekitd_response_description_dump_filedesc(EdResp, STDOUT_FILENO);
|
|
sourcekitd_response_dispose(EdResp);
|
|
sourcekitd_request_release(EdReq);
|
|
}
|
|
break;
|
|
|
|
case SourceKitRequest::Format:
|
|
{
|
|
sourcekitd_object_t Fmt = sourcekitd_request_dictionary_create(nullptr,
|
|
nullptr, 0);
|
|
sourcekitd_request_dictionary_set_uid(Fmt, KeyRequest,
|
|
RequestEditorFormatText);
|
|
sourcekitd_request_dictionary_set_string(Fmt, KeyName,
|
|
SourceFile.c_str());
|
|
sourcekitd_request_dictionary_set_string(Fmt, KeySourceText, "");
|
|
sourcekitd_request_dictionary_set_int64(Fmt, KeyLine, Opts.Line);
|
|
sourcekitd_request_dictionary_set_int64(Fmt, KeyLength, Opts.Length);
|
|
|
|
if (!Opts.RequestOptions.empty()) {
|
|
sourcekitd_object_t FO = sourcekitd_request_dictionary_create(nullptr,
|
|
nullptr, 0);
|
|
for (auto &FmtOpt : Opts.RequestOptions) {
|
|
auto KeyValue = StringRef(FmtOpt).split('=');
|
|
std::string KeyStr("key.editor.format.");
|
|
KeyStr.append(KeyValue.first);
|
|
sourcekitd_uid_t Key = sourcekitd_uid_get_from_cstr(KeyStr.c_str());
|
|
int64_t Value = 0;
|
|
KeyValue.second.getAsInteger(0, Value);
|
|
sourcekitd_request_dictionary_set_int64(FO, Key, Value);
|
|
}
|
|
sourcekitd_request_dictionary_set_value(Fmt, KeyFormatOptions, FO);
|
|
sourcekitd_request_release(FO);
|
|
}
|
|
|
|
sourcekitd_response_t FmtResp = sourcekitd_send_request_sync(Fmt);
|
|
sourcekitd_response_description_dump_filedesc(FmtResp, STDOUT_FILENO);
|
|
sourcekitd_response_dispose(FmtResp);
|
|
sourcekitd_request_release(Fmt);
|
|
}
|
|
break;
|
|
|
|
case SourceKitRequest::ExpandPlaceholder:
|
|
expandPlaceholders(SourceBuf.get(), llvm::outs());
|
|
break;
|
|
case SourceKitRequest::ModuleGroups:
|
|
printModuleGroupNames(Info, llvm::outs());
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!KeepResponseAlive)
|
|
sourcekitd_response_dispose(Resp);
|
|
return IsError;
|
|
}
|
|
|
|
sourcekitd_variant_t LatestSemaAnnotations = {{0,0,0}};
|
|
sourcekitd_variant_t LatestSemaDiags = {{0,0,0}};
|
|
|
|
static void getSemanticInfoImpl(sourcekitd_variant_t Info) {
|
|
LatestSemaAnnotations =
|
|
sourcekitd_variant_dictionary_get_value(Info, KeyAnnotations);
|
|
LatestSemaDiags =
|
|
sourcekitd_variant_dictionary_get_value(Info, KeyDiagnostics);
|
|
}
|
|
|
|
static void getSemanticInfo(sourcekitd_variant_t Info, StringRef Filename) {
|
|
getSemanticInfoImpl(Info);
|
|
|
|
// Wait for the notification that semantic info is available.
|
|
// But only for 1 min.
|
|
bool expired = semaSemaphore.wait(60 * 1000);
|
|
if (expired) {
|
|
llvm::report_fatal_error("Never got notification for semantic info");
|
|
}
|
|
|
|
if (Filename != semaName){
|
|
llvm::report_fatal_error(
|
|
llvm::Twine("Got notification for different doc name: ") + semaName);
|
|
}
|
|
getSemanticInfoImpl(sourcekitd_response_get_value(semaResponse));
|
|
}
|
|
|
|
static int printAnnotations() {
|
|
sourcekitd_variant_description_dump_filedesc(LatestSemaAnnotations,
|
|
STDOUT_FILENO);
|
|
return 0;
|
|
}
|
|
|
|
static int printDiags() {
|
|
sourcekitd_variant_description_dump_filedesc(LatestSemaDiags, STDOUT_FILENO);
|
|
return 0;
|
|
}
|
|
|
|
static void printSemanticInfo() {
|
|
printAnnotations();
|
|
if (sourcekitd_variant_get_type(LatestSemaDiags) != SOURCEKITD_VARIANT_TYPE_NULL)
|
|
printDiags();
|
|
}
|
|
|
|
static void notification_receiver(sourcekitd_response_t resp) {
|
|
if (sourcekitd_response_is_error(resp)) {
|
|
sourcekitd_response_description_dump(resp);
|
|
exit(1);
|
|
}
|
|
|
|
sourcekitd_variant_t payload = sourcekitd_response_get_value(resp);
|
|
sourcekitd_uid_t note =
|
|
sourcekitd_variant_dictionary_get_uid(payload, KeyNotification);
|
|
semaName = sourcekitd_variant_dictionary_get_string(payload, KeyName);
|
|
|
|
if (note == NoteDocUpdate) {
|
|
sourcekitd_object_t edReq = sourcekitd_request_dictionary_create(nullptr,
|
|
nullptr, 0);
|
|
sourcekitd_request_dictionary_set_uid(edReq, KeyRequest,
|
|
RequestEditorReplaceText);
|
|
sourcekitd_request_dictionary_set_string(edReq, KeyName, semaName);
|
|
sourcekitd_request_dictionary_set_string(edReq, KeySourceText, "");
|
|
semaResponse = sourcekitd_send_request_sync(edReq);
|
|
sourcekitd_request_release(edReq);
|
|
semaSemaphore.signal();
|
|
}
|
|
}
|
|
|
|
static void printNameTranslationInfo(sourcekitd_variant_t Info,
|
|
llvm::raw_ostream &OS) {
|
|
sourcekitd_uid_t KindUID = sourcekitd_variant_dictionary_get_uid(Info,
|
|
KeyNameKind);
|
|
if (KindUID == nullptr) {
|
|
OS << "<empty name translation info>\n";
|
|
return;
|
|
}
|
|
const char *Kind = sourcekitd_uid_get_string_ptr(KindUID);
|
|
const char *BaseName = sourcekitd_variant_dictionary_get_string(Info,
|
|
KeyBaseName);
|
|
std::vector<const char *> Selectors;
|
|
sourcekitd_variant_t SelectorsObj =
|
|
sourcekitd_variant_dictionary_get_value(Info, KeySelectorPieces);
|
|
for (unsigned i = 0, e = sourcekitd_variant_array_get_count(SelectorsObj);
|
|
i != e; ++i) {
|
|
sourcekitd_variant_t Entry =
|
|
sourcekitd_variant_array_get_value(SelectorsObj, i);
|
|
Selectors.push_back(sourcekitd_variant_dictionary_get_string(Entry, KeyName));
|
|
}
|
|
|
|
bool IsZeroArgSelector = false;
|
|
auto IsZeroArgObj = sourcekitd_variant_dictionary_get_value(Info, KeyIsZeroArgSelector);
|
|
if (sourcekitd_variant_get_type(IsZeroArgObj) != SOURCEKITD_VARIANT_TYPE_NULL) {
|
|
IsZeroArgSelector = sourcekitd_variant_int64_get_value(IsZeroArgObj);
|
|
}
|
|
|
|
std::vector<const char *> Args;
|
|
sourcekitd_variant_t ArgsObj =
|
|
sourcekitd_variant_dictionary_get_value(Info, KeyArgNames);
|
|
for (unsigned i = 0, e = sourcekitd_variant_array_get_count(ArgsObj);
|
|
i != e; ++i) {
|
|
sourcekitd_variant_t Entry =
|
|
sourcekitd_variant_array_get_value(ArgsObj, i);
|
|
Args.push_back(sourcekitd_variant_dictionary_get_string(Entry, KeyName));
|
|
}
|
|
|
|
OS << Kind << "\n";
|
|
OS << StringRef(BaseName);
|
|
if (!Args.empty()) {
|
|
OS << "(";
|
|
for (auto A : Args) {
|
|
StringRef Text(A);
|
|
if (Text.empty())
|
|
OS << "_" << ":";
|
|
else
|
|
OS << Text << ":";
|
|
}
|
|
OS << ")";
|
|
}
|
|
for (auto S : Selectors) {
|
|
OS << S;
|
|
if (!IsZeroArgSelector) {
|
|
OS << ":";
|
|
}
|
|
}
|
|
OS << '\n';
|
|
}
|
|
|
|
static void printCursorInfo(sourcekitd_variant_t Info, StringRef FilenameIn,
|
|
llvm::raw_ostream &OS) {
|
|
sourcekitd_uid_t KindUID = sourcekitd_variant_dictionary_get_uid(Info,
|
|
sourcekitd_uid_get_from_cstr("key.kind"));
|
|
if (KindUID == nullptr) {
|
|
OS << "<empty cursor info>\n";
|
|
return;
|
|
}
|
|
|
|
std::string Filename = FilenameIn;
|
|
char full_path[MAXPATHLEN];
|
|
if (const char *path = realpath(Filename.c_str(), full_path))
|
|
Filename = path;
|
|
|
|
const char *Kind = sourcekitd_uid_get_string_ptr(KindUID);
|
|
const char *USR = sourcekitd_variant_dictionary_get_string(Info, KeyUSR);
|
|
const char *Name = sourcekitd_variant_dictionary_get_string(Info, KeyName);
|
|
const char *Typename = sourcekitd_variant_dictionary_get_string(Info,
|
|
KeyTypeName);
|
|
const char *TypeUsr = sourcekitd_variant_dictionary_get_string(Info,
|
|
KeyTypeUsr);
|
|
const char *ContainerTypeUsr = sourcekitd_variant_dictionary_get_string(Info,
|
|
KeyContainerTypeUsr);
|
|
const char *ModuleName = sourcekitd_variant_dictionary_get_string(Info,
|
|
KeyModuleName);
|
|
const char *GroupName = sourcekitd_variant_dictionary_get_string(Info,
|
|
KeyGroupName);
|
|
|
|
const char *LocalizationKey =
|
|
sourcekitd_variant_dictionary_get_string(Info, KeyLocalizationKey);
|
|
const char *ModuleInterfaceName =
|
|
sourcekitd_variant_dictionary_get_string(Info, KeyModuleInterfaceName);
|
|
const char *TypeInterface =
|
|
sourcekitd_variant_dictionary_get_string(Info, KeyTypeInterface);
|
|
bool IsSystem = sourcekitd_variant_dictionary_get_bool(Info, KeyIsSystem);
|
|
const char *AnnotDecl = sourcekitd_variant_dictionary_get_string(Info,
|
|
KeyAnnotatedDecl);
|
|
const char *FullAnnotDecl =
|
|
sourcekitd_variant_dictionary_get_string(Info, KeyFullyAnnotatedDecl);
|
|
const char *DocFullAsXML =
|
|
sourcekitd_variant_dictionary_get_string(Info, KeyDocFullAsXML);
|
|
sourcekitd_variant_t OffsetObj =
|
|
sourcekitd_variant_dictionary_get_value(Info, KeyOffset);
|
|
llvm::Optional<int64_t> Offset;
|
|
unsigned Length = 0;
|
|
if (sourcekitd_variant_get_type(OffsetObj) != SOURCEKITD_VARIANT_TYPE_NULL) {
|
|
Offset = sourcekitd_variant_int64_get_value(OffsetObj);
|
|
Length = sourcekitd_variant_dictionary_get_int64(Info, KeyLength);
|
|
}
|
|
const char *FilePath = sourcekitd_variant_dictionary_get_string(Info, KeyFilePath);
|
|
|
|
std::vector<const char *> OverrideUSRs;
|
|
sourcekitd_variant_t OverridesObj =
|
|
sourcekitd_variant_dictionary_get_value(Info, KeyOverrides);
|
|
for (unsigned i = 0, e = sourcekitd_variant_array_get_count(OverridesObj);
|
|
i != e; ++i) {
|
|
sourcekitd_variant_t Entry =
|
|
sourcekitd_variant_array_get_value(OverridesObj, i);
|
|
OverrideUSRs.push_back(sourcekitd_variant_dictionary_get_string(Entry, KeyUSR));
|
|
}
|
|
|
|
std::vector<const char *> GroupNames;
|
|
sourcekitd_variant_t GroupObj =
|
|
sourcekitd_variant_dictionary_get_value(Info, KeyModuleGroups);
|
|
for (unsigned i = 0, e = sourcekitd_variant_array_get_count(GroupObj);
|
|
i != e; ++i) {
|
|
sourcekitd_variant_t Entry =
|
|
sourcekitd_variant_array_get_value(GroupObj, i);
|
|
GroupNames.push_back(sourcekitd_variant_dictionary_get_string(Entry, KeyGroupName));
|
|
}
|
|
|
|
std::vector<const char *> RelatedDecls;
|
|
sourcekitd_variant_t RelatedDeclsObj =
|
|
sourcekitd_variant_dictionary_get_value(Info, KeyRelatedDecls);
|
|
for (unsigned i = 0, e = sourcekitd_variant_array_get_count(RelatedDeclsObj);
|
|
i != e; ++i) {
|
|
sourcekitd_variant_t Entry =
|
|
sourcekitd_variant_array_get_value(RelatedDeclsObj, i);
|
|
RelatedDecls.push_back(sourcekitd_variant_dictionary_get_string(Entry,
|
|
KeyAnnotatedDecl));
|
|
}
|
|
|
|
std::vector<const char *> AvailableActions;
|
|
sourcekitd_variant_t ActionsObj =
|
|
sourcekitd_variant_dictionary_get_value(Info, KeyActionable);
|
|
for (unsigned i = 0, e = sourcekitd_variant_array_get_count(ActionsObj);
|
|
i != e; ++i) {
|
|
sourcekitd_variant_t Entry =
|
|
sourcekitd_variant_array_get_value(ActionsObj, i);
|
|
AvailableActions.push_back(sourcekitd_variant_dictionary_get_string(Entry,
|
|
KeyActionName));
|
|
}
|
|
|
|
uint64_t ParentOffset =
|
|
sourcekitd_variant_dictionary_get_int64(Info, KeyParentLoc);
|
|
|
|
OS << Kind << " (";
|
|
if (Offset.hasValue()) {
|
|
if (Filename != FilePath)
|
|
OS << FilePath << ":";
|
|
auto LineCol = resolveToLineCol(Offset.getValue(), FilePath);
|
|
OS << LineCol.first << ':' << LineCol.second;
|
|
auto EndLineCol = resolveToLineCol(Offset.getValue()+Length, FilePath);
|
|
OS << '-' << EndLineCol.first << ':' << EndLineCol.second;
|
|
}
|
|
OS << ")\n";
|
|
OS << Name << '\n';
|
|
if (USR)
|
|
OS << USR << '\n';
|
|
if (Typename)
|
|
OS << Typename << '\n';
|
|
if (TypeUsr)
|
|
OS << TypeUsr << '\n';
|
|
if (ContainerTypeUsr)
|
|
OS << "<Container>" << ContainerTypeUsr << "</Container>" << '\n';
|
|
if (ModuleName)
|
|
OS << ModuleName << '\n';
|
|
if (GroupName)
|
|
OS << "<Group>" << GroupName << "</Group>" << '\n';
|
|
if (ModuleInterfaceName)
|
|
OS << ModuleInterfaceName << '\n';
|
|
if (IsSystem)
|
|
OS << "SYSTEM\n";
|
|
if (AnnotDecl)
|
|
OS << AnnotDecl << '\n';
|
|
if (FullAnnotDecl)
|
|
OS << FullAnnotDecl << '\n';
|
|
if (DocFullAsXML)
|
|
OS << DocFullAsXML << '\n';
|
|
if (LocalizationKey)
|
|
OS << "<LocalizationKey>" << LocalizationKey;
|
|
OS << "</LocalizationKey>" << '\n';
|
|
OS << "OVERRIDES BEGIN\n";
|
|
for (auto OverUSR : OverrideUSRs)
|
|
OS << OverUSR << '\n';
|
|
OS << "OVERRIDES END\n";
|
|
OS << "RELATED BEGIN\n";
|
|
for (auto RelDecl : RelatedDecls)
|
|
OS << RelDecl << '\n';
|
|
OS << "RELATED END\n";
|
|
OS << "TYPE INTERFACE BEGIN\n";
|
|
if (TypeInterface)
|
|
OS << TypeInterface << '\n';
|
|
OS << "TYPE INTERFACE END\n";
|
|
OS << "MODULE GROUPS BEGIN\n";
|
|
for (auto Group : GroupNames)
|
|
OS << Group << '\n';
|
|
OS << "MODULE GROUPS END\n";
|
|
OS << "ACTIONS BEGIN\n";
|
|
for (auto Action : AvailableActions)
|
|
OS << Action << '\n';
|
|
OS << "ACTIONS END\n";
|
|
if (ParentOffset) {
|
|
OS << "PARENT OFFSET: " << ParentOffset << "\n";
|
|
}
|
|
}
|
|
|
|
static void printRangeInfo(sourcekitd_variant_t Info, StringRef FilenameIn,
|
|
llvm::raw_ostream &OS) {
|
|
sourcekitd_uid_t KindUID = sourcekitd_variant_dictionary_get_uid(Info,
|
|
sourcekitd_uid_get_from_cstr("key.kind"));
|
|
if (KindUID == nullptr) {
|
|
OS << "<empty range info>\n";
|
|
return;
|
|
}
|
|
|
|
std::string Filename = FilenameIn;
|
|
char full_path[MAXPATHLEN];
|
|
if (const char *path = realpath(Filename.c_str(), full_path))
|
|
Filename = path;
|
|
|
|
sourcekitd_variant_t OffsetObj =
|
|
sourcekitd_variant_dictionary_get_value(Info, KeyOffset);
|
|
llvm::Optional<int64_t> Offset;
|
|
if (sourcekitd_variant_get_type(OffsetObj) != SOURCEKITD_VARIANT_TYPE_NULL) {
|
|
Offset = sourcekitd_variant_int64_get_value(OffsetObj);
|
|
}
|
|
const char *Kind = sourcekitd_uid_get_string_ptr(KindUID);
|
|
const char *Typename = sourcekitd_variant_dictionary_get_string(Info,
|
|
KeyTypeName);
|
|
|
|
const char *RangeContent = sourcekitd_variant_dictionary_get_string(Info,
|
|
KeyRangeContent);
|
|
OS << "<kind>" << Kind << "</kind>\n";
|
|
OS << "<content>" <<RangeContent << "</content>\n";
|
|
if (Typename)
|
|
OS << "<type>" <<Typename << "</type>\n";
|
|
}
|
|
static void printFoundInterface(sourcekitd_variant_t Info,
|
|
llvm::raw_ostream &OS) {
|
|
const char *Name = sourcekitd_variant_dictionary_get_string(Info,
|
|
KeyModuleInterfaceName);
|
|
OS << "DOC: (";
|
|
if (Name)
|
|
OS << Name;
|
|
OS << ")\n";
|
|
|
|
sourcekitd_variant_t ArgObj =
|
|
sourcekitd_variant_dictionary_get_value(Info, KeyCompilerArgs);
|
|
|
|
OS << "ARGS: [";
|
|
if (sourcekitd_variant_get_type(ArgObj) != SOURCEKITD_VARIANT_TYPE_NULL) {
|
|
for (unsigned i = 0, e = sourcekitd_variant_array_get_count(ArgObj);
|
|
i != e; ++i) {
|
|
OS << sourcekitd_variant_array_get_string(ArgObj, i);
|
|
OS << ' ';
|
|
}
|
|
}
|
|
OS << "]\n";
|
|
}
|
|
|
|
static void printFoundUSR(sourcekitd_variant_t Info,
|
|
llvm::MemoryBuffer *SourceBuf,
|
|
llvm::raw_ostream &OS) {
|
|
sourcekitd_variant_t OffsetObj =
|
|
sourcekitd_variant_dictionary_get_value(Info, KeyOffset);
|
|
llvm::Optional<int64_t> Offset;
|
|
if (sourcekitd_variant_get_type(OffsetObj) != SOURCEKITD_VARIANT_TYPE_NULL)
|
|
Offset = sourcekitd_variant_int64_get_value(OffsetObj);
|
|
|
|
if (!Offset.hasValue()) {
|
|
OS << "USR NOT FOUND\n";
|
|
return;
|
|
}
|
|
|
|
int64_t Length = sourcekitd_variant_dictionary_get_int64(Info, KeyLength);
|
|
|
|
auto LineCol1 = resolveToLineCol(Offset.getValue(), SourceBuf);
|
|
auto LineCol2 = resolveToLineCol(Offset.getValue() + Length, SourceBuf);
|
|
OS << '(' << LineCol1.first << ':' << LineCol1.second << '-'
|
|
<< LineCol2.first << ':' << LineCol2.second << ")\n";
|
|
}
|
|
|
|
static void printNormalizedDocComment(sourcekitd_variant_t Info) {
|
|
sourcekitd_variant_t Source =
|
|
sourcekitd_variant_dictionary_get_value(Info, KeySourceText);
|
|
sourcekitd_variant_description_dump_filedesc(Source, STDOUT_FILENO);
|
|
}
|
|
|
|
static void printDocInfo(sourcekitd_variant_t Info, StringRef Filename) {
|
|
const char *text =
|
|
sourcekitd_variant_dictionary_get_string(Info, KeySourceText);
|
|
llvm::raw_fd_ostream OS(STDOUT_FILENO, /*shouldClose=*/false);
|
|
if (text) {
|
|
OS << text << '\n';
|
|
OS.flush();
|
|
}
|
|
|
|
sourcekitd_variant_t annotations =
|
|
sourcekitd_variant_dictionary_get_value(Info, KeyAnnotations);
|
|
sourcekitd_variant_t entities =
|
|
sourcekitd_variant_dictionary_get_value(Info,
|
|
sourcekitd_uid_get_from_cstr("key.entities"));
|
|
sourcekitd_variant_t diags =
|
|
sourcekitd_variant_dictionary_get_value(Info, KeyDiagnostics);
|
|
|
|
sourcekitd_variant_description_dump_filedesc(annotations, STDOUT_FILENO);
|
|
sourcekitd_variant_description_dump_filedesc(entities, STDOUT_FILENO);
|
|
|
|
if (sourcekitd_variant_get_type(diags) != SOURCEKITD_VARIANT_TYPE_NULL)
|
|
sourcekitd_variant_description_dump_filedesc(diags, STDOUT_FILENO);
|
|
}
|
|
|
|
static void checkTextIsASCII(const char *Text) {
|
|
for (const char *p = Text; *p; ++p) {
|
|
if (*p & 0x80) {
|
|
auto LineCol = resolveToLineColFromBuf(p - Text, Text);
|
|
|
|
llvm::errs() << "!!Interface text is non-ascii!!\n"
|
|
<< "@ " << LineCol.first << ":" << LineCol.second << "\n";
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void printModuleGroupNames(sourcekitd_variant_t Info,
|
|
llvm::raw_ostream &OS) {
|
|
sourcekitd_variant_t Groups =
|
|
sourcekitd_variant_dictionary_get_value(Info, KeyModuleGroups);
|
|
OS << "<GROUPS>\n";
|
|
for (unsigned i = 0, e = sourcekitd_variant_array_get_count(Groups);
|
|
i != e; ++i) {
|
|
sourcekitd_variant_t Entry =
|
|
sourcekitd_variant_array_get_value(Groups, i);
|
|
OS << sourcekitd_variant_dictionary_get_string(Entry, KeyGroupName) << "\n";
|
|
}
|
|
OS << "<\\GROUPS>\n";
|
|
}
|
|
|
|
static void printInterfaceGen(sourcekitd_variant_t Info, bool CheckASCII) {
|
|
const char *text =
|
|
sourcekitd_variant_dictionary_get_string(Info, KeySourceText);
|
|
|
|
if (text) {
|
|
llvm::raw_fd_ostream OS(STDOUT_FILENO, /*shouldClose=*/false);
|
|
OS << text << '\n';
|
|
}
|
|
|
|
if (CheckASCII) {
|
|
checkTextIsASCII(text);
|
|
}
|
|
|
|
sourcekitd_variant_t syntaxmap =
|
|
sourcekitd_variant_dictionary_get_value(Info, KeySyntaxMap);
|
|
sourcekitd_variant_description_dump_filedesc(syntaxmap, STDOUT_FILENO);
|
|
sourcekitd_variant_t annotations =
|
|
sourcekitd_variant_dictionary_get_value(Info, KeyAnnotations);
|
|
sourcekitd_variant_description_dump_filedesc(annotations, STDOUT_FILENO);
|
|
sourcekitd_variant_t structure =
|
|
sourcekitd_variant_dictionary_get_value(Info, KeySubStructure);
|
|
sourcekitd_variant_description_dump_filedesc(structure, STDOUT_FILENO);
|
|
}
|
|
|
|
static void printRelatedIdents(sourcekitd_variant_t Info, StringRef Filename,
|
|
llvm::raw_ostream &OS) {
|
|
OS << "START RANGES\n";
|
|
sourcekitd_variant_t Res =
|
|
sourcekitd_variant_dictionary_get_value(Info, KeyResults);
|
|
for (unsigned i=0, e = sourcekitd_variant_array_get_count(Res); i != e; ++i) {
|
|
sourcekitd_variant_t Range = sourcekitd_variant_array_get_value(Res, i);
|
|
int64_t Offset = sourcekitd_variant_dictionary_get_int64(Range, KeyOffset);
|
|
int64_t Length = sourcekitd_variant_dictionary_get_int64(Range, KeyLength);
|
|
auto LineCol = resolveToLineCol(Offset, Filename);
|
|
OS << LineCol.first << ':' << LineCol.second << " - " << Length << '\n';
|
|
}
|
|
OS << "END RANGES\n";
|
|
}
|
|
|
|
static void prepareDemangleRequest(sourcekitd_object_t Req,
|
|
const TestOptions &Opts) {
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestDemangle);
|
|
if (Opts.SimplifiedDemangling)
|
|
sourcekitd_request_dictionary_set_int64(Req, KeySimplified, 1);
|
|
sourcekitd_object_t arr = sourcekitd_request_array_create(nullptr, 0);
|
|
sourcekitd_request_dictionary_set_value(Req, KeyNames, arr);
|
|
|
|
auto addName = [&](StringRef MangledName) {
|
|
sourcekitd_request_array_set_stringbuf(arr, SOURCEKITD_ARRAY_APPEND,
|
|
MangledName.data(), MangledName.size());
|
|
};
|
|
|
|
if (Opts.Inputs.empty()) {
|
|
auto input = llvm::MemoryBuffer::getSTDIN();
|
|
if (!input) {
|
|
llvm::errs() << input.getError().message() << '\n';
|
|
::exit(1);
|
|
}
|
|
llvm::StringRef inputContents = input.get()->getBuffer();
|
|
|
|
// This doesn't handle Unicode symbols, but maybe that's okay.
|
|
llvm::Regex maybeSymbol("(_T|" MANGLING_PREFIX_STR ")[_a-zA-Z0-9$]+");
|
|
llvm::SmallVector<llvm::StringRef, 1> matches;
|
|
while (maybeSymbol.match(inputContents, &matches)) {
|
|
addName(matches.front());
|
|
}
|
|
|
|
} else {
|
|
for (llvm::StringRef name : Opts.Inputs) {
|
|
addName(name);
|
|
}
|
|
}
|
|
|
|
sourcekitd_request_release(arr);
|
|
}
|
|
|
|
static void prepareMangleRequest(sourcekitd_object_t Req,
|
|
const TestOptions &Opts) {
|
|
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestMangleSimpleClass);
|
|
sourcekitd_object_t arr = sourcekitd_request_array_create(nullptr, 0);
|
|
sourcekitd_request_dictionary_set_value(Req, KeyNames, arr);
|
|
|
|
auto addPair = [&](StringRef ModuleName, StringRef ClassName) {
|
|
sourcekitd_object_t pair =
|
|
sourcekitd_request_dictionary_create(nullptr, nullptr, 0);
|
|
sourcekitd_request_dictionary_set_stringbuf(pair, KeyModuleName,
|
|
ModuleName.data(), ModuleName.size());
|
|
sourcekitd_request_dictionary_set_stringbuf(pair, KeyName,
|
|
ClassName.data(), ClassName.size());
|
|
sourcekitd_request_array_set_value(arr, SOURCEKITD_ARRAY_APPEND, pair);
|
|
sourcekitd_request_release(pair);
|
|
};
|
|
|
|
for (StringRef pair : Opts.Inputs) {
|
|
auto Idx = pair.find('.');
|
|
if (Idx == StringRef::npos) {
|
|
errs() << "expected pairs with format '<module>.<class name>'\n";
|
|
::exit(1);
|
|
}
|
|
StringRef moduleName = pair.substr(0, Idx);
|
|
StringRef className = pair.substr(Idx+1);
|
|
addPair(moduleName, className);
|
|
}
|
|
|
|
sourcekitd_request_release(arr);
|
|
}
|
|
|
|
static void printDemangleResults(sourcekitd_variant_t Info, raw_ostream &OS) {
|
|
OS << "START DEMANGLE\n";
|
|
sourcekitd_variant_t results =
|
|
sourcekitd_variant_dictionary_get_value(Info, KeyResults);
|
|
sourcekitd_variant_array_apply(results, ^bool(size_t index, sourcekitd_variant_t value) {
|
|
StringRef name = sourcekitd_variant_dictionary_get_string(value, KeyName);
|
|
if (name.empty())
|
|
OS << "<empty>";
|
|
else
|
|
OS << name;
|
|
OS << '\n';
|
|
return true;
|
|
});
|
|
OS << "END DEMANGLE\n";
|
|
}
|
|
|
|
static void printMangleResults(sourcekitd_variant_t Info, raw_ostream &OS) {
|
|
OS << "START MANGLE\n";
|
|
sourcekitd_variant_t results =
|
|
sourcekitd_variant_dictionary_get_value(Info, KeyResults);
|
|
sourcekitd_variant_array_apply(results, ^bool(size_t index, sourcekitd_variant_t value) {
|
|
StringRef name = sourcekitd_variant_dictionary_get_string(value, KeyName);
|
|
if (name.empty())
|
|
OS << "<empty>";
|
|
else
|
|
OS << name;
|
|
OS << '\n';
|
|
return true;
|
|
});
|
|
OS << "END MANGLE\n";
|
|
}
|
|
|
|
static void initializeRewriteBuffer(StringRef Input,
|
|
clang::RewriteBuffer &RewriteBuf) {
|
|
RewriteBuf.Initialize(Input);
|
|
StringRef CheckStr = "CHECK";
|
|
size_t Pos = 0;
|
|
while (true) {
|
|
Pos = Input.find(CheckStr, 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;
|
|
RewriteBuf.RemoveText(Pos, EndLine-Pos);
|
|
Pos = EndLine;
|
|
}
|
|
}
|
|
|
|
static std::vector<std::pair<unsigned, unsigned>>
|
|
getPlaceholderRanges(StringRef Source) {
|
|
const char *StartPtr = Source.data();
|
|
std::vector<std::pair<unsigned, unsigned>> Ranges;
|
|
while (true) {
|
|
size_t Pos = Source.find("<#");
|
|
if (Pos == StringRef::npos)
|
|
break;
|
|
unsigned OffsetStart = Source.data() + Pos - StartPtr;
|
|
Source = Source.substr(Pos+2);
|
|
Pos = Source.find("#>");
|
|
if (Pos == StringRef::npos)
|
|
break;
|
|
unsigned OffsetEnd = Source.data() + Pos + 2 - StartPtr;
|
|
Source = Source.substr(Pos+2);
|
|
Ranges.emplace_back(OffsetStart, OffsetEnd-OffsetStart);
|
|
}
|
|
return Ranges;
|
|
}
|
|
|
|
static void expandPlaceholders(llvm::MemoryBuffer *SourceBuf,
|
|
llvm::raw_ostream &OS) {
|
|
clang::RewriteBuffer RewriteBuf;
|
|
initializeRewriteBuffer(SourceBuf->getBuffer(), RewriteBuf);
|
|
auto Ranges = getPlaceholderRanges(SourceBuf->getBuffer());
|
|
for (auto Range : Ranges) {
|
|
unsigned Offset = Range.first;
|
|
unsigned Length = Range.second;
|
|
sourcekitd_object_t Exp = sourcekitd_request_dictionary_create(nullptr,
|
|
nullptr, 0);
|
|
sourcekitd_request_dictionary_set_uid(Exp, KeyRequest,
|
|
RequestEditorExpandPlaceholder);
|
|
auto SourceBufID = SourceBuf->getBufferIdentifier();
|
|
sourcekitd_request_dictionary_set_stringbuf(Exp, KeyName,
|
|
SourceBufID.data(),
|
|
SourceBufID.size());
|
|
sourcekitd_request_dictionary_set_string(Exp, KeySourceText, "");
|
|
sourcekitd_request_dictionary_set_int64(Exp, KeyOffset, Offset);
|
|
sourcekitd_request_dictionary_set_int64(Exp, KeyLength, Length);
|
|
|
|
sourcekitd_response_t Resp = sourcekitd_send_request_sync(Exp);
|
|
if (sourcekitd_response_is_error(Resp)) {
|
|
sourcekitd_response_description_dump(Resp);
|
|
exit(1);
|
|
}
|
|
sourcekitd_request_release(Exp);
|
|
sourcekitd_variant_t Info = sourcekitd_response_get_value(Resp);
|
|
const char *Text = sourcekitd_variant_dictionary_get_string(Info, KeySourceText);
|
|
if (!Text) {
|
|
sourcekitd_response_dispose(Resp);
|
|
continue;
|
|
}
|
|
unsigned EditOffset = sourcekitd_variant_dictionary_get_int64(Info, KeyOffset);
|
|
unsigned EditLength = sourcekitd_variant_dictionary_get_int64(Info, KeyLength);
|
|
RewriteBuf.ReplaceText(EditOffset, EditLength, Text);
|
|
sourcekitd_response_dispose(Resp);
|
|
}
|
|
|
|
RewriteBuf.write(OS);
|
|
}
|
|
|
|
static std::pair<unsigned, unsigned>
|
|
resolveToLineCol(unsigned Offset, StringRef Filename) {
|
|
return resolveToLineCol(Offset, getBufferForFilename(Filename));
|
|
}
|
|
|
|
static std::pair<unsigned, unsigned>
|
|
resolveToLineCol(unsigned Offset, llvm::MemoryBuffer *InputBuf) {
|
|
if (Offset >= InputBuf->getBufferSize()) {
|
|
llvm::errs() << "offset " << Offset << " for filename '"
|
|
<< InputBuf->getBufferIdentifier() << "' is too large\n";
|
|
exit(1);
|
|
}
|
|
return resolveToLineColFromBuf(Offset, InputBuf->getBufferStart());
|
|
}
|
|
|
|
static std::pair<unsigned, unsigned>
|
|
resolveToLineColFromBuf(unsigned Offset, const char *Ptr) {
|
|
const char *End = Ptr+Offset;
|
|
|
|
unsigned Line = 1;
|
|
const char *LineStart = Ptr;
|
|
for (; Ptr < End; ++Ptr) {
|
|
if (*Ptr == '\n') {
|
|
++Line;
|
|
LineStart = Ptr+1;
|
|
}
|
|
}
|
|
unsigned Col = Ptr-LineStart + 1;
|
|
|
|
return { Line, Col };
|
|
}
|
|
|
|
static unsigned resolveFromLineCol(unsigned Line, unsigned Col,
|
|
StringRef Filename) {
|
|
return resolveFromLineCol(Line, Col, getBufferForFilename(Filename));
|
|
}
|
|
|
|
static unsigned resolveFromLineCol(unsigned Line, unsigned Col,
|
|
llvm::MemoryBuffer *InputBuf) {
|
|
if (Line == 0 || Col == 0) {
|
|
llvm::errs() << "wrong pos format, line/col should start from 1\n";
|
|
exit(1);
|
|
}
|
|
|
|
const char *Ptr = InputBuf->getBufferStart();
|
|
const char *End = InputBuf->getBufferEnd();
|
|
const char *LineStart = Ptr;
|
|
for (; Ptr < End; ++Ptr) {
|
|
if (*Ptr == '\n') {
|
|
--Line;
|
|
if (Line == 0)
|
|
break;
|
|
LineStart = Ptr+1;
|
|
}
|
|
}
|
|
if (Line != 0) {
|
|
llvm::errs() << "wrong pos format, line too large\n";
|
|
exit(1);
|
|
}
|
|
Ptr = LineStart;
|
|
for (; Ptr < End; ++Ptr) {
|
|
--Col;
|
|
if (Col == 0)
|
|
return Ptr - InputBuf->getBufferStart();
|
|
if (*Ptr == '\n')
|
|
break;
|
|
}
|
|
|
|
llvm::errs() << "wrong pos format, column too large\n";
|
|
exit(1);
|
|
}
|
|
|
|
static llvm::StringMap<llvm::MemoryBuffer*> Buffers;
|
|
|
|
static llvm::MemoryBuffer *getBufferForFilename(StringRef Filename) {
|
|
auto It = Buffers.find(Filename);
|
|
if (It != Buffers.end())
|
|
return It->second;
|
|
|
|
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr =
|
|
llvm::MemoryBuffer::getFile(Filename);
|
|
if (!FileBufOrErr) {
|
|
llvm::errs() << "error opening input file '" << Filename << "' ("
|
|
<< FileBufOrErr.getError().message() << ")\n";
|
|
exit(1);
|
|
}
|
|
|
|
return Buffers[Filename] = FileBufOrErr.get().release();
|
|
}
|