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