Files
swift-mirror/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp
Alastair Houghton 18496c5626 [Backtracing] Remove support for implicit import of _Backtracing.
We're going to rename the module to Runtime, and it isn't going to be an
implicit import, so we don't need any of this.

rdar://124913332
2025-01-17 10:09:36 +00:00

2839 lines
106 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 "SourceKit/Support/Concurrency.h"
#include "TestOptions.h"
#include "swift/Demangling/ManglingMacros.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ConvertUTF.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/Regex.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/thread.h"
#include <fstream>
#include <optional>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <sys/param.h>
#include <unistd.h>
#elif defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#endif
// FIXME: Platform compatibility.
#include <dispatch/dispatch.h>
using namespace llvm;
using namespace sourcekitd_test;
#if defined(_WIN32)
namespace {
int STDOUT_FILENO = _fileno(stdout);
}
#endif
static int handleTestInvocation(ArrayRef<const char *> Args, TestOptions &InitOpts,
bool IsFirstInvocation);
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,
const llvm::StringMap<TestOptions::VFSFile> &VFSFiles,
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 printExpressionType(sourcekitd_variant_t Info, llvm::raw_ostream &OS);
static void printVariableType(sourcekitd_variant_t Info,
llvm::MemoryBuffer *SourceBuf,
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,
const llvm::StringMap<TestOptions::VFSFile> &VFSFiles,
llvm::raw_ostream &OS);
static void printActiveRegions(sourcekitd_variant_t Info, StringRef Filename,
const llvm::StringMap<TestOptions::VFSFile> &VFSFiles,
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,
const llvm::StringMap<TestOptions::VFSFile> &VFSFiles);
static unsigned resolveFromLineCol(unsigned Line, unsigned Col,
llvm::MemoryBuffer *InputBuf);
static std::pair<unsigned, unsigned>
resolveToLineCol(unsigned Offset, StringRef Filename,
const llvm::StringMap<TestOptions::VFSFile> &VFSFiles,
bool ExitOnError = true);
static std::pair<unsigned, unsigned>
resolveToLineCol(unsigned Offset, llvm::MemoryBuffer *InputBuf,
bool ExitOnError = true);
static std::pair<unsigned, unsigned> resolveToLineColFromBuf(unsigned Offset,
const char *Buf);
static llvm::MemoryBuffer *
getBufferForFilename(StringRef Filename,
const llvm::StringMap<TestOptions::VFSFile> &VFSFiles,
bool ExitOnError = true);
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/Refactoring/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;
sourcekitd_request_handle_t requestHandle;
};
} // 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 printRawResponse(sourcekitd_response_t resp) {
llvm::outs().flush();
sourcekitd_response_description_dump_filedesc(resp, STDOUT_FILENO);
}
static void printRawVariant(sourcekitd_variant_t obj) {
llvm::outs().flush();
sourcekitd_variant_description_dump_filedesc(obj, STDOUT_FILENO);
}
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) { printRawResponse(note); });
}
struct skt_args {
int argc;
const char **argv;
int ret;
};
static void skt_main(skt_args *args);
int main(int argc, const char **argv) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
skt_args args = {argc, argv, 0};
llvm::thread thread(llvm::thread::DefaultStackSize,
skt_main, &args);
thread.join();
exit(args.ret);
});
dispatch_main();
}
static void skt_main(skt_args *args) {
int argc = args->argc;
const char **argv = args->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/Refactoring/RefactoringKinds.def"
// A test invocation may initialize the options to be used for subsequent
// invocations.
TestOptions InitOpts;
auto Args = llvm::ArrayRef(argv + 1, argc - 1);
bool firstInvocation = true;
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,
firstInvocation)) {
sourcekitd_shutdown();
args->ret = ret;
return;
}
Args = Args.slice(i + 1);
firstInvocation = false;
}
if (int ret = handleTestInvocation(Args, InitOpts, firstInvocation)) {
sourcekitd_shutdown();
args->ret = ret;
return;
}
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();
args->ret = 1;
return;
}
}
printBufferedNotifications();
sourcekitd_shutdown();
args->ret = 0;
return;
}
static inline std::string getInterfaceGenDocumentName() {
// "Absolute path" on all platforms since handleTestInvocation will attempt to make this absolute
llvm::SmallString<64> path = llvm::StringRef("/<interface-gen>");
llvm::sys::fs::make_absolute(path);
llvm::sys::path::native(path);
return std::string(path.str());
}
static int printAnnotations();
static int printDiags();
static void getSemanticInfo(sourcekitd_variant_t Info, StringRef Filename);
static std::optional<int64_t> getReqOptValueAsInt(StringRef Value) {
if (Value.equals_insensitive("true"))
return 1;
if (Value.equals_insensitive("false"))
return 0;
int64_t Ret;
if (Value.find_first_not_of("-0123456789") != StringRef::npos ||
Value.getAsInteger(0, Ret)) {
return std::nullopt;
}
return Ret;
}
static std::optional<sourcekitd_uid_t> getReqOptValueAsUID(StringRef Value) {
if (!Value.starts_with("uid:"))
return std::nullopt;
Value = Value.drop_front(4);
return sourcekitd_uid_get_from_buf(Value.data(), Value.size());
}
static std::optional<sourcekitd_object_t>
getReqOptValueAsArray(StringRef Value) {
if (!Value.starts_with("[") || !Value.ends_with("]"))
return std::nullopt;
SmallVector<StringRef, 4> Elements;
Value.drop_front().drop_back().split(Elements, ';');
auto Array = sourcekitd_request_array_create(nullptr, 0);
for (auto &Elem : Elements) {
if (auto Val = getReqOptValueAsInt(Elem)) {
sourcekitd_request_array_set_int64(Array, SOURCEKITD_ARRAY_APPEND, *Val);
} else if (auto Val = getReqOptValueAsUID(Elem)) {
sourcekitd_request_array_set_uid(Array, SOURCEKITD_ARRAY_APPEND, *Val);
} else if (auto Val = getReqOptValueAsArray(Elem)) {
sourcekitd_request_array_set_value(Array, SOURCEKITD_ARRAY_APPEND, *Val);
} else {
sourcekitd_request_array_set_stringbuf(Array, SOURCEKITD_ARRAY_APPEND,
Elem.data(), Elem.size());
}
}
return Array;
}
static void addRequestOptionsDirect(sourcekitd_object_t Req, TestOptions &Opts,
StringRef prefix = "key.") {
if (Opts.RequestOptions.empty())
return;
for (auto &Opt: Opts.RequestOptions) {
auto KeyValue = StringRef(Opt).split('=');
std::string KeyStr(prefix.str());
KeyStr.append(KeyValue.first.str());
sourcekitd_uid_t Key = sourcekitd_uid_get_from_cstr(KeyStr.c_str());
StringRef RawValue = KeyValue.second;
if (auto Val = getReqOptValueAsInt(RawValue)) {
sourcekitd_request_dictionary_set_int64(Req, Key, *Val);
} else if (auto Val = getReqOptValueAsUID(RawValue)) {
sourcekitd_request_dictionary_set_uid(Req, Key, *Val);
} else if (auto Val = getReqOptValueAsArray(RawValue)) {
sourcekitd_request_dictionary_set_value(Req, Key, *Val);
sourcekitd_request_release(*Val);
} else {
sourcekitd_request_dictionary_set_stringbuf(Req, Key, RawValue.data(), RawValue.size());
}
}
}
static void addRequestOptions(sourcekitd_object_t Req, TestOptions &Opts,
sourcekitd_uid_t Key, StringRef prefix = "key.") {
if (Opts.RequestOptions.empty())
return;
sourcekitd_object_t CCOpts =
sourcekitd_request_dictionary_create(nullptr, nullptr, 0);
addRequestOptionsDirect(CCOpts, Opts, prefix);
sourcekitd_request_dictionary_set_value(Req, Key, CCOpts);
sourcekitd_request_release(CCOpts);
}
static bool readPopularAPIList(StringRef filename,
std::vector<std::string> &result) {
std::ifstream in(filename.str());
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;
}
/// Read '-req-opts' for syntactic macro expansion request and apply it to 'req'
/// object.
/// The format of the argument is '-req-opts={line}:{column}:{path}'
/// where {path} is a path to a JSON file that has macro roles and definition.
/// {line} and {column} is resolved to 'offset' using \p inputBuf .
static bool setSyntacticMacroExpansions(sourcekitd_object_t req,
TestOptions &opts,
llvm::MemoryBuffer *inputBuf) {
SmallVector<sourcekitd_object_t, 4> expansions;
for (std::string &opt : opts.RequestOptions) {
SmallVector<StringRef, 3> args;
StringRef(opt).split(args, ":", /*maxSplits=*/2);
unsigned line, column;
if (args.size() != 3 || args[0].getAsInteger(10, line) ||
args[1].getAsInteger(10, column)) {
llvm::errs() << "-req-opts should be {line}:{column}:{json-path}";
return true;
}
unsigned offset = resolveFromLineCol(line, column, inputBuf);
auto Buffer = getBufferForFilename(args[2], opts.VFSFiles)->getBuffer();
char *Err = nullptr;
auto expansion = sourcekitd_request_create_from_yaml(Buffer.data(), &Err);
if (!expansion) {
assert(Err);
llvm::errs() << Err;
free(Err);
return true;
}
sourcekitd_request_dictionary_set_int64(expansion, KeyOffset,
int64_t(offset));
expansions.push_back(expansion);
}
sourcekitd_request_dictionary_set_value(
req, KeyExpansions,
sourcekitd_request_array_create(expansions.data(), expansions.size()));
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);
std::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, Opts.VFSFiles)->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) {
printRawResponse(Resp);
}
return Error ? 1 : 0;
}
static int performShellExecution(ArrayRef<const char *> Args) {
llvm::outs().flush();
llvm::errs().flush();
auto Program = llvm::sys::findProgramByName(Args[0]);
if (std::error_code ec = Program.getError()) {
llvm::errs() << "command not found: " << Args[0] << "\n";
return ec.value();
}
SmallVector<StringRef, 8> execArgs(Args.begin(), Args.end());
return llvm::sys::ExecuteAndWait(*Program, execArgs);
}
static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts);
static int handleTestInvocation(ArrayRef<const char *> Args,
TestOptions &InitOpts, bool firstInvocation) {
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.ModuleCachePath.empty())
InitOpts.ModuleCachePath = Opts.ModuleCachePath;
if (Optargc < Args.size())
Opts.CompilerArgs = Args.slice(Optargc+1);
if (Opts.ShellExecution)
return performShellExecution(Opts.CompilerArgs);
if (!Opts.CancelRequest.empty()) {
for (auto &asyncResponse : asyncResponses) {
if (asyncResponse.options.RequestId == Opts.CancelRequest) {
sourcekitd_cancel_request(asyncResponse.requestHandle);
}
}
return 0;
}
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 void setRefactoringFields(sourcekitd_object_t &Req, TestOptions Opts,
sourcekitd_uid_t RefactoringKind,
llvm::MemoryBuffer *SourceBuf) {
unsigned line = Opts.Line;
unsigned col = Opts.Col;
if (SourceBuf && Opts.Offset && line == 0) {
std::tie(line, col) = resolveToLineCol(*Opts.Offset, SourceBuf);
}
sourcekitd_request_dictionary_set_uid(Req, KeyRequest,
RequestSemanticRefactoring);
sourcekitd_request_dictionary_set_uid(Req, KeyActionUID, RefactoringKind);
sourcekitd_request_dictionary_set_string(Req, KeyName, Opts.Name.c_str());
sourcekitd_request_dictionary_set_int64(Req, KeyLine, line);
sourcekitd_request_dictionary_set_int64(Req, KeyColumn, col);
sourcekitd_request_dictionary_set_int64(Req, KeyLength, Opts.Length);
}
/// Returns the number of instructions executed by the SourceKit process since
/// its launch. If SourceKit is running in-process this is the instruction count
/// of the current process. If it's running out-of process it is the instruction
/// count of the XPC process.
int64_t getSourceKitInstructionCount() {
sourcekitd_object_t Req =
sourcekitd_request_dictionary_create(nullptr, nullptr, 0);
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestStatistics);
sourcekitd_response_t Resp = sourcekitd_send_request_sync(Req);
sourcekitd_variant_t Info = sourcekitd_response_get_value(Resp);
sourcekitd_variant_t Results =
sourcekitd_variant_dictionary_get_value(Info, KeyResults);
__block size_t InstructionCount = 0;
sourcekitd_variant_array_apply(
Results, ^bool(size_t index, sourcekitd_variant_t value) {
auto UID = sourcekitd_variant_dictionary_get_uid(value, KeyKind);
if (UID == KindStatInstructionCount) {
InstructionCount =
sourcekitd_variant_dictionary_get_int64(value, KeyValue);
return false;
}
return true;
});
return InstructionCount;
}
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();
if (!Opts.SourceFile.empty() && Opts.PrimaryFile.empty()) {
// Only canonicalize if primary file isn't set (since we expect sourcefile
// to be a relative name otherwise).
llvm::SmallString<64> AbsSourceFile;
AbsSourceFile += Opts.SourceFile;
llvm::sys::fs::make_absolute(AbsSourceFile);
llvm::sys::path::native(AbsSourceFile);
Opts.SourceFile = std::string(AbsSourceFile.str());
if (Opts.PrimaryFile.empty()) {
Opts.PrimaryFile = Opts.SourceFile;
}
}
// FIXME: It's super confusing that we use name for the file sometimes.
std::string SemaName = !Opts.Name.empty() ? Opts.Name : Opts.SourceFile;
if (!Opts.TextInputFile.empty()) {
auto Buf = getBufferForFilename(Opts.TextInputFile, Opts.VFSFiles);
Opts.SourceText = Buf->getBuffer().str();
}
std::unique_ptr<llvm::MemoryBuffer> SourceBuf;
if (Opts.SourceText.has_value()) {
SourceBuf = llvm::MemoryBuffer::getMemBuffer(*Opts.SourceText, Opts.SourceFile);
} else if (!Opts.SourceFile.empty() && Opts.PrimaryFile == Opts.SourceFile) {
// If we have a primary file, that implies that the source file is actually
// a generated buffer. In that case we can't get the text.
SourceBuf = llvm::MemoryBuffer::getMemBuffer(
getBufferForFilename(Opts.SourceFile, Opts.VFSFiles)->getBuffer(),
Opts.SourceFile);
}
unsigned ByteOffset = Opts.Offset.value_or(0);
if (SourceBuf) {
// Fill in offset/length if we're able to, ie. we have access to the
// underlying sourcefile. Ideally we would error here if any of these
// are needed but not (or cannot) be set.
if (!Opts.Offset && Opts.Line > 0) {
ByteOffset = resolveFromLineCol(Opts.Line, Opts.Col, SourceBuf.get());
}
if (Opts.Length == 0 && Opts.EndLine > 0) {
Opts.Length =
resolveFromLineCol(Opts.EndLine, Opts.EndCol, SourceBuf.get()) -
ByteOffset;
}
}
bool compilerArgsAreClang = false;
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::GlobalConfiguration:
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestGlobalConfiguration);
for (auto &Opt : Opts.RequestOptions) {
auto KeyValue = StringRef(Opt).split('=');
std::string KeyStr("key.");
KeyStr.append(KeyValue.first.str());
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(Req, Key, Value);
}
break;
case SourceKitRequest::ProtocolVersion:
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestProtocolVersion);
break;
case SourceKitRequest::CompilerVersion:
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestCompilerVersion);
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);
sourcekitd_request_dictionary_set_string(Req, KeyName, SemaName.c_str());
// Default to sort by name.
Opts.RequestOptions.insert(Opts.RequestOptions.begin(), "sort.byname=1");
addRequestOptions(Req, Opts, KeyCodeCompleteOptions, "key.codecomplete.");
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());
addRequestOptions(Req, Opts, KeyCodeCompleteOptions, "key.codecomplete.");
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());
addRequestOptions(Req, Opts, KeyCodeCompleteOptions, "key.codecomplete.");
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::TypeContextInfo:
sourcekitd_request_dictionary_set_uid(Req, KeyRequest,
RequestTypeContextInfo);
sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset);
addRequestOptions(Req, Opts, KeyTypeContextInfoOptions,
"key.typecontextinfo.");
break;
case SourceKitRequest::ConformingMethodList:
sourcekitd_request_dictionary_set_uid(Req, KeyRequest,
RequestConformingMethodList);
sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset);
addRequestOptionsDirect(Req, Opts);
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);
}
addRequestOptionsDirect(Req, Opts);
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,
Opts.SourceFile, Opts.VFSFiles);
Length = EndOff - ByteOffset;
}
sourcekitd_request_dictionary_set_int64(Req, KeyLength, Length);
break;
}
case SourceKitRequest::CollectExpressionType: {
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestCollectExpressionType);
addRequestOptionsDirect(Req, Opts);
break;
}
case SourceKitRequest::CollectVariableType: {
sourcekitd_request_dictionary_set_uid(Req, KeyRequest,
RequestCollectVariableType);
if (Opts.Length) {
sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset);
sourcekitd_request_dictionary_set_int64(Req, KeyLength, Opts.Length);
}
addRequestOptionsDirect(Req, Opts);
break;
}
#define SEMANTIC_REFACTORING(KIND, NAME, ID) \
case SourceKitRequest::KIND: \
setRefactoringFields(Req, Opts, KindRefactoring##KIND, SourceBuf.get()); \
break;
#include "swift/Refactoring/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.str();
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.str();
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::ActiveRegions:
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestActiveRegions);
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_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_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_int64(Req, KeySyntacticOnly, !Opts.UsedSema);
break;
case SourceKitRequest::ExpandPlaceholder:
if (Opts.Length) {
// Single placeholder by location.
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestEditorExpandPlaceholder);
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);
} else {
if (ByteOffset) {
llvm::errs() << "Missing '-length <number>'\n";
return 1;
}
// Expand all placeholders.
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::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());
addRequestOptionsDirect(Req, Opts);
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.value().c_str());
addRequestOptionsDirect(Req, Opts);
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);
else
compilerArgsAreClang = true;
sourcekitd_request_dictionary_set_uid(Req, KeyRequest,
RequestEditorOpenHeaderInterface);
}
sourcekitd_request_dictionary_set_string(Req, KeyName, getInterfaceGenDocumentName().c_str());
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());
// add possible EnableDeclarations in particular
addRequestOptionsDirect(Req, Opts);
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::FindRenameRanges: {
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, Opts.VFSFiles)->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;
case SourceKitRequest::DependencyUpdated:
sourcekitd_request_dictionary_set_uid(Req, KeyRequest,
RequestDependencyUpdated);
break;
case SourceKitRequest::Diagnostics:
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestDiagnostics);
break;
case SourceKitRequest::SemanticTokens:
sourcekitd_request_dictionary_set_uid(Req, KeyRequest,
RequestSemanticTokens);
break;
case SourceKitRequest::Compile:
sourcekitd_request_dictionary_set_string(Req, KeyName, SemaName.c_str());
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestCompile);
break;
case SourceKitRequest::CompileClose:
sourcekitd_request_dictionary_set_string(Req, KeyName, SemaName.c_str());
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestCompileClose);
break;
case SourceKitRequest::SyntacticMacroExpansion:
sourcekitd_request_dictionary_set_uid(Req, KeyRequest,
RequestSyntacticMacroExpansion);
setSyntacticMacroExpansions(Req, Opts, SourceBuf.get());
break;
case SourceKitRequest::IndexToStore:
sourcekitd_request_dictionary_set_string(Req, KeyName, SemaName.c_str());
sourcekitd_request_dictionary_set_string(Req, KeyIndexStorePath, Opts.IndexStorePath.c_str());
sourcekitd_request_dictionary_set_string(Req, KeyIndexUnitOutputPath, Opts.IndexUnitOutputPath.c_str());
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestIndexToStore);
addRequestOptionsDirect(Req, Opts);
break;
}
if (!Opts.SourceFile.empty()) {
if (Opts.PassAsSourceText) {
auto Buf = getBufferForFilename(Opts.SourceFile, Opts.VFSFiles);
sourcekitd_request_dictionary_set_string(Req, KeySourceText,
Buf->getBufferStart());
}
sourcekitd_request_dictionary_set_string(Req, KeySourceFile,
Opts.SourceFile.c_str());
}
if (!Opts.PrimaryFile.empty()) {
sourcekitd_request_dictionary_set_string(Req, KeyPrimaryFile,
Opts.PrimaryFile.c_str());
}
if (Opts.SourceText) {
sourcekitd_request_dictionary_set_string(Req, KeySourceText,
Opts.SourceText->c_str());
sourcekitd_request_dictionary_set_string(Req, KeySourceFile,
SemaName.c_str());
}
if (!Opts.CompilerArgs.empty() ||
!Opts.ModuleCachePath.empty() ||
Opts.DisableImplicitConcurrencyModuleImport) {
sourcekitd_object_t Args = sourcekitd_request_array_create(nullptr, 0);
if (!Opts.ModuleCachePath.empty()) {
if (compilerArgsAreClang) {
// We need -fmodules or else the clang argument parsing does not honour
// -fmodules-cache-path. In reality, the swift ClangImporter will always
// enable modules when importing, so this should only impact the
// clang argument parsing. This is needed even if the header doesn't
// use modules, since Swift itself will import its shims module, and
// that needs to honour the -module-cache-path option when testing.
sourcekitd_request_array_set_string(Args, SOURCEKITD_ARRAY_APPEND, "-fmodules");
std::string opt = "-fmodules-cache-path=" + Opts.ModuleCachePath;
sourcekitd_request_array_set_string(Args, SOURCEKITD_ARRAY_APPEND, opt.c_str());
} else {
sourcekitd_request_array_set_string(Args, SOURCEKITD_ARRAY_APPEND, "-module-cache-path");
sourcekitd_request_array_set_string(Args, SOURCEKITD_ARRAY_APPEND, Opts.ModuleCachePath.c_str());
}
}
if (Opts.DisableImplicitConcurrencyModuleImport && !compilerArgsAreClang) {
sourcekitd_request_array_set_string(Args, SOURCEKITD_ARRAY_APPEND,
"-Xfrontend");
sourcekitd_request_array_set_string(Args, SOURCEKITD_ARRAY_APPEND,
"-disable-implicit-concurrency-module-import");
}
if (Opts.DisableImplicitStringProcessingModuleImport &&
!compilerArgsAreClang) {
sourcekitd_request_array_set_string(Args, SOURCEKITD_ARRAY_APPEND,
"-Xfrontend");
sourcekitd_request_array_set_string(Args, SOURCEKITD_ARRAY_APPEND,
"-disable-implicit-string-processing-module-import");
}
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.has_value()) {
sourcekitd_request_dictionary_set_int64(Req, KeyCancelOnSubsequentRequest,
*Opts.CancelOnSubsequentRequest);
}
if (Opts.SimulateLongRequest.has_value()) {
sourcekitd_request_dictionary_set_int64(Req, KeySimulateLongRequest,
*Opts.SimulateLongRequest);
}
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.VFSName) {
sourcekitd_request_dictionary_set_string(Req, KeyVFSName, Opts.VFSName->c_str());
}
if (!Opts.VFSFiles.empty()) {
sourcekitd_object_t files = sourcekitd_request_array_create(nullptr, 0);
for (auto &NameAndTarget : Opts.VFSFiles) {
sourcekitd_object_t file = sourcekitd_request_dictionary_create(nullptr, nullptr, 0);
sourcekitd_request_dictionary_set_string(file, KeyName, NameAndTarget.first().data());
if (NameAndTarget.second.passAsSourceText) {
auto content = getBufferForFilename(NameAndTarget.first(), Opts.VFSFiles);
sourcekitd_request_dictionary_set_string(file, KeySourceText, content->getBufferStart());
} else {
sourcekitd_request_dictionary_set_string(file, KeySourceFile, NameAndTarget.second.path.c_str());
}
sourcekitd_request_array_set_value(files, SOURCEKITD_ARRAY_APPEND, file);
}
sourcekitd_object_t vfsOpts = sourcekitd_request_dictionary_create(nullptr, nullptr, 0);
sourcekitd_request_dictionary_set_value(vfsOpts, KeyFiles, files);
sourcekitd_request_dictionary_set_value(Req, KeyVFSOptions, vfsOpts);
sourcekitd_request_release(vfsOpts);
sourcekitd_request_release(files);
}
int64_t BeforeInstructions;
if (Opts.measureInstructions)
BeforeInstructions = getSourceKitInstructionCount();
if (!Opts.isAsyncRequest) {
sourcekitd_response_t Resp = sendRequestSync(Req, Opts);
if (Opts.measureInstructions) {
int64_t AfterInstructions = getSourceKitInstructionCount();
llvm::errs() << "request instructions: "
<< (AfterInstructions - BeforeInstructions);
}
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, &asyncResponses[respIndex].requestHandle,
^(sourcekitd_response_t resp) {
auto &info = asyncResponses[respIndex];
info.response = resp;
sourcekitd_request_handle_dispose(
info.requestHandle);
info.semaphore.signal(); // Ready to be handled!
});
#else
llvm::report_fatal_error(
"-async not supported when sourcekitd is built without blocks support");
#endif
if (Opts.measureInstructions) {
int64_t AfterInstructions = getSourceKitInstructionCount();
llvm::errs() << "request instructions: "
<< (AfterInstructions - BeforeInstructions);
}
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) {
printRawResponse(Resp);
} 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: {
// Length=0, replace="" is a nop and will not trigger sema.
bool SyntacticOnly = Opts.Length == 0 && Opts.ReplaceText->empty();
for (const std::string &ReqOpt : Opts.RequestOptions) {
if (SyntacticOnly)
break;
if (ReqOpt.find("syntactic_only=1") != std::string::npos) {
SyntacticOnly = true;
}
}
if (SyntacticOnly) {
printRawResponse(Resp);
} 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::GlobalConfiguration:
case SourceKitRequest::ProtocolVersion:
case SourceKitRequest::CompilerVersion:
case SourceKitRequest::Close:
case SourceKitRequest::Index:
case SourceKitRequest::CodeComplete:
case SourceKitRequest::CodeCompleteOpen:
case SourceKitRequest::CodeCompleteClose:
case SourceKitRequest::CodeCompleteUpdate:
case SourceKitRequest::CodeCompleteCacheOnDisk:
case SourceKitRequest::CodeCompleteSetPopularAPI:
case SourceKitRequest::TypeContextInfo:
case SourceKitRequest::ConformingMethodList:
case SourceKitRequest::DependencyUpdated:
case SourceKitRequest::Diagnostics:
case SourceKitRequest::SemanticTokens:
printRawResponse(Resp);
break;
case SourceKitRequest::Compile:
sourcekitd_response_description_dump_filedesc(Resp, STDOUT_FILENO);
break;
case SourceKitRequest::CompileClose:
sourcekitd_response_description_dump_filedesc(Resp, STDOUT_FILENO);
break;
case SourceKitRequest::RelatedIdents:
printRelatedIdents(Info, SourceFile, Opts.VFSFiles, llvm::outs());
break;
case SourceKitRequest::ActiveRegions:
printActiveRegions(Info, SourceFile, Opts.VFSFiles, llvm::outs());
break;
case SourceKitRequest::CursorInfo:
printCursorInfo(Info, SourceFile, Opts.VFSFiles, llvm::outs());
break;
case SourceKitRequest::NameTranslation:
printNameTranslationInfo(Info, llvm::outs());
break;
case SourceKitRequest::RangeInfo:
printRangeInfo(Info, SourceFile, llvm::outs());
break;
case SourceKitRequest::CollectExpressionType:
printExpressionType(Info, llvm::outs());
break;
case SourceKitRequest::CollectVariableType:
printVariableType(Info, SourceBuf.get(), 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:
printRawResponse(Resp);
if (Opts.ReplaceText.has_value()) {
unsigned Offset =
resolveFromLineCol(Opts.Line, Opts.Col, SourceFile, Opts.VFSFiles);
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.value().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);
printRawResponse(EdResp);
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.str());
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);
printRawResponse(FmtResp);
sourcekitd_response_dispose(FmtResp);
sourcekitd_request_release(Fmt);
}
break;
case SourceKitRequest::ExpandPlaceholder:
if (Opts.Length) {
// Single placeholder by location.
printRawResponse(Resp);
} else {
// Expand all placeholders.
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/Refactoring/RefactoringKinds.def"
case SourceKitRequest::SyntacticMacroExpansion:
printSyntacticRenameEdits(Info, llvm::outs());
break;
case SourceKitRequest::FindRenameRanges:
case SourceKitRequest::FindLocalRenameRanges:
printRenameRanges(Info, llvm::outs());
break;
case SourceKitRequest::Statistics:
printStatistics(Info, llvm::outs());
break;
case SourceKitRequest::IndexToStore:
printRawResponse(Resp);
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 getSemanticInfoImplAfterDocUpdate(sourcekitd_variant_t EditOrOpen,
sourcekitd_variant_t DocUpdate) {
if (sourcekitd_variant_dictionary_get_uid(EditOrOpen, KeyDiagnosticStage) ==
SemaDiagnosticStage) {
// FIXME: currently we only return annotations once, so if the original edit
// or open request was slow enough, it may "take" the annotations. If that
// is fixed, we can skip checking the diagnostic stage and always use the
// DocUpdate variant.
assert(sourcekitd_variant_get_type(sourcekitd_variant_dictionary_get_value(
DocUpdate, KeyAnnotations)) == SOURCEKITD_VARIANT_TYPE_NULL);
getSemanticInfoImpl(EditOrOpen);
} else {
getSemanticInfoImpl(DocUpdate);
}
}
static void getSemanticInfo(sourcekitd_variant_t Info, StringRef Filename) {
// 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);
}
getSemanticInfoImplAfterDocUpdate(
Info, sourcekitd_response_get_value(semaResponse));
}
static int printAnnotations() {
printRawVariant(LatestSemaAnnotations);
return 0;
}
static int printDiags() {
printRawVariant(LatestSemaDiags);
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) {
const char *InternalDiagnostic =
sourcekitd_variant_dictionary_get_string(Info, KeyInternalDiagnostic);
if (InternalDiagnostic) {
OS << "<empty name translation info; internal diagnostic: \""
<< InternalDiagnostic << "\">\n";
return;
}
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';
}
template <typename T>
static std::vector<T> readArray(
sourcekitd_variant_t Info, sourcekitd_uid_t Key,
std::function<T(sourcekitd_variant_t)> elementFromEntry) {
std::vector<T> Elements;
sourcekitd_variant_t Obj =
sourcekitd_variant_dictionary_get_value(Info, Key);
for (unsigned i = 0, e = sourcekitd_variant_array_get_count(Obj);
i != e; ++i) {
sourcekitd_variant_t Entry = sourcekitd_variant_array_get_value(Obj, i);
Elements.push_back(elementFromEntry(Entry));
}
return Elements;
}
static std::vector<const char *> readStringArray(
sourcekitd_variant_t Info, sourcekitd_uid_t Key,
sourcekitd_uid_t ElementKey) {
return readArray<const char *>(Info, Key, [&](sourcekitd_variant_t Entry) {
return sourcekitd_variant_dictionary_get_string(Entry, ElementKey);
});
}
struct ResponseSymbolInfo {
struct ParentInfo {
const char *Title;
const char *Kind;
const char *USR;
};
struct ReferencedSymbol {
const char *USR;
const char *AccessLevel;
const char *Filename;
const char *ModuleName;
const char *DeclLang;
bool IsSystem;
bool IsSPI;
std::vector<ParentInfo> ParentContexts;
};
const char *Kind = nullptr;
const char *Lang = nullptr;
const char *Name = nullptr;
const char *USR = nullptr;
const char *TypeName = nullptr;
const char *TypeUSR = nullptr;
const char *ContainerTypeUSR = nullptr;
const char *DocComment = nullptr;
const char *DocCommentAsXML = nullptr;
const char *GroupName = nullptr;
const char *LocalizationKey = nullptr;
const char *AnnotatedDeclaration = nullptr;
const char *FullyAnnotatedDeclaration = nullptr;
const char *SymbolGraph = nullptr;
const char *ModuleName = nullptr;
const char *ModuleInterfaceName = nullptr;
const char *FilePath = nullptr;
unsigned Offset = 0;
unsigned Length = 0;
unsigned Line = 0;
unsigned Column = 0;
std::vector<const char *> OverrideUSRs;
std::vector<const char *> AnnotatedRelatedDeclarations;
std::vector<const char *> ModuleGroups;
std::vector<ParentInfo> ParentContexts;
std::vector<ReferencedSymbol> ReferencedSymbols;
std::vector<const char *> ReceiverUSRs;
bool IsSystem = false;
bool IsDynamic = false;
bool IsSynthesized = false;
unsigned ParentOffset = 0;
static ResponseSymbolInfo read(sourcekitd_variant_t Info) {
ResponseSymbolInfo Symbol;
sourcekitd_uid_t KindUID =
sourcekitd_variant_dictionary_get_uid(Info, KeyKind);
if (KindUID == nullptr)
return Symbol;
Symbol.Kind = sourcekitd_uid_get_string_ptr(KindUID);
sourcekitd_uid_t LangUID =
sourcekitd_variant_dictionary_get_uid(Info, KeyDeclarationLang);
if (LangUID)
Symbol.Lang = sourcekitd_uid_get_string_ptr(LangUID);
Symbol.Name = sourcekitd_variant_dictionary_get_string(Info, KeyName);
Symbol.USR = sourcekitd_variant_dictionary_get_string(Info, KeyUSR);
Symbol.TypeName =
sourcekitd_variant_dictionary_get_string(Info, KeyTypeName);
Symbol.TypeUSR = sourcekitd_variant_dictionary_get_string(Info, KeyTypeUsr);
Symbol.ContainerTypeUSR =
sourcekitd_variant_dictionary_get_string(Info, KeyContainerTypeUsr);
Symbol.DocComment =
sourcekitd_variant_dictionary_get_string(Info, KeyDocComment);
Symbol.DocCommentAsXML =
sourcekitd_variant_dictionary_get_string(Info, KeyDocFullAsXML);
Symbol.GroupName =
sourcekitd_variant_dictionary_get_string(Info, KeyGroupName);
Symbol.LocalizationKey =
sourcekitd_variant_dictionary_get_string(Info, KeyLocalizationKey);
Symbol.AnnotatedDeclaration =
sourcekitd_variant_dictionary_get_string(Info, KeyAnnotatedDecl);
Symbol.FullyAnnotatedDeclaration =
sourcekitd_variant_dictionary_get_string(Info, KeyFullyAnnotatedDecl);
Symbol.SymbolGraph =
sourcekitd_variant_dictionary_get_string(Info, KeySymbolGraph);
Symbol.ModuleName =
sourcekitd_variant_dictionary_get_string(Info, KeyModuleName);
Symbol.ModuleInterfaceName =
sourcekitd_variant_dictionary_get_string(Info, KeyModuleInterfaceName);
Symbol.FilePath =
sourcekitd_variant_dictionary_get_string(Info, KeyFilePath);
if (Symbol.FilePath) {
Symbol.Offset = sourcekitd_variant_dictionary_get_int64(Info, KeyOffset);
Symbol.Length = sourcekitd_variant_dictionary_get_int64(Info, KeyLength);
Symbol.Line = sourcekitd_variant_dictionary_get_int64(Info, KeyLine);
Symbol.Column = sourcekitd_variant_dictionary_get_int64(Info, KeyColumn);
}
Symbol.OverrideUSRs = readStringArray(Info, KeyOverrides, KeyUSR);
Symbol.AnnotatedRelatedDeclarations =
readStringArray(Info, KeyRelatedDecls, KeyAnnotatedDecl);
Symbol.ModuleGroups = readStringArray(Info, KeyModuleGroups, KeyGroupName);
Symbol.ParentContexts = readArray<ParentInfo>(
Info, KeyParentContexts, [&](sourcekitd_variant_t Entry) {
return ParentInfo{
sourcekitd_variant_dictionary_get_string(Entry, KeyName),
sourcekitd_variant_dictionary_get_string(Entry, KeyKind),
sourcekitd_variant_dictionary_get_string(Entry, KeyUSR)};
});
Symbol.ReferencedSymbols = readArray<ReferencedSymbol>(
Info, KeyReferencedSymbols, [&](sourcekitd_variant_t Entry){
return ReferencedSymbol{
sourcekitd_variant_dictionary_get_string(Entry, KeyUSR),
sourcekitd_variant_dictionary_get_string(Entry, KeyAccessLevel),
sourcekitd_variant_dictionary_get_string(Entry, KeyFilePath),
sourcekitd_variant_dictionary_get_string(Entry, KeyModuleName),
sourcekitd_uid_get_string_ptr(
sourcekitd_variant_dictionary_get_uid(Entry, KeyDeclarationLang)),
sourcekitd_variant_dictionary_get_bool(Entry, KeyIsSystem),
sourcekitd_variant_dictionary_get_bool(Entry, KeyIsSPI),
readArray<ParentInfo>(Entry, KeyParentContexts,
[&](sourcekitd_variant_t Entry){
return ParentInfo{
sourcekitd_variant_dictionary_get_string(Entry, KeyName),
sourcekitd_variant_dictionary_get_string(Entry, KeyKind),
sourcekitd_variant_dictionary_get_string(Entry, KeyUSR)};
})
};
});
Symbol.ReceiverUSRs = readStringArray(Info, KeyReceivers, KeyUSR);
Symbol.IsSystem = sourcekitd_variant_dictionary_get_bool(Info, KeyIsSystem);
Symbol.IsDynamic =
sourcekitd_variant_dictionary_get_bool(Info, KeyIsDynamic);
Symbol.IsSynthesized =
sourcekitd_variant_dictionary_get_bool(Info, KeyIsSynthesized);
Symbol.ParentOffset =
sourcekitd_variant_dictionary_get_int64(Info, KeyParentLoc);
return Symbol;
}
void print(llvm::raw_ostream &OS, StringRef CurrentFilename,
const llvm::StringMap<TestOptions::VFSFile> &VFSFiles) {
if (Kind == nullptr) {
OS << "<empty symbol info>\n";
return;
}
OS << Kind << " (";
if (FilePath) {
if (CurrentFilename != StringRef(FilePath))
OS << FilePath << ':';
auto LineCol =
resolveToLineCol(Offset, FilePath, VFSFiles, /*ExitOnError=*/false);
if (LineCol.first == 0 && LineCol.second == 0) {
OS << "*missing file*";
} else if (LineCol.first != Line || LineCol.second != Column) {
OS << "*offset does not match line/column in response*";
} else {
OS << LineCol.first << ':' << LineCol.second;
auto EndLineCol = resolveToLineCol(Offset + Length, FilePath,
VFSFiles);
OS << '-' << EndLineCol.first << ':' << EndLineCol.second;
}
}
OS << ")" << '\n';
if (Name)
OS << Name << '\n';
if (USR)
OS << USR << '\n';
if (Lang)
OS << Lang << '\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 (AnnotatedDeclaration)
OS << AnnotatedDeclaration << '\n';
if (FullyAnnotatedDeclaration)
OS << FullyAnnotatedDeclaration << '\n';
OS << "DOC COMMENT\n";
if (DocComment)
OS << DocComment << '\n';
OS << "DOC COMMENT XML\n";
if (DocCommentAsXML)
OS << DocCommentAsXML << '\n';
if (LocalizationKey) {
OS << "<LocalizationKey>" << LocalizationKey;
OS << "</LocalizationKey>" << '\n';
}
if (IsDynamic)
OS << "DYNAMIC\n";
if (IsSynthesized)
OS << "SYNTHESIZED\n";
if (ParentOffset) {
OS << "PARENT OFFSET: " << ParentOffset << "\n";
}
OS << "SYMBOL GRAPH BEGIN\n";
if (SymbolGraph) {
if (auto PrettyJsonOrErr = json::parse(StringRef(SymbolGraph))) {
OS << formatv("{0:2}", *PrettyJsonOrErr);
} else {
llvm::handleAllErrors(PrettyJsonOrErr.takeError(),
[&](const llvm::ErrorInfoBase &E) {});
OS << SymbolGraph;
}
OS << "\n";
}
OS << "SYMBOL GRAPH END\n";
OS << "PARENT CONTEXTS BEGIN\n";
for (auto Parent : ParentContexts)
OS << Parent.Title << " " << Parent.Kind << " " << Parent.USR << '\n';
OS << "PARENT CONTEXTS END\n";
OS << "REFERENCED DECLS BEGIN\n";
for (auto Ref : ReferencedSymbols) {
OS << Ref.USR << " | " << Ref.AccessLevel << " | "
<< (strlen(Ref.Filename) ? Ref.Filename : "<empty>") << " | "
<< (strlen(Ref.ModuleName) ? Ref.ModuleName : "<empty>") << " | "
<< (Ref.IsSystem ? "System" : "User") << " | "
<< (Ref.IsSPI ? "SPI" : "NonSPI") << " | "
<< Ref.DeclLang << "\n";
for (auto Parent: Ref.ParentContexts)
OS << " " << Parent.Title << " " << Parent.Kind << " " << Parent.USR
<< '\n';
}
OS << "REFERENCED DECLS END\n";
OS << "OVERRIDES BEGIN\n";
for (auto OverUSR : OverrideUSRs)
OS << OverUSR << '\n';
OS << "OVERRIDES END\n";
OS << "RELATED BEGIN\n";
for (auto RelDecl : AnnotatedRelatedDeclarations)
OS << RelDecl << '\n';
OS << "RELATED END\n";
OS << "MODULE GROUPS BEGIN\n";
for (auto Group : ModuleGroups)
OS << Group << '\n';
OS << "MODULE GROUPS END\n";
OS << "RECEIVERS BEGIN\n";
for (auto Receiver : ReceiverUSRs)
OS << Receiver << '\n';
OS << "RECEIVERS END\n";
}
};
static void printCursorInfo(sourcekitd_variant_t Info, StringRef FilenameIn,
const llvm::StringMap<TestOptions::VFSFile> &VFSFiles,
llvm::raw_ostream &OS) {
const char *InternalDiagnostic =
sourcekitd_variant_dictionary_get_string(Info, KeyInternalDiagnostic);
if (InternalDiagnostic) {
OS << "<empty cursor info; internal diagnostic: \""
<< InternalDiagnostic << "\">\n";
return;
}
auto SymbolInfo = ResponseSymbolInfo::read(Info);
struct ActionInfo {
const char *KindUID;
const char *KindName;
const char *UnavailReason;
};
std::vector<ActionInfo> AvailableActions = readArray<ActionInfo>(
Info, KeyRefactorActions, [&](sourcekitd_variant_t Entry) {
return ActionInfo {
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)
};
});
std::vector<ResponseSymbolInfo> SecondarySymbols =
readArray<ResponseSymbolInfo>(Info, KeySecondarySymbols,
[&](sourcekitd_variant_t Entry) {
return ResponseSymbolInfo::read(Entry);
});
std::string Filename = FilenameIn.str();
llvm::SmallString<256> output;
if (!llvm::sys::fs::real_path(Filename, output))
Filename = std::string(output.str());
SymbolInfo.print(OS, Filename, VFSFiles);
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";
OS << "SECONDARY SYMBOLS BEGIN\n";
for (auto Secondary : SecondarySymbols) {
Secondary.print(OS, Filename, VFSFiles);
OS << "-----\n";
}
OS << "SECONDARY SYMBOLS END\n";
OS << "DID REUSE AST CONTEXT: "
<< sourcekitd_variant_dictionary_get_bool(Info, KeyReusingASTContext)
<< '\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.str();
llvm::SmallString<256> output;
if (llvm::sys::fs::real_path(Filename, output))
Filename = std::string(output.str());
sourcekitd_variant_t OffsetObj =
sourcekitd_variant_dictionary_get_value(Info, KeyOffset);
std::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 printExpressionType(sourcekitd_variant_t Info, llvm::raw_ostream &OS) {
auto TypeBuffer = sourcekitd_variant_dictionary_get_value(Info, KeyExpressionTypeList);
unsigned Count = sourcekitd_variant_array_get_count(TypeBuffer);
if (!Count) {
OS << "cannot find expression types in the file\n";
return;
}
OS << "<ExpressionTypes>\n";
for (unsigned i = 0; i != Count; ++i) {
sourcekitd_variant_t Item = sourcekitd_variant_array_get_value(TypeBuffer, i);
unsigned Offset = sourcekitd_variant_dictionary_get_int64(Item, KeyExpressionOffset);
unsigned Length = sourcekitd_variant_dictionary_get_int64(Item, KeyExpressionLength);
OS << "(" << Offset << ", " << Offset + Length << "): " <<
sourcekitd_variant_dictionary_get_string(Item, KeyExpressionType) << "\n";
sourcekitd_variant_t protocols = sourcekitd_variant_dictionary_get_value(Item,
KeyExpectedTypes);
unsigned Count = sourcekitd_variant_array_get_count(protocols);
for (unsigned i = 0; i != Count; i ++) {
OS << "conforming to: " << sourcekitd_variant_array_get_string(protocols, i) << "\n";
}
}
OS << "</ExpressionTypes>\n";
}
static void printVariableType(sourcekitd_variant_t Info,
llvm::MemoryBuffer *SourceBuf,
llvm::raw_ostream &OS) {
auto TypeBuffer =
sourcekitd_variant_dictionary_get_value(Info, KeyVariableTypeList);
unsigned Count = sourcekitd_variant_array_get_count(TypeBuffer);
if (!Count) {
OS << "cannot find variable types in the file\n";
return;
}
OS << "<VariableTypes>\n";
for (unsigned i = 0; i != Count; ++i) {
sourcekitd_variant_t Item = sourcekitd_variant_array_get_value(TypeBuffer, i);
unsigned Offset = sourcekitd_variant_dictionary_get_int64(Item, KeyVariableOffset);
unsigned Length = sourcekitd_variant_dictionary_get_int64(Item, KeyVariableLength);
auto Start = resolveToLineCol(Offset, SourceBuf);
auto End = resolveToLineCol(Offset + Length, SourceBuf);
bool HasExplicitType = sourcekitd_variant_dictionary_get_bool(Item, KeyVariableTypeExplicit);
auto PrintedType = sourcekitd_variant_dictionary_get_string(Item, KeyVariableType);
OS << "("
<< Start.first << ":" << Start.second
<< ", "
<< End.first << ":" << End.second
<< "): "
<< PrintedType
<< " (explicit type: " << HasExplicitType << ")\n";
}
OS << "</VariableTypes>\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);
std::optional<int64_t> Offset;
if (sourcekitd_variant_get_type(OffsetObj) != SOURCEKITD_VARIANT_TYPE_NULL)
Offset = sourcekitd_variant_int64_get_value(OffsetObj);
if (!Offset.has_value()) {
OS << "USR NOT FOUND\n";
return;
}
int64_t Length = sourcekitd_variant_dictionary_get_int64(Info, KeyLength);
auto LineCol1 = resolveToLineCol(Offset.value(), SourceBuf);
auto LineCol2 = resolveToLineCol(Offset.value() + 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);
printRawVariant(Source);
}
static void printDocInfo(sourcekitd_variant_t Info, StringRef Filename) {
const char *text =
sourcekitd_variant_dictionary_get_string(Info, KeySourceText);
if (text) {
llvm::outs() << text << '\n';
}
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);
printRawVariant(annotations);
printRawVariant(entities);
if (sourcekitd_variant_get_type(diags) != SOURCEKITD_VARIANT_TYPE_NULL)
printRawVariant(diags);
}
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);
StringRef Path(
sourcekitd_variant_dictionary_get_string(Edit, KeyFilePath));
if (!Path.empty()) {
OS << Path << " ";
}
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 BufferName(
sourcekitd_variant_dictionary_get_string(Edit, KeyBufferName));
if (!BufferName.empty()) {
OS << "(" << BufferName << ") ";
}
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::outs() << text << '\n';
}
if (CheckASCII) {
checkTextIsASCII(text);
}
sourcekitd_variant_t syntaxmap =
sourcekitd_variant_dictionary_get_value(Info, KeySyntaxMap);
printRawVariant(syntaxmap);
sourcekitd_variant_t annotations =
sourcekitd_variant_dictionary_get_value(Info, KeyAnnotations);
printRawVariant(annotations);
sourcekitd_variant_t structure =
sourcekitd_variant_dictionary_get_value(Info, KeySubStructure);
printRawVariant(structure);
sourcekitd_variant_t declarations =
sourcekitd_variant_dictionary_get_value(Info, KeyDeclarations);
// only output declarations if there are any (because this might have been
// disabled in the request itself)
if (sourcekitd_variant_get_type(declarations) != SOURCEKITD_VARIANT_TYPE_NULL)
printRawVariant(declarations);
}
static void printRelatedIdents(sourcekitd_variant_t Info, StringRef Filename,
const llvm::StringMap<TestOptions::VFSFile> &VFSFiles,
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 Usage = sourcekitd_variant_dictionary_get_uid(Range, KeyNameType);
auto LineCol = resolveToLineCol(Offset, Filename, VFSFiles);
OS << LineCol.first << ':' << LineCol.second << " - " << Length << " - "
<< sourcekitd_uid_get_string_ptr(Usage) << '\n';
}
OS << "END RANGES\n";
OS << "NAME: " << sourcekitd_variant_dictionary_get_string(Info, KeyName);
}
static void printActiveRegions(sourcekitd_variant_t Info, StringRef Filename,
const llvm::StringMap<TestOptions::VFSFile> &VFSFiles,
llvm::raw_ostream &OS) {
OS << "START IF CONFIGS\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 IfConfig = sourcekitd_variant_array_get_value(Res, i);
int64_t Offset = sourcekitd_variant_dictionary_get_int64(IfConfig, KeyOffset);
auto LineCol = resolveToLineCol(Offset, Filename, VFSFiles);
bool IsActive = sourcekitd_variant_dictionary_get_bool(IfConfig, KeyIsActive);
OS << LineCol.first << ':' << LineCol.second << " - " << (IsActive ? "active" : "inactive") << '\n';
}
OS << "END IF CONFIGS\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 std::string initializeSource(StringRef Input) {
std::string result;
{
llvm::raw_string_ostream OS(result);
StringRef CheckStr = "CHECK";
size_t Pos = 0;
while (true) {
auto checkPos = Input.find(CheckStr, Pos);
if (checkPos == StringRef::npos)
break;
checkPos = Input.substr(0, checkPos).rfind("//");
assert(checkPos != StringRef::npos);
size_t EndLine = Input.find('\n', checkPos);
assert(EndLine != StringRef::npos);
++EndLine;
OS << Input.slice(Pos, checkPos);
Pos = EndLine;
}
OS << Input.slice(Pos, StringRef::npos);
}
return result;
}
static std::optional<std::pair<unsigned, unsigned>>
firstPlaceholderRange(StringRef Source, unsigned from) {
const char *StartPtr = Source.data();
Source = Source.drop_front(from);
while (true) {
size_t Pos = Source.find("<#");
if (Pos == StringRef::npos)
break;
unsigned OffsetStart = Source.data() + Pos - StartPtr;
Source = Source.substr(Pos+2);
if (Source.starts_with("__skip__") || Source.starts_with("T##__skip__"))
continue;
Pos = Source.find("#>");
if (Pos == StringRef::npos)
break;
unsigned OffsetEnd = Source.data() + Pos + 2 - StartPtr;
Source = Source.substr(Pos+2);
return std::make_pair(OffsetStart, OffsetEnd-OffsetStart);
}
return std::nullopt;
}
static void expandPlaceholders(llvm::MemoryBuffer *SourceBuf,
llvm::raw_ostream &OS) {
auto syncEdit = [=](unsigned offset, unsigned length, const char *text) {
auto SourceBufID = SourceBuf->getBufferIdentifier();
auto req = sourcekitd_request_dictionary_create(nullptr, nullptr, 0);
sourcekitd_request_dictionary_set_uid(req, KeyRequest,
RequestEditorReplaceText);
sourcekitd_request_dictionary_set_stringbuf(req, KeyName,
SourceBufID.data(),
SourceBufID.size());
sourcekitd_request_dictionary_set_int64(req, KeyOffset, offset);
sourcekitd_request_dictionary_set_int64(req, KeyLength, length);
sourcekitd_request_dictionary_set_string(req, KeySourceText, text);
sourcekitd_response_t resp = sourcekitd_send_request_sync(req);
if (sourcekitd_response_is_error(resp)) {
sourcekitd_response_description_dump(resp);
exit(1);
}
sourcekitd_request_release(req);
sourcekitd_response_dispose(resp);
};
std::string source = initializeSource(SourceBuf->getBuffer());
// Sync contents with modified source.
syncEdit(0, SourceBuf->getBuffer().size(), source.c_str());
unsigned cursor = 0;
while (auto Range = firstPlaceholderRange(source, cursor)) {
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) {
cursor = Offset + Length;
sourcekitd_response_dispose(Resp);
continue;
}
unsigned EditOffset = sourcekitd_variant_dictionary_get_int64(Info, KeyOffset);
unsigned EditLength = sourcekitd_variant_dictionary_get_int64(Info, KeyLength);
// Apply edit locally.
source.replace(EditOffset, EditLength, Text);
// Apply edit on server.
syncEdit(EditOffset, EditLength, Text);
// Adjust cursor to after the edit (we do not expand recursively).
cursor = EditOffset + strlen(Text);
sourcekitd_response_dispose(Resp);
}
OS << source;
}
static std::pair<unsigned, unsigned>
resolveToLineCol(unsigned Offset, StringRef Filename,
const llvm::StringMap<TestOptions::VFSFile> &VFSFiles,
bool ExitOnError) {
return resolveToLineCol(Offset,
getBufferForFilename(Filename, VFSFiles, ExitOnError),
ExitOnError);
}
/// Maps \p Offset to the {Line, Col} position in \p InputBuf. If it could not
/// be resolved and \p ExitOnError is \c true, the process exits with an error
/// message. Otherwise, {0, 0} is returned.
static std::pair<unsigned, unsigned>
resolveToLineCol(unsigned Offset, llvm::MemoryBuffer *InputBuf,
bool ExitOnError) {
if (Offset >= InputBuf->getBufferSize()) {
if (!ExitOnError)
return {0, 0};
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,
const llvm::StringMap<TestOptions::VFSFile> &VFSFiles) {
return resolveFromLineCol(Line, Col,
getBufferForFilename(Filename, VFSFiles));
}
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;
--Line;
for (; Line && (Ptr < End); ++Ptr) {
if (*Ptr == '\n') {
--Line;
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);
}
/// Opens \p Filename, first checking \p VFSFiles and then falling back to the
/// filesystem otherwise. If the file could not be opened and \p ExitOnError is
/// true, the process exits with an error message. Otherwise a buffer
/// containing "<missing file>" is returned.
static llvm::MemoryBuffer *
getBufferForFilename(StringRef Filename,
const llvm::StringMap<TestOptions::VFSFile> &VFSFiles,
bool ExitOnError) {
llvm::SmallString<128> nativeName;
llvm::sys::path::native(Filename, nativeName);
auto VFSFileIt = VFSFiles.find(nativeName);
auto MappedFilename =
VFSFileIt == VFSFiles.end() ? Filename : StringRef(VFSFileIt->second.path);
auto FileBufOrErr = llvm::MemoryBuffer::getFile(MappedFilename);
std::unique_ptr<llvm::MemoryBuffer> Buffer;
if (!FileBufOrErr) {
if (ExitOnError) {
llvm::errs() << "error opening input file '" << MappedFilename << "' ("
<< FileBufOrErr.getError().message() << ")\n";
exit(1);
}
Buffer = llvm::MemoryBuffer::getMemBuffer("<missing file>");
} else {
Buffer = std::move(FileBufOrErr.get());
}
return Buffer.release();
}