blob: 75275e44f9c97ba3c76262e505230572aac9cd54 [file] [log] [blame]
//===--- 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 "llvm/Support/FormatVariadic.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 printSyntacticRenameEdits(sourcekitd_variant_t Info,
llvm::raw_ostream &OS);
static void printRenameRanges(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 void printStatistics(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"
#define REFACTORING(KIND, NAME, ID) static sourcekitd_uid_t Kind##Refactoring##KIND;
#include "swift/IDE/RefactoringKinds.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;
static sourcekitd_uid_t NoteTest;
static SourceKit::Semaphore noteSyncSemaphore(0);
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;
struct NotificationBuffer {
std::vector<sourcekitd_response_t> notes;
/// Add a notification to the buffer, taking ownership of it. Must be called
/// from the main queue.
void add(sourcekitd_response_t note) {
notes.push_back(note);
}
/// Call the given handler for all notifications currently buffered.
void handleNotifications(llvm::function_ref<void(sourcekitd_response_t)> f) {
// Notifications are handled on the main queue.
dispatch_sync(dispatch_get_main_queue(), ^{
for (auto note : notes) {
f(note);
sourcekitd_response_dispose(note);
}
notes.clear();
});
}
};
static NotificationBuffer notificationBuffer;
static void syncNotificationsWithService() {
// Send TestNotification request, then wait for the notification. This ensures
// that all notifications previously posted on the service side have been
// passed to our notification handler.
sourcekitd_object_t req = sourcekitd_request_dictionary_create(nullptr, nullptr, 0);
sourcekitd_request_dictionary_set_uid(req, KeyRequest, RequestTestNotification);
auto resp = sourcekitd_send_request_sync(req);
if (sourcekitd_response_is_error(resp)) {
sourcekitd_response_description_dump(resp);
exit(1);
}
sourcekitd_response_dispose(resp);
sourcekitd_request_release(req);
if (noteSyncSemaphore.wait(60 * 1000)) {
llvm::report_fatal_error("Test notification not received");
}
}
static void printBufferedNotifications(bool syncWithService = true) {
if (syncWithService) {
syncNotificationsWithService();
}
notificationBuffer.handleNotifications([](sourcekitd_response_t note) {
sourcekitd_response_description_dump_filedesc(note, STDOUT_FILENO);
});
}
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");
NoteTest = sourcekitd_uid_get_from_cstr("source.notification.test");
#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"
#define REFACTORING(KIND, NAME, ID) Kind##Refactoring##KIND = sourcekitd_uid_get_from_cstr("source.refactoring.kind."#ID);
#include "swift/IDE/RefactoringKinds.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;
}
}
printBufferedNotifications();
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;
}
namespace {
class PrintingTimer {
std::string desc;
llvm::sys::TimePoint<> start;
llvm::raw_ostream &OS;
public:
PrintingTimer(std::string desc, llvm::raw_ostream &OS = llvm::errs())
: desc(std::move(desc)), start(std::chrono::system_clock::now()), OS(OS) {
}
~PrintingTimer() {
std::chrono::duration<float, std::milli> delta(
std::chrono::system_clock::now() - start);
OS << desc << ": " << llvm::formatv("{0:ms+f3}", delta) << "\n";
}
};
}
/// Wrapper for sourcekitd_send_request_sync that handles printing options.
static sourcekitd_response_t sendRequestSync(sourcekitd_object_t req,
const TestOptions &opts) {
if (opts.PrintRequest)
sourcekitd_request_description_dump(req);
Optional<PrintingTimer> timer;
if (opts.timeRequest)
timer.emplace("request time");
return sourcekitd_send_request_sync(req);
}
static int handleJsonRequestPath(StringRef QueryPath, const TestOptions &Opts) {
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_response_t Resp = sendRequestSync(Req, Opts);
auto Error = sourcekitd_response_is_error(Resp);
if (Opts.PrintResponse) {
sourcekitd_response_description_dump_filedesc(Resp, STDOUT_FILENO);
}
return Error ? 1 : 0;
}
static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts);
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 (Optargc < Args.size())
Opts.CompilerArgs = Args.slice(Optargc+1);
assert(Opts.repeatRequest >= 1);
for (unsigned i = 0; i < Opts.repeatRequest; ++i) {
if (int ret = handleTestInvocation(Opts, InitOpts)) {
printBufferedNotifications(/*syncWithService=*/true);
return ret;
}
// We will sync with the service before exiting; don't do so here.
printBufferedNotifications(/*syncWithService=*/false);
}
return 0;
}
static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) {
if (!Opts.JsonRequestPath.empty())
return handleJsonRequestPath(Opts.JsonRequestPath, Opts);
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();
}
std::string SemaName = !Opts.Name.empty() ? Opts.Name : SourceFile;
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());
}
if (Opts.EndLine != 0) {
Opts.Length = resolveFromLineCol(Opts.EndLine, Opts.EndCol, SourceBuf.get()) -
ByteOffset;
}
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::EnableCompileNotifications: {
sourcekitd_request_dictionary_set_uid(Req, KeyRequest,
RequestEnableCompileNotifications);
int64_t value = 1;
for (auto &Opt : Opts.RequestOptions) {
auto KeyValue = StringRef(Opt).split('=');
if (KeyValue.first == "value") {
KeyValue.second.getAsInteger(0, value);
} else {
llvm::errs() << "unknown parameter '" << KeyValue.first
<< "' in -req-opts";
return 1;
}
}
sourcekitd_request_dictionary_set_int64(Req, KeyValue, value);
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, SemaName.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, SemaName.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, SemaName.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.CollectActionables) {
sourcekitd_request_dictionary_set_int64(Req, KeyRetrieveRefactorActions, 1);
}
if (Opts.Length) {
sourcekitd_request_dictionary_set_int64(Req, KeyLength, Opts.Length);
}
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;
}
#define SEMANTIC_REFACTORING(KIND, NAME, ID) case SourceKitRequest::KIND: \
{ \
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestSemanticRefactoring); \
sourcekitd_request_dictionary_set_uid(Req, KeyActionUID, KindRefactoring##KIND); \
sourcekitd_request_dictionary_set_string(Req, KeyName, Opts.Name.c_str()); \
sourcekitd_request_dictionary_set_int64(Req, KeyLine, Opts.Line); \
sourcekitd_request_dictionary_set_int64(Req, KeyColumn, Opts.Col); \
sourcekitd_request_dictionary_set_int64(Req, KeyLength, Opts.Length); \
break; \
}
#include "swift/IDE/RefactoringKinds.def"
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 (!ArgPieces.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, SemaName.c_str());
sourcekitd_request_dictionary_set_int64(Req, KeyEnableSyntaxMap, true);
sourcekitd_request_dictionary_set_int64(Req, KeyEnableStructure, false);
sourcekitd_request_dictionary_set_uid(Req, KeySyntaxTreeTransferMode,
KindSyntaxTreeOff);
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, SemaName.c_str());
sourcekitd_request_dictionary_set_int64(Req, KeyEnableSyntaxMap, false);
sourcekitd_request_dictionary_set_int64(Req, KeyEnableStructure, true);
sourcekitd_request_dictionary_set_uid(Req, KeySyntaxTreeTransferMode,
KindSyntaxTreeOff);
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, SemaName.c_str());
sourcekitd_request_dictionary_set_int64(Req, KeyEnableSyntaxMap, false);
sourcekitd_request_dictionary_set_int64(Req, KeyEnableStructure, false);
sourcekitd_request_dictionary_set_uid(Req, KeySyntaxTreeTransferMode,
KindSyntaxTreeOff);
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, SemaName.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::SyntaxTree:
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestEditorOpen);
sourcekitd_request_dictionary_set_string(Req, KeyName, SemaName.c_str());
sourcekitd_request_dictionary_set_int64(Req, KeyEnableSyntaxMap, false);
sourcekitd_request_dictionary_set_int64(Req, KeyEnableStructure, false);
sourcekitd_request_dictionary_set_uid(Req, KeySyntaxTreeTransferMode,
KindSyntaxTreeFull);
sourcekitd_request_dictionary_set_int64(Req, KeySyntacticOnly, true);
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, SemaName.c_str());
break;
case SourceKitRequest::Open:
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestEditorOpen);
sourcekitd_request_dictionary_set_string(Req, KeyName, SemaName.c_str());
break;
case SourceKitRequest::Close:
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestEditorClose);
sourcekitd_request_dictionary_set_string(Req, KeyName, SemaName.c_str());
break;
case SourceKitRequest::Edit:
sourcekitd_request_dictionary_set_uid(Req, KeyRequest,
RequestEditorReplaceText);
sourcekitd_request_dictionary_set_string(Req, KeyName, SemaName.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.UsingSwiftArgs)
sourcekitd_request_dictionary_set_int64(Req, KeyUsingSwiftArgs, 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;
case SourceKitRequest::FindLocalRenameRanges:
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestFindLocalRenameRanges);
sourcekitd_request_dictionary_set_int64(Req, KeyLine, Opts.Line);
sourcekitd_request_dictionary_set_int64(Req, KeyColumn, Opts.Col);
sourcekitd_request_dictionary_set_int64(Req, KeyLength, Opts.Length);
break;
case SourceKitRequest::SyntacticRename:
case SourceKitRequest::FindRenameRanges: {
if (Opts.Request == SourceKitRequest::SyntacticRename) {
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestSyntacticRename);
} else {
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestFindRenameRanges);
}
if (Opts.RenameSpecPath.empty()) {
llvm::errs() << "Missing '-rename-spec <file path>'\n";
return 1;
}
auto Buffer = getBufferForFilename(Opts.RenameSpecPath)->getBuffer();
char *Err = nullptr;
auto RenameSpec = sourcekitd_request_create_from_yaml(Buffer.data(), &Err);
if (!RenameSpec) {
assert(Err);
llvm::errs() << Err;
free(Err);
return 1;
}
sourcekitd_request_dictionary_set_value(Req, KeyRenameLocations, RenameSpec);
break;
}
case SourceKitRequest::Statistics:
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestStatistics);
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.empty()) {
if (Opts.PassVersionAsString) {
sourcekitd_request_dictionary_set_string(Req, KeySwiftVersion,
Opts.SwiftVersion.c_str());
} else {
unsigned ver;
if (StringRef(Opts.SwiftVersion).getAsInteger(10, ver)) {
llvm::errs() << "error: expected integer for 'swift-version'\n";
return true;
}
sourcekitd_request_dictionary_set_int64(Req, KeySwiftVersion, ver);
}
}
if (!Opts.isAsyncRequest) {
sourcekitd_response_t Resp = sendRequestSync(Req, Opts);
sourcekitd_request_release(Req);
return handleResponse(Resp, Opts, SemaName, std::move(SourceBuf),
&InitOpts)
? 1
: 0;
} else {
#if SOURCEKITD_HAS_BLOCKS
AsyncResponseInfo info;
info.options = Opts;
info.sourceFilename = std::move(SemaName);
info.sourceBuffer = std::move(SourceBuf);
unsigned respIndex = asyncResponses.size();
asyncResponses.push_back(std::move(info));
if (Opts.PrintRequest)
sourcekitd_request_description_dump(Req);
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.PrintResponse) {
// Nothing.
} 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::EnableCompileNotifications:
// Ignore the response. If it was an error it is handled above.
break;
case SourceKitRequest::Open:
getSemanticInfo(Info, SourceFile);
KeepResponseAlive = true;
break;
case SourceKitRequest::Edit:
if (Opts.Length == 0 && Opts.ReplaceText->empty()) {
// Length=0, replace="" is a nop and will not trigger sema.
sourcekitd_response_description_dump_filedesc(Resp, STDOUT_FILENO);
} else {
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::Close:
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::SyntaxTree: {
// Print only the serialized syntax tree.
llvm::outs() << sourcekitd_variant_dictionary_get_string(
sourcekitd_response_get_value(Resp), KeySerializedSyntaxTree);
llvm::outs() << '\n';
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 = sendRequestSync(EdReq, Opts);
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 = sendRequestSync(Fmt, Opts);
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;
#define SEMANTIC_REFACTORING(KIND, NAME, ID) case SourceKitRequest::KIND:
#include "swift/IDE/RefactoringKinds.def"
case SourceKitRequest::SyntacticRename:
printSyntacticRenameEdits(Info, llvm::outs());
break;
case SourceKitRequest::FindRenameRanges:
case SourceKitRequest::FindLocalRenameRanges:
printRenameRanges(Info, llvm::outs());
break;
case SourceKitRequest::Statistics:
printStatistics(Info, llvm::outs());
}
}
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);
if (note == NoteDocUpdate) {
semaName = sourcekitd_variant_dictionary_get_string(payload, KeyName);
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();
} else if (note == NoteTest) {
noteSyncSemaphore.signal();
} else {
notificationBuffer.add(resp);
}
}
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));
}
struct ActionInfo {
const char* KindUID;
const char* KindName;
const char* UnavailReason;
};
std::vector<ActionInfo> AvailableActions;
sourcekitd_variant_t ActionsObj =
sourcekitd_variant_dictionary_get_value(Info, KeyRefactorActions);
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_uid_get_string_ptr(sourcekitd_variant_dictionary_get_uid(Entry,
KeyActionUID)),
sourcekitd_variant_dictionary_get_string(Entry, KeyActionName),
sourcekitd_variant_dictionary_get_string(Entry, KeyActionUnavailableReason)
});
}
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.KindUID << '\n';
OS << Action.KindName << '\n';
if (Action.UnavailReason) {
OS << Action.UnavailReason << '\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 StringRef getLineColRange(StringRef Text, int64_t StartLine,
int64_t StartCol, int64_t EndLine,
int64_t EndCol) {
unsigned Line = 1;
size_t Length = 0;
while (Line < StartLine) {
Text = Text.split('\n').second;
++Line;
}
Text = Text.drop_front(StartCol - 1);
if (StartLine == EndLine)
return Text.substr(0, EndCol - StartCol);
while(Line < EndLine) {
Length = Text.find('\n', Length) + 1;
++Line;
}
Length += EndCol - 1;
return Text.substr(0, Length);
}
static void printSyntacticRenameEdits(sourcekitd_variant_t Info,
llvm::raw_ostream &OS) {
sourcekitd_variant_t CategorizedEdits =
sourcekitd_variant_dictionary_get_value(Info, KeyCategorizedEdits);
for (unsigned i = 0, e = sourcekitd_variant_array_get_count(CategorizedEdits);
i != e; ++i) {
sourcekitd_variant_t
Categorized = sourcekitd_variant_array_get_value(CategorizedEdits, i);
sourcekitd_uid_t
Category = sourcekitd_variant_dictionary_get_uid(Categorized, KeyCategory);
OS << sourcekitd_uid_get_string_ptr(Category) << ":\n";
sourcekitd_variant_t Edits =
sourcekitd_variant_dictionary_get_value(Categorized, KeyEdits);
for(unsigned j = 0, je = sourcekitd_variant_array_get_count(Edits);
j != je; ++j) {
OS << " "; // indent
sourcekitd_variant_t Edit = sourcekitd_variant_array_get_value(Edits, j);
int64_t Line = sourcekitd_variant_dictionary_get_int64(Edit, KeyLine);
int64_t Column = sourcekitd_variant_dictionary_get_int64(Edit, KeyColumn);
int64_t EndLine = sourcekitd_variant_dictionary_get_int64(Edit, KeyEndLine);
int64_t EndColumn = sourcekitd_variant_dictionary_get_int64(Edit, KeyEndColumn);
OS << Line << ':' << Column << '-' << EndLine << ':' << EndColumn << " \"";
StringRef Text(sourcekitd_variant_dictionary_get_string(Edit, KeyText));
OS << Text << "\"\n";
sourcekitd_variant_t NoteRanges =
sourcekitd_variant_dictionary_get_value(Edit, KeyRangesWorthNote);
if (unsigned e = sourcekitd_variant_array_get_count(NoteRanges)) {
for (unsigned i = 0; i != e; ++i) {
OS << " <note>";
sourcekitd_variant_t Note =
sourcekitd_variant_array_get_value(NoteRanges, i);
int64_t Line = sourcekitd_variant_dictionary_get_int64(Note, KeyLine);
int64_t Column = sourcekitd_variant_dictionary_get_int64(Note, KeyColumn);
int64_t EndLine = sourcekitd_variant_dictionary_get_int64(Note, KeyEndLine);
int64_t EndColumn = sourcekitd_variant_dictionary_get_int64(Note, KeyEndColumn);
auto index = sourcekitd_variant_dictionary_get_value(Note, KeyArgIndex);
sourcekitd_uid_t Kind = sourcekitd_variant_dictionary_get_uid(Note,
KeyKind);
StringRef NoteText = getLineColRange(Text, Line, Column, EndLine, EndColumn);
OS << sourcekitd_uid_get_string_ptr(Kind) << " ";
OS << Line << ":" << Column << "-" << EndLine << ":" << EndColumn;
if (sourcekitd_variant_get_type(index) != SOURCEKITD_VARIANT_TYPE_NULL) {
OS << " arg-index=" << sourcekitd_variant_int64_get_value(index);
}
OS << " \"" << NoteText << "\"";
OS << "</note>\n";
}
}
}
}
}
static void printRenameRanges(sourcekitd_variant_t Info,
llvm::raw_ostream &OS) {
sourcekitd_variant_t CategorizedRanges =
sourcekitd_variant_dictionary_get_value(Info, KeyCategorizedRanges);
sourcekitd_variant_array_apply(CategorizedRanges, ^bool(
size_t i,
sourcekitd_variant_t Categorized) {
sourcekitd_uid_t Category =
sourcekitd_variant_dictionary_get_uid(Categorized, KeyCategory);
OS << sourcekitd_uid_get_string_ptr(Category) << ":\n";
sourcekitd_variant_t Ranges =
sourcekitd_variant_dictionary_get_value(Categorized, KeyRanges);
sourcekitd_variant_array_apply(Ranges, ^bool(size_t j,
sourcekitd_variant_t Range) {
OS << " "; // indent
int64_t Line = sourcekitd_variant_dictionary_get_int64(Range, KeyLine);
int64_t Column =
sourcekitd_variant_dictionary_get_int64(Range, KeyColumn);
int64_t EndLine =
sourcekitd_variant_dictionary_get_int64(Range, KeyEndLine);
int64_t EndColumn =
sourcekitd_variant_dictionary_get_int64(Range, KeyEndColumn);
OS << Line << ':' << Column << '-' << EndLine << ':' << EndColumn << " ";
auto Kind = sourcekitd_variant_dictionary_get_uid(Range, KeyKind);
OS << sourcekitd_uid_get_string_ptr(Kind);
auto index = sourcekitd_variant_dictionary_get_value(Range, KeyArgIndex);
if (sourcekitd_variant_get_type(index) != SOURCEKITD_VARIANT_TYPE_NULL) {
OS << " arg-index=" << sourcekitd_variant_int64_get_value(index);
}
OS << "\n";
return true;
});
return true;
});
}
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.
// Also accept the future mangling prefix.
llvm::Regex maybeSymbol("(_T|_?\\$[Ss])[_a-zA-Z0-9$.]+");
llvm::SmallVector<llvm::StringRef, 1> matches;
while (maybeSymbol.match(inputContents, &matches)) {
addName(matches.front());
auto offset = matches.front().data() - inputContents.data();
inputContents = inputContents.substr(offset + matches.front().size());
}
} 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 printStatistics(sourcekitd_variant_t Info, raw_ostream &OS) {
sourcekitd_variant_t results =
sourcekitd_variant_dictionary_get_value(Info, KeyResults);
sourcekitd_variant_array_apply(results, ^bool(size_t index, sourcekitd_variant_t value) {
auto uid = sourcekitd_variant_dictionary_get_uid(value, KeyKind);
auto desc = sourcekitd_variant_dictionary_get_string(value, KeyDescription);
auto statValue = sourcekitd_variant_dictionary_get_int64(value, KeyValue);
OS << statValue << "\t" << desc << "\t- " << sourcekitd_uid_get_string_ptr(uid) << "\n";
return true;
});
}
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();
}