| //===--- 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" |
| |
| // SWIFT_ENABLE_TENSORFLOW |
| #include "sourcekitd/FileSystemProvider.h" |
| // SWIFT_ENABLE_TENSORFLOW END |
| #include "SourceKit/Support/Concurrency.h" |
| #include "TestOptions.h" |
| #include "swift/Demangling/ManglingMacros.h" |
| // SWIFT_ENABLE_TENSORFLOW |
| #include "clang/Basic/InMemoryOutputFileSystem.h" |
| // SWIFT_ENABLE_TENSORFLOW END |
| #include "llvm/ADT/ArrayRef.h" |
| #include "llvm/ADT/IntrusiveRefCntPtr.h" |
| #include "llvm/ADT/Optional.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/Threading.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include <fstream> |
| #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 bool sendGlobalConfigRequest(); |
| 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 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 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); |
| static std::pair<unsigned, unsigned> resolveToLineCol(unsigned Offset, |
| llvm::MemoryBuffer *InputBuf); |
| static std::pair<unsigned, unsigned> resolveToLineColFromBuf(unsigned Offset, |
| const char *Buf); |
| static llvm::MemoryBuffer * |
| getBufferForFilename(StringRef Filename, |
| const llvm::StringMap<TestOptions::VFSFile> &VFSFiles); |
| |
| static void notification_receiver(sourcekitd_response_t resp); |
| |
| static SourceKitRequest ActiveRequest = SourceKitRequest::None; |
| |
| #define KEY(NAME, CONTENT) static sourcekitd_uid_t Key##NAME; |
| #define REQUEST(NAME, CONTENT) static sourcekitd_uid_t Request##NAME; |
| #define KIND(NAME, CONTENT) static sourcekitd_uid_t Kind##NAME; |
| #include "SourceKit/Core/ProtocolUIDs.def" |
| |
| #define REFACTORING(KIND, NAME, ID) static sourcekitd_uid_t Kind##Refactoring##KIND; |
| #include "swift/IDE/RefactoringKinds.def" |
| |
| static sourcekitd_uid_t SemaDiagnosticStage; |
| |
| static sourcekitd_uid_t NoteDocUpdate; |
| static SourceKit::Semaphore semaSemaphore(0); |
| static sourcekitd_response_t semaResponse; |
| static const char *semaName; |
| |
| static sourcekitd_uid_t NoteTest; |
| static SourceKit::Semaphore noteSyncSemaphore(0); |
| |
| namespace { |
| struct AsyncResponseInfo { |
| SourceKit::Semaphore semaphore{0}; |
| sourcekitd_response_t response = nullptr; |
| TestOptions options; |
| std::string sourceFilename; |
| std::unique_ptr<llvm::MemoryBuffer> sourceBuffer; |
| }; |
| } // end anonymous namespace |
| |
| static std::vector<AsyncResponseInfo> asyncResponses; |
| |
| struct NotificationBuffer { |
| std::vector<sourcekitd_response_t> notes; |
| /// Add a notification to the buffer, taking ownership of it. Must be called |
| /// from the main queue. |
| void add(sourcekitd_response_t note) { |
| notes.push_back(note); |
| } |
| /// Call the given handler for all notifications currently buffered. |
| void handleNotifications(llvm::function_ref<void(sourcekitd_response_t)> f) { |
| // Notifications are handled on the main queue. |
| dispatch_sync(dispatch_get_main_queue(), ^{ |
| for (auto note : notes) { |
| f(note); |
| sourcekitd_response_dispose(note); |
| } |
| notes.clear(); |
| }); |
| } |
| }; |
| static NotificationBuffer notificationBuffer; |
| |
| static void syncNotificationsWithService() { |
| // Send TestNotification request, then wait for the notification. This ensures |
| // that all notifications previously posted on the service side have been |
| // passed to our notification handler. |
| sourcekitd_object_t req = sourcekitd_request_dictionary_create(nullptr, nullptr, 0); |
| sourcekitd_request_dictionary_set_uid(req, KeyRequest, RequestTestNotification); |
| auto resp = sourcekitd_send_request_sync(req); |
| if (sourcekitd_response_is_error(resp)) { |
| sourcekitd_response_description_dump(resp); |
| exit(1); |
| } |
| sourcekitd_response_dispose(resp); |
| sourcekitd_request_release(req); |
| if (noteSyncSemaphore.wait(60 * 1000)) { |
| llvm::report_fatal_error("Test notification not received"); |
| } |
| } |
| |
| static void printBufferedNotifications(bool syncWithService = true) { |
| if (syncWithService) { |
| syncNotificationsWithService(); |
| } |
| notificationBuffer.handleNotifications([](sourcekitd_response_t note) { |
| sourcekitd_response_description_dump_filedesc(note, STDOUT_FILENO); |
| }); |
| } |
| |
| 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::llvm_execute_on_thread((void (*)(void *))skt_main, &args); |
| 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/IDE/RefactoringKinds.def" |
| |
| // A test invocation may initialize the options to be used for subsequent |
| // invocations. |
| TestOptions InitOpts; |
| auto Args = llvm::makeArrayRef(argv+1, argc-1); |
| 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 Optional<int64_t> getReqOptValueAsInt(StringRef Value) { |
| if (Value.equals_lower("true")) |
| return 1; |
| if (Value.equals_lower("false")) |
| return 0; |
| int64_t Ret; |
| if (Value.find_first_not_of("-0123456789") != StringRef::npos || |
| Value.getAsInteger(0, Ret)) { |
| return None; |
| } |
| return Ret; |
| } |
| |
| static Optional<sourcekitd_uid_t> getReqOptValueAsUID(StringRef Value) { |
| if (!Value.startswith("uid:")) |
| return None; |
| Value = Value.drop_front(4); |
| return sourcekitd_uid_get_from_buf(Value.data(), Value.size()); |
| } |
| |
| static Optional<sourcekitd_object_t> getReqOptValueAsArray(StringRef Value) { |
| if (!Value.startswith("[") || !Value.endswith("]")) |
| return None; |
| 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; |
| } |
| |
| namespace { |
| class PrintingTimer { |
| std::string desc; |
| llvm::sys::TimePoint<> start; |
| llvm::raw_ostream &OS; |
| public: |
| PrintingTimer(std::string desc, llvm::raw_ostream &OS = llvm::errs()) |
| : desc(std::move(desc)), start(std::chrono::system_clock::now()), OS(OS) { |
| } |
| ~PrintingTimer() { |
| std::chrono::duration<float, std::milli> delta( |
| std::chrono::system_clock::now() - start); |
| OS << desc << ": " << llvm::formatv("{0:ms+f3}", delta) << "\n"; |
| } |
| }; |
| } |
| |
| /// Wrapper for sourcekitd_send_request_sync that handles printing options. |
| static sourcekitd_response_t sendRequestSync(sourcekitd_object_t req, |
| const TestOptions &opts) { |
| if (opts.PrintRequest) |
| sourcekitd_request_description_dump(req); |
| Optional<PrintingTimer> timer; |
| if (opts.timeRequest) |
| timer.emplace("request time"); |
| return sourcekitd_send_request_sync(req); |
| } |
| |
| static int handleJsonRequestPath(StringRef QueryPath, const TestOptions &Opts) { |
| auto Buffer = getBufferForFilename(QueryPath, 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) { |
| sourcekitd_response_description_dump_filedesc(Resp, STDOUT_FILENO); |
| } |
| return Error ? 1 : 0; |
| } |
| |
| static int performShellExecution(ArrayRef<const char *> Args) { |
| 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 (firstInvocation && Opts.Request != SourceKitRequest::GlobalConfiguration && |
| !Opts.SuppressDefaultConfigRequest) { |
| // We don't fail if this request fails for now so that sourcekitd-test is |
| // still usable with older versions of sourcekitd that don't have the |
| // global-configuration request. |
| if (sendGlobalConfigRequest()) { |
| llvm::outs() << "warning: global configuration request failed\n"; |
| } |
| } |
| |
| if (Opts.ShellExecution) |
| return performShellExecution(Opts.CompilerArgs); |
| |
| 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 bool sendGlobalConfigRequest() { |
| TestOptions Opts; |
| sourcekitd_object_t Req = sourcekitd_request_dictionary_create(nullptr, |
| nullptr, 0); |
| sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestGlobalConfiguration); |
| |
| // For test invocations we default to setting OptimizeForIDE to true. This |
| // matches the use case of the most popular clients of sourcekitd (editors) |
| // and also disables loading locations from .swiftsourceinfo files. This is |
| // desirable for testing because the .swiftsourceinfo for the stdlib is |
| // available when sourcekitd is tested, and can make some stdlib-dependent |
| // sourcekitd tests unstable due to changing source locations from the stdlib |
| // module. |
| sourcekitd_request_dictionary_set_int64(Req, KeyOptimizeForIDE, static_cast<int64_t>(true)); |
| sourcekitd_response_t Resp = sendRequestSync(Req, Opts); |
| bool IsError = sourcekitd_response_is_error(Resp); |
| if (IsError) |
| sourcekitd_response_description_dump(Resp); |
| sourcekitd_request_release(Req); |
| return IsError; |
| } |
| |
| static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { |
| // SWIFT_ENABLE_TENSORFLOW |
| #ifdef SWIFT_SOURCEKIT_USE_INPROC_LIBRARY |
| if (Opts.InMemoryClangModuleCache) { |
| SourceKit::setGlobalInMemoryOutputFileSystem( |
| new clang::InMemoryOutputFileSystem()); |
| } else { |
| SourceKit::setGlobalInMemoryOutputFileSystem(nullptr); |
| } |
| #endif |
| |
| if (!Opts.JsonRequestPath.empty()) |
| return handleJsonRequestPath(Opts.JsonRequestPath, Opts); |
| |
| if (Opts.Request == SourceKitRequest::DemangleNames || |
| Opts.Request == SourceKitRequest::MangleSimpleClasses) |
| Opts.SourceFile.clear(); |
| |
| std::string SourceFile = Opts.SourceFile; |
| if (!SourceFile.empty()) { |
| llvm::SmallString<64> AbsSourceFile; |
| AbsSourceFile += SourceFile; |
| llvm::sys::fs::make_absolute(AbsSourceFile); |
| llvm::sys::path::native(AbsSourceFile); |
| SourceFile = std::string(AbsSourceFile.str()); |
| } |
| std::string SemaName = !Opts.Name.empty() ? Opts.Name : 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.hasValue()) { |
| SourceBuf = llvm::MemoryBuffer::getMemBuffer(*Opts.SourceText, Opts.SourceFile); |
| } else if (!SourceFile.empty()) { |
| SourceBuf = llvm::MemoryBuffer::getMemBuffer( |
| getBufferForFilename(SourceFile, Opts.VFSFiles)->getBuffer(), |
| SourceFile); |
| } |
| |
| // FIXME: we should detect if offset is required but not set. |
| unsigned ByteOffset = Opts.Offset; |
| if (Opts.Line != 0) { |
| ByteOffset = resolveFromLineCol(Opts.Line, Opts.Col, SourceBuf.get()); |
| } |
| |
| if (Opts.EndLine != 0) { |
| Opts.Length = resolveFromLineCol(Opts.EndLine, Opts.EndCol, SourceBuf.get()) - |
| ByteOffset; |
| } |
| |
| 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, SourceFile, |
| Opts.VFSFiles); |
| Length = EndOff - ByteOffset; |
| } |
| sourcekitd_request_dictionary_set_int64(Req, KeyLength, Length); |
| break; |
| } |
| |
| case SourceKitRequest::CollectExpresstionType: { |
| sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestCollectExpressionType); |
| addRequestOptionsDirect(Req, Opts); |
| break; |
| } |
| |
| #define SEMANTIC_REFACTORING(KIND, NAME, ID) case SourceKitRequest::KIND: \ |
| { \ |
| sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestSemanticRefactoring); \ |
| sourcekitd_request_dictionary_set_uid(Req, KeyActionUID, KindRefactoring##KIND); \ |
| sourcekitd_request_dictionary_set_string(Req, KeyName, Opts.Name.c_str()); \ |
| sourcekitd_request_dictionary_set_int64(Req, KeyLine, Opts.Line); \ |
| sourcekitd_request_dictionary_set_int64(Req, KeyColumn, Opts.Col); \ |
| sourcekitd_request_dictionary_set_int64(Req, KeyLength, Opts.Length); \ |
| break; \ |
| } |
| #include "swift/IDE/RefactoringKinds.def" |
| |
| case SourceKitRequest::MarkupToXML: { |
| sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestMarkupToXML); |
| break; |
| } |
| case SourceKitRequest::NameTranslation: { |
| sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestNameTranslation); |
| sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset); |
| StringRef BaseName; |
| llvm::SmallVector<StringRef, 4> ArgPieces; |
| sourcekitd_uid_t ArgName; |
| if (!Opts.SwiftName.empty()) { |
| sourcekitd_request_dictionary_set_uid(Req, KeyNameKind, KindNameSwift); |
| ArgName = KeyArgNames; |
| StringRef Text(Opts.SwiftName); |
| auto ArgStart = Text.find_first_of('('); |
| if (ArgStart == StringRef::npos) { |
| BaseName = Text; |
| } else { |
| BaseName = Text.substr(0, ArgStart); |
| auto ArgEnd = Text.find_last_of(')'); |
| if (ArgEnd == StringRef::npos) { |
| llvm::errs() << "Swift name is malformed.\n"; |
| return 1; |
| } |
| StringRef AllArgs = Text.substr(ArgStart + 1, ArgEnd - ArgStart - 1); |
| AllArgs.split(ArgPieces, ':'); |
| if (!ArgPieces.empty()) { |
| if (!ArgPieces.back().empty()) { |
| llvm::errs() << "Swift name is malformed.\n"; |
| return 1; |
| } |
| ArgPieces.pop_back(); |
| } |
| } |
| } else if (!Opts.ObjCName.empty()) { |
| sourcekitd_request_dictionary_set_uid(Req, KeyNameKind, KindNameObjc); |
| BaseName = Opts.ObjCName; |
| ArgName = KeySelectorPieces; |
| } else if (!Opts.ObjCSelector.empty()) { |
| sourcekitd_request_dictionary_set_uid(Req, KeyNameKind, KindNameObjc); |
| StringRef Name(Opts.ObjCSelector); |
| Name.split(ArgPieces, ':'); |
| if (ArgPieces.back().empty()) |
| ArgPieces.pop_back(); |
| ArgName = KeySelectorPieces; |
| } else { |
| llvm::errs() << "must specify either -swift-name or -objc-name or -objc-selector\n"; |
| return 1; |
| } |
| if (!BaseName.empty()) { |
| std::string S = BaseName.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::SyntaxMap: |
| sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestEditorOpen); |
| sourcekitd_request_dictionary_set_string(Req, KeyName, SemaName.c_str()); |
| sourcekitd_request_dictionary_set_int64(Req, KeyEnableSyntaxMap, true); |
| sourcekitd_request_dictionary_set_int64(Req, KeyEnableStructure, false); |
| sourcekitd_request_dictionary_set_uid(Req, KeySyntaxTreeTransferMode, |
| KindSyntaxTreeOff); |
| sourcekitd_request_dictionary_set_int64(Req, KeySyntacticOnly, !Opts.UsedSema); |
| break; |
| |
| case SourceKitRequest::Structure: |
| sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestEditorOpen); |
| sourcekitd_request_dictionary_set_string(Req, KeyName, SemaName.c_str()); |
| sourcekitd_request_dictionary_set_int64(Req, KeyEnableSyntaxMap, false); |
| sourcekitd_request_dictionary_set_int64(Req, KeyEnableStructure, true); |
| sourcekitd_request_dictionary_set_uid(Req, KeySyntaxTreeTransferMode, |
| KindSyntaxTreeOff); |
| sourcekitd_request_dictionary_set_int64(Req, KeySyntacticOnly, !Opts.UsedSema); |
| break; |
| |
| case SourceKitRequest::Format: |
| sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestEditorOpen); |
| sourcekitd_request_dictionary_set_string(Req, KeyName, SemaName.c_str()); |
| sourcekitd_request_dictionary_set_int64(Req, KeyEnableSyntaxMap, false); |
| sourcekitd_request_dictionary_set_int64(Req, KeyEnableStructure, false); |
| sourcekitd_request_dictionary_set_uid(Req, KeySyntaxTreeTransferMode, |
| KindSyntaxTreeOff); |
| sourcekitd_request_dictionary_set_int64(Req, KeySyntacticOnly, !Opts.UsedSema); |
| break; |
| |
| case SourceKitRequest::ExpandPlaceholder: |
| sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestEditorOpen); |
| sourcekitd_request_dictionary_set_string(Req, KeyName, SemaName.c_str()); |
| sourcekitd_request_dictionary_set_int64(Req, KeyEnableSyntaxMap, false); |
| sourcekitd_request_dictionary_set_int64(Req, KeyEnableStructure, false); |
| sourcekitd_request_dictionary_set_int64(Req, KeySyntacticOnly, !Opts.UsedSema); |
| break; |
| |
| case SourceKitRequest::SyntaxTree: |
| sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestEditorOpen); |
| sourcekitd_request_dictionary_set_string(Req, KeyName, SemaName.c_str()); |
| sourcekitd_request_dictionary_set_int64(Req, KeyEnableSyntaxMap, false); |
| sourcekitd_request_dictionary_set_int64(Req, KeyEnableStructure, false); |
| sourcekitd_request_dictionary_set_uid(Req, KeySyntaxTreeTransferMode, |
| KindSyntaxTreeFull); |
| sourcekitd_request_dictionary_set_int64(Req, KeySyntacticOnly, true); |
| break; |
| |
| case SourceKitRequest::DocInfo: |
| sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestDocInfo); |
| break; |
| |
| case SourceKitRequest::SemanticInfo: |
| InitOpts.UsedSema = true; |
| sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestEditorOpen); |
| sourcekitd_request_dictionary_set_string(Req, KeyName, SemaName.c_str()); |
| break; |
| |
| case SourceKitRequest::Open: |
| sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestEditorOpen); |
| sourcekitd_request_dictionary_set_string(Req, KeyName, SemaName.c_str()); |
| break; |
| |
| case SourceKitRequest::Close: |
| sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestEditorClose); |
| sourcekitd_request_dictionary_set_string(Req, KeyName, SemaName.c_str()); |
| break; |
| |
| case SourceKitRequest::Edit: |
| sourcekitd_request_dictionary_set_uid(Req, KeyRequest, |
| RequestEditorReplaceText); |
| sourcekitd_request_dictionary_set_string(Req, KeyName, SemaName.c_str()); |
| sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset); |
| sourcekitd_request_dictionary_set_int64(Req, KeyLength, Opts.Length); |
| sourcekitd_request_dictionary_set_string(Req, KeySourceText, |
| Opts.ReplaceText.getValue().c_str()); |
| break; |
| |
| case SourceKitRequest::PrintAnnotations: |
| return printAnnotations(); |
| case SourceKitRequest::PrintDiags: |
| return printDiags(); |
| case SourceKitRequest::ExtractComment: |
| if (Opts.SourceFile.empty()) { |
| llvm::errs() << "Missing '<source-file>' \n"; |
| return 1; |
| } |
| sourcekitd_request_dictionary_set_uid(Req, KeyRequest, |
| RequestEditorExtractTextFromComment); |
| break; |
| |
| case SourceKitRequest::InterfaceGen: |
| case SourceKitRequest::InterfaceGenOpen: |
| sourcekitd_request_dictionary_set_int64(Req, KeySynthesizedExtension, |
| Opts.SynthesizedExtensions); |
| if (Opts.ModuleName.empty() && Opts.HeaderPath.empty() && |
| Opts.SourceFile.empty() && Opts.USR.empty()) { |
| llvm::errs() << "Missing '-module <module name>' or '-header <path>'" << |
| "or '<source-file>' or '-usr <USR>' \n"; |
| return 1; |
| } |
| if (!Opts.ModuleName.empty()) { |
| sourcekitd_request_dictionary_set_uid(Req, KeyRequest, |
| RequestEditorOpenInterface); |
| } else if (!Opts.USR.empty()) { |
| sourcekitd_request_dictionary_set_uid(Req, KeyRequest, |
| RequestEditorOpenSwiftTypeInterface); |
| } else if (!Opts.SourceFile.empty()) { |
| sourcekitd_request_dictionary_set_uid(Req, KeyRequest, |
| RequestEditorOpenSwiftSourceInterface); |
| } else { |
| if (Opts.UsingSwiftArgs) |
| sourcekitd_request_dictionary_set_int64(Req, KeyUsingSwiftArgs, true); |
| 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()); |
| break; |
| |
| case SourceKitRequest::FindInterfaceDoc: |
| if (Opts.ModuleName.empty()) { |
| llvm::errs() << "Missing '-module <module name>'\n"; |
| return 1; |
| } |
| sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestEditorFindInterfaceDoc); |
| break; |
| |
| case SourceKitRequest::FindUSR: |
| if (Opts.USR.empty()) { |
| llvm::errs() << "Missing '-usr <USR string>'\n"; |
| return 1; |
| } |
| sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestEditorFindUSR); |
| sourcekitd_request_dictionary_set_string(Req, KeyUSR, Opts.USR.c_str()); |
| break; |
| |
| case SourceKitRequest::ModuleGroups: |
| if (Opts.ModuleName.empty()) { |
| llvm::errs() << "Missing '-module <module name>'\n"; |
| return 1; |
| } |
| sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestModuleGroups); |
| break; |
| |
| case SourceKitRequest::FindLocalRenameRanges: |
| sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestFindLocalRenameRanges); |
| sourcekitd_request_dictionary_set_int64(Req, KeyLine, Opts.Line); |
| sourcekitd_request_dictionary_set_int64(Req, KeyColumn, Opts.Col); |
| sourcekitd_request_dictionary_set_int64(Req, KeyLength, Opts.Length); |
| break; |
| |
| case SourceKitRequest::SyntacticRename: |
| case SourceKitRequest::FindRenameRanges: { |
| if (Opts.Request == SourceKitRequest::SyntacticRename) { |
| sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestSyntacticRename); |
| } else { |
| sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestFindRenameRanges); |
| } |
| if (Opts.RenameSpecPath.empty()) { |
| llvm::errs() << "Missing '-rename-spec <file path>'\n"; |
| return 1; |
| } |
| auto Buffer = |
| getBufferForFilename(Opts.RenameSpecPath, 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; |
| } |
| |
| if (!SourceFile.empty()) { |
| if (Opts.PassAsSourceText) { |
| auto Buf = getBufferForFilename(SourceFile, Opts.VFSFiles); |
| sourcekitd_request_dictionary_set_string(Req, KeySourceText, |
| Buf->getBufferStart()); |
| } |
| sourcekitd_request_dictionary_set_string(Req, KeySourceFile, |
| SourceFile.c_str()); |
| } |
| |
| if (Opts.SourceText) { |
| sourcekitd_request_dictionary_set_string(Req, KeySourceText, |
| Opts.SourceText->c_str()); |
| sourcekitd_request_dictionary_set_string(Req, KeySourceFile, |
| SemaName.c_str()); |
| } |
| |
| if (!Opts.CompilerArgs.empty()) { |
| 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()); |
| } |
| } |
| for (auto Arg : Opts.CompilerArgs) |
| sourcekitd_request_array_set_string(Args, SOURCEKITD_ARRAY_APPEND, Arg); |
| sourcekitd_request_dictionary_set_value(Req, KeyCompilerArgs, Args); |
| sourcekitd_request_release(Args); |
| } |
| |
| if (!Opts.ModuleName.empty()) { |
| sourcekitd_request_dictionary_set_string(Req, KeyModuleName, |
| Opts.ModuleName.c_str()); |
| } |
| if (!Opts.HeaderPath.empty()) { |
| sourcekitd_request_dictionary_set_string(Req, KeyFilePath, |
| Opts.HeaderPath.c_str()); |
| } |
| if (Opts.CancelOnSubsequentRequest.hasValue()) { |
| sourcekitd_request_dictionary_set_int64(Req, KeyCancelOnSubsequentRequest, |
| *Opts.CancelOnSubsequentRequest); |
| } |
| |
| if (!Opts.SwiftVersion.empty()) { |
| if (Opts.PassVersionAsString) { |
| sourcekitd_request_dictionary_set_string(Req, KeySwiftVersion, |
| Opts.SwiftVersion.c_str()); |
| } else { |
| unsigned ver; |
| if (StringRef(Opts.SwiftVersion).getAsInteger(10, ver)) { |
| llvm::errs() << "error: expected integer for 'swift-version'\n"; |
| return true; |
| } |
| sourcekitd_request_dictionary_set_int64(Req, KeySwiftVersion, ver); |
| } |
| } |
| |
| if (Opts.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); |
| } |
| |
| if (!Opts.isAsyncRequest) { |
| sourcekitd_response_t Resp = sendRequestSync(Req, Opts); |
| sourcekitd_request_release(Req); |
| return handleResponse(Resp, Opts, SemaName, std::move(SourceBuf), |
| &InitOpts) |
| ? 1 |
| : 0; |
| } else { |
| #if SOURCEKITD_HAS_BLOCKS |
| AsyncResponseInfo info; |
| info.options = Opts; |
| info.sourceFilename = std::move(SemaName); |
| info.sourceBuffer = std::move(SourceBuf); |
| unsigned respIndex = asyncResponses.size(); |
| asyncResponses.push_back(std::move(info)); |
| |
| if (Opts.PrintRequest) |
| sourcekitd_request_description_dump(Req); |
| |
| sourcekitd_send_request(Req, nullptr, ^(sourcekitd_response_t resp) { |
| auto &info = asyncResponses[respIndex]; |
| info.response = resp; |
| info.semaphore.signal(); // Ready to be handled! |
| }); |
| |
| #else |
| llvm::report_fatal_error( |
| "-async not supported when sourcekitd is built without blocks support"); |
| #endif |
| |
| sourcekitd_request_release(Req); |
| return 0; |
| } |
| } |
| |
| static bool handleResponse(sourcekitd_response_t Resp, const TestOptions &Opts, |
| const std::string &SourceFile, |
| std::unique_ptr<llvm::MemoryBuffer> SourceBuf, |
| TestOptions *InitOpts) { |
| bool KeepResponseAlive = false; |
| bool IsError = sourcekitd_response_is_error(Resp); |
| if (IsError) { |
| sourcekitd_response_description_dump(Resp); |
| |
| } else if (!Opts.PrintResponse) { |
| // Nothing. |
| } else if (Opts.PrintResponseAsJSON) { |
| sourcekitd_variant_t Info = sourcekitd_response_get_value(Resp); |
| char *json = sourcekitd_variant_json_description_copy(Info); |
| llvm::outs() << json << '\n'; |
| free(json); |
| |
| } else if (Opts.PrintRawResponse) { |
| sourcekitd_response_description_dump_filedesc(Resp, STDOUT_FILENO); |
| |
| } else { |
| sourcekitd_variant_t Info = sourcekitd_response_get_value(Resp); |
| switch (Opts.Request) { |
| case SourceKitRequest::None: |
| llvm_unreachable("request should be set"); |
| case SourceKitRequest::PrintAnnotations: |
| case SourceKitRequest::PrintDiags: |
| llvm_unreachable("print-annotations/print-diags is handled elsewhere"); |
| |
| case SourceKitRequest::EnableCompileNotifications: |
| // Ignore the response. If it was an error it is handled above. |
| break; |
| |
| case SourceKitRequest::Open: |
| getSemanticInfo(Info, SourceFile); |
| KeepResponseAlive = true; |
| break; |
| |
| case SourceKitRequest::Edit: |
| if (Opts.Length == 0 && Opts.ReplaceText->empty()) { |
| // Length=0, replace="" is a nop and will not trigger sema. |
| sourcekitd_response_description_dump_filedesc(Resp, STDOUT_FILENO); |
| } else { |
| getSemanticInfo(Info, SourceFile); |
| KeepResponseAlive = true; |
| } |
| break; |
| |
| case SourceKitRequest::DemangleNames: |
| printDemangleResults(sourcekitd_response_get_value(Resp), outs()); |
| break; |
| |
| case SourceKitRequest::MangleSimpleClasses: |
| printMangleResults(sourcekitd_response_get_value(Resp), outs()); |
| break; |
| |
| case SourceKitRequest::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: |
| sourcekitd_response_description_dump_filedesc(Resp, STDOUT_FILENO); |
| break; |
| |
| case SourceKitRequest::RelatedIdents: |
| printRelatedIdents(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::CollectExpresstionType: |
| printExpressionType(Info, llvm::outs()); |
| break; |
| |
| case SourceKitRequest::DocInfo: |
| printDocInfo(Info, SourceFile); |
| break; |
| |
| case SourceKitRequest::SemanticInfo: |
| getSemanticInfo(Info, SourceFile); |
| printSemanticInfo(); |
| break; |
| |
| case SourceKitRequest::InterfaceGen: |
| printInterfaceGen(Info, Opts.CheckInterfaceIsASCII); |
| break; |
| |
| case SourceKitRequest::ExtractComment: |
| case SourceKitRequest::MarkupToXML: |
| printNormalizedDocComment(Info); |
| break; |
| |
| case SourceKitRequest::InterfaceGenOpen: |
| // Just initialize the options for the subsequent request. |
| assert(!Opts.isAsyncRequest && InitOpts && |
| "async interface-gen-open is not supported"); |
| InitOpts->SourceFile = getInterfaceGenDocumentName(); |
| InitOpts->SourceText = |
| sourcekitd_variant_dictionary_get_string(Info, KeySourceText); |
| break; |
| |
| case SourceKitRequest::FindInterfaceDoc: |
| printFoundInterface(Info, llvm::outs()); |
| break; |
| |
| case SourceKitRequest::FindUSR: |
| printFoundUSR(Info, SourceBuf.get(), llvm::outs()); |
| break; |
| |
| case SourceKitRequest::SyntaxTree: { |
| // Print only the serialized syntax tree. |
| llvm::outs() << sourcekitd_variant_dictionary_get_string( |
| sourcekitd_response_get_value(Resp), KeySerializedSyntaxTree); |
| llvm::outs() << '\n'; |
| break; |
| } |
| case SourceKitRequest::SyntaxMap: |
| case SourceKitRequest::Structure: |
| sourcekitd_response_description_dump_filedesc(Resp, STDOUT_FILENO); |
| if (Opts.ReplaceText.hasValue()) { |
| unsigned Offset = |
| resolveFromLineCol(Opts.Line, Opts.Col, SourceFile, 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.getValue().c_str()); |
| bool EnableSyntaxMax = Opts.Request == SourceKitRequest::SyntaxMap; |
| bool EnableSubStructure = Opts.Request == SourceKitRequest::Structure; |
| sourcekitd_request_dictionary_set_int64(EdReq, KeyEnableSyntaxMap, |
| EnableSyntaxMax); |
| sourcekitd_request_dictionary_set_int64(EdReq, KeyEnableStructure, |
| EnableSubStructure); |
| sourcekitd_request_dictionary_set_int64(EdReq, KeySyntacticOnly, |
| !Opts.UsedSema); |
| |
| sourcekitd_response_t EdResp = sendRequestSync(EdReq, Opts); |
| sourcekitd_response_description_dump_filedesc(EdResp, STDOUT_FILENO); |
| sourcekitd_response_dispose(EdResp); |
| sourcekitd_request_release(EdReq); |
| } |
| break; |
| |
| case SourceKitRequest::Format: |
| { |
| sourcekitd_object_t Fmt = sourcekitd_request_dictionary_create(nullptr, |
| nullptr, 0); |
| sourcekitd_request_dictionary_set_uid(Fmt, KeyRequest, |
| RequestEditorFormatText); |
| sourcekitd_request_dictionary_set_string(Fmt, KeyName, |
| SourceFile.c_str()); |
| sourcekitd_request_dictionary_set_string(Fmt, KeySourceText, ""); |
| sourcekitd_request_dictionary_set_int64(Fmt, KeyLine, Opts.Line); |
| sourcekitd_request_dictionary_set_int64(Fmt, KeyLength, Opts.Length); |
| |
| if (!Opts.RequestOptions.empty()) { |
| sourcekitd_object_t FO = sourcekitd_request_dictionary_create(nullptr, |
| nullptr, 0); |
| for (auto &FmtOpt : Opts.RequestOptions) { |
| auto KeyValue = StringRef(FmtOpt).split('='); |
| std::string KeyStr("key.editor.format."); |
| KeyStr.append(KeyValue.first.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); |
| sourcekitd_response_description_dump_filedesc(FmtResp, STDOUT_FILENO); |
| sourcekitd_response_dispose(FmtResp); |
| sourcekitd_request_release(Fmt); |
| } |
| break; |
| |
| case SourceKitRequest::ExpandPlaceholder: |
| expandPlaceholders(SourceBuf.get(), llvm::outs()); |
| break; |
| case SourceKitRequest::ModuleGroups: |
| printModuleGroupNames(Info, llvm::outs()); |
| break; |
| #define SEMANTIC_REFACTORING(KIND, NAME, ID) case SourceKitRequest::KIND: |
| #include "swift/IDE/RefactoringKinds.def" |
| case SourceKitRequest::SyntacticRename: |
| printSyntacticRenameEdits(Info, llvm::outs()); |
| break; |
| case SourceKitRequest::FindRenameRanges: |
| case SourceKitRequest::FindLocalRenameRanges: |
| printRenameRanges(Info, llvm::outs()); |
| break; |
| case SourceKitRequest::Statistics: |
| printStatistics(Info, llvm::outs()); |
| } |
| } |
| |
| if (!KeepResponseAlive) |
| sourcekitd_response_dispose(Resp); |
| return IsError; |
| } |
| |
| sourcekitd_variant_t LatestSemaAnnotations = {{0,0,0}}; |
| sourcekitd_variant_t LatestSemaDiags = {{0,0,0}}; |
| |
| static void getSemanticInfoImpl(sourcekitd_variant_t Info) { |
| LatestSemaAnnotations = |
| sourcekitd_variant_dictionary_get_value(Info, KeyAnnotations); |
| LatestSemaDiags = |
| sourcekitd_variant_dictionary_get_value(Info, KeyDiagnostics); |
| } |
| |
| static void 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() { |
| sourcekitd_variant_description_dump_filedesc(LatestSemaAnnotations, |
| STDOUT_FILENO); |
| return 0; |
| } |
| |
| static int printDiags() { |
| sourcekitd_variant_description_dump_filedesc(LatestSemaDiags, STDOUT_FILENO); |
| return 0; |
| } |
| |
| static void printSemanticInfo() { |
| printAnnotations(); |
| if (sourcekitd_variant_get_type(LatestSemaDiags) != SOURCEKITD_VARIANT_TYPE_NULL) |
| printDiags(); |
| } |
| |
| static void notification_receiver(sourcekitd_response_t resp) { |
| if (sourcekitd_response_is_error(resp)) { |
| sourcekitd_response_description_dump(resp); |
| exit(1); |
| } |
| |
| sourcekitd_variant_t payload = sourcekitd_response_get_value(resp); |
| sourcekitd_uid_t note = |
| sourcekitd_variant_dictionary_get_uid(payload, KeyNotification); |
| |
| if (note == NoteDocUpdate) { |
| semaName = sourcekitd_variant_dictionary_get_string(payload, KeyName); |
| |
| sourcekitd_object_t edReq = sourcekitd_request_dictionary_create(nullptr, |
| nullptr, 0); |
| sourcekitd_request_dictionary_set_uid(edReq, KeyRequest, |
| RequestEditorReplaceText); |
| sourcekitd_request_dictionary_set_string(edReq, KeyName, semaName); |
| sourcekitd_request_dictionary_set_string(edReq, KeySourceText, ""); |
| semaResponse = sourcekitd_send_request_sync(edReq); |
| sourcekitd_request_release(edReq); |
| semaSemaphore.signal(); |
| } else if (note == NoteTest) { |
| noteSyncSemaphore.signal(); |
| } else { |
| notificationBuffer.add(resp); |
| } |
| } |
| |
| static void printNameTranslationInfo(sourcekitd_variant_t Info, |
| llvm::raw_ostream &OS) { |
| 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'; |
| } |
| |
| 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; |
| } |
| sourcekitd_uid_t KindUID = sourcekitd_variant_dictionary_get_uid(Info, |
| sourcekitd_uid_get_from_cstr("key.kind")); |
| if (KindUID == nullptr) { |
| OS << "<empty cursor info>\n"; |
| return; |
| } |
| |
| std::string Filename = FilenameIn.str(); |
| llvm::SmallString<256> output; |
| if (!llvm::sys::fs::real_path(Filename, output)) |
| Filename = std::string(output.str()); |
| |
| const char *Kind = sourcekitd_uid_get_string_ptr(KindUID); |
| const char *USR = sourcekitd_variant_dictionary_get_string(Info, KeyUSR); |
| const char *Name = sourcekitd_variant_dictionary_get_string(Info, KeyName); |
| const char *Typename = sourcekitd_variant_dictionary_get_string(Info, |
| KeyTypeName); |
| const char *TypeUsr = sourcekitd_variant_dictionary_get_string(Info, |
| KeyTypeUsr); |
| const char *ContainerTypeUsr = sourcekitd_variant_dictionary_get_string(Info, |
| KeyContainerTypeUsr); |
| const char *ModuleName = sourcekitd_variant_dictionary_get_string(Info, |
| KeyModuleName); |
| const char *GroupName = sourcekitd_variant_dictionary_get_string(Info, |
| KeyGroupName); |
| |
| const char *LocalizationKey = |
| sourcekitd_variant_dictionary_get_string(Info, KeyLocalizationKey); |
| const char *ModuleInterfaceName = |
| sourcekitd_variant_dictionary_get_string(Info, KeyModuleInterfaceName); |
| const char *TypeInterface = |
| sourcekitd_variant_dictionary_get_string(Info, KeyTypeInterface); |
| bool IsSystem = sourcekitd_variant_dictionary_get_bool(Info, KeyIsSystem); |
| const char *AnnotDecl = sourcekitd_variant_dictionary_get_string(Info, |
| KeyAnnotatedDecl); |
| const char *FullAnnotDecl = |
| sourcekitd_variant_dictionary_get_string(Info, KeyFullyAnnotatedDecl); |
| const char *SymbolGraph = |
| sourcekitd_variant_dictionary_get_string(Info, KeySymbolGraph); |
| const char *DocFullAsXML = |
| sourcekitd_variant_dictionary_get_string(Info, KeyDocFullAsXML); |
| sourcekitd_variant_t OffsetObj = |
| sourcekitd_variant_dictionary_get_value(Info, KeyOffset); |
| llvm::Optional<int64_t> Offset; |
| unsigned Length = 0; |
| if (sourcekitd_variant_get_type(OffsetObj) != SOURCEKITD_VARIANT_TYPE_NULL) { |
| Offset = sourcekitd_variant_int64_get_value(OffsetObj); |
| Length = sourcekitd_variant_dictionary_get_int64(Info, KeyLength); |
| } |
| const char *FilePath = sourcekitd_variant_dictionary_get_string(Info, KeyFilePath); |
| |
| std::vector<const char *> OverrideUSRs; |
| sourcekitd_variant_t OverridesObj = |
| sourcekitd_variant_dictionary_get_value(Info, KeyOverrides); |
| for (unsigned i = 0, e = sourcekitd_variant_array_get_count(OverridesObj); |
| i != e; ++i) { |
| sourcekitd_variant_t Entry = |
| sourcekitd_variant_array_get_value(OverridesObj, i); |
| OverrideUSRs.push_back(sourcekitd_variant_dictionary_get_string(Entry, KeyUSR)); |
| } |
| |
| std::vector<const char *> GroupNames; |
| sourcekitd_variant_t GroupObj = |
| sourcekitd_variant_dictionary_get_value(Info, KeyModuleGroups); |
| for (unsigned i = 0, e = sourcekitd_variant_array_get_count(GroupObj); |
| i != e; ++i) { |
| sourcekitd_variant_t Entry = |
| sourcekitd_variant_array_get_value(GroupObj, i); |
| GroupNames.push_back(sourcekitd_variant_dictionary_get_string(Entry, KeyGroupName)); |
| } |
| |
| std::vector<const char *> RelatedDecls; |
| sourcekitd_variant_t RelatedDeclsObj = |
| sourcekitd_variant_dictionary_get_value(Info, KeyRelatedDecls); |
| for (unsigned i = 0, e = sourcekitd_variant_array_get_count(RelatedDeclsObj); |
| i != e; ++i) { |
| sourcekitd_variant_t Entry = |
| sourcekitd_variant_array_get_value(RelatedDeclsObj, i); |
| RelatedDecls.push_back(sourcekitd_variant_dictionary_get_string(Entry, |
| KeyAnnotatedDecl)); |
| } |
| |
| struct ActionInfo { |
| const char* KindUID; |
| const char* KindName; |
| const char* UnavailReason; |
| }; |
| std::vector<ActionInfo> AvailableActions; |
| sourcekitd_variant_t ActionsObj = |
| sourcekitd_variant_dictionary_get_value(Info, KeyRefactorActions); |
| for (unsigned i = 0, e = sourcekitd_variant_array_get_count(ActionsObj); |
| i != e; ++i) { |
| sourcekitd_variant_t Entry = |
| sourcekitd_variant_array_get_value(ActionsObj, i); |
| AvailableActions.push_back({ |
| sourcekitd_uid_get_string_ptr(sourcekitd_variant_dictionary_get_uid(Entry, |
| KeyActionUID)), |
| sourcekitd_variant_dictionary_get_string(Entry, KeyActionName), |
| sourcekitd_variant_dictionary_get_string(Entry, KeyActionUnavailableReason) |
| }); |
| } |
| |
| uint64_t ParentOffset = |
| sourcekitd_variant_dictionary_get_int64(Info, KeyParentLoc); |
| |
| OS << Kind << " ("; |
| if (Offset.hasValue()) { |
| if (Filename != FilePath) |
| OS << FilePath << ":"; |
| auto LineCol = resolveToLineCol(Offset.getValue(), FilePath, VFSFiles); |
| OS << LineCol.first << ':' << LineCol.second; |
| auto EndLineCol = |
| resolveToLineCol(Offset.getValue() + Length, FilePath, VFSFiles); |
| OS << '-' << EndLineCol.first << ':' << EndLineCol.second; |
| } |
| OS << ")\n"; |
| OS << Name << '\n'; |
| if (USR) |
| OS << USR << '\n'; |
| if (Typename) |
| OS << Typename << '\n'; |
| if (TypeUsr) |
| OS << TypeUsr << '\n'; |
| if (ContainerTypeUsr) |
| OS << "<Container>" << ContainerTypeUsr << "</Container>" << '\n'; |
| if (ModuleName) |
| OS << ModuleName << '\n'; |
| if (GroupName) |
| OS << "<Group>" << GroupName << "</Group>" << '\n'; |
| if (ModuleInterfaceName) |
| OS << ModuleInterfaceName << '\n'; |
| if (IsSystem) |
| OS << "SYSTEM\n"; |
| if (AnnotDecl) |
| OS << AnnotDecl << '\n'; |
| if (FullAnnotDecl) |
| OS << FullAnnotDecl << '\n'; |
| if (DocFullAsXML) |
| OS << DocFullAsXML << '\n'; |
| if (LocalizationKey) { |
| OS << "<LocalizationKey>" << LocalizationKey; |
| OS << "</LocalizationKey>" << '\n'; |
| } |
| if (SymbolGraph) { |
| OS << "SYMBOL GRAPH BEGIN\n"; |
| if (auto Val = json::parse(StringRef(SymbolGraph))) { |
| OS << formatv("{0:2}", Val.get()); |
| } else { |
| OS << SymbolGraph; |
| } |
| OS << "\nSYMBOL GRAPH END\n"; |
| } |
| OS << "OVERRIDES BEGIN\n"; |
| for (auto OverUSR : OverrideUSRs) |
| OS << OverUSR << '\n'; |
| OS << "OVERRIDES END\n"; |
| OS << "RELATED BEGIN\n"; |
| for (auto RelDecl : RelatedDecls) |
| OS << RelDecl << '\n'; |
| OS << "RELATED END\n"; |
| OS << "TYPE INTERFACE BEGIN\n"; |
| if (TypeInterface) |
| OS << TypeInterface << '\n'; |
| OS << "TYPE INTERFACE END\n"; |
| OS << "MODULE GROUPS BEGIN\n"; |
| for (auto Group : GroupNames) |
| OS << Group << '\n'; |
| OS << "MODULE GROUPS END\n"; |
| OS << "ACTIONS BEGIN\n"; |
| for (auto Action : AvailableActions) { |
| OS << Action.KindUID << '\n'; |
| OS << Action.KindName << '\n'; |
| if (Action.UnavailReason) { |
| OS << Action.UnavailReason << '\n'; |
| } |
| } |
| OS << "ACTIONS END\n"; |
| if (ParentOffset) { |
| OS << "PARENT OFFSET: " << ParentOffset << "\n"; |
| } |
| } |
| |
| static void printRangeInfo(sourcekitd_variant_t Info, StringRef FilenameIn, |
| llvm::raw_ostream &OS) { |
| sourcekitd_uid_t KindUID = sourcekitd_variant_dictionary_get_uid(Info, |
| sourcekitd_uid_get_from_cstr("key.kind")); |
| if (KindUID == nullptr) { |
| OS << "<empty range info>\n"; |
| return; |
| } |
| |
| std::string Filename = FilenameIn.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); |
| llvm::Optional<int64_t> Offset; |
| if (sourcekitd_variant_get_type(OffsetObj) != SOURCEKITD_VARIANT_TYPE_NULL) { |
| Offset = sourcekitd_variant_int64_get_value(OffsetObj); |
| } |
| const char *Kind = sourcekitd_uid_get_string_ptr(KindUID); |
| const char *Typename = sourcekitd_variant_dictionary_get_string(Info, |
| KeyTypeName); |
| |
| const char *RangeContent = sourcekitd_variant_dictionary_get_string(Info, |
| KeyRangeContent); |
| OS << "<kind>" << Kind << "</kind>\n"; |
| OS << "<content>" <<RangeContent << "</content>\n"; |
| if (Typename) |
| OS << "<type>" <<Typename << "</type>\n"; |
| } |
| |
| static void 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 printFoundInterface(sourcekitd_variant_t Info, |
| llvm::raw_ostream &OS) { |
| const char *Name = sourcekitd_variant_dictionary_get_string(Info, |
| KeyModuleInterfaceName); |
| OS << "DOC: ("; |
| if (Name) |
| OS << Name; |
| OS << ")\n"; |
| |
| sourcekitd_variant_t ArgObj = |
| sourcekitd_variant_dictionary_get_value(Info, KeyCompilerArgs); |
| |
| OS << "ARGS: ["; |
| if (sourcekitd_variant_get_type(ArgObj) != SOURCEKITD_VARIANT_TYPE_NULL) { |
| for (unsigned i = 0, e = sourcekitd_variant_array_get_count(ArgObj); |
| i != e; ++i) { |
| OS << sourcekitd_variant_array_get_string(ArgObj, i); |
| OS << ' '; |
| } |
| } |
| OS << "]\n"; |
| } |
| |
| static void printFoundUSR(sourcekitd_variant_t Info, |
| llvm::MemoryBuffer *SourceBuf, |
| llvm::raw_ostream &OS) { |
| sourcekitd_variant_t OffsetObj = |
| sourcekitd_variant_dictionary_get_value(Info, KeyOffset); |
| llvm::Optional<int64_t> Offset; |
| if (sourcekitd_variant_get_type(OffsetObj) != SOURCEKITD_VARIANT_TYPE_NULL) |
| Offset = sourcekitd_variant_int64_get_value(OffsetObj); |
| |
| if (!Offset.hasValue()) { |
| OS << "USR NOT FOUND\n"; |
| return; |
| } |
| |
| int64_t Length = sourcekitd_variant_dictionary_get_int64(Info, KeyLength); |
| |
| auto LineCol1 = resolveToLineCol(Offset.getValue(), SourceBuf); |
| auto LineCol2 = resolveToLineCol(Offset.getValue() + Length, SourceBuf); |
| OS << '(' << LineCol1.first << ':' << LineCol1.second << '-' |
| << LineCol2.first << ':' << LineCol2.second << ")\n"; |
| } |
| |
| static void printNormalizedDocComment(sourcekitd_variant_t Info) { |
| sourcekitd_variant_t Source = |
| sourcekitd_variant_dictionary_get_value(Info, KeySourceText); |
| sourcekitd_variant_description_dump_filedesc(Source, STDOUT_FILENO); |
| } |
| |
| static void printDocInfo(sourcekitd_variant_t Info, StringRef Filename) { |
| const char *text = |
| sourcekitd_variant_dictionary_get_string(Info, KeySourceText); |
| llvm::raw_fd_ostream OS(STDOUT_FILENO, /*shouldClose=*/false); |
| if (text) { |
| OS << text << '\n'; |
| OS.flush(); |
| } |
| |
| sourcekitd_variant_t annotations = |
| sourcekitd_variant_dictionary_get_value(Info, KeyAnnotations); |
| sourcekitd_variant_t entities = |
| sourcekitd_variant_dictionary_get_value(Info, |
| sourcekitd_uid_get_from_cstr("key.entities")); |
| sourcekitd_variant_t diags = |
| sourcekitd_variant_dictionary_get_value(Info, KeyDiagnostics); |
| |
| sourcekitd_variant_description_dump_filedesc(annotations, STDOUT_FILENO); |
| sourcekitd_variant_description_dump_filedesc(entities, STDOUT_FILENO); |
| |
| if (sourcekitd_variant_get_type(diags) != SOURCEKITD_VARIANT_TYPE_NULL) |
| sourcekitd_variant_description_dump_filedesc(diags, STDOUT_FILENO); |
| } |
| |
| static void checkTextIsASCII(const char *Text) { |
| for (const char *p = Text; *p; ++p) { |
| if (*p & 0x80) { |
| auto LineCol = resolveToLineColFromBuf(p - Text, Text); |
| |
| llvm::errs() << "!!Interface text is non-ascii!!\n" |
| << "@ " << LineCol.first << ":" << LineCol.second << "\n"; |
| exit(1); |
| } |
| } |
| } |
| |
| static void printModuleGroupNames(sourcekitd_variant_t Info, |
| llvm::raw_ostream &OS) { |
| sourcekitd_variant_t Groups = |
| sourcekitd_variant_dictionary_get_value(Info, KeyModuleGroups); |
| OS << "<GROUPS>\n"; |
| for (unsigned i = 0, e = sourcekitd_variant_array_get_count(Groups); |
| i != e; ++i) { |
| sourcekitd_variant_t Entry = |
| sourcekitd_variant_array_get_value(Groups, i); |
| OS << sourcekitd_variant_dictionary_get_string(Entry, KeyGroupName) << "\n"; |
| } |
| OS << "<\\GROUPS>\n"; |
| } |
| |
| static StringRef getLineColRange(StringRef Text, int64_t StartLine, |
| int64_t StartCol, int64_t EndLine, |
| int64_t EndCol) { |
| unsigned Line = 1; |
| size_t Length = 0; |
| |
| while (Line < StartLine) { |
| Text = Text.split('\n').second; |
| ++Line; |
| } |
| Text = Text.drop_front(StartCol - 1); |
| if (StartLine == EndLine) |
| return Text.substr(0, EndCol - StartCol); |
| |
| while(Line < EndLine) { |
| Length = Text.find('\n', Length) + 1; |
| ++Line; |
| } |
| Length += EndCol - 1; |
| |
| return Text.substr(0, Length); |
| } |
| |
| static void printSyntacticRenameEdits(sourcekitd_variant_t Info, |
| llvm::raw_ostream &OS) { |
| sourcekitd_variant_t CategorizedEdits = |
| sourcekitd_variant_dictionary_get_value(Info, KeyCategorizedEdits); |
| for (unsigned i = 0, e = sourcekitd_variant_array_get_count(CategorizedEdits); |
| i != e; ++i) { |
| sourcekitd_variant_t |
| Categorized = sourcekitd_variant_array_get_value(CategorizedEdits, i); |
| sourcekitd_uid_t |
| Category = sourcekitd_variant_dictionary_get_uid(Categorized, KeyCategory); |
| OS << sourcekitd_uid_get_string_ptr(Category) << ":\n"; |
| |
| sourcekitd_variant_t Edits = |
| sourcekitd_variant_dictionary_get_value(Categorized, KeyEdits); |
| for(unsigned j = 0, je = sourcekitd_variant_array_get_count(Edits); |
| j != je; ++j) { |
| OS << " "; // indent |
| sourcekitd_variant_t Edit = sourcekitd_variant_array_get_value(Edits, j); |
| int64_t Line = sourcekitd_variant_dictionary_get_int64(Edit, KeyLine); |
| int64_t Column = sourcekitd_variant_dictionary_get_int64(Edit, KeyColumn); |
| int64_t EndLine = sourcekitd_variant_dictionary_get_int64(Edit, KeyEndLine); |
| int64_t EndColumn = sourcekitd_variant_dictionary_get_int64(Edit, KeyEndColumn); |
| OS << Line << ':' << Column << '-' << EndLine << ':' << EndColumn << " \""; |
| StringRef Text(sourcekitd_variant_dictionary_get_string(Edit, KeyText)); |
| OS << Text << "\"\n"; |
| sourcekitd_variant_t NoteRanges = |
| sourcekitd_variant_dictionary_get_value(Edit, KeyRangesWorthNote); |
| if (unsigned e = sourcekitd_variant_array_get_count(NoteRanges)) { |
| for (unsigned i = 0; i != e; ++i) { |
| OS << " <note>"; |
| sourcekitd_variant_t Note = |
| sourcekitd_variant_array_get_value(NoteRanges, i); |
| int64_t Line = sourcekitd_variant_dictionary_get_int64(Note, KeyLine); |
| int64_t Column = sourcekitd_variant_dictionary_get_int64(Note, KeyColumn); |
| int64_t EndLine = sourcekitd_variant_dictionary_get_int64(Note, KeyEndLine); |
| int64_t EndColumn = sourcekitd_variant_dictionary_get_int64(Note, KeyEndColumn); |
| auto index = sourcekitd_variant_dictionary_get_value(Note, KeyArgIndex); |
| sourcekitd_uid_t Kind = sourcekitd_variant_dictionary_get_uid(Note, |
| KeyKind); |
| StringRef NoteText = getLineColRange(Text, Line, Column, EndLine, EndColumn); |
| OS << sourcekitd_uid_get_string_ptr(Kind) << " "; |
| OS << Line << ":" << Column << "-" << EndLine << ":" << EndColumn; |
| if (sourcekitd_variant_get_type(index) != SOURCEKITD_VARIANT_TYPE_NULL) { |
| OS << " arg-index=" << sourcekitd_variant_int64_get_value(index); |
| } |
| OS << " \"" << NoteText << "\""; |
| OS << "</note>\n"; |
| } |
| } |
| } |
| } |
| } |
| |
| static void printRenameRanges(sourcekitd_variant_t Info, |
| llvm::raw_ostream &OS) { |
| sourcekitd_variant_t CategorizedRanges = |
| sourcekitd_variant_dictionary_get_value(Info, KeyCategorizedRanges); |
| sourcekitd_variant_array_apply(CategorizedRanges, ^bool( |
| size_t i, |
| sourcekitd_variant_t Categorized) { |
| sourcekitd_uid_t Category = |
| sourcekitd_variant_dictionary_get_uid(Categorized, KeyCategory); |
| OS << sourcekitd_uid_get_string_ptr(Category) << ":\n"; |
| |
| sourcekitd_variant_t Ranges = |
| sourcekitd_variant_dictionary_get_value(Categorized, KeyRanges); |
| sourcekitd_variant_array_apply(Ranges, ^bool(size_t j, |
| sourcekitd_variant_t Range) { |
| |
| OS << " "; // indent |
| int64_t Line = sourcekitd_variant_dictionary_get_int64(Range, KeyLine); |
| int64_t Column = |
| sourcekitd_variant_dictionary_get_int64(Range, KeyColumn); |
| int64_t EndLine = |
| sourcekitd_variant_dictionary_get_int64(Range, KeyEndLine); |
| int64_t EndColumn = |
| sourcekitd_variant_dictionary_get_int64(Range, KeyEndColumn); |
| OS << Line << ':' << Column << '-' << EndLine << ':' << EndColumn << " "; |
| auto Kind = sourcekitd_variant_dictionary_get_uid(Range, KeyKind); |
| OS << sourcekitd_uid_get_string_ptr(Kind); |
| auto index = sourcekitd_variant_dictionary_get_value(Range, KeyArgIndex); |
| if (sourcekitd_variant_get_type(index) != SOURCEKITD_VARIANT_TYPE_NULL) { |
| OS << " arg-index=" << sourcekitd_variant_int64_get_value(index); |
| } |
| OS << "\n"; |
| return true; |
| }); |
| return true; |
| }); |
| } |
| |
| static void printInterfaceGen(sourcekitd_variant_t Info, bool CheckASCII) { |
| const char *text = |
| sourcekitd_variant_dictionary_get_string(Info, KeySourceText); |
| |
| if (text) { |
| llvm::raw_fd_ostream OS(STDOUT_FILENO, /*shouldClose=*/false); |
| OS << text << '\n'; |
| } |
| |
| if (CheckASCII) { |
| checkTextIsASCII(text); |
| } |
| |
| sourcekitd_variant_t syntaxmap = |
| sourcekitd_variant_dictionary_get_value(Info, KeySyntaxMap); |
| sourcekitd_variant_description_dump_filedesc(syntaxmap, STDOUT_FILENO); |
| sourcekitd_variant_t annotations = |
| sourcekitd_variant_dictionary_get_value(Info, KeyAnnotations); |
| sourcekitd_variant_description_dump_filedesc(annotations, STDOUT_FILENO); |
| sourcekitd_variant_t structure = |
| sourcekitd_variant_dictionary_get_value(Info, KeySubStructure); |
| sourcekitd_variant_description_dump_filedesc(structure, STDOUT_FILENO); |
| } |
| |
| static void printRelatedIdents(sourcekitd_variant_t Info, StringRef Filename, |
| 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 LineCol = resolveToLineCol(Offset, Filename, VFSFiles); |
| OS << LineCol.first << ':' << LineCol.second << " - " << Length << '\n'; |
| } |
| OS << "END RANGES\n"; |
| } |
| |
| static void prepareDemangleRequest(sourcekitd_object_t Req, |
| const TestOptions &Opts) { |
| sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestDemangle); |
| if (Opts.SimplifiedDemangling) |
| sourcekitd_request_dictionary_set_int64(Req, KeySimplified, 1); |
| sourcekitd_object_t arr = sourcekitd_request_array_create(nullptr, 0); |
| sourcekitd_request_dictionary_set_value(Req, KeyNames, arr); |
| |
| auto addName = [&](StringRef MangledName) { |
| sourcekitd_request_array_set_stringbuf(arr, SOURCEKITD_ARRAY_APPEND, |
| MangledName.data(), MangledName.size()); |
| }; |
| |
| if (Opts.Inputs.empty()) { |
| auto input = llvm::MemoryBuffer::getSTDIN(); |
| if (!input) { |
| llvm::errs() << input.getError().message() << '\n'; |
| ::exit(1); |
| } |
| llvm::StringRef inputContents = input.get()->getBuffer(); |
| |
| // This doesn't handle Unicode symbols, but maybe that's okay. |
| // Also accept the future mangling prefix. |
| llvm::Regex maybeSymbol("(_T|_?\\$[Ss])[_a-zA-Z0-9$.]+"); |
| llvm::SmallVector<llvm::StringRef, 1> matches; |
| while (maybeSymbol.match(inputContents, &matches)) { |
| addName(matches.front()); |
| auto offset = matches.front().data() - inputContents.data(); |
| inputContents = inputContents.substr(offset + matches.front().size()); |
| } |
| |
| } else { |
| for (llvm::StringRef name : Opts.Inputs) { |
| addName(name); |
| } |
| } |
| |
| sourcekitd_request_release(arr); |
| } |
| |
| static void prepareMangleRequest(sourcekitd_object_t Req, |
| const TestOptions &Opts) { |
| sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestMangleSimpleClass); |
| sourcekitd_object_t arr = sourcekitd_request_array_create(nullptr, 0); |
| sourcekitd_request_dictionary_set_value(Req, KeyNames, arr); |
| |
| auto addPair = [&](StringRef ModuleName, StringRef ClassName) { |
| sourcekitd_object_t pair = |
| sourcekitd_request_dictionary_create(nullptr, nullptr, 0); |
| sourcekitd_request_dictionary_set_stringbuf(pair, KeyModuleName, |
| ModuleName.data(), ModuleName.size()); |
| sourcekitd_request_dictionary_set_stringbuf(pair, KeyName, |
| ClassName.data(), ClassName.size()); |
| sourcekitd_request_array_set_value(arr, SOURCEKITD_ARRAY_APPEND, pair); |
| sourcekitd_request_release(pair); |
| }; |
| |
| for (StringRef pair : Opts.Inputs) { |
| auto Idx = pair.find('.'); |
| if (Idx == StringRef::npos) { |
| errs() << "expected pairs with format '<module>.<class name>'\n"; |
| ::exit(1); |
| } |
| StringRef moduleName = pair.substr(0, Idx); |
| StringRef className = pair.substr(Idx+1); |
| addPair(moduleName, className); |
| } |
| |
| sourcekitd_request_release(arr); |
| } |
| |
| static void printDemangleResults(sourcekitd_variant_t Info, raw_ostream &OS) { |
| OS << "START DEMANGLE\n"; |
| sourcekitd_variant_t results = |
| sourcekitd_variant_dictionary_get_value(Info, KeyResults); |
| sourcekitd_variant_array_apply(results, ^bool(size_t index, sourcekitd_variant_t value) { |
| StringRef name = sourcekitd_variant_dictionary_get_string(value, KeyName); |
| if (name.empty()) |
| OS << "<empty>"; |
| else |
| OS << name; |
| OS << '\n'; |
| return true; |
| }); |
| OS << "END DEMANGLE\n"; |
| } |
| |
| static void printMangleResults(sourcekitd_variant_t Info, raw_ostream &OS) { |
| OS << "START MANGLE\n"; |
| sourcekitd_variant_t results = |
| sourcekitd_variant_dictionary_get_value(Info, KeyResults); |
| sourcekitd_variant_array_apply(results, ^bool(size_t index, sourcekitd_variant_t value) { |
| StringRef name = sourcekitd_variant_dictionary_get_string(value, KeyName); |
| if (name.empty()) |
| OS << "<empty>"; |
| else |
| OS << name; |
| OS << '\n'; |
| return true; |
| }); |
| OS << "END MANGLE\n"; |
| } |
| |
| static void printStatistics(sourcekitd_variant_t Info, raw_ostream &OS) { |
| sourcekitd_variant_t results = |
| sourcekitd_variant_dictionary_get_value(Info, KeyResults); |
| sourcekitd_variant_array_apply(results, ^bool(size_t index, sourcekitd_variant_t value) { |
| auto uid = sourcekitd_variant_dictionary_get_uid(value, KeyKind); |
| auto desc = sourcekitd_variant_dictionary_get_string(value, KeyDescription); |
| auto statValue = sourcekitd_variant_dictionary_get_int64(value, KeyValue); |
| OS << statValue << "\t" << desc << "\t- " << sourcekitd_uid_get_string_ptr(uid) << "\n"; |
| return true; |
| }); |
| } |
| |
| static 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 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.startswith("__skip__") || Source.startswith("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 llvm::None; |
| } |
| |
| 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) { |
| return resolveToLineCol(Offset, getBufferForFilename(Filename, VFSFiles)); |
| } |
| |
| static std::pair<unsigned, unsigned> |
| resolveToLineCol(unsigned Offset, llvm::MemoryBuffer *InputBuf) { |
| if (Offset >= InputBuf->getBufferSize()) { |
| llvm::errs() << "offset " << Offset << " for filename '" |
| << InputBuf->getBufferIdentifier() << "' is too large\n"; |
| exit(1); |
| } |
| return resolveToLineColFromBuf(Offset, InputBuf->getBufferStart()); |
| } |
| |
| static std::pair<unsigned, unsigned> |
| resolveToLineColFromBuf(unsigned Offset, const char *Ptr) { |
| const char *End = Ptr+Offset; |
| |
| unsigned Line = 1; |
| const char *LineStart = Ptr; |
| for (; Ptr < End; ++Ptr) { |
| if (*Ptr == '\n') { |
| ++Line; |
| LineStart = Ptr+1; |
| } |
| } |
| unsigned Col = Ptr-LineStart + 1; |
| |
| return { Line, Col }; |
| } |
| |
| static unsigned |
| resolveFromLineCol(unsigned Line, unsigned Col, StringRef Filename, |
| 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); |
| } |
| |
| static llvm::StringMap<llvm::MemoryBuffer*> Buffers; |
| |
| static llvm::MemoryBuffer * |
| getBufferForFilename(StringRef Filename, |
| const llvm::StringMap<TestOptions::VFSFile> &VFSFiles) { |
| auto VFSFileIt = VFSFiles.find(Filename); |
| auto MappedFilename = |
| VFSFileIt == VFSFiles.end() ? Filename : StringRef(VFSFileIt->second.path); |
| |
| auto It = Buffers.find(MappedFilename); |
| if (It != Buffers.end()) |
| return It->second; |
| |
| auto FileBufOrErr = llvm::MemoryBuffer::getFile(MappedFilename); |
| if (!FileBufOrErr) { |
| llvm::errs() << "error opening input file '" << MappedFilename << "' (" |
| << FileBufOrErr.getError().message() << ")\n"; |
| exit(1); |
| } |
| |
| return Buffers[MappedFilename] = FileBufOrErr.get().release(); |
| } |