//===--- Requests.cpp -----------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #include "DictionaryKeys.h" #include "sourcekitd/CodeCompletionResultsArray.h" #include "sourcekitd/DocSupportAnnotationArray.h" #include "sourcekitd/TokenAnnotationsArray.h" #include "SourceKit/Core/Context.h" #include "SourceKit/Core/LangSupport.h" #include "SourceKit/Core/NotificationCenter.h" #include "SourceKit/Support/Concurrency.h" #include "SourceKit/Support/Logging.h" #include "SourceKit/Support/UIdent.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" #include // FIXME: Portability. #include using namespace sourcekitd; using namespace SourceKit; namespace { class LazySKDUID { const char *Name; mutable std::atomic UID { nullptr }; public: LazySKDUID(const char *Name) : Name(Name) { } sourcekitd_uid_t get() const { if (!UID) UID = SKDUIDFromUIdent(UIdent(Name)); return UID; } operator sourcekitd_uid_t() const { return get(); } }; } // anonymous namespace. static LazySKDUID RequestIndex("source.request.indexsource"); static LazySKDUID RequestDocInfo("source.request.docinfo"); static LazySKDUID RequestCodeComplete("source.request.codecomplete"); static LazySKDUID RequestCodeCompleteOpen("source.request.codecomplete.open"); static LazySKDUID RequestCodeCompleteClose("source.request.codecomplete.close"); static LazySKDUID RequestCodeCompleteUpdate("source.request.codecomplete.update"); static LazySKDUID RequestCodeCompleteCacheOnDisk("source.request.codecomplete.cache.ondisk"); static LazySKDUID RequestCodeCompleteSetPopularAPI( "source.request.codecomplete.setpopularapi"); static LazySKDUID RequestCodeCompleteSetCustom("source.request.codecomplete.setcustom"); static LazySKDUID RequestCursorInfo("source.request.cursorinfo"); static LazySKDUID RequestRelatedIdents("source.request.relatedidents"); static LazySKDUID RequestEditorOpen("source.request.editor.open"); static LazySKDUID RequestEditorOpenInterface( "source.request.editor.open.interface"); static LazySKDUID RequestEditorOpenHeaderInterface( "source.request.editor.open.interface.header"); static LazySKDUID RequestEditorOpenSwiftSourceInterface( "source.request.editor.open.interface.swiftsource"); static LazySKDUID RequestEditorExtractTextFromComment( "source.request.editor.extract.comment"); static LazySKDUID RequestEditorClose("source.request.editor.close"); static LazySKDUID RequestEditorReplaceText("source.request.editor.replacetext"); static LazySKDUID RequestEditorFormatText("source.request.editor.formattext"); static LazySKDUID RequestEditorExpandPlaceholder( "source.request.editor.expand_placeholder"); static LazySKDUID RequestEditorFindUSR("source.request.editor.find_usr"); static LazySKDUID RequestEditorFindInterfaceDoc( "source.request.editor.find_interface_doc"); static LazySKDUID RequestBuildSettingsRegister( "source.request.buildsettings.register"); static LazySKDUID RequestModuleGroups( "source.request.module.groups"); static LazySKDUID KindExpr("source.lang.swift.expr"); static LazySKDUID KindStmt("source.lang.swift.stmt"); static LazySKDUID KindType("source.lang.swift.type"); static LazySKDUID KindEverything("source.codecompletion.everything"); static LazySKDUID KindModule("source.codecompletion.module"); static LazySKDUID KindKeyword("source.codecompletion.keyword"); static LazySKDUID KindLiteral("source.codecompletion.literal"); static LazySKDUID KindCustom("source.codecompletion.custom"); static LazySKDUID KindIdentifier("source.codecompletion.identifier"); static UIdent DiagKindNote("source.diagnostic.severity.note"); static UIdent DiagKindWarning("source.diagnostic.severity.warning"); static UIdent DiagKindError("source.diagnostic.severity.error"); static void onDocumentUpdateNotification(StringRef DocumentName) { static UIdent DocumentUpdateNotificationUID( "source.notification.editor.documentupdate"); ResponseBuilder RespBuilder; auto Dict = RespBuilder.getDictionary(); Dict.set(KeyNotification, DocumentUpdateNotificationUID); Dict.set(KeyName, DocumentName); sourcekitd::postNotification(RespBuilder.createResponse()); } static SourceKit::Context *GlobalCtx = nullptr; void sourcekitd::initialize() { GlobalCtx = new SourceKit::Context(sourcekitd::getRuntimeLibPath()); GlobalCtx->getNotificationCenter().addDocumentUpdateNotificationReceiver( onDocumentUpdateNotification); } void sourcekitd::shutdown() { delete GlobalCtx; GlobalCtx = nullptr; } static SourceKit::Context &getGlobalContext() { assert(GlobalCtx); return *GlobalCtx; } static sourcekitd_response_t indexSource(StringRef Filename, ArrayRef Args, StringRef KnownHash); static sourcekitd_response_t reportDocInfo(llvm::MemoryBuffer *InputBuf, StringRef ModuleName, ArrayRef Args); static void reportCursorInfo(StringRef Filename, int64_t Offset, ArrayRef Args, ResponseReceiver Rec); static void findRelatedIdents(StringRef Filename, int64_t Offset, ArrayRef Args, ResponseReceiver Rec); static sourcekitd_response_t codeComplete(llvm::MemoryBuffer *InputBuf, int64_t Offset, ArrayRef Args); static sourcekitd_response_t codeCompleteOpen(StringRef name, llvm::MemoryBuffer *InputBuf, int64_t Offset, Optional optionsDict, ArrayRef Args); static sourcekitd_response_t codeCompleteUpdate(StringRef name, int64_t Offset, Optional optionsDict); static sourcekitd_response_t codeCompleteClose(StringRef name, int64_t Offset); static sourcekitd_response_t editorOpen(StringRef Name, llvm::MemoryBuffer *Buf, bool EnableSyntaxMap, bool EnableStructure, bool EnableDiagnostics, bool SyntacticOnly, ArrayRef Args); static sourcekitd_response_t editorOpenInterface(StringRef Name, StringRef ModuleName, Optional Group, ArrayRef Args); static sourcekitd_response_t editorOpenHeaderInterface(StringRef Name, StringRef HeaderName, ArrayRef Args); static void editorOpenSwiftSourceInterface(StringRef Name, StringRef SourceName, ArrayRef Args, ResponseReceiver Rec); static sourcekitd_response_t editorExtractTextFromComment(StringRef Source); static sourcekitd_response_t editorClose(StringRef Name, bool RemoveCache); static sourcekitd_response_t editorReplaceText(StringRef Name, llvm::MemoryBuffer *Buf, unsigned Offset, unsigned Length, bool EnableSyntaxMap, bool EnableStructure, bool EnableDiagnostics, bool SyntacticOnly); static void editorApplyFormatOptions(StringRef Name, RequestDict &FmtOptions); static sourcekitd_response_t editorFormatText(StringRef Name, unsigned Line, unsigned Length); static sourcekitd_response_t editorExpandPlaceholder(StringRef Name, unsigned Offset, unsigned Length); static sourcekitd_response_t editorFindUSR(StringRef DocumentName, StringRef USR); static sourcekitd_response_t editorFindInterfaceDoc(StringRef ModuleName, ArrayRef Args); static sourcekitd_response_t editorFindModuleGroups(StringRef ModuleName, ArrayRef Args); static bool isSemanticEditorDisabled(); static void fillDictionaryForDiagnosticInfo( ResponseBuilder::Dictionary Elem, const DiagnosticEntryInfoBase &Info); static void handleRequestImpl(sourcekitd_object_t Req, ResponseReceiver Receiver); void sourcekitd::handleRequest(sourcekitd_object_t Req, ResponseReceiver Receiver) { LOG_SECTION("handleRequest-before", InfoHighPrio) { sourcekitd::printRequestObject(Req, Log->getOS()); } handleRequestImpl(Req, [Receiver](sourcekitd_response_t Resp) { LOG_SECTION("handleRequest-after", InfoHighPrio) { // Responses are big, print them out with info medium priority. if (Logger::isLoggingEnabledForLevel(Logger::Level::InfoMediumPrio)) sourcekitd::printResponse(Resp, Log->getOS()); } Receiver(Resp); }); } static std::unique_ptr getInputBufForRequest(Optional SourceFile, Optional SourceText, llvm::SmallString<64> &ErrBuf) { std::unique_ptr InputBuf; if (SourceText.hasValue()) { StringRef BufName; if (SourceFile.hasValue()) BufName = *SourceFile; else BufName = ""; InputBuf = llvm::MemoryBuffer::getMemBuffer(*SourceText, BufName); } else if (SourceFile.hasValue()) { llvm::ErrorOr> FileBufOrErr = llvm::MemoryBuffer::getFile(*SourceFile); if (FileBufOrErr) { InputBuf = std::move(FileBufOrErr.get()); } else { llvm::raw_svector_ostream OSErr(ErrBuf); OSErr << "error opening input file '" << *SourceFile << "' (" << FileBufOrErr.getError().message() << ')'; return nullptr; } } else { InputBuf = llvm::MemoryBuffer::getNewMemBuffer(0, ""); } return InputBuf; } static void handleSemanticRequest(RequestDict Req, ResponseReceiver Receiver, sourcekitd_uid_t ReqUID, Optional SourceFile, Optional SourceText, ArrayRef Args); void handleRequestImpl(sourcekitd_object_t ReqObj, ResponseReceiver Rec) { RequestDict Req(ReqObj); sourcekitd_uid_t ReqUID = Req.getUID(KeyRequest); if (!ReqUID) return Rec(createErrorRequestInvalid("missing 'key.request' with UID value")); // Just accept 'source.request.buildsettings.register' for now, don't do // anything else. // FIXME: Heavy WIP here. if (ReqUID == RequestBuildSettingsRegister) { return Rec(ResponseBuilder().createResponse()); } Optional SourceFile = Req.getString(KeySourceFile); Optional SourceText = Req.getString(KeySourceText); llvm::SmallString<64> ErrBuf; SmallVector Args; bool Failed = Req.getStringArray(KeyCompilerArgs, Args, /*isOptional=*/true); if (Failed) { return Rec(createErrorRequestInvalid( "'key.compilerargs' not an array of strings")); } if (ReqUID == RequestDocInfo) { std::unique_ptr InputBuf = getInputBufForRequest(SourceFile, SourceText, ErrBuf); if (!InputBuf) return Rec(createErrorRequestFailed(ErrBuf.c_str())); StringRef ModuleName; Optional ModuleNameOpt = Req.getString(KeyModuleName); if (ModuleNameOpt.hasValue()) ModuleName = *ModuleNameOpt; return Rec(reportDocInfo(InputBuf.get(), ModuleName, Args)); } if (ReqUID == RequestEditorOpen) { Optional Name = Req.getString(KeyName); if (!Name.hasValue()) return Rec(createErrorRequestInvalid("missing 'key.name'")); std::unique_ptr InputBuf = getInputBufForRequest(SourceFile, SourceText, ErrBuf); if (!InputBuf) return Rec(createErrorRequestFailed(ErrBuf.c_str())); int64_t EnableSyntaxMap = true; Req.getInt64(KeyEnableSyntaxMap, EnableSyntaxMap, /*isOptional=*/true); int64_t EnableStructure = true; Req.getInt64(KeyEnableStructure, EnableStructure, /*isOptional=*/true); int64_t EnableDiagnostics = true; Req.getInt64(KeyEnableDiagnostics, EnableDiagnostics, /*isOptional=*/true); int64_t SyntacticOnly = false; Req.getInt64(KeySyntacticOnly, SyntacticOnly, /*isOptional=*/true); return Rec(editorOpen(*Name, InputBuf.get(), EnableSyntaxMap, EnableStructure, EnableDiagnostics, SyntacticOnly, Args)); } if (ReqUID == RequestEditorClose) { Optional Name = Req.getString(KeyName); if (!Name.hasValue()) return Rec(createErrorRequestInvalid("missing 'key.name'")); // Whether we remove the cached AST from libcache, by default, false. int64_t RemoveCache = false; Req.getInt64(KeyRemoveCache, RemoveCache, /*isOptional=*/true); return Rec(editorClose(*Name, RemoveCache)); } if (ReqUID == RequestEditorReplaceText) { Optional Name = Req.getString(KeyName); if (!Name.hasValue()) return Rec(createErrorRequestInvalid("missing 'key.name'")); std::unique_ptr InputBuf = getInputBufForRequest(SourceFile, SourceText, ErrBuf); if (!InputBuf) return Rec(createErrorRequestFailed(ErrBuf.c_str())); int64_t Offset = 0; Req.getInt64(KeyOffset, Offset, /*isOptional=*/true); int64_t Length = 0; Req.getInt64(KeyLength, Length, /*isOptional=*/true); int64_t EnableSyntaxMap = true; Req.getInt64(KeyEnableSyntaxMap, EnableSyntaxMap, /*isOptional=*/true); int64_t EnableStructure = true; Req.getInt64(KeyEnableStructure, EnableStructure, /*isOptional=*/true); int64_t EnableDiagnostics = true; Req.getInt64(KeyEnableDiagnostics, EnableDiagnostics, /*isOptional=*/true); int64_t SyntacticOnly = false; Req.getInt64(KeySyntacticOnly, SyntacticOnly, /*isOptional=*/true); return Rec(editorReplaceText(*Name, InputBuf.get(), Offset, Length, EnableSyntaxMap, EnableStructure, EnableDiagnostics, SyntacticOnly)); } if (ReqUID == RequestEditorFormatText) { Optional Name = Req.getString(KeyName); if (!Name.hasValue()) return Rec(createErrorRequestInvalid("missing 'key.name'")); Optional FmtOptions = Req.getDictionary(KeyFormatOptions); if (FmtOptions.hasValue()) editorApplyFormatOptions(*Name, *FmtOptions); int64_t Line = 0; Req.getInt64(KeyLine, Line, /*isOptional=*/false); int64_t Length = 0; Req.getInt64(KeyLength, Length, /*isOptional=*/true); return Rec(editorFormatText(*Name, Line, Length)); } if (ReqUID == RequestEditorExpandPlaceholder) { Optional Name = Req.getString(KeyName); if (!Name.hasValue()) return Rec(createErrorRequestInvalid("missing 'key.name'")); int64_t Offset = 0; Req.getInt64(KeyOffset, Offset, /*isOptional=*/false); int64_t Length = 0; Req.getInt64(KeyLength, Length, /*isOptional=*/false); return Rec(editorExpandPlaceholder(*Name, Offset, Length)); } if (ReqUID == RequestEditorOpenInterface) { Optional Name = Req.getString(KeyName); if (!Name.hasValue()) return Rec(createErrorRequestInvalid("missing 'key.name'")); Optional ModuleName = Req.getString(KeyModuleName); if (!ModuleName.hasValue()) return Rec(createErrorRequestInvalid("missing 'key.modulename'")); Optional GroupName = Req.getString(KeyGroupName); return Rec(editorOpenInterface(*Name, *ModuleName, GroupName, Args)); } if (ReqUID == RequestEditorOpenHeaderInterface) { Optional Name = Req.getString(KeyName); if (!Name.hasValue()) return Rec(createErrorRequestInvalid("missing 'key.name'")); Optional HeaderName = Req.getString(KeyFilePath); if (!HeaderName.hasValue()) return Rec(createErrorRequestInvalid("missing 'key.filepath'")); return Rec(editorOpenHeaderInterface(*Name, *HeaderName, Args)); } if (ReqUID == RequestEditorOpenSwiftSourceInterface) { Optional Name = Req.getString(KeyName); if (!Name.hasValue()) return Rec(createErrorRequestInvalid("missing 'key.name'")); Optional FileName = Req.getString(KeySourceFile); if (!FileName.hasValue()) return Rec(createErrorRequestInvalid("missing 'key.sourcefile'")); return editorOpenSwiftSourceInterface(*Name, *FileName, Args, Rec); } if (ReqUID == RequestEditorExtractTextFromComment) { Optional Source = Req.getString(KeySourceText); if (!Source.hasValue()) return Rec(createErrorRequestInvalid("missing 'key.sourcetext'")); return Rec(editorExtractTextFromComment(Source.getValue())); } if (ReqUID == RequestEditorFindUSR) { Optional Name = Req.getString(KeySourceFile); if (!Name.hasValue()) return Rec(createErrorRequestInvalid("missing 'key.sourcefile'")); Optional USR = Req.getString(KeyUSR); if (!USR.hasValue()) return Rec(createErrorRequestInvalid("missing 'key.usr'")); return Rec(editorFindUSR(*Name, *USR)); } if (ReqUID == RequestEditorFindInterfaceDoc) { Optional ModuleName = Req.getString(KeyModuleName); if (!ModuleName.hasValue()) return Rec(createErrorRequestInvalid("missing 'key.modulename'")); return Rec(editorFindInterfaceDoc(*ModuleName, Args)); } if (ReqUID == RequestModuleGroups) { Optional ModuleName = Req.getString(KeyModuleName); if (!ModuleName.hasValue()) return Rec(createErrorRequestInvalid("missing 'key.modulename'")); return Rec(editorFindModuleGroups(*ModuleName, Args)); } if (ReqUID == RequestCodeCompleteClose) { // Unlike opening code completion, this is not a semantic request. Optional Name = Req.getString(KeyName); if (!Name.hasValue()) return Rec(createErrorRequestInvalid("missing 'key.name'")); int64_t Offset; if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false)) return Rec(createErrorRequestInvalid("missing 'key.offset'")); return Rec(codeCompleteClose(*Name, Offset)); } if (ReqUID == RequestCodeCompleteCacheOnDisk) { Optional Name = Req.getString(KeyName); if (!Name.hasValue()) return Rec(createErrorRequestInvalid("missing 'key.name'")); LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); Lang.codeCompleteCacheOnDisk(*Name); ResponseBuilder b; return Rec(b.createResponse()); } if (ReqUID == RequestCodeCompleteSetPopularAPI) { llvm::SmallVector popular; llvm::SmallVector unpopular; Req.getStringArray(KeyPopular, popular, /*isOptional=*/false); Req.getStringArray(KeyUnpopular, unpopular, /*isOptional=*/false); LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); Lang.codeCompleteSetPopularAPI(popular, unpopular); ResponseBuilder b; return Rec(b.createResponse()); } if (ReqUID == RequestCodeCompleteSetCustom) { SmallVector customCompletions; sourcekitd_response_t err = createErrorRequestInvalid("missing 'key.results'"); bool failed = Req.dictionaryArrayApply(KeyResults, [&](RequestDict dict) { CustomCompletionInfo CCInfo; Optional Name = dict.getString(KeyName); if (!Name.hasValue()) { err = createErrorRequestInvalid("missing 'key.name'"); return true; } CCInfo.Name = *Name; sourcekitd_uid_t Kind = dict.getUID(KeyKind); if (!Kind) { err = createErrorRequestInvalid("missing 'key.kind'"); return true; } CCInfo.Kind = Kind; SmallVector contexts; if (dict.getUIDArray(KeyContext, contexts, false)) { err = createErrorRequestInvalid("missing 'key.context'"); return true; } for (auto context : contexts) { if (context == KindExpr) { CCInfo.Contexts |= CustomCompletionInfo::Expr; } else if (context == KindStmt) { CCInfo.Contexts |= CustomCompletionInfo::Stmt; } else if (context == KindType) { CCInfo.Contexts |= CustomCompletionInfo::Type; } else { err = createErrorRequestInvalid("invalid value for 'key.context'"); return true; } } customCompletions.push_back(std::move(CCInfo)); return false; }); if (failed) return Rec(err); LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); Lang.codeCompleteSetCustom(customCompletions); return Rec(ResponseBuilder().createResponse()); } if (!SourceFile.hasValue() && !SourceText.hasValue() && ReqUID != RequestCodeCompleteUpdate) return Rec(createErrorRequestInvalid( "missing 'key.sourcefile' or 'key.sourcetext'")); // Requests that need semantic typechecking. // Typechecking arrays can blow up the stack currently. // Run them under a malloc'ed stack. static WorkQueue SemaQueue{ WorkQueue::Dequeuing::Concurrent, "sourcekit.request.semantic" }; sourcekitd_request_retain(ReqObj); SemaQueue.dispatch( [ReqObj, Rec, ReqUID, SourceFile, SourceText, Args] { RequestDict Req(ReqObj); handleSemanticRequest(Req, Rec, ReqUID, SourceFile, SourceText, Args); sourcekitd_request_release(ReqObj); }, /*isStackDeep=*/true); } static void handleSemanticRequest(RequestDict Req, ResponseReceiver Rec, sourcekitd_uid_t ReqUID, Optional SourceFile, Optional SourceText, ArrayRef Args) { llvm::SmallString<64> ErrBuf; if (ReqUID == RequestCodeComplete) { std::unique_ptr InputBuf = getInputBufForRequest(SourceFile, SourceText, ErrBuf); if (!InputBuf) return Rec(createErrorRequestFailed(ErrBuf.c_str())); int64_t Offset; if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false)) return Rec(createErrorRequestInvalid("missing 'key.offset'")); return Rec(codeComplete(InputBuf.get(), Offset, Args)); } if (ReqUID == RequestCodeCompleteOpen) { std::unique_ptr InputBuf = getInputBufForRequest(SourceFile, SourceText, ErrBuf); if (!InputBuf) return Rec(createErrorRequestFailed(ErrBuf.c_str())); Optional Name = Req.getString(KeyName); if (!Name.hasValue()) return Rec(createErrorRequestInvalid("missing 'key.name'")); int64_t Offset; if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false)) return Rec(createErrorRequestInvalid("missing 'key.offset'")); Optional options = Req.getDictionary(KeyCodeCompleteOptions); return Rec(codeCompleteOpen(*Name, InputBuf.get(), Offset, options, Args)); } if (ReqUID == RequestCodeCompleteUpdate) { Optional Name = Req.getString(KeyName); if (!Name.hasValue()) return Rec(createErrorRequestInvalid("missing 'key.name'")); int64_t Offset; if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false)) return Rec(createErrorRequestInvalid("missing 'key.offset'")); Optional options = Req.getDictionary(KeyCodeCompleteOptions); return Rec(codeCompleteUpdate(*Name, Offset, options)); } if (!SourceFile.hasValue()) return Rec(createErrorRequestInvalid("missing 'key.sourcefile'")); if (ReqUID == RequestIndex) { StringRef Hash; Optional HashOpt = Req.getString(KeyHash); if (HashOpt.hasValue()) Hash = *HashOpt; return Rec(indexSource(*SourceFile, Args, Hash)); } if (isSemanticEditorDisabled()) return Rec(createErrorRequestFailed("semantic editor is disabled")); if (ReqUID == RequestCursorInfo) { int64_t Offset; if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false)) return Rec(createErrorRequestInvalid("missing 'key.offset'")); return reportCursorInfo(*SourceFile, Offset, Args, Rec); } if (ReqUID == RequestRelatedIdents) { int64_t Offset; if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false)) return Rec(createErrorRequestInvalid("missing 'key.offset'")); return findRelatedIdents(*SourceFile, Offset, Args, Rec); } { llvm::raw_svector_ostream OSErr(ErrBuf); OSErr << "unknown request: " << UIdentFromSKDUID(ReqUID).getName(); } return Rec(createErrorRequestInvalid(ErrBuf.c_str())); } //===----------------------------------------------------------------------===// // Index //===----------------------------------------------------------------------===// namespace { class SKIndexingConsumer : public IndexingConsumer { struct Entity { UIdent Kind; ResponseBuilder::Dictionary Data; ResponseBuilder::Array Entities; ResponseBuilder::Array Related; }; SmallVector EntitiesStack; struct Dependency { UIdent Kind; ResponseBuilder::Dictionary Data; ResponseBuilder::Array Dependencies; }; SmallVector DependenciesStack; ResponseBuilder::Dictionary TopDict; bool Cancelled = false; public: std::string ErrorDescription; explicit SKIndexingConsumer(ResponseBuilder &RespBuilder) { TopDict = RespBuilder.getDictionary(); // First in stack is the top-level "key.entities" container. EntitiesStack.push_back( { UIdent(), TopDict, ResponseBuilder::Array(), ResponseBuilder::Array() }); DependenciesStack.push_back({UIdent(), TopDict, ResponseBuilder::Array() }); } ~SKIndexingConsumer() { assert(Cancelled || (EntitiesStack.size() == 1 && DependenciesStack.size() == 1)); (void) Cancelled; } void failed(StringRef ErrDescription) override; bool recordHash(StringRef Hash, bool isKnown) override; bool startDependency(UIdent Kind, StringRef Name, StringRef Path, bool IsSystem, StringRef Hash) override; bool finishDependency(UIdent Kind) override; bool startSourceEntity(const EntityInfo &Info) override; bool recordRelatedEntity(const EntityInfo &Info) override; bool finishSourceEntity(UIdent Kind) override; }; } static sourcekitd_response_t indexSource(StringRef Filename, ArrayRef Args, StringRef KnownHash) { ResponseBuilder RespBuilder; SKIndexingConsumer IdxConsumer(RespBuilder); LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); Lang.indexSource(Filename, IdxConsumer, Args, KnownHash); if (!IdxConsumer.ErrorDescription.empty()) return createErrorRequestFailed(IdxConsumer.ErrorDescription.c_str()); return RespBuilder.createResponse(); } void SKIndexingConsumer::failed(StringRef ErrDescription) { ErrorDescription = ErrDescription; } bool SKIndexingConsumer::recordHash(StringRef Hash, bool isKnown) { assert(!Hash.empty()); TopDict.set(KeyHash, Hash); if (!isKnown) { // If the hash is known key.entities should be missing otherwise it should // exist, even as an empty array, so create it here. assert(EntitiesStack.size() == 1); Entity &Top = EntitiesStack.back(); ResponseBuilder::Array &Arr = Top.Entities; assert(Arr.isNull()); Arr = Top.Data.setArray(KeyEntities); } return true; } bool SKIndexingConsumer::startDependency(UIdent Kind, StringRef Name, StringRef Path, bool IsSystem, StringRef Hash) { Dependency &Parent = DependenciesStack.back(); ResponseBuilder::Array &Arr = Parent.Dependencies; if (Arr.isNull()) Arr = Parent.Data.setArray(KeyDependencies); auto Elem = Arr.appendDictionary(); Elem.set(KeyKind, Kind); Elem.set(KeyName, Name); Elem.set(KeyFilePath, Path); if (IsSystem) Elem.setBool(KeyIsSystem, IsSystem); if (!Hash.empty()) Elem.set(KeyHash, Hash); DependenciesStack.push_back({ Kind, Elem, ResponseBuilder::Array() }); return true; } bool SKIndexingConsumer::finishDependency(UIdent Kind) { assert(DependenciesStack.back().Kind == Kind); DependenciesStack.pop_back(); return true; } bool SKIndexingConsumer::startSourceEntity(const EntityInfo &Info) { Entity &Parent = EntitiesStack.back(); ResponseBuilder::Array &Arr = Parent.Entities; if (Arr.isNull()) Arr = Parent.Data.setArray(KeyEntities); auto Elem = Arr.appendDictionary(); Elem.set(KeyKind, Info.Kind); if (!Info.Name.empty()) Elem.set(KeyName, Info.Name); if (!Info.USR.empty()) Elem.set(KeyUSR, Info.USR); if (Info.Line != 0) { assert(Info.Column != 0); Elem.set(KeyLine, Info.Line); Elem.set(KeyColumn, Info.Column); } if (Info.EntityType == EntityInfo::FuncDecl) { const FuncDeclEntityInfo &FDInfo = static_cast(Info); if (FDInfo.IsTestCandidate) Elem.setBool(KeyIsTestCandidate, true); } if (Info.EntityType == EntityInfo::CallReference) { const CallRefEntityInfo &CRInfo = static_cast(Info); if (!CRInfo.ReceiverUSR.empty()) Elem.set(KeyReceiverUSR, CRInfo.ReceiverUSR); if (CRInfo.IsDynamic) Elem.setBool(KeyIsDynamic, true); } EntitiesStack.push_back({ Info.Kind, Elem, ResponseBuilder::Array(), ResponseBuilder::Array()}); return true; } bool SKIndexingConsumer::recordRelatedEntity(const EntityInfo &Info) { assert(EntitiesStack.size() > 1 && "Related entity at top-level ?"); Entity &Parent = EntitiesStack.back(); ResponseBuilder::Array &Arr = Parent.Related; if (Arr.isNull()) Arr = Parent.Data.setArray(KeyRelated); auto Elem = Arr.appendDictionary(); Elem.set(KeyKind, Info.Kind); if (!Info.Name.empty()) Elem.set(KeyName, Info.Name); if (!Info.USR.empty()) Elem.set(KeyUSR, Info.USR); if (Info.Line != 0) { assert(Info.Column != 0); Elem.set(KeyLine, Info.Line); Elem.set(KeyColumn, Info.Column); } return true; } bool SKIndexingConsumer::finishSourceEntity(UIdent Kind) { Entity &CurrEnt = EntitiesStack.back(); assert(CurrEnt.Kind == Kind); (void) CurrEnt; EntitiesStack.pop_back(); return true; } //===----------------------------------------------------------------------===// // ReportDocInfo //===----------------------------------------------------------------------===// namespace { class SKDocConsumer : public DocInfoConsumer { ResponseBuilder &RespBuilder; struct Entity { UIdent Kind; ResponseBuilder::Dictionary Data; ResponseBuilder::Array Entities; ResponseBuilder::Array Inherits; ResponseBuilder::Array Conforms; ResponseBuilder::Array Attrs; }; SmallVector EntitiesStack; ResponseBuilder::Dictionary TopDict; ResponseBuilder::Array Diags; DocSupportAnnotationArrayBuilder AnnotationsBuilder; bool Cancelled = false; void addDocEntityInfoToDict(const DocEntityInfo &Info, ResponseBuilder::Dictionary Dict); public: std::string ErrorDescription; explicit SKDocConsumer(ResponseBuilder &RespBuilder) : RespBuilder(RespBuilder) { TopDict = RespBuilder.getDictionary(); // First in stack is the top-level "key.entities" container. EntitiesStack.push_back( { UIdent(), TopDict, ResponseBuilder::Array(), ResponseBuilder::Array(), ResponseBuilder::Array(), ResponseBuilder::Array() }); } ~SKDocConsumer() { assert(Cancelled || EntitiesStack.size() == 1); (void) Cancelled; } sourcekitd_response_t createResponse() { TopDict.setCustomBuffer(KeyAnnotations, CustomBufferKind::DocSupportAnnotationArray, AnnotationsBuilder.createBuffer()); return RespBuilder.createResponse(); } void failed(StringRef ErrDescription) override; bool handleSourceText(StringRef Text) override; bool handleAnnotation(const DocEntityInfo &Info) override; bool startSourceEntity(const DocEntityInfo &Info) override; bool handleInheritsEntity(const DocEntityInfo &Info) override; bool handleConformsToEntity(const DocEntityInfo &Info) override; bool handleExtendsEntity(const DocEntityInfo &Info) override; bool handleAvailableAttribute(const AvailableAttrInfo &Info) override; bool finishSourceEntity(UIdent Kind) override; bool handleDiagnostic(const DiagnosticEntryInfo &Info) override; }; } static sourcekitd_response_t reportDocInfo(llvm::MemoryBuffer *InputBuf, StringRef ModuleName, ArrayRef Args) { ResponseBuilder RespBuilder; SKDocConsumer DocConsumer(RespBuilder); LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); Lang.getDocInfo(InputBuf, ModuleName, Args, DocConsumer); if (!DocConsumer.ErrorDescription.empty()) return createErrorRequestFailed(DocConsumer.ErrorDescription.c_str()); return DocConsumer.createResponse(); } void SKDocConsumer::addDocEntityInfoToDict(const DocEntityInfo &Info, ResponseBuilder::Dictionary Elem) { Elem.set(KeyKind, Info.Kind); if (!Info.Name.empty()) Elem.set(KeyName, Info.Name); if (!Info.Argument.empty()) Elem.set(KeyKeyword, Info.Argument); if (!Info.USR.empty()) Elem.set(KeyUSR, Info.USR); if (Info.Length > 0) { Elem.set(KeyOffset, Info.Offset); Elem.set(KeyLength, Info.Length); } if (Info.IsUnavailable) Elem.set(KeyIsUnavailable, Info.IsUnavailable); if (Info.IsDeprecated) Elem.set(KeyIsDeprecated, Info.IsDeprecated); if (!Info.DocComment.empty()) Elem.set(KeyDocFullAsXML, Info.DocComment); if (!Info.GenericParams.empty()) { auto GPArray = Elem.setArray(KeyGenericParams); for (auto &GP : Info.GenericParams) { auto GPElem = GPArray.appendDictionary(); GPElem.set(KeyName, GP.Name); if (!GP.Inherits.empty()) GPElem.set(KeyInherits, GP.Inherits); } } // Note that due to protocol extensions, GenericRequirements may be non-empty // while GenericParams is empty. if (!Info.GenericRequirements.empty()) { auto ReqArray = Elem.setArray(KeyGenericRequirements); for (auto &Req : Info.GenericRequirements) { auto ReqElem = ReqArray.appendDictionary(); ReqElem.set(KeyDescription, Req); } } } void SKDocConsumer::failed(StringRef ErrDescription) { ErrorDescription = ErrDescription; } bool SKDocConsumer::handleSourceText(StringRef Text) { TopDict.set(KeySourceText, Text); return true; } bool SKDocConsumer::handleAnnotation(const DocEntityInfo &Info) { AnnotationsBuilder.add(Info); return true; } bool SKDocConsumer::startSourceEntity(const DocEntityInfo &Info) { Entity &Parent = EntitiesStack.back(); ResponseBuilder::Array &Arr = Parent.Entities; if (Arr.isNull()) Arr = Parent.Data.setArray(KeyEntities); auto Elem = Arr.appendDictionary(); addDocEntityInfoToDict(Info, Elem); EntitiesStack.push_back({ Info.Kind, Elem, ResponseBuilder::Array(), ResponseBuilder::Array(), ResponseBuilder::Array(), ResponseBuilder::Array()}); return true; } bool SKDocConsumer::handleInheritsEntity(const DocEntityInfo &Info) { assert(EntitiesStack.size() > 1 && "Related entity at top-level ?"); Entity &Parent = EntitiesStack.back(); ResponseBuilder::Array &Arr = Parent.Inherits; if (Arr.isNull()) Arr = Parent.Data.setArray(KeyInherits); addDocEntityInfoToDict(Info, Arr.appendDictionary()); return true; } bool SKDocConsumer::handleConformsToEntity(const DocEntityInfo &Info) { assert(EntitiesStack.size() > 1 && "Related entity at top-level ?"); Entity &Parent = EntitiesStack.back(); ResponseBuilder::Array &Arr = Parent.Conforms; if (Arr.isNull()) Arr = Parent.Data.setArray(KeyConforms); addDocEntityInfoToDict(Info, Arr.appendDictionary()); return true; } bool SKDocConsumer::handleExtendsEntity(const DocEntityInfo &Info) { assert(EntitiesStack.size() > 1 && "Related entity at top-level ?"); Entity &Parent = EntitiesStack.back(); addDocEntityInfoToDict(Info, Parent.Data.setDictionary(KeyExtends)); return true; } bool SKDocConsumer::handleAvailableAttribute(const AvailableAttrInfo &Info) { Entity &Parent = EntitiesStack.back(); ResponseBuilder::Array &Arr = Parent.Attrs; if (Arr.isNull()) Arr = Parent.Data.setArray(KeyAttributes); auto Elem = Arr.appendDictionary(); Elem.set(KeyKind, Info.AttrKind); if (Info.IsUnavailable) Elem.set(KeyIsUnavailable, Info.IsUnavailable); if (Info.IsDeprecated) Elem.set(KeyIsDeprecated, Info.IsDeprecated); if (Info.Platform.isValid()) Elem.set(KeyPlatform, Info.Platform); if (!Info.Message.empty()) Elem.set(KeyMessage, Info.Message); if (Info.Introduced.hasValue()) Elem.set(KeyIntroduced, Info.Introduced.getValue().getAsString()); if (Info.Deprecated.hasValue()) Elem.set(KeyDeprecated, Info.Deprecated.getValue().getAsString()); if (Info.Obsoleted.hasValue()) Elem.set(KeyObsoleted, Info.Obsoleted.getValue().getAsString()); return true; } bool SKDocConsumer::finishSourceEntity(UIdent Kind) { Entity &CurrEnt = EntitiesStack.back(); assert(CurrEnt.Kind == Kind); (void) CurrEnt; EntitiesStack.pop_back(); return true; } bool SKDocConsumer::handleDiagnostic(const DiagnosticEntryInfo &Info) { ResponseBuilder::Array &Arr = Diags; if (Arr.isNull()) Arr = TopDict.setArray(KeyDiagnostics); auto Elem = Arr.appendDictionary(); UIdent SeverityUID; switch (Info.Severity) { case DiagnosticSeverityKind::Warning: SeverityUID = DiagKindWarning; break; case DiagnosticSeverityKind::Error: SeverityUID = DiagKindError; break; } Elem.set(KeySeverity, SeverityUID); fillDictionaryForDiagnosticInfo(Elem, Info); if (!Info.Notes.empty()) { auto NotesArr = Elem.setArray(KeyDiagnostics); for (auto &NoteDiag : Info.Notes) { auto NoteElem = NotesArr.appendDictionary(); NoteElem.set(KeySeverity, DiagKindNote); fillDictionaryForDiagnosticInfo(NoteElem, NoteDiag); } } return true; } //===----------------------------------------------------------------------===// // ReportCursorInfo //===----------------------------------------------------------------------===// static void reportCursorInfo(StringRef Filename, int64_t Offset, ArrayRef Args, ResponseReceiver Rec) { LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); Lang.getCursorInfo(Filename, Offset, Args, [Rec](const CursorInfo &Info) { if (Info.IsCancelled) return Rec(createErrorRequestCancelled()); ResponseBuilder RespBuilder; if (Info.Kind.isInvalid()) return Rec(RespBuilder.createResponse()); auto Elem = RespBuilder.getDictionary(); Elem.set(KeyKind, Info.Kind); Elem.set(KeyName, Info.Name); if (!Info.USR.empty()) Elem.set(KeyUSR, Info.USR); if (!Info.TypeName.empty()) Elem.set(KeyTypeName, Info.TypeName); if (!Info.DocComment.empty()) Elem.set(KeyDocFullAsXML, Info.DocComment); if (!Info.AnnotatedDeclaration.empty()) Elem.set(KeyAnnotatedDecl, Info.AnnotatedDeclaration); if (!Info.FullyAnnotatedDeclaration.empty()) Elem.set(KeyFullyAnnotatedDecl, Info.FullyAnnotatedDeclaration); if (!Info.ModuleName.empty()) Elem.set(KeyModuleName, Info.ModuleName); if (!Info.GroupName.empty()) Elem.set(KeyGroupName, Info.GroupName); if (!Info.ModuleInterfaceName.empty()) Elem.set(KeyModuleInterfaceName, Info.ModuleInterfaceName); if (Info.DeclarationLoc.hasValue()) { Elem.set(KeyOffset, Info.DeclarationLoc.getValue().first); Elem.set(KeyLength, Info.DeclarationLoc.getValue().second); if (!Info.Filename.empty()) Elem.set(KeyFilePath, Info.Filename); } if (!Info.OverrideUSRs.empty()) { auto Overrides = Elem.setArray(KeyOverrides); for (auto USR : Info.OverrideUSRs) { auto Override = Overrides.appendDictionary(); Override.set(KeyUSR, USR); } } if (!Info.AnnotatedRelatedDeclarations.empty()) { auto RelDecls = Elem.setArray(KeyRelatedDecls); for (auto AnnotDecl : Info.AnnotatedRelatedDeclarations) { auto RelDecl = RelDecls.appendDictionary(); RelDecl.set(KeyAnnotatedDecl, AnnotDecl); } } if (Info.IsSystem) Elem.setBool(KeyIsSystem, true); if (!Info.TypeInterface.empty()) Elem.set(KeyTypeInterface, Info.TypeInterface); return Rec(RespBuilder.createResponse()); }); } //===----------------------------------------------------------------------===// // FindRelatedIdents //===----------------------------------------------------------------------===// static void findRelatedIdents(StringRef Filename, int64_t Offset, ArrayRef Args, ResponseReceiver Rec) { LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); Lang.findRelatedIdentifiersInFile(Filename, Offset, Args, [Rec](const RelatedIdentsInfo &Info) { if (Info.IsCancelled) return Rec(createErrorRequestCancelled()); ResponseBuilder RespBuilder; auto Arr = RespBuilder.getDictionary().setArray(KeyResults); for (auto R : Info.Ranges) { auto Elem = Arr.appendDictionary(); Elem.set(KeyOffset, R.first); Elem.set(KeyLength, R.second); } Rec(RespBuilder.createResponse()); }); } //===----------------------------------------------------------------------===// // CodeComplete //===----------------------------------------------------------------------===// namespace { class SKCodeCompletionConsumer : public CodeCompletionConsumer { ResponseBuilder &RespBuilder; CodeCompletionResultsArrayBuilder ResultsBuilder; std::string ErrorDescription; public: explicit SKCodeCompletionConsumer(ResponseBuilder &RespBuilder) : RespBuilder(RespBuilder) { } sourcekitd_response_t createResponse() { if (!ErrorDescription.empty()) return createErrorRequestFailed(ErrorDescription.c_str()); RespBuilder.getDictionary().setCustomBuffer(KeyResults, CustomBufferKind::CodeCompletionResultsArray, ResultsBuilder.createBuffer()); return RespBuilder.createResponse(); } void failed(StringRef ErrDescription) override; bool handleResult(const CodeCompletionInfo &Info) override; }; } static sourcekitd_response_t codeComplete(llvm::MemoryBuffer *InputBuf, int64_t Offset, ArrayRef Args) { ResponseBuilder RespBuilder; SKCodeCompletionConsumer CCC(RespBuilder); LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); Lang.codeComplete(InputBuf, Offset, CCC, Args); return CCC.createResponse(); } void SKCodeCompletionConsumer::failed(StringRef ErrDescription) { ErrorDescription = ErrDescription; } bool SKCodeCompletionConsumer::handleResult(const CodeCompletionInfo &R) { Optional ModuleNameOpt; if (!R.ModuleName.empty()) ModuleNameOpt = R.ModuleName; Optional DocBriefOpt; if (!R.DocBrief.empty()) DocBriefOpt = R.DocBrief; Optional AssocUSRsOpt; if (!R.AssocUSRs.empty()) AssocUSRsOpt = R.AssocUSRs; assert(!R.ModuleImportDepth && "not implemented on CompactArray path"); ResultsBuilder.add(R.Kind, R.Name, R.Description, R.SourceText, R.TypeName, ModuleNameOpt, DocBriefOpt, AssocUSRsOpt, R.SemanticContext, R.NotRecommended, R.NumBytesToErase); return true; } //===----------------------------------------------------------------------===// // (New) CodeComplete //===----------------------------------------------------------------------===// namespace { class SKGroupedCodeCompletionConsumer : public GroupedCodeCompletionConsumer { ResponseBuilder &RespBuilder; ResponseBuilder::Dictionary Response; SmallVector GroupContentsStack; std::string ErrorDescription; public: explicit SKGroupedCodeCompletionConsumer(ResponseBuilder &RespBuilder) : RespBuilder(RespBuilder) {} sourcekitd_response_t createResponse() { if (!ErrorDescription.empty()) return createErrorRequestFailed(ErrorDescription.c_str()); assert(GroupContentsStack.empty() && "mismatched start/endGroup"); return RespBuilder.createResponse(); } void failed(StringRef ErrDescription) override; bool handleResult(const CodeCompletionInfo &Info) override; void startGroup(UIdent kind, StringRef name) override; void endGroup() override; void setNextRequestStart(unsigned offset) override; }; class SKOptionsDictionary : public OptionsDictionary { RequestDict &Options; public: explicit SKOptionsDictionary(RequestDict &Options) : Options(Options) {} bool valueForOption(UIdent Key, unsigned &Val) override { int64_t result; if (Options.getInt64(Key, result, false)) return false; Val = static_cast(result); return true; } bool valueForOption(UIdent Key, bool &Val) override { int64_t result; if (Options.getInt64(Key, result, false)) return false; Val = result ? true : false; return true; } bool valueForOption(UIdent Key, StringRef &Val) override { Optional value = Options.getString(Key); if (!value) return false; Val = *value; return true; } }; } // end anonymous namespace static sourcekitd_response_t codeCompleteOpen(StringRef Name, llvm::MemoryBuffer *InputBuf, int64_t Offset, Optional optionsDict, ArrayRef Args) { ResponseBuilder RespBuilder; SKGroupedCodeCompletionConsumer CCC(RespBuilder); std::unique_ptr options; std::vector filterRules; if (optionsDict) { options = llvm::make_unique(*optionsDict); bool failed = false; optionsDict->dictionaryArrayApply(KeyFilterRules, [&](RequestDict dict) { FilterRule rule; auto kind = dict.getUID(KeyKind); if (kind == KindEverything) { rule.kind = FilterRule::Everything; } else if (kind == KindModule) { rule.kind = FilterRule::Module; } else if (kind == KindKeyword) { rule.kind = FilterRule::Keyword; } else if (kind == KindLiteral) { rule.kind = FilterRule::Literal; } else if (kind == KindCustom) { rule.kind = FilterRule::CustomCompletion; } else if (kind == KindIdentifier) { rule.kind = FilterRule::Identifier; } else { // Warning: unknown } int64_t hide; if (dict.getInt64(KeyHide, hide, false)) { failed = true; CCC.failed("filter rule missing required key 'key.hide'"); return true; } rule.hide = hide; switch (rule.kind) { case FilterRule::Everything: break; case FilterRule::Module: case FilterRule::Identifier: { SmallVector names; if (dict.getStringArray(KeyNames, names, false)) { failed = true; CCC.failed("filter rule missing required key 'key.names'"); return true; } rule.names.assign(names.begin(), names.end()); break; } case FilterRule::Keyword: case FilterRule::Literal: case FilterRule::CustomCompletion: { SmallVector uids; dict.getUIDArray(KeyUIDs, uids, true); for (auto uid : uids) rule.uids.push_back(UIdentFromSKDUID(uid)); break; } } filterRules.push_back(std::move(rule)); return false; // continue }); if (failed) return CCC.createResponse(); } LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); Lang.codeCompleteOpen(Name, InputBuf, Offset, options.get(), filterRules, CCC, Args); return CCC.createResponse(); } static sourcekitd_response_t codeCompleteClose(StringRef Name, int64_t Offset) { ResponseBuilder RespBuilder; SKGroupedCodeCompletionConsumer CCC(RespBuilder); LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); Lang.codeCompleteClose(Name, Offset, CCC); return CCC.createResponse(); } static sourcekitd_response_t codeCompleteUpdate(StringRef name, int64_t offset, Optional optionsDict) { ResponseBuilder RespBuilder; SKGroupedCodeCompletionConsumer CCC(RespBuilder); std::unique_ptr options; if (optionsDict) options = llvm::make_unique(*optionsDict); LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); Lang.codeCompleteUpdate(name, offset, options.get(), CCC); return CCC.createResponse(); } void SKGroupedCodeCompletionConsumer::failed(StringRef ErrDescription) { ErrorDescription = ErrDescription; } bool SKGroupedCodeCompletionConsumer::handleResult(const CodeCompletionInfo &R) { assert(!GroupContentsStack.empty() && "missing root group"); auto result = GroupContentsStack.back().appendDictionary(); if (R.CustomKind) result.set(KeyKind, sourcekitd_uid_t(R.CustomKind)); else result.set(KeyKind, R.Kind); result.set(KeyName, R.Name); result.set(KeyDescription, R.Description); result.set(KeySourceText, R.SourceText); result.set(KeyTypeName, R.TypeName); result.set(KeyContext, R.SemanticContext); if (!R.ModuleName.empty()) result.set(KeyModuleName, R.ModuleName); if (!R.DocBrief.empty()) result.set(KeyDocBrief, R.DocBrief); if (!R.AssocUSRs.empty()) result.set(KeyAssociatedUSRs, R.AssocUSRs); if (R.ModuleImportDepth) result.set(KeyModuleImportDepth, *R.ModuleImportDepth); if (R.NotRecommended) result.set(KeyNotRecommended, R.NotRecommended); result.set(KeyNumBytesToErase, R.NumBytesToErase); if (R.descriptionStructure) { auto addRange = [](ResponseBuilder::Dictionary dict, UIdent offset, UIdent length, CodeCompletionInfo::IndexRange range) { if (!range.empty()) { dict.set(offset, range.begin); dict.set(length, range.length()); } }; auto structure = result.setDictionary(KeySubStructure); addRange(structure, KeyNameOffset, KeyNameLength, R.descriptionStructure->baseName); addRange(structure, KeyBodyOffset, KeyBodyLength, R.descriptionStructure->parameterRange); addRange(structure, KeyThrowOffset, KeyThrowLength, R.descriptionStructure->throwsRange); if (R.parametersStructure) { auto params = structure.setArray(KeySubStructure); for (auto &P : *R.parametersStructure) { auto param = params.appendDictionary(); addRange(param, KeyNameOffset, KeyNameLength, P.name); addRange(param, KeyBodyOffset, KeyBodyLength, P.afterColon); if (P.isLocalName) param.set(KeyIsLocal, true); } } } return true; } void SKGroupedCodeCompletionConsumer::startGroup(UIdent kind, StringRef name) { ResponseBuilder::Dictionary group; if (GroupContentsStack.empty()) { group = RespBuilder.getDictionary(); Response = group; } else { group = GroupContentsStack.back().appendDictionary(); } group.set(KeyKind, kind); group.set(KeyName, name); auto contents = group.setArray(KeyResults); GroupContentsStack.push_back(contents); } void SKGroupedCodeCompletionConsumer::endGroup() { assert(!GroupContentsStack.empty()); GroupContentsStack.pop_back(); } void SKGroupedCodeCompletionConsumer::setNextRequestStart(unsigned offset) { assert(!Response.isNull()); Response.set(KeyNextRequestStart, offset); } //===----------------------------------------------------------------------===// // Editor //===----------------------------------------------------------------------===// namespace { class SKEditorConsumer : public EditorConsumer { ResponseReceiver RespReceiver; ResponseBuilder RespBuilder; ResponseBuilder::Dictionary Dict; TokenAnnotationsArrayBuilder SyntaxMap; TokenAnnotationsArrayBuilder SemanticAnnotations; struct StructureNode { ResponseBuilder::Dictionary Dict; ResponseBuilder::Array SubStructures; ResponseBuilder::Array Elements; explicit StructureNode(ResponseBuilder::Dictionary Dict) : Dict(Dict) {} }; std::vector Structures; ResponseBuilder::Array Diags; sourcekitd_response_t Error = nullptr; bool EnableSyntaxMap; bool EnableDiagnostics; bool SyntacticOnly; public: SKEditorConsumer(bool EnableSyntaxMap, bool EnableStructure, bool EnableDiagnostics, bool SyntacticOnly) : EnableSyntaxMap(EnableSyntaxMap), EnableDiagnostics(EnableDiagnostics), SyntacticOnly(SyntacticOnly) { Dict = RespBuilder.getDictionary(); if (EnableStructure) Structures.push_back(StructureNode{Dict}); } SKEditorConsumer(ResponseReceiver RespReceiver, bool EnableSyntaxMap, bool EnableStructure, bool EnableDiagnostics, bool SyntacticOnly) : SKEditorConsumer(EnableSyntaxMap, EnableStructure, EnableDiagnostics, SyntacticOnly) { this->RespReceiver = RespReceiver; } sourcekitd_response_t createResponse(); bool needsSemanticInfo() override { return !SyntacticOnly && !isSemanticEditorDisabled(); } void handleRequestError(const char *Description) override; bool handleSyntaxMap(unsigned Offset, unsigned Length, UIdent Kind) override; bool handleSemanticAnnotation(unsigned Offset, unsigned Length, UIdent Kind, bool isSystem) override; bool beginDocumentSubStructure(unsigned Offset, unsigned Length, UIdent Kind, UIdent AccessLevel, UIdent SetterAccessLevel, unsigned NameOffset, unsigned NameLength, unsigned BodyOffset, unsigned BodyLength, StringRef DisplayName, StringRef TypeName, StringRef RuntimeName, StringRef SelectorName, ArrayRef InheritedTypes, ArrayRef Attrs) override; bool endDocumentSubStructure() override; bool handleDocumentSubStructureElement(UIdent Kind, unsigned Offset, unsigned Length) override; bool recordAffectedRange(unsigned Offset, unsigned Length) override; bool recordAffectedLineRange(unsigned Line, unsigned Length) override; bool recordFormattedText(StringRef Text) override; bool setDiagnosticStage(UIdent DiagStage) override; bool handleDiagnostic(const DiagnosticEntryInfo &Info, UIdent DiagStage) override; bool handleSourceText(StringRef Text) override; virtual void finished() override { if (RespReceiver) RespReceiver(createResponse()); } }; } static sourcekitd_response_t editorOpen(StringRef Name, llvm::MemoryBuffer *Buf, bool EnableSyntaxMap, bool EnableStructure, bool EnableDiagnostics, bool SyntacticOnly, ArrayRef Args) { SKEditorConsumer EditC(EnableSyntaxMap, EnableStructure, EnableDiagnostics, SyntacticOnly); LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); Lang.editorOpen(Name, Buf, EnableSyntaxMap, EditC, Args); return EditC.createResponse(); } static sourcekitd_response_t editorOpenInterface(StringRef Name, StringRef ModuleName, Optional Group, ArrayRef Args) { SKEditorConsumer EditC(/*EnableSyntaxMap=*/true, /*EnableStructure=*/true, /*EnableDiagnostics=*/false, /*SyntacticOnly=*/false); LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); Lang.editorOpenInterface(EditC, Name, ModuleName, Group, Args); return EditC.createResponse(); } /// Getting the interface from a swift source file differs from getting interfaces /// from headers or modules for its performing asynchronously. static void editorOpenSwiftSourceInterface(StringRef Name, StringRef HeaderName, ArrayRef Args, ResponseReceiver Rec) { auto EditC = std::make_shared(Rec, /*EnableSyntaxMap=*/true, /*EnableStructure=*/true, /*EnableDiagnostics=*/false, /*SyntacticOnly=*/false); LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); Lang.editorOpenSwiftSourceInterface(Name, HeaderName, Args, EditC); } static sourcekitd_response_t editorExtractTextFromComment(StringRef Source) { SKEditorConsumer EditC(/*EnableSyntaxMap=*/false, /*EnableStructure=*/false, /*EnableDiagnostics=*/false, /*SyntacticOnly=*/true); LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); Lang.editorExtractTextFromComment(Source, EditC); return EditC.createResponse(); } static sourcekitd_response_t editorOpenHeaderInterface(StringRef Name, StringRef HeaderName, ArrayRef Args) { SKEditorConsumer EditC(/*EnableSyntaxMap=*/true, /*EnableStructure=*/true, /*EnableDiagnostics=*/false, /*SyntacticOnly=*/false); LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); Lang.editorOpenHeaderInterface(EditC, Name, HeaderName, Args); return EditC.createResponse(); } static sourcekitd_response_t editorClose(StringRef Name, bool RemoveCache) { ResponseBuilder RespBuilder; LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); Lang.editorClose(Name, RemoveCache); return RespBuilder.createResponse(); } static sourcekitd_response_t editorReplaceText(StringRef Name, llvm::MemoryBuffer *Buf, unsigned Offset, unsigned Length, bool EnableSyntaxMap, bool EnableStructure, bool EnableDiagnostics, bool SyntacticOnly) { SKEditorConsumer EditC(EnableSyntaxMap, EnableStructure, EnableDiagnostics, SyntacticOnly); LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); Lang.editorReplaceText(Name, Buf, Offset, Length, EditC); return EditC.createResponse(); } static void editorApplyFormatOptions(StringRef Name, RequestDict &FmtOptions) { LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); SKOptionsDictionary SKFmtOptions(FmtOptions); Lang.editorApplyFormatOptions(Name, SKFmtOptions); } static sourcekitd_response_t editorFormatText(StringRef Name, unsigned Line, unsigned Length) { SKEditorConsumer EditC(false, false, false, /*SyntacticOnly=*/true); LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); Lang.editorFormatText(Name, Line, Length, EditC); return EditC.createResponse(); } static sourcekitd_response_t editorExpandPlaceholder(StringRef Name, unsigned Offset, unsigned Length) { SKEditorConsumer EditC(false, false, false, /*SyntacticOnly=*/true); LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); Lang.editorExpandPlaceholder(Name, Offset, Length, EditC); return EditC.createResponse(); } sourcekitd_response_t SKEditorConsumer::createResponse() { if (Error) return Error; if (EnableSyntaxMap) { Dict.setCustomBuffer(KeySyntaxMap, CustomBufferKind::TokenAnnotationsArray, SyntaxMap.createBuffer()); } if (!SemanticAnnotations.empty()) { Dict.setCustomBuffer(KeyAnnotations, CustomBufferKind::TokenAnnotationsArray, SemanticAnnotations.createBuffer()); } return RespBuilder.createResponse(); } void SKEditorConsumer::handleRequestError(const char *Description) { if (!Error) { Error = createErrorRequestFailed(Description); } if (RespReceiver) { RespReceiver(Error); RespReceiver = ResponseReceiver(); } } bool SKEditorConsumer::handleSyntaxMap(unsigned Offset, unsigned Length, UIdent Kind) { if (!EnableSyntaxMap) return true; SyntaxMap.add(Kind, Offset, Length, /*IsSystem=*/false); return true; } bool SKEditorConsumer::handleSemanticAnnotation(unsigned Offset, unsigned Length, UIdent Kind, bool isSystem) { assert(Kind.isValid()); SemanticAnnotations.add(Kind, Offset, Length, isSystem); return true; } bool SKEditorConsumer::beginDocumentSubStructure(unsigned Offset, unsigned Length, UIdent Kind, UIdent AccessLevel, UIdent SetterAccessLevel, unsigned NameOffset, unsigned NameLength, unsigned BodyOffset, unsigned BodyLength, StringRef DisplayName, StringRef TypeName, StringRef RuntimeName, StringRef SelectorName, ArrayRef InheritedTypes, ArrayRef Attrs) { if (Structures.empty()) return true; auto &Parent = Structures.back(); if (Parent.SubStructures.isNull()) Parent.SubStructures = Parent.Dict.setArray(KeySubStructure); auto Node = Parent.SubStructures.appendDictionary(); Node.set(KeyOffset, Offset); Node.set(KeyLength, Length); Node.set(KeyKind, Kind); if (AccessLevel.isValid()) Node.set(KeyAccessibility, AccessLevel); if (SetterAccessLevel.isValid()) Node.set(KeySetterAccessibility, SetterAccessLevel); Node.set(KeyNameOffset, NameOffset); Node.set(KeyNameLength, NameLength); if (BodyOffset != 0 || BodyLength !=0) { Node.set(KeyBodyOffset, BodyOffset); Node.set(KeyBodyLength, BodyLength); } if (!DisplayName.empty()) Node.set(KeyName, DisplayName); if (!TypeName.empty()) Node.set(KeyTypeName,TypeName); if (!RuntimeName.empty()) Node.set(KeyRuntimeName, RuntimeName); if (!SelectorName.empty()) Node.set(KeySelectorName, SelectorName); if (!InheritedTypes.empty()) { auto TypeArray = Node.setArray(KeyInheritedTypes); for (const StringRef &TypeName : InheritedTypes) { TypeArray.appendDictionary().set(KeyName, TypeName); } } if (!Attrs.empty()) { auto AttrArray = Node.setArray(KeyAttributes); for (auto Attr : Attrs) { auto AttrDict = AttrArray.appendDictionary(); AttrDict.set(KeyAttribute, Attr); } } Structures.push_back(StructureNode{Node}); return true; } bool SKEditorConsumer::endDocumentSubStructure() { if (!Structures.empty()) Structures.pop_back(); return true; } bool SKEditorConsumer::handleDocumentSubStructureElement(UIdent Kind, unsigned Offset, unsigned Length) { if (Structures.empty()) return true; auto &Parent = Structures.back(); if (Parent.Elements.isNull()) Parent.Elements = Parent.Dict.setArray(KeyElements); auto Node = Parent.Elements.appendDictionary(); Node.set(KeyKind, Kind); Node.set(KeyOffset, Offset); Node.set(KeyLength, Length); return true; } bool SKEditorConsumer::recordAffectedRange(unsigned Offset, unsigned Length) { Dict.set(KeyOffset, Offset); Dict.set(KeyLength, Length); return true; } bool SKEditorConsumer::recordAffectedLineRange(unsigned Line, unsigned Length) { Dict.set(KeyLine, Line); Dict.set(KeyLength, Length); return true; } bool SKEditorConsumer::recordFormattedText(StringRef Text) { Dict.set(KeySourceText, Text); return true; } static void fillDictionaryForDiagnosticInfo( ResponseBuilder::Dictionary Elem, const DiagnosticEntryInfoBase &Info) { Elem.set(KeyDescription, Info.Description); if (Info.Line != 0) { Elem.set(KeyLine, Info.Line); Elem.set(KeyColumn, Info.Column); } else { Elem.set(KeyOffset, Info.Offset); } if (!Info.Filename.empty()) Elem.set(KeyFilePath, Info.Filename); if (!Info.Ranges.empty()) { auto RangesArr = Elem.setArray(KeyRanges); for (auto R : Info.Ranges) { auto RangeElem = RangesArr.appendDictionary(); RangeElem.set(KeyOffset, R.first); RangeElem.set(KeyLength, R.second); } } if (!Info.Fixits.empty()) { auto FixitsArr = Elem.setArray(KeyFixits); for (auto F : Info.Fixits) { auto FixitElem = FixitsArr.appendDictionary(); FixitElem.set(KeyOffset, F.Offset); FixitElem.set(KeyLength, F.Length); FixitElem.set(KeySourceText, F.Text); } } } bool SKEditorConsumer::setDiagnosticStage(UIdent DiagStage) { Dict.set(KeyDiagnosticStage, DiagStage); return true; } bool SKEditorConsumer::handleDiagnostic(const DiagnosticEntryInfo &Info, UIdent DiagStage) { if (!EnableDiagnostics) return true; ResponseBuilder::Array &Arr = Diags; if (Arr.isNull()) Arr = Dict.setArray(KeyDiagnostics); auto Elem = Arr.appendDictionary(); UIdent SeverityUID; switch (Info.Severity) { case DiagnosticSeverityKind::Warning: SeverityUID = DiagKindWarning; break; case DiagnosticSeverityKind::Error: SeverityUID = DiagKindError; break; } Elem.set(KeySeverity, SeverityUID); Elem.set(KeyDiagnosticStage, DiagStage); fillDictionaryForDiagnosticInfo(Elem, Info); if (!Info.Notes.empty()) { auto NotesArr = Elem.setArray(KeyDiagnostics); for (auto &NoteDiag : Info.Notes) { auto NoteElem = NotesArr.appendDictionary(); NoteElem.set(KeySeverity, DiagKindNote); fillDictionaryForDiagnosticInfo(NoteElem, NoteDiag); } } return true; } bool SKEditorConsumer::handleSourceText(StringRef Text) { Dict.set(KeySourceText, Text); return true; } static sourcekitd_response_t editorFindUSR(StringRef DocumentName, StringRef USR) { ResponseBuilder RespBuilder; LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); llvm::Optional> Range = Lang.findUSRRange(DocumentName, USR); if (Range.hasValue()) { RespBuilder.getDictionary().set(KeyOffset, Range->first); RespBuilder.getDictionary().set(KeyLength, Range->second); } return RespBuilder.createResponse(); } static sourcekitd_response_t editorFindInterfaceDoc(StringRef ModuleName, ArrayRef Args) { ResponseBuilder RespBuilder; sourcekitd_response_t Resp; LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); Lang.findInterfaceDocument(ModuleName, Args, [&](const InterfaceDocInfo &Info) { if (!Info.Error.empty()) { SmallString<128> Err(Info.Error); Resp = createErrorRequestFailed(Err.c_str()); return; } auto Elem = RespBuilder.getDictionary(); if (!Info.ModuleInterfaceName.empty()) Elem.set(KeyModuleInterfaceName, Info.ModuleInterfaceName); if (!Info.CompilerArgs.empty()) Elem.set(KeyCompilerArgs, Info.CompilerArgs); Resp = RespBuilder.createResponse(); }); return Resp; } static sourcekitd_response_t editorFindModuleGroups(StringRef ModuleName, ArrayRef Args) { ResponseBuilder RespBuilder; sourcekitd_response_t Resp; LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); Lang.findModuleGroups(ModuleName, Args, [&](ArrayRef Groups, StringRef Error) { if (!Error.empty()) { Resp = createErrorRequestFailed(Error.str().c_str()); return; } auto Dict = RespBuilder.getDictionary(); auto Arr = Dict.setArray(KeyModuleGroups); for (auto G : Groups) { auto Entry = Arr.appendDictionary(); Entry.set(KeyGroupName, G); } Resp = RespBuilder.createResponse(); }); return Resp; } static bool isSemanticEditorDisabled() { enum class SemaInfoToggle : char { None, Disable, Enable }; static SemaInfoToggle Toggle = SemaInfoToggle::None; if (Toggle == SemaInfoToggle::None) { static std::once_flag flag; std::call_once(flag, []() { Toggle = SemaInfoToggle::Enable; const char *EnvOpt = ::getenv("SOURCEKIT_DELAY_SEMA_EDITOR"); if (!EnvOpt) { return; } unsigned Seconds; if (StringRef(EnvOpt).getAsInteger(10, Seconds)) return; // A crash occurred previously. Disable semantic info in the editor for // the given amount, to avoid repeated crashers. LOG_WARN_FUNC("delaying semantic editor for " << Seconds << " seconds"); Toggle = SemaInfoToggle::Disable; dispatch_time_t When = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * Seconds); dispatch_after(When, dispatch_get_main_queue(), ^{ Toggle = SemaInfoToggle::Enable; }); }); } assert(Toggle != SemaInfoToggle::None); return Toggle == SemaInfoToggle::Disable; }