//===--- SwiftSourceDocInfo.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 "SourceKit/Support/FileSystemProvider.h" #include "SourceKit/Support/ImmutableTextBuffer.h" #include "SourceKit/Support/Logging.h" #include "SourceKit/Support/UIdent.h" #include "SwiftASTManager.h" #include "SwiftEditorDiagConsumer.h" #include "SwiftLangSupport.h" #include "swift/AST/ASTDemangler.h" #include "swift/AST/ASTPrinter.h" #include "swift/AST/Decl.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/LookupKinds.h" #include "swift/AST/ModuleNameLookup.h" #include "swift/AST/NameLookup.h" #include "swift/AST/SwiftNameTranslation.h" #include "swift/Basic/SourceManager.h" #include "swift/Frontend/Frontend.h" #include "swift/Frontend/PrintingDiagnosticConsumer.h" #include "swift/IDE/CodeCompletion.h" #include "swift/IDE/CommentConversion.h" #include "swift/IDE/IDERequests.h" #include "swift/IDE/ModuleInterfacePrinting.h" #include "swift/Refactoring/Refactoring.h" #include "swift/IDE/SourceEntityWalker.h" #include "swift/IDE/Utils.h" #include "swift/Markup/XMLUtils.h" #include "swift/Sema/IDETypeChecking.h" #include "swift/SymbolGraphGen/SymbolGraphGen.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/Module.h" #include "clang/Basic/SourceManager.h" #include "clang/Index/USRGeneration.h" #include "clang/Lex/Lexer.h" #include "llvm/Support/MemoryBuffer.h" #include using namespace SourceKit; using namespace swift; using namespace swift::ide; namespace { class AnnotatedDeclarationPrinter : public XMLEscapingPrinter { public: AnnotatedDeclarationPrinter(raw_ostream &OS) :XMLEscapingPrinter(OS) { } private: void printTypeRef( Type T, const TypeDecl *TD, Identifier Name, PrintNameContext NameContext = PrintNameContext::Normal) override { printXML(""); StreamPrinter::printTypeRef(T, TD, Name, NameContext); printXML(""); } }; } // end anonymous namespace static StringRef getTagForDecl(const Decl *D, bool isRef) { auto UID = SwiftLangSupport::getUIDForDecl(D, isRef); static const char *prefix = "source.lang.swift."; assert(UID.getName().startswith(prefix)); return UID.getName().drop_front(strlen(prefix)); } static StringRef ExternalParamNameTag = "decl.var.parameter.argument_label"; static StringRef LocalParamNameTag = "decl.var.parameter.name"; static StringRef GenericParamNameTag = "decl.generic_type_param.name"; static StringRef SyntaxKeywordTag = "syntaxtype.keyword"; static StringRef getTagForParameter(PrintStructureKind context) { switch (context) { case PrintStructureKind::FunctionParameter: return "decl.var.parameter"; case PrintStructureKind::FunctionReturnType: return "decl.function.returntype"; case PrintStructureKind::FunctionType: return ""; case PrintStructureKind::TupleType: return "tuple"; case PrintStructureKind::TupleElement: return "tuple.element"; case PrintStructureKind::GenericParameter: return "decl.generic_type_param"; case PrintStructureKind::GenericRequirement: return "decl.generic_type_requirement"; case PrintStructureKind::BuiltinAttribute: return "syntaxtype.attribute.builtin"; case PrintStructureKind::NumberLiteral: return "syntaxtype.number"; case PrintStructureKind::StringLiteral: return "syntaxtype.string"; case PrintStructureKind::DefaultArgumentClause: case PrintStructureKind::DeclGenericParameterClause: case PrintStructureKind::DeclGenericRequirementClause: case PrintStructureKind::EffectsSpecifiers: case PrintStructureKind::DeclResultTypeClause: case PrintStructureKind::FunctionParameterList: case PrintStructureKind::FunctionParameterType: // These kinds are ignored by 'isIgnoredPrintStructureKind()' llvm_unreachable("ignored structure kind"); } llvm_unreachable("unexpected structure kind"); } static StringRef getDeclNameTagForDecl(const Decl *D) { switch (D->getKind()) { case DeclKind::Param: // When we're examining the parameter itself, it is the local name that is // the name of the variable. return LocalParamNameTag; case DeclKind::GenericTypeParam: return ""; // Handled by printName. case DeclKind::Constructor: case DeclKind::Destructor: case DeclKind::Subscript: // The names 'init'/'deinit'/'subscript' are actually keywords. return SyntaxKeywordTag; default: return "decl.name"; } } namespace { /// A typesafe union of contexts that the printer can be inside. /// Currently: Decl, PrintStructureKind class PrintContext { // Use the low bit to determine the type; store the enum value shifted left // to leave the low bit free. const uintptr_t value; static constexpr unsigned declTag = 0; static constexpr unsigned PrintStructureKindTag = 1; static constexpr unsigned typeTag = 2; static constexpr unsigned tagMask = 3; static constexpr unsigned tagShift = 2; bool hasTag(unsigned tag) const { return (value & tagMask) == tag; } public: PrintContext(const Decl *D) : value(uintptr_t(D)) { static_assert(llvm::PointerLikeTypeTraits::NumLowBitsAvailable >= tagShift, "missing spare bit in Decl *"); } PrintContext(PrintStructureKind K) : value((uintptr_t(K) << tagShift) | PrintStructureKindTag) {} PrintContext(TypeLoc unused) : value(typeTag) {} /// Get the context as a Decl, or nullptr. const Decl *getDecl() const { return hasTag(declTag) ? (const Decl *)value : nullptr; } /// Get the context as a PrintStructureKind, or None. llvm::Optional getPrintStructureKind() const { if (!hasTag(PrintStructureKindTag)) return llvm::None; return PrintStructureKind(value >> tagShift); } /// Whether this is a PrintStructureKind context of the given \p kind. bool is(PrintStructureKind kind) const { auto storedKind = getPrintStructureKind(); return storedKind && *storedKind == kind; } bool isType() const { return hasTag(typeTag); } }; /// An ASTPrinter for annotating declarations with XML tags that describe the /// key substructure of the declaration for CursorInfo/DocInfo. /// /// Prints declarations with decl- and type-specific tags derived from the /// UIDs used for decl/refs. For example (including newlines purely for ease of /// reading): /// /// \verbatim /// /// func foo /// ( /// /// x: /// Int /// /// ) -> /// Int /// /// \endverbatim class FullyAnnotatedDeclarationPrinter final : public XMLEscapingPrinter { public: FullyAnnotatedDeclarationPrinter(raw_ostream &OS) : XMLEscapingPrinter(OS) {} private: // MARK: The ASTPrinter callback interface. void printDeclPre(const Decl *D, llvm::Optional Bracket) override { contextStack.emplace_back(PrintContext(D)); openTag(getTagForDecl(D, /*isRef=*/false)); } void printDeclPost(const Decl *D, llvm::Optional Bracket) override { assert(contextStack.back().getDecl() == D && "unmatched printDeclPre"); contextStack.pop_back(); closeTag(getTagForDecl(D, /*isRef=*/false)); } void printDeclLoc(const Decl *D) override { auto tag = getDeclNameTagForDecl(D); if (!tag.empty()) openTag(tag); } void printDeclNameEndLoc(const Decl *D) override { auto tag = getDeclNameTagForDecl(D); if (!tag.empty()) closeTag(tag); } void printTypePre(const TypeLoc &TL) override { auto tag = getTypeTagForCurrentContext(); contextStack.emplace_back(PrintContext(TL)); if (!tag.empty()) openTag(tag); } void printTypePost(const TypeLoc &TL) override { assert(contextStack.back().isType()); contextStack.pop_back(); auto tag = getTypeTagForCurrentContext(); if (!tag.empty()) closeTag(tag); } bool isIgnoredPrintStructureKind(PrintStructureKind kind) { switch (kind) { case PrintStructureKind::DefaultArgumentClause: case PrintStructureKind::DeclGenericParameterClause: case PrintStructureKind::DeclGenericRequirementClause: case PrintStructureKind::EffectsSpecifiers: case PrintStructureKind::DeclResultTypeClause: case PrintStructureKind::FunctionParameterList: case PrintStructureKind::FunctionParameterType: return true; default: return false; } } void printStructurePre(PrintStructureKind kind, const Decl *D) override { if (isIgnoredPrintStructureKind(kind)) return; if (kind == PrintStructureKind::TupleElement || kind == PrintStructureKind::TupleType) fixupTuple(kind); contextStack.emplace_back(PrintContext(kind)); auto tag = getTagForParameter(kind); if (tag.empty()) return; if (D && kind == PrintStructureKind::GenericParameter) { assert(isa(D) && "unexpected non-value decl for param"); openTagWithUSRForDecl(tag, cast(D)); } else { openTag(tag); } } void printStructurePost(PrintStructureKind kind, const Decl *D) override { if (isIgnoredPrintStructureKind(kind)) return; if (kind == PrintStructureKind::TupleElement || kind == PrintStructureKind::TupleType) { auto prev = contextStack.pop_back_val(); (void)prev; fixupTuple(kind); assert(prev.is(kind) && "unmatched printStructurePre"); } else { assert(contextStack.back().is(kind) && "unmatched printStructurePre"); contextStack.pop_back(); } auto tag = getTagForParameter(kind); if (!tag.empty()) closeTag(tag); } void printNamePre(PrintNameContext context) override { auto tag = getTagForPrintNameContext(context); if (!tag.empty()) openTag(tag); } void printNamePost(PrintNameContext context) override { auto tag = getTagForPrintNameContext(context); if (!tag.empty()) closeTag(tag); } void printTypeRef( Type T, const TypeDecl *TD, Identifier name, PrintNameContext NameContext = PrintNameContext::Normal) override { auto tag = getTagForDecl(TD, /*isRef=*/true); openTagWithUSRForDecl(tag, TD); insideRef = true; XMLEscapingPrinter::printTypeRef(T, TD, name, NameContext); insideRef = false; closeTag(tag); } // MARK: Convenience functions for printing. void openTag(StringRef tag) { OS << "<" << tag << ">"; } void closeTag(StringRef tag) { OS << ""; } void openTagWithUSRForDecl(StringRef tag, const ValueDecl *VD) { OS << "<" << tag << " usr=\""; SwiftLangSupport::printUSR(VD, OS); OS << "\">"; } // MARK: Misc. StringRef getTypeTagForCurrentContext() const { if (contextStack.empty()) return ""; static StringRef parameterTypeTag = "decl.var.parameter.type"; static StringRef genericParamTypeTag = "decl.generic_type_param.constraint"; auto context = contextStack.back(); if (context.is(PrintStructureKind::FunctionParameter)) return parameterTypeTag; if (context.is(PrintStructureKind::GenericParameter)) return genericParamTypeTag; if (context.is(PrintStructureKind::TupleElement)) return "tuple.element.type"; if (context.getPrintStructureKind().has_value() || context.isType()) return ""; assert(context.getDecl() && "unexpected context kind"); switch (context.getDecl()->getKind()) { case DeclKind::Param: return parameterTypeTag; case DeclKind::GenericTypeParam: return genericParamTypeTag; case DeclKind::Var: return "decl.var.type"; case DeclKind::Subscript: case DeclKind::Func: default: return ""; } } StringRef getTagForPrintNameContext(PrintNameContext context) { if (insideRef) return ""; bool insideParam = !contextStack.empty() && contextStack.back().is(PrintStructureKind::FunctionParameter); switch (context) { case PrintNameContext::FunctionParameterExternal: return ExternalParamNameTag; case PrintNameContext::FunctionParameterLocal: return LocalParamNameTag; case PrintNameContext::TupleElement: if (insideParam) return ExternalParamNameTag; return "tuple.element.argument_label"; case PrintNameContext::Keyword: case PrintNameContext::IntroducerKeyword: return SyntaxKeywordTag; case PrintNameContext::GenericParameter: return GenericParamNameTag; case PrintNameContext::Attribute: return "syntaxtype.attribute.name"; default: return ""; } } /// 'Fix' a tuple or tuple element structure kind to be a function parameter /// or function type if we are currently inside a function type. This /// simplifies functions that need to differentiate a tuple from the input /// part of a function type. void fixupTuple(PrintStructureKind &kind) { assert(kind == PrintStructureKind::TupleElement || kind == PrintStructureKind::TupleType); // Skip over 'type's in the context stack. for (auto I = contextStack.rbegin(), E = contextStack.rend(); I != E; ++I) { if (I->is(PrintStructureKind::FunctionType)) { if (kind == PrintStructureKind::TupleElement) kind = PrintStructureKind::FunctionParameter; else kind = PrintStructureKind::FunctionType; break; } else if (!I->isType()) { break; } } } private: /// A stack of contexts being printed, used to determine the context for /// subsequent ASTPrinter callbacks. llvm::SmallVector contextStack; bool insideRef = false; }; } // end anonymous namespace static Type findBaseTypeForReplacingArchetype(const ValueDecl *VD, const Type Ty) { if (Ty.isNull()) return Type(); // Find the nominal type decl related to VD. if (!VD->getDeclContext()->isTypeContext()) return Type(); return Ty->getRValueType()->getInOutObjectType()->getMetatypeInstanceType(); } static void printAnnotatedDeclaration(const ValueDecl *VD, const Type BaseTy, raw_ostream &OS) { AnnotatedDeclarationPrinter Printer(OS); PrintOptions PO = PrintOptions::printQuickHelpDeclaration(); if (BaseTy) { PO.setBaseType(BaseTy); PO.PrintAsMember = true; } // If it's implicit, try to find an overridden ValueDecl that's not implicit. // This will ensure we can properly annotate TypeRepr with a usr // in AnnotatedDeclarationPrinter. while (VD->isImplicit() && VD->getOverriddenDecl()) VD = VD->getOverriddenDecl(); // VD may be a compiler synthesized member, constructor, or shorthand argument // so always print it even if it's implicit. // // FIXME: Update PrintOptions::printQuickHelpDeclaration to print implicit // decls by default. That causes issues due to newlines being printed before // implicit OpaqueTypeDecls at time of writing. PO.TreatAsExplicitDeclList.push_back(VD); // Wrap this up in XML, as that's what we'll use for documentation comments. OS<<""; VD->print(Printer, PO); OS<<""; } void SwiftLangSupport::printFullyAnnotatedDeclaration(const ValueDecl *VD, Type BaseTy, raw_ostream &OS) { FullyAnnotatedDeclarationPrinter Printer(OS); PrintOptions PO = PrintOptions::printQuickHelpDeclaration(); if (BaseTy) { PO.setBaseType(BaseTy); PO.PrintAsMember = true; } // If it's implicit, try to find an overridden ValueDecl that's not implicit. // This will ensure we can properly annotate TypeRepr with a usr // in AnnotatedDeclarationPrinter. while (VD->isImplicit() && VD->getOverriddenDecl()) VD = VD->getOverriddenDecl(); // VD may be a compiler synthesized member, constructor, or shorthand argument // so always print it even if it's implicit. // // FIXME: Update PrintOptions::printQuickHelpDeclaration to print implicit // decls by default. That causes issues due to newlines being printed before // implicit OpaqueTypeDecls at time of writing. PO.TreatAsExplicitDeclList.push_back(VD); VD->print(Printer, PO); } void SwiftLangSupport::printFullyAnnotatedDeclaration(const ExtensionDecl *ED, raw_ostream &OS) { FullyAnnotatedDeclarationPrinter Printer(OS); PrintOptions PO = PrintOptions::printQuickHelpDeclaration(); ED->print(Printer, PO); } void SwiftLangSupport::printFullyAnnotatedSynthesizedDeclaration( const swift::ValueDecl *VD, TypeOrExtensionDecl Target, llvm::raw_ostream &OS) { FullyAnnotatedDeclarationPrinter Printer(OS); PrintOptions PO = PrintOptions::printQuickHelpDeclaration(); PO.initForSynthesizedExtension(Target); PO.PrintAsMember = true; VD->print(Printer, PO); } void SwiftLangSupport::printFullyAnnotatedSynthesizedDeclaration( const swift::ExtensionDecl *ED, TypeOrExtensionDecl Target, llvm::raw_ostream &OS) { FullyAnnotatedDeclarationPrinter Printer(OS); PrintOptions PO = PrintOptions::printQuickHelpDeclaration(); PO.initForSynthesizedExtension(Target); ED->print(Printer, PO); } template static void walkRelatedDecls(const ValueDecl *VD, const FnTy &Fn) { if (isa(VD)) return; // Parameters don't have interesting related declarations. auto &ctx = VD->getASTContext(); llvm::SmallDenseMap NamesSeen; ++NamesSeen[VD->getName()]; auto *DC = VD->getDeclContext(); bool typeLookup = DC->isTypeContext(); SmallVector results; if (typeLookup) { auto type = DC->getDeclaredInterfaceType(); if (!type->is()) { DC->lookupQualified(type, DeclNameRef(VD->getBaseName()), VD->getLoc(), NL_QualifiedDefault, results); } } else { namelookup::lookupInModule(DC->getModuleScopeContext(), VD->getBaseName(), results, NLKind::UnqualifiedLookup, namelookup::ResolutionKind::Overloadable, DC->getModuleScopeContext(), VD->getLoc(), NL_UnqualifiedDefault); } SmallVector RelatedDecls; for (auto result : results) { if (result->getAttrs().isUnavailable(ctx)) continue; if (result != VD) { ++NamesSeen[result->getName()]; RelatedDecls.push_back(result); } } // Now provide the results along with whether the name is duplicate or not. for (auto result : RelatedDecls) Fn(result, typeLookup, NamesSeen[result->getName()] > 1); } //===----------------------------------------------------------------------===// // SwiftLangSupport::getCursorInfo //===----------------------------------------------------------------------===// static StringRef getSourceToken(unsigned Offset, ImmutableTextSnapshotRef Snap) { auto MemBuf = Snap->getBuffer()->getInternalBuffer(); // FIXME: Invalid offset shouldn't reach here. if (Offset >= MemBuf->getBufferSize()) return StringRef(); SourceManager SM; auto MemBufRef = llvm::MemoryBuffer::getMemBuffer(MemBuf->getBuffer(), MemBuf->getBufferIdentifier()); auto BufId = SM.addNewSourceBuffer(std::move(MemBufRef)); SourceLoc Loc = SM.getLocForOffset(BufId, Offset); return Lexer::getTokenAtLocation(SM, Loc).getText(); } static llvm::Optional mapOffsetToOlderSnapshot(unsigned Offset, ImmutableTextSnapshotRef NewSnap, ImmutableTextSnapshotRef OldSnap) { SmallVector Updates; OldSnap->foreachReplaceUntil(NewSnap, [&](ReplaceImmutableTextUpdateRef Upd)->bool { Updates.push_back(Upd); return true; }); // Walk the updates backwards and "undo" them. for (auto I = Updates.rbegin(), E = Updates.rend(); I != E; ++I) { auto Upd = *I; if (Upd->getByteOffset() <= Offset && Offset < Upd->getByteOffset() + Upd->getText().size()) return llvm::None; // Offset is part of newly inserted text. if (Upd->getByteOffset() <= Offset) { Offset += Upd->getLength(); // "bring back" what was removed. Offset -= Upd->getText().size(); // "remove" what was added. } } return Offset; } static llvm::Optional mapOffsetToNewerSnapshot(unsigned Offset, ImmutableTextSnapshotRef OldSnap, ImmutableTextSnapshotRef NewSnap) { bool Completed = OldSnap->foreachReplaceUntil(NewSnap, [&](ReplaceImmutableTextUpdateRef Upd)->bool { if (Upd->getByteOffset() <= Offset && Offset < Upd->getByteOffset() + Upd->getLength()) return false; // Offset is part of removed text. if (Upd->getByteOffset() <= Offset) { Offset += Upd->getText().size(); Offset -= Upd->getLength(); } return true; }); if (Completed) return Offset; return llvm::None; } /// Tries to remap the location from a previous snapshot to the latest one and /// then sets the location's line and column. static void mapLocToLatestSnapshot( SwiftLangSupport &Lang, LocationInfo &Location, ArrayRef PreviousASTSnaps) { auto EditorDoc = Lang.getEditorDocuments()->findByPath(Location.Filename, /*IsRealpath=*/true); if (!EditorDoc) return; ImmutableTextSnapshotRef LatestSnap = EditorDoc->getLatestSnapshot(); if (!LatestSnap) return; for (auto &PrevSnap : PreviousASTSnaps) { if (PrevSnap->isFromSameBuffer(LatestSnap)) { if (PrevSnap->getStamp() == LatestSnap->getStamp()) break; auto OptBegin = mapOffsetToNewerSnapshot(Location.Offset, PrevSnap, LatestSnap); if (!OptBegin.has_value()) { Location.Filename = StringRef(); return; } auto OptEnd = mapOffsetToNewerSnapshot(Location.Offset + Location.Length, PrevSnap, LatestSnap); if (!OptEnd.has_value()) { Location.Filename = StringRef(); return; } Location.Offset = *OptBegin; Location.Length = *OptEnd - *OptBegin; } } std::tie(Location.Line, Location.Column) = LatestSnap->getBuffer()->getLineAndColumn(Location.Offset); } /// Returns true for error. static bool passCursorInfoForModule(ModuleEntity Mod, SwiftInterfaceGenMap &IFaceGenContexts, const CompilerInvocation &Invok, std::function &)> Receiver) { std::string FullName = Mod.getFullName(); SmallVector Symbols; SmallVector ModuleGroups; CursorSymbolInfo &Symbol = Symbols.emplace_back(); Symbol.Kind = SwiftLangSupport::getUIDForModuleRef(); Symbol.Name = Mod.getName(); Symbol.ModuleName = FullName; if (auto IFaceGenRef = IFaceGenContexts.find(Symbol.ModuleName, Invok)) Symbol.ModuleInterfaceName = IFaceGenRef->getDocumentName(); Symbol.IsSystem = Mod.isNonUserModule(); if (auto MD = Mod.getAsSwiftModule()) { ide::collectModuleGroups(const_cast(MD), ModuleGroups); Symbol.ModuleGroupArray = llvm::makeArrayRef(ModuleGroups); } CursorInfoData Data; Data.Symbols = Symbols; Receiver(RequestResult::fromResult(Data)); return false; } static void addRefactorings( SmallVectorImpl &intoLangInfos, const SmallVectorImpl &availableInfos) { for (auto info : availableInfos) { auto uid = SwiftLangSupport::getUIDForRefactoringKind(info.Kind); bool hasRefactoringWithSameUID = llvm::any_of(intoLangInfos, [&](RefactoringInfo &existing) { return uid == existing.Kind; }); if (hasRefactoringWithSameUID) continue; intoLangInfos.emplace_back( uid, ide::getDescriptiveRefactoringKindName(info.Kind), ide::getDescriptiveRenameUnavailableReason(info.AvailableKind)); } } static llvm::Optional getParamParentNameOffset(const ValueDecl *VD, SourceLoc Cursor) { if (Cursor.isInvalid()) return llvm::None; SourceLoc Loc; if (auto PD = dyn_cast(VD)) { // Avoid returning parent loc for internal-only names. if (PD->getArgumentNameLoc().isValid() && PD->getArgumentNameLoc() != Cursor) return llvm::None; auto *DC = PD->getDeclContext(); switch (DC->getContextKind()) { case DeclContextKind::SubscriptDecl: Loc = cast(DC)->getNameLoc(); break; case DeclContextKind::AbstractFunctionDecl: Loc = cast(DC)->getNameLoc(); break; default: break; } } if (Loc.isInvalid()) return llvm::None; auto &SM = VD->getASTContext().SourceMgr; return SM.getLocOffsetInBuffer(Loc, SM.findBufferContainingLoc(Loc)); } static StringRef getModuleName(const ValueDecl *VD, llvm::BumpPtrAllocator &Allocator) { ASTContext &Ctx = VD->getASTContext(); ClangImporter *Importer = static_cast(Ctx.getClangModuleLoader()); if (auto ClangNode = VD->getClangNode()) { if (const auto *ClangMod = Importer->getClangOwningModule(ClangNode)) return StringRef(ClangMod->getFullModuleName()).copy(Allocator); return ""; } ModuleDecl *MD = VD->getModuleContext(); // If the decl is from a cross-import overlay module, report the // overlay's declaring module as the owning module. if (ModuleDecl *Declaring = MD->getDeclaringModuleIfCrossImportOverlay()) MD = Declaring; return MD->getNameStr(); } struct DeclInfo { const ValueDecl *VD; Type ContainerType; bool IsRef; bool IsDynamic; ArrayRef ReceiverTypes; /// If VD is a synthesized property wrapper backing storage (_foo) or /// projected value ($foo) of a property (foo), the property instead. /// Otherwise, VD. const ValueDecl *OriginalProperty = nullptr; bool Unavailable = true; Type BaseType; /// Whether the \c VD is in a synthesized extension of \c BaseType bool InSynthesizedExtension = false; DeclInfo(const ValueDecl *VD, Type ContainerType, bool IsRef, bool IsDynamic, ArrayRef ReceiverTypes, const CompilerInvocation &Invoc) : VD(VD), ContainerType(ContainerType), IsRef(IsRef), IsDynamic(IsDynamic), ReceiverTypes(ReceiverTypes) { if (VD == nullptr) return; // The synthesized properties $foo and _foo aren't unavailable even if // the original property foo is, so check them rather than the original // property. Unavailable = AvailableAttr::isUnavailable(VD); // No point computing the rest since they won't be used anyway. if (Unavailable) return; OriginalProperty = VD; if (auto *VarD = dyn_cast(VD)) { if (auto *Wrapped = VarD->getOriginalWrappedProperty()) OriginalProperty = Wrapped; } BaseType = findBaseTypeForReplacingArchetype(VD, ContainerType); if (BaseType) { if (auto *Target = BaseType->getAnyNominal()) { SynthesizedExtensionAnalyzer Analyzer( Target, PrintOptions::printModuleInterface( Invoc.getFrontendOptions().PrintFullConvention)); InSynthesizedExtension = Analyzer.isInSynthesizedExtension(VD); } } } }; static StringRef copyAndClearString(llvm::BumpPtrAllocator &Allocator, SmallVectorImpl &Str) { auto Ref = StringRef(Str.data(), Str.size()).copy(Allocator); Str.clear(); return Ref; } template static ArrayRef copyAndClearArray(llvm::BumpPtrAllocator &Allocator, SmallVectorImpl &Array) { auto Ref = copyArray(Allocator, llvm::makeArrayRef(Array)); Array.clear(); return Ref; } static void setLocationInfoForClangNode(ClangNode ClangNode, ClangImporter *Importer, LocationInfo &Location) { clang::ASTContext &ClangCtx = Importer->getClangASTContext(); clang::SourceManager &ClangSM = ClangCtx.getSourceManager(); clang::SourceRange SR = ClangNode.getLocation(); if (auto MD = dyn_cast_or_null(ClangNode.getAsDecl())) { SR = clang::SourceRange(MD->getSelectorStartLoc(), MD->getDeclaratorEndLoc()); } clang::CharSourceRange CharRange = clang::Lexer::makeFileCharRange(clang::CharSourceRange::getTokenRange(SR), ClangSM, ClangCtx.getLangOpts()); if (CharRange.isInvalid()) return; std::pair Decomp = ClangSM.getDecomposedLoc(CharRange.getBegin()); if (!Decomp.first.isInvalid()) { if (auto FE = ClangSM.getFileEntryForID(Decomp.first)) { Location.Filename = FE->getName(); std::pair EndDecomp = ClangSM.getDecomposedLoc(CharRange.getEnd()); Location.Offset = Decomp.second; Location.Length = EndDecomp.second - Decomp.second; Location.Line = ClangSM.getLineNumber(Decomp.first, Decomp.second); Location.Column = ClangSM.getColumnNumber(Decomp.first, Decomp.second); } } } static unsigned getCharLength(SourceManager &SM, SourceRange TokenRange) { SourceLoc CharEndLoc = Lexer::getLocForEndOfToken(SM, TokenRange.End); return SM.getByteDistance(TokenRange.Start, CharEndLoc); } static void setLocationInfo(const ValueDecl *VD, LocationInfo &Location) { ASTContext &Ctx = VD->getASTContext(); SourceManager &SM = Ctx.SourceMgr; auto ClangNode = VD->getClangNode(); auto Loc = VD->getLoc(/*SerializedOK=*/true); if (Loc.isValid()) { auto getSignatureRange = [&](const ValueDecl *VD) -> llvm::Optional { if (auto FD = dyn_cast(VD)) { SourceRange R = FD->getSignatureSourceRange(); if (R.isValid()) return getCharLength(SM, R); } return llvm::None; }; unsigned NameLen; if (auto SigLen = getSignatureRange(VD)) { NameLen = SigLen.value(); } else if (VD->hasName()) { NameLen = VD->getBaseName().userFacingName().size(); } else { NameLen = getCharLength(SM, Loc); } auto [DeclBufID, DeclLoc] = VD->getModuleContext()->getOriginalLocation(Loc); Location.Filename = SM.getIdentifierForBuffer(DeclBufID); Location.Offset = SM.getLocOffsetInBuffer(DeclLoc, DeclBufID); Location.Length = NameLen; std::tie(Location.Line, Location.Column) = SM.getLineAndColumnInBuffer(DeclLoc, DeclBufID); if (auto GeneratedSourceInfo = SM.getGeneratedSourceInfo(DeclBufID)) { if (GeneratedSourceInfo->kind == GeneratedSourceInfo::ReplacedFunctionBody) { // The location was in a temporary source buffer that just contains the // function body and which we created while reusing the ASTContext for // the rest of the file. Map the location back to the original file. unsigned OriginalBufID = SM.findBufferContainingLoc( GeneratedSourceInfo->originalSourceRange.getStart()); auto OriginalStartOffset = SM.getLocOffsetInBuffer( GeneratedSourceInfo->originalSourceRange.getStart(), OriginalBufID); auto GeneratedStartOffset = SM.getLocOffsetInBuffer( GeneratedSourceInfo->generatedSourceRange.getStart(), DeclBufID); Location.Offset += OriginalStartOffset - GeneratedStartOffset; std::tie(Location.Line, Location.Column) = SM.getPresumedLineAndColumnForLoc(DeclLoc, DeclBufID); } } } else if (ClangNode) { ClangImporter *Importer = static_cast(Ctx.getClangModuleLoader()); setLocationInfoForClangNode(ClangNode, Importer, Location); } } static llvm::Error fillSymbolInfo(CursorSymbolInfo &Symbol, const DeclInfo &DInfo, SourceLoc CursorLoc, bool AddSymbolGraph, SwiftLangSupport &Lang, const CompilerInvocation &Invoc, ArrayRef PreviousSnaps, llvm::BumpPtrAllocator &Allocator) { SmallString<256> Buffer; SmallVector Strings; llvm::raw_svector_ostream OS(Buffer); Symbol.DeclarationLang = SwiftLangSupport::getUIDForDeclLanguage(DInfo.VD); Symbol.Kind = SwiftLangSupport::getUIDForDecl(DInfo.VD, DInfo.IsRef); SwiftLangSupport::printDisplayName(DInfo.VD, OS); Symbol.Name = copyAndClearString(Allocator, Buffer); SwiftLangSupport::printUSR(DInfo.OriginalProperty, OS); if (DInfo.InSynthesizedExtension) { OS << LangSupport::SynthesizedUSRSeparator; SwiftLangSupport::printUSR(DInfo.BaseType->getAnyNominal(), OS); } Symbol.USR = copyAndClearString(Allocator, Buffer); { PrintOptions Options; Options.PrintTypeAliasUnderlyingType = true; DInfo.VD->getInterfaceType().print(OS, Options); } Symbol.TypeName = copyAndClearString(Allocator, Buffer); // ParameterizedProtocolType should always be wrapped in ExistentialType and // cannot be mangled on its own. // But ParameterizedProtocolType can currently occur in 'typealias' // declarations. rdar://99176683 // To avoid crashing in USR generation, return an error for now. if (auto Ty = DInfo.VD->getInterfaceType()) { while (auto MetaTy = Ty->getAs()) { Ty = MetaTy->getInstanceType(); } if (Ty && Ty->getCanonicalType()->is()) { return llvm::createStringError( llvm::inconvertibleErrorCode(), "Cannot mangle USR for ParameterizedProtocolType without 'any'."); } } SwiftLangSupport::printDeclTypeUSR(DInfo.VD, OS); Symbol.TypeUSR = copyAndClearString(Allocator, Buffer); if (DInfo.ContainerType && !DInfo.ContainerType->hasArchetype()) { SwiftLangSupport::printTypeUSR(DInfo.ContainerType, OS); } Symbol.ContainerTypeUSR = copyAndClearString(Allocator, Buffer); ide::getDocumentationCommentAsXML(DInfo.OriginalProperty, OS); Symbol.DocComment = copyAndClearString(Allocator, Buffer); { auto *Group = DInfo.InSynthesizedExtension ? DInfo.BaseType->getAnyNominal() : DInfo.VD; if (auto Name = Group->getGroupName()) Symbol.GroupName = Name.value(); } ide::getLocalizationKey(DInfo.VD, OS); Symbol.LocalizationKey = copyAndClearString(Allocator, Buffer); printAnnotatedDeclaration(DInfo.VD, DInfo.BaseType, OS); Symbol.AnnotatedDeclaration = copyAndClearString(Allocator, Buffer); SwiftLangSupport::printFullyAnnotatedDeclaration(DInfo.VD, DInfo.BaseType, OS); Symbol.FullyAnnotatedDeclaration = copyAndClearString(Allocator, Buffer); if (AddSymbolGraph) { SmallVector PathComponents; SmallVector FragmentInfos; symbolgraphgen::SymbolGraphOptions Options; Options.Target = Invoc.getLangOptions().Target; Options.MinimumAccessLevel = AccessLevel::Private; Options.IncludeSPISymbols = true; Options.IncludeClangDocs = true; Options.PrintPrivateStdlibSymbols = true; symbolgraphgen::printSymbolGraphForDecl(DInfo.VD, DInfo.BaseType, DInfo.InSynthesizedExtension, Options, OS, PathComponents, FragmentInfos); Symbol.SymbolGraph = copyAndClearString(Allocator, Buffer); SmallVector Parents; for (auto &Component : PathComponents) { SwiftLangSupport::printUSR(Component.VD, OS); Parents.emplace_back(Component.Title.str().copy(Allocator), Component.Kind, copyAndClearString(Allocator, Buffer)); }; Symbol.ParentContexts = copyArray(Allocator, llvm::makeArrayRef(Parents)); SmallVector ReferencedDecls; for (auto &FI: FragmentInfos) { SmallVector FIParents; for (auto &Component: FI.ParentContexts) { SwiftLangSupport::printUSR(Component.VD, OS); FIParents.emplace_back(Component.Title.str().copy(Allocator), Component.Kind, copyAndClearString(Allocator, Buffer)); } ASTContext &Ctx = FI.VD->getASTContext(); StringRef Filename = ""; if (auto Loc = FI.VD->getLoc(/*SerializedOK=*/true)) { Filename = Ctx.SourceMgr.getDisplayNameForLoc(Loc); } else if (auto ClangNode = FI.VD->getClangNode()) { auto Loc = ClangNode.getLocation(); if (Loc.isValid()) { Filename = Ctx.getClangModuleLoader()->getClangASTContext() .getSourceManager() .getFilename(Loc); } } SwiftLangSupport::printUSR(FI.VD, OS); ReferencedDecls.emplace_back( copyAndClearString(Allocator, Buffer), SwiftLangSupport::getUIDForDeclLanguage(FI.VD), swift::getAccessLevelSpelling(FI.VD->getFormalAccess()), Filename, getModuleName(FI.VD, Allocator), FI.VD->getModuleContext()->isNonUserModule(), FI.VD->isSPI(), copyArray(Allocator, llvm::makeArrayRef(FIParents))); } Symbol.ReferencedSymbols = copyArray(Allocator, llvm::makeArrayRef(ReferencedDecls)); } Symbol.ModuleName = getModuleName(DInfo.VD, Allocator); if (auto IFaceGenRef = Lang.getIFaceGenContexts().find(Symbol.ModuleName, Invoc)) Symbol.ModuleInterfaceName = IFaceGenRef->getDocumentName(); setLocationInfo(DInfo.OriginalProperty, Symbol.Location); if (!Symbol.Location.Filename.empty()) { mapLocToLatestSnapshot(Lang, Symbol.Location, PreviousSnaps); if (Symbol.Location.Filename.empty()) { return llvm::createStringError( llvm::inconvertibleErrorCode(), "Failed to remap declaration to latest snapshot."); } } ide::walkOverriddenDecls( DInfo.VD, [&](llvm::PointerUnion D) { // Could have junk in from previous failing USR print Buffer.clear(); if (auto VD = D.dyn_cast()) { if (SwiftLangSupport::printUSR(VD, OS)) return; } else { if (clang::index::generateUSRForDecl( D.get(), Buffer)) return; } Strings.push_back(copyAndClearString(Allocator, Buffer)); }); Symbol.OverrideUSRs = copyAndClearArray(Allocator, Strings); walkRelatedDecls(DInfo.VD, [&](const ValueDecl *RelatedDecl, bool UseOriginalBase, bool DuplicateName) { OS << ""; if (isa(RelatedDecl) && DuplicateName) { // Related decls are generally overloads, so print parameter types to // differentiate them. PrintOptions PO; PO.SkipAttributes = true; PO.PrintStaticKeyword = false; PO.PrintSelfAccessKindKeyword = false; PO.SkipIntroducerKeywords = true; PO.ArgAndParamPrinting = PrintOptions::ArgAndParamPrintingMode::ArgumentOnly; XMLEscapingPrinter Printer(OS); if (UseOriginalBase && DInfo.BaseType) { PO.setBaseType(DInfo.BaseType); PO.PrintAsMember = true; } RelatedDecl->print(Printer, PO); } else { SmallString<128> RelatedBuffer; llvm::raw_svector_ostream RelatedOS(RelatedBuffer); SwiftLangSupport::printDisplayName(RelatedDecl, RelatedOS); swift::markup::appendWithXMLEscaping(OS, RelatedBuffer); } OS << ""; Strings.push_back(copyAndClearString(Allocator, Buffer)); }); Symbol.AnnotatedRelatedDeclarations = copyAndClearArray(Allocator, Strings); for (auto *ReceiverTy : DInfo.ReceiverTypes) { if (!SwiftLangSupport::printUSR(ReceiverTy, OS)) Strings.push_back(copyAndClearString(Allocator, Buffer)); } Symbol.ReceiverUSRs = copyAndClearArray(Allocator, Strings); Symbol.IsSystem = DInfo.VD->getModuleContext()->isNonUserModule(); Symbol.IsDynamic = DInfo.IsDynamic; Symbol.IsSynthesized = DInfo.VD->isImplicit(); Symbol.ParentNameOffset = getParamParentNameOffset(DInfo.VD, CursorLoc); return llvm::Error::success(); } // If \p E is a literal, returns the declaration that should be reported by // cursor info for that initializer. static ValueDecl *getCursorInfoDeclForLiteral(Expr *E) { if (auto *CollectionLit = dyn_cast(E)) { return CollectionLit->getInitializer().getDecl(); } LiteralExpr* LitExpr = dyn_cast(E); if (!LitExpr) { return nullptr; } bool IsObjectLiteral = isa(E); if (!IsObjectLiteral && LitExpr->getInitializer().getDecl()) { return LitExpr->getInitializer().getDecl(); } // We shouldn’t report the builtin initializer to the user because it’s // underscored and not visible. Instead, return the type of the literal. if (IsObjectLiteral || isa(E)) { Type Ty = E->getType(); if (!Ty) { return nullptr; } auto NominalTy = Ty->getAs(); if (!NominalTy) { return nullptr; } return NominalTy->getDecl(); } return nullptr; } static bool addCursorInfoForLiteral( CursorInfoData &Data, Expr *LitExpr, SwiftLangSupport &Lang, const CompilerInvocation &CompInvoc, SourceLoc CursorLoc, ArrayRef PreviousSnaps) { if (!LitExpr) { return false; } ValueDecl *Decl = getCursorInfoDeclForLiteral(LitExpr); if (!Decl) { return false; } auto &Symbol = Data.Symbols.emplace_back(); DeclInfo Info(Decl, nullptr, true, false, {}, CompInvoc); auto Err = fillSymbolInfo(Symbol, Info, CursorLoc, false, Lang, CompInvoc, PreviousSnaps, Data.Allocator); bool Success = true; llvm::handleAllErrors(std::move(Err), [&](const llvm::StringError &E) { Data.InternalDiagnostic = copyCString(E.getMessage(), Data.Allocator); Data.Symbols.pop_back(); Success = false; }); return Success; } static bool addCursorInfoForDecl(CursorInfoData &Data, ResolvedValueRefCursorInfoPtr Info, bool AddRefactorings, bool AddSymbolGraph, SwiftLangSupport &Lang, const CompilerInvocation &Invoc, std::string &Diagnostic, ArrayRef PreviousSnaps) { DeclInfo OrigInfo(Info->getValueD(), Info->getContainerType(), Info->isRef(), Info->isDynamic(), Info->getReceiverTypes(), Invoc); DeclInfo CtorTypeInfo(Info->getCtorTyRef(), Type(), true, false, ArrayRef(), Invoc); DeclInfo &MainInfo = CtorTypeInfo.VD ? CtorTypeInfo : OrigInfo; if (MainInfo.Unavailable) { Diagnostic = "Unavailable in the current compilation context."; return false; } CursorSymbolInfo &MainSymbol = Data.Symbols.emplace_back(); // The primary result for constructor calls, eg. `MyType()` should be // the type itself, rather than the constructor. The constructor will be // added as a secondary result. if (auto Err = fillSymbolInfo(MainSymbol, MainInfo, Info->getLoc(), AddSymbolGraph, Lang, Invoc, PreviousSnaps, Data.Allocator)) { llvm::handleAllErrors(std::move(Err), [&](const llvm::StringError &E) { Diagnostic = E.message(); }); return false; } if (MainInfo.VD != OrigInfo.VD && !OrigInfo.Unavailable) { CursorSymbolInfo &CtorSymbol = Data.Symbols.emplace_back(); if (auto Err = fillSymbolInfo(CtorSymbol, OrigInfo, Info->getLoc(), AddSymbolGraph, Lang, Invoc, PreviousSnaps, Data.Allocator)) { // Ignore but make sure to remove the partially-filled symbol llvm::handleAllErrors(std::move(Err), [](const llvm::StringError &E) {}); Data.Symbols.pop_back(); } } // Add in shadowed declarations if on a decl. For references just go to the // actual declaration. if (!Info->isRef()) { for (auto D : Info->getShorthandShadowedDecls()) { CursorSymbolInfo &SymbolInfo = Data.Symbols.emplace_back(); DeclInfo DInfo(D, Type(), /*IsRef=*/true, /*IsDynamic=*/false, ArrayRef(), Invoc); if (auto Err = fillSymbolInfo(SymbolInfo, DInfo, Info->getLoc(), AddSymbolGraph, Lang, Invoc, PreviousSnaps, Data.Allocator)) { // Ignore but make sure to remove the partially-filled symbol llvm::handleAllErrors(std::move(Err), [](const llvm::StringError &E) {}); Data.Symbols.pop_back(); } } } if (AddRefactorings) { addRefactorings(Data.AvailableActions, collectRefactorings(Info, /*ExcludeRename=*/false)); } return true; } /// Returns true on success, false on error (and sets `Diagnostic` accordingly). static bool passCursorInfoForDecl( ResolvedValueRefCursorInfoPtr Info, bool AddRefactorings, bool AddSymbolGraph, ArrayRef KnownRefactoringInfo, SwiftLangSupport &Lang, const CompilerInvocation &Invoc, std::string &Diagnostic, ArrayRef PreviousSnaps, bool DidReuseAST, std::function &)> Receiver) { CursorInfoData Data; bool success = addCursorInfoForDecl(Data, Info, AddRefactorings, AddSymbolGraph, Lang, Invoc, Diagnostic, PreviousSnaps); if (!success) { return false; } Data.AvailableActions.append(KnownRefactoringInfo.begin(), KnownRefactoringInfo.end()); Data.DidReuseAST = DidReuseAST; Receiver(RequestResult::fromResult(Data)); return true; } static clang::DeclarationName getClangDeclarationName(const clang::NamedDecl *ND, NameTranslatingInfo &Info) { auto &Ctx = ND->getASTContext(); auto OrigName = ND->getDeclName(); assert(SwiftLangSupport::getNameKindForUID(Info.NameKind) == NameKind::ObjC); if (Info.BaseName.empty() == Info.ArgNames.empty()) { // cannot have both. return clang::DeclarationName(); } if (!Info.BaseName.empty()) { return clang::DeclarationName(&Ctx.Idents.get(Info.BaseName)); } else { switch (OrigName.getNameKind()) { case clang::DeclarationName::ObjCZeroArgSelector: case clang::DeclarationName::ObjCOneArgSelector: case clang::DeclarationName::ObjCMultiArgSelector: break; default: return clang::DeclarationName(); } auto OrigSel = OrigName.getObjCSelector(); unsigned NumPieces = OrigSel.isUnarySelector() ? 1 : OrigSel.getNumArgs(); if (Info.ArgNames.size() > NumPieces) return clang::DeclarationName(); ArrayRef Args = llvm::makeArrayRef(Info.ArgNames); std::vector Pieces; for (unsigned i = 0; i < NumPieces; ++i) { if (i >= Info.ArgNames.size() || Info.ArgNames[i].empty()) { Pieces.push_back(OrigSel.getIdentifierInfoForSlot(i)); } else { StringRef T = Args[i]; Pieces.push_back(&Ctx.Idents.get(T.endswith(":") ? T.drop_back() : T)); } } return clang::DeclarationName( Ctx.Selectors.getSelector(OrigSel.getNumArgs(), Pieces.data())); } } static DeclName getSwiftDeclName(const ValueDecl *VD, NameTranslatingInfo &Info) { auto &Ctx = VD->getDeclContext()->getASTContext(); assert(SwiftLangSupport::getNameKindForUID(Info.NameKind) == NameKind::Swift); const DeclName OrigName = VD->getName(); DeclBaseName BaseName = Info.BaseName.empty() ? OrigName.getBaseName() : DeclBaseName( Info.BaseName == "init" ? DeclBaseName::createConstructor() : Ctx.getIdentifier(Info.BaseName)); auto OrigArgs = OrigName.getArgumentNames(); SmallVector Args(OrigArgs.begin(), OrigArgs.end()); if (Info.ArgNames.size() > OrigArgs.size()) return DeclName(); for (unsigned i = 0; i < OrigArgs.size(); ++i) { if (i < Info.ArgNames.size() && !Info.ArgNames[i].empty()) { StringRef Arg = Info.ArgNames[i]; Args[i] = Ctx.getIdentifier(Arg == "_" ? StringRef() : Arg); } } return DeclName(Ctx, BaseName, llvm::makeArrayRef(Args)); } /// Returns true on success, false on error (and sets `Diagnostic` accordingly). static bool passNameInfoForDecl( const ResolvedValueRefCursorInfo &CursorInfo, NameTranslatingInfo &Info, std::string &Diagnostic, std::function &)> Receiver) { auto *VD = CursorInfo.getValueD(); // If the given name is not a function name, and the cursor points to // a constructor call, we use the type declaration instead of the init // declaration to translate the name. if (Info.ArgNames.empty() && !Info.IsZeroArgSelector) { if (auto *TD = CursorInfo.getCtorTyRef()) { VD = TD; } } switch (SwiftLangSupport::getNameKindForUID(Info.NameKind)) { case NameKind::Swift: { NameTranslatingInfo Result; auto DeclName = getSwiftDeclName(VD, Info); if (!DeclName) { Diagnostic = "Unable to resolve Swift declaration name."; return false; } auto ResultPair = swift::objc_translation::getObjCNameForSwiftDecl(VD, DeclName); Identifier Name = ResultPair.first; if (!Name.empty()) { Result.NameKind = SwiftLangSupport::getUIDForNameKind(NameKind::ObjC); Result.BaseName = Name.str(); Receiver(RequestResult::fromResult(Result)); } else if (ObjCSelector Selector = ResultPair.second) { Result.NameKind = SwiftLangSupport::getUIDForNameKind(NameKind::ObjC); SmallString<64> Buffer; StringRef Total = Selector.getString(Buffer); SmallVector Pieces; Total.split(Pieces, ":"); if (Selector.getNumArgs()) { assert(Pieces.back().empty()); Pieces.pop_back(); } else { Result.IsZeroArgSelector = true; } Result.ArgNames.insert(Result.ArgNames.begin(), Pieces.begin(), Pieces.end()); Receiver(RequestResult::fromResult(Result)); } else { Diagnostic = "Unable to resolve name info."; return false; } return true; } case NameKind::ObjC: { ClangImporter *Importer = static_cast(VD->getDeclContext()-> getASTContext().getClangModuleLoader()); const clang::NamedDecl *Named = nullptr; auto *BaseDecl = VD; while (!Named && BaseDecl) { Named = dyn_cast_or_null(BaseDecl->getClangDecl()); BaseDecl = BaseDecl->getOverriddenDecl(); } if (!Named) { Diagnostic = "Unable to resolve a named declaration."; return false; } auto ObjCName = getClangDeclarationName(Named, Info); if (!ObjCName) { Diagnostic = "Unable to resolve ObjC declaration name."; return false; } DeclName Name = Importer->importName(Named, ObjCName); NameTranslatingInfo Result; Result.NameKind = SwiftLangSupport::getUIDForNameKind(NameKind::Swift); Result.BaseName = Name.getBaseName().userFacingName(); llvm::transform(Name.getArgumentNames(), std::back_inserter(Result.ArgNames), [](Identifier Id) { return Id.str(); }); Receiver(RequestResult::fromResult(Result)); return true; } } } class CursorRangeInfoConsumer : public SwiftASTConsumer { protected: SwiftLangSupport ⟪ SwiftInvocationRef ASTInvok; std::string PrimaryFilePath; std::string InputBufferName; unsigned Offset; unsigned Length; private: const bool TryExistingAST; SmallVector PreviousASTSnaps; protected: bool CancelOnSubsequentRequest; protected: ArrayRef getPreviousASTSnaps() { return llvm::makeArrayRef(PreviousASTSnaps); } public: CursorRangeInfoConsumer(StringRef PrimaryFilePath, StringRef InputBufferName, unsigned Offset, unsigned Length, SwiftLangSupport &Lang, SwiftInvocationRef ASTInvok, bool TryExistingAST, bool CancelOnSubsequentRequest) : Lang(Lang), ASTInvok(ASTInvok), PrimaryFilePath(PrimaryFilePath.str()), InputBufferName(InputBufferName.str()), Offset(Offset), Length(Length), TryExistingAST(TryExistingAST), CancelOnSubsequentRequest(CancelOnSubsequentRequest) {} bool canUseASTWithSnapshots(ArrayRef Snapshots) override { if (!TryExistingAST) { LOG_INFO_FUNC(High, "will resolve using up-to-date AST"); return false; } // If there is an existing AST and the offset can be mapped back to the // document snapshot that was used to create it, then use that AST. // The downside is that we may return stale information, but we get the // benefit of increased responsiveness, since the request will not be // blocked waiting on the AST to be fully typechecked. ImmutableTextSnapshotRef InputSnap; if (auto EditorDoc = Lang.getEditorDocuments()->findByPath( PrimaryFilePath, /*IsRealpath=*/true)) InputSnap = EditorDoc->getLatestSnapshot(); if (!InputSnap) return false; auto mappedBackOffset = [&]()->llvm::Optional { for (auto &Snap : Snapshots) { if (Snap->isFromSameBuffer(InputSnap)) { if (Snap->getStamp() == InputSnap->getStamp()) return Offset; auto OptOffset = mapOffsetToOlderSnapshot(Offset, InputSnap, Snap); if (!OptOffset.has_value()) return llvm::None; // Check that the new and old offset still point to the same token. StringRef NewTok = getSourceToken(Offset, InputSnap); if (NewTok.empty()) return llvm::None; if (NewTok == getSourceToken(OptOffset.value(), Snap)) return OptOffset; return llvm::None; } } return llvm::None; }; auto OldOffsetOpt = mappedBackOffset(); if (OldOffsetOpt.has_value()) { Offset = *OldOffsetOpt; PreviousASTSnaps.append(Snapshots.begin(), Snapshots.end()); LOG_INFO_FUNC(High, "will try existing AST"); return true; } LOG_INFO_FUNC(High, "will resolve using up-to-date AST"); return false; } }; SourceFile *SourceKit::retrieveInputFile(StringRef inputBufferName, const CompilerInstance &CI, bool haveRealPath) { // Don't bother looking up if we have the same file as the primary file or // we weren't given a separate input file if (inputBufferName.empty() || CI.getPrimarySourceFile()->getFilename() == inputBufferName) return CI.getPrimarySourceFile(); // Otherwise, try to find the given buffer identifier const SourceManager &SM = CI.getSourceMgr(); llvm::Optional inputBufferID = SM.getIDForBufferIdentifier(inputBufferName); if (!inputBufferID) { // If that failed, try again with symlinks resolved (unless we've already // done that) if (haveRealPath) return nullptr; std::string realPath = SwiftLangSupport::resolvePathSymlinks(inputBufferName); return retrieveInputFile(realPath, CI, /*haveRealPath=*/true); } return CI.getMainModule()->getSourceFileContainingLocation( SM.getRangeForBuffer(*inputBufferID).getStart()); } static void resolveCursor( SwiftLangSupport &Lang, StringRef PrimaryFile, StringRef InputBufferName, unsigned Offset, unsigned Length, bool Actionables, bool SymbolGraph, SwiftInvocationRef Invok, bool TryExistingAST, bool CancelOnSubsequentRequest, llvm::IntrusiveRefCntPtr fileSystem, SourceKitCancellationToken CancellationToken, std::function &)> Receiver) { assert(Invok); assert(fileSystem); class CursorInfoConsumer : public CursorRangeInfoConsumer { bool Actionables; bool SymbolGraph; SourceKitCancellationToken CancellationToken; std::function &)> Receiver; public: CursorInfoConsumer( StringRef PrimaryFile, StringRef InputBufferName, unsigned Offset, unsigned Length, bool Actionables, bool SymbolGraph, SwiftLangSupport &Lang, SwiftInvocationRef ASTInvok, bool TryExistingAST, bool CancelOnSubsequentRequest, SourceKitCancellationToken CancellationToken, std::function &)> Receiver) : CursorRangeInfoConsumer(PrimaryFile, InputBufferName, Offset, Length, Lang, ASTInvok, TryExistingAST, CancelOnSubsequentRequest), Actionables(Actionables), SymbolGraph(SymbolGraph), CancellationToken(CancellationToken), Receiver(std::move(Receiver)) {} void handlePrimaryAST(ASTUnitRef AstUnit) override { auto &CompIns = AstUnit->getCompilerInstance(); SourceFile *SF = retrieveInputFile(InputBufferName, CompIns); if (!SF) { Receiver(RequestResult::fromError( "Unable to find input file")); return; } SourceManager &SM = CompIns.getSourceMgr(); unsigned BufferID = SF->getBufferID().value(); SourceLoc Loc = Lexer::getLocForStartOfToken(SM, BufferID, Offset); if (Loc.isInvalid()) { Receiver(RequestResult::fromError( "Unable to find initial lookup location")); return; } // Sanitize length. if (Length) { SourceLoc TokEnd = Lexer::getLocForEndOfToken(SM, Loc); SourceLoc EndLoc = SM.getLocForOffset(BufferID, Offset + Length); // If TokEnd is not before the given EndLoc, the EndLoc contains no // more stuff than this token, so set the length to 0. if (SM.isBeforeInBuffer(EndLoc, TokEnd) || TokEnd == EndLoc) Length = 0; } // Retrieve relevant actions on the code under selection. llvm::SmallVector Actions; if (Actionables && Length) { RangeConfig Range; Range.BufferID = BufferID; auto Pair = SM.getLineAndColumnInBuffer(Loc); Range.Line = Pair.first; Range.Column = Pair.second; Range.Length = Length; bool CollectRangeStartRefactorings = false; addRefactorings( Actions, collectRefactorings(SF, Range, CollectRangeStartRefactorings, {})); if (!CollectRangeStartRefactorings) { // If Length is given then this request is only for refactorings, // return straight away unless we need cursor based refactorings as // well. CursorInfoData Data; Data.AvailableActions = Actions; Receiver(RequestResult::fromResult(Data)); return; } // Fall through to collect cursor based refactorings } ResolvedCursorInfoPtr CursorInfo = evaluateOrDefault(CompIns.getASTContext().evaluator, CursorInfoRequest{CursorInfoOwner(SF, Loc)}, new ResolvedCursorInfo()); CompilerInvocation CompInvok; ASTInvok->applyTo(CompInvok); switch (CursorInfo->getKind()) { case CursorInfoKind::ModuleRef: { auto ModuleRefInfo = cast(CursorInfo); passCursorInfoForModule(ModuleRefInfo->getMod(), Lang.getIFaceGenContexts(), CompInvok, Receiver); return; } case CursorInfoKind::ValueRef: { std::string Diagnostic; bool Success = passCursorInfoForDecl( cast(CursorInfo), Actionables, SymbolGraph, Actions, Lang, CompInvok, Diagnostic, getPreviousASTSnaps(), /*DidReuseAST=*/!getPreviousASTSnaps().empty(), Receiver); if (!Success) { if (!getPreviousASTSnaps().empty()) { // Attempt again using the up-to-date AST. resolveCursor(Lang, PrimaryFilePath, InputBufferName, Offset, Length, Actionables, SymbolGraph, ASTInvok, /*TryExistingAST=*/false, CancelOnSubsequentRequest, SM.getFileSystem(), CancellationToken, Receiver); } else { CursorInfoData Info; Info.InternalDiagnostic = Diagnostic; Receiver(RequestResult::fromResult(Info)); } } return; } case CursorInfoKind::ExprStart: case CursorInfoKind::StmtStart: { // If code under cursor is literal expression returns Expr, // otherwise nullptr. auto tryGetLiteralExpr = [](auto CI) -> Expr * { auto *ExprInfo = dyn_cast(CI); if (!ExprInfo || !ExprInfo->getTrailingExpr()) { return nullptr; } Expr *E = ExprInfo->getTrailingExpr(); if (dyn_cast(E) || dyn_cast(E)) { return E; } return nullptr; }; CursorInfoData Data; if (Actionables) { addRefactorings( Actions, collectRefactorings(CursorInfo, /*ExcludeRename=*/true)); Data.AvailableActions = Actions; } // Handle literal expression. if (auto *LitExpr = tryGetLiteralExpr(CursorInfo)) { addCursorInfoForLiteral(Data, LitExpr, Lang, CompInvok, CursorInfo->getLoc(), getPreviousASTSnaps()); } if (Data.isEmpty()) { Data.InternalDiagnostic = "Resolved to incomplete expression or statement."; } Receiver(RequestResult::fromResult(Data)); return; } case CursorInfoKind::Invalid: CursorInfoData Data; if (Actionables) { Data.AvailableActions = Actions; } else { Data.InternalDiagnostic = "Unable to resolve cursor info."; } Receiver(RequestResult::fromResult(Data)); return; } } void cancelled() override { Receiver(RequestResult::cancelled()); } void failed(StringRef Error) override { LOG_WARN_FUNC("cursor info failed: " << Error); Receiver(RequestResult::fromError(Error)); } }; auto Consumer = std::make_shared( PrimaryFile, InputBufferName, Offset, Length, Actionables, SymbolGraph, Lang, Invok, TryExistingAST, CancelOnSubsequentRequest, CancellationToken, Receiver); /// FIXME: When request cancellation is implemented and Xcode adopts it, /// don't use 'OncePerASTToken'. static const char OncePerASTToken = 0; static const char OncePerASTTokenWithActionables = 0; const void *Once = nullptr; if (CancelOnSubsequentRequest) Once = Actionables ? &OncePerASTTokenWithActionables : &OncePerASTToken; Lang.getASTManager()->processASTAsync(Invok, std::move(Consumer), Once, CancellationToken, fileSystem); } static void computeDiagnostics( SwiftLangSupport &Lang, SwiftInvocationRef Invok, llvm::IntrusiveRefCntPtr FileSystem, SourceKitCancellationToken CancellationToken, std::function &)> Receiver) { class DiagnosticsConsumer : public SwiftASTConsumer { std::function &)> Receiver; public: DiagnosticsConsumer( std::function &)> Receiver) : Receiver(Receiver) {} void handlePrimaryAST(ASTUnitRef AstUnit) override { unsigned BufferID = *AstUnit->getPrimarySourceFile().getBufferID(); auto &DiagConsumer = AstUnit->getEditorDiagConsumer(); auto Diagnostics = DiagConsumer.getDiagnosticsForBuffer(BufferID); Receiver(RequestResult::fromResult(Diagnostics)); } void cancelled() override { Receiver(RequestResult::cancelled()); } }; auto Consumer = std::make_shared(std::move(Receiver)); Lang.getASTManager()->processASTAsync(Invok, std::move(Consumer), /*OncePerASTToken=*/nullptr, CancellationToken, FileSystem); } static void resolveName( SwiftLangSupport &Lang, StringRef PrimaryFilePath, StringRef InputBufferName, unsigned Offset, SwiftInvocationRef Invok, bool TryExistingAST, NameTranslatingInfo &Input, SourceKitCancellationToken CancellationToken, std::function &)> Receiver) { assert(Invok); class NameInfoConsumer : public CursorRangeInfoConsumer { NameTranslatingInfo Input; SourceKitCancellationToken CancellationToken; std::function &)> Receiver; public: NameInfoConsumer( StringRef PrimaryFilePath, StringRef InputBufferName, unsigned Offset, SwiftLangSupport &Lang, SwiftInvocationRef ASTInvok, bool TryExistingAST, NameTranslatingInfo Input, SourceKitCancellationToken CancellationToken, std::function &)> Receiver) : CursorRangeInfoConsumer(PrimaryFilePath, InputBufferName, Offset, 0, Lang, ASTInvok, TryExistingAST, /*CancelOnSubsequentRequest=*/false), Input(std::move(Input)), CancellationToken(CancellationToken), Receiver(std::move(Receiver)) {} void handlePrimaryAST(ASTUnitRef AstUnit) override { auto &CompIns = AstUnit->getCompilerInstance(); SourceFile *SF = retrieveInputFile(InputBufferName, CompIns); if (!SF) { Receiver(RequestResult::fromError( "Unable to find input file")); return; } SourceLoc Loc = Lexer::getLocForStartOfToken(CompIns.getSourceMgr(), *SF->getBufferID(), Offset); if (Loc.isInvalid()) { Receiver(RequestResult::fromError( "Unable to resolve the start of the token.")); return; } ResolvedCursorInfoPtr CursorInfo = evaluateOrDefault(CompIns.getASTContext().evaluator, CursorInfoRequest{CursorInfoOwner(SF, Loc)}, new ResolvedCursorInfo()); if (CursorInfo->isInvalid()) { NameTranslatingInfo Info; Info.InternalDiagnostic = "Unable to resolve cursor info."; Receiver(RequestResult::fromResult(Info)); return; } CompilerInvocation CompInvok; ASTInvok->applyTo(CompInvok); switch (CursorInfo->getKind()) { case CursorInfoKind::ModuleRef: return; case CursorInfoKind::ValueRef: { std::string Diagnostic; bool Success = passNameInfoForDecl(*cast(CursorInfo), Input, Diagnostic, Receiver); if (!Success) { if (!getPreviousASTSnaps().empty()) { // Attempt again using the up-to-date AST. resolveName( Lang, PrimaryFilePath, InputBufferName, Offset, ASTInvok, /*TryExistingAST=*/false, Input, CancellationToken, Receiver); } else { NameTranslatingInfo Info; Info.InternalDiagnostic = Diagnostic; Receiver(RequestResult::fromResult(Info)); } } return; } case CursorInfoKind::ExprStart: case CursorInfoKind::StmtStart: { NameTranslatingInfo Info; Info.InternalDiagnostic = "Resolved to incomplete expression or statement."; Receiver(RequestResult::fromResult(Info)); return; } case CursorInfoKind::Invalid: llvm_unreachable("bad sema token kind."); } } void cancelled() override { Receiver(RequestResult::cancelled()); } void failed(StringRef Error) override { LOG_WARN_FUNC("name info failed: " << Error); Receiver(RequestResult::fromError(Error)); } }; auto Consumer = std::make_shared( PrimaryFilePath, InputBufferName, Offset, Lang, Invok, TryExistingAST, Input, CancellationToken, Receiver); Lang.getASTManager()->processASTAsync( Invok, std::move(Consumer), /*OncePerASTToken=*/nullptr, CancellationToken, llvm::vfs::getRealFileSystem()); } static void resolveRange(SwiftLangSupport &Lang, StringRef PrimaryFilePath, StringRef InputBufferName, unsigned Offset, unsigned Length, SwiftInvocationRef Invok, bool TryExistingAST, bool CancelOnSubsequentRequest, SourceKitCancellationToken CancellationToken, std::function &)> Receiver) { assert(Invok); class RangeInfoConsumer : public CursorRangeInfoConsumer { SourceKitCancellationToken CancellationToken; std::function &)> Receiver; public: RangeInfoConsumer( StringRef PrimaryFilePath, StringRef InputBufferName, unsigned Offset, unsigned Length, SwiftLangSupport &Lang, SwiftInvocationRef ASTInvok, bool TryExistingAST, bool CancelOnSubsequentRequest, SourceKitCancellationToken CancellationToken, std::function &)> Receiver) : CursorRangeInfoConsumer(PrimaryFilePath, InputBufferName, Offset, Length, Lang, ASTInvok, TryExistingAST, CancelOnSubsequentRequest), CancellationToken(CancellationToken), Receiver(std::move(Receiver)) {} void handlePrimaryAST(ASTUnitRef AstUnit) override { // FIXME: Implement tracing auto &CompIns = AstUnit->getCompilerInstance(); SourceFile *SF = retrieveInputFile(InputBufferName, CompIns); if (!SF) { Receiver( RequestResult::fromError("Unable to find input file")); return; } ResolvedRangeInfo Info = evaluateOrDefault( CompIns.getASTContext().evaluator, RangeInfoRequest(RangeInfoOwner({SF, Offset, Length})), ResolvedRangeInfo()); CompilerInvocation CompInvok; ASTInvok->applyTo(CompInvok); RangeInfo Result; Result.RangeKind = Lang.getUIDForRangeKind(Info.Kind); if (Info.Kind == RangeKind::Invalid) { Result.RangeContent = ""; } else { assert(Info.ContentRange.isValid()); Result.RangeContent = Info.ContentRange.str(); } switch (Info.Kind) { case RangeKind::SingleExpression: { SmallString<64> SS; llvm::raw_svector_ostream OS(SS); Info.ExitInfo.ReturnType->print(OS); Result.ExprType = OS.str(); Receiver(RequestResult::fromResult(Result)); return; } case RangeKind::SingleDecl: case RangeKind::MultiTypeMemberDecl: case RangeKind::MultiStatement: case RangeKind::SingleStatement: { Receiver(RequestResult::fromResult(Result)); return; } case RangeKind::PartOfExpression: case RangeKind::Invalid: if (!getPreviousASTSnaps().empty()) { // Attempt again using the up-to-date AST. resolveRange(Lang, PrimaryFilePath, InputBufferName, Offset, Length, ASTInvok, /*TryExistingAST=*/false, CancelOnSubsequentRequest, CancellationToken, Receiver); } else { Receiver(RequestResult::fromResult(Result)); } return; } } void cancelled() override { Receiver(RequestResult::cancelled()); } void failed(StringRef Error) override { LOG_WARN_FUNC("range info failed: " << Error); Receiver(RequestResult::fromError(Error)); } }; auto Consumer = std::make_shared( PrimaryFilePath, InputBufferName, Offset, Length, Lang, Invok, TryExistingAST, CancelOnSubsequentRequest, CancellationToken, Receiver); /// FIXME: When request cancellation is implemented and Xcode adopts it, /// don't use 'OncePerASTToken'. static const char OncePerASTToken = 0; const void *Once = CancelOnSubsequentRequest ? &OncePerASTToken : nullptr; Lang.getASTManager()->processASTAsync(Invok, std::move(Consumer), Once, CancellationToken, llvm::vfs::getRealFileSystem()); } static void deliverCursorInfoResults( std::function &)> Receiver, CancellableResult Results, SwiftLangSupport &Lang, const CompilerInvocation &Invoc, bool AddRefactorings, bool AddSymbolGraph) { switch (Results.getKind()) { case CancellableResultKind::Success: { // TODO: Implement delivery of other result types as more cursor info kinds // are migrated to be completion-like. CursorInfoData Data; for (auto ResolvedCursorInfo : Results->ResolvedCursorInfos) { if (auto Result = dyn_cast_or_null( ResolvedCursorInfo)) { std::string Diagnostic; // Unused addCursorInfoForDecl(Data, Result, AddRefactorings, AddSymbolGraph, Lang, Invoc, Diagnostic, /*PreviousSnaps=*/{}); } } Data.DidReuseAST = Results->DidReuseAST; if (!Data.Symbols.empty()) { Receiver(RequestResult::fromResult(Data)); } break; } case CancellableResultKind::Failure: Receiver(RequestResult::fromError(Results.getError())); break; case CancellableResultKind::Cancelled: Receiver(RequestResult::cancelled()); break; } } void SwiftLangSupport::getCursorInfo( StringRef PrimaryFilePath, StringRef InputBufferName, unsigned Offset, unsigned Length, bool Actionables, bool SymbolGraph, bool CancelOnSubsequentRequest, ArrayRef Args, llvm::Optional vfsOptions, SourceKitCancellationToken CancellationToken, std::function &)> Receiver) { std::string error; auto fileSystem = getFileSystem(vfsOptions, PrimaryFilePath, error); if (!fileSystem) return Receiver(RequestResult::fromError(error)); if (auto IFaceGenRef = IFaceGenContexts.get(PrimaryFilePath)) { IFaceGenRef->accessASTAsync([this, IFaceGenRef, Offset, Actionables, SymbolGraph, Receiver] { SwiftInterfaceGenContext::ResolvedEntity Entity; Entity = IFaceGenRef->resolveEntityForOffset(Offset); if (Entity.isResolved()) { CompilerInvocation Invok; IFaceGenRef->applyTo(Invok); if (Entity.Mod) { passCursorInfoForModule(Entity.Mod, IFaceGenContexts, Invok, Receiver); } else { std::string Diagnostic; // Unused. ResolvedValueRefCursorInfoPtr Info = new ResolvedValueRefCursorInfo( /*SourcFile=*/nullptr, SourceLoc(), const_cast(Entity.Dcl), /*CtorTyRef=*/nullptr, /*ExtTyRef=*/nullptr, Entity.IsRef, /*Ty=*/Type(), /*ContainerType=*/Type(), /*CustomAttrRef=*/llvm::None, /*IsKeywordArgument=*/false, /*IsDynamic=*/false, /*ReceiverTypes=*/{}, /*ShorthandShadowedDecls=*/{}); passCursorInfoForDecl( Info, Actionables, SymbolGraph, {}, *this, Invok, Diagnostic, /*PreviousSnaps=*/{}, /*DidReuseAST=*/false, Receiver); } } else { CursorInfoData Info; Info.InternalDiagnostic = "Unable to resolve entity from generated interface."; Receiver(RequestResult::fromResult(Info)); } }); return; } std::string Error; SwiftInvocationRef Invok = ASTMgr->getTypecheckInvocation(Args, PrimaryFilePath, fileSystem, Error); if (!Error.empty()) { LOG_WARN_FUNC("error creating ASTInvocation: " << Error); } if (!Invok) { Receiver(RequestResult::fromError(Error)); return; } // Solver based cursor info cannot handle generated buffers or range based // cursor info. std::shared_ptr InputBuffer; if (InputBufferName.empty() && Length == 0) { std::string InputFileError; llvm::SmallString<128> RealInputFilePath; fileSystem->getRealPath(PrimaryFilePath, RealInputFilePath); InputBuffer = std::shared_ptr(getASTManager()->getMemoryBuffer( RealInputFilePath, fileSystem, InputFileError)); } // Receiver is async, so be careful about captured values. This is all // fairly hacky in order to run AST based cursor info before solver based. // ie. we shouldn't need to apply the compiler invocation again or copy any // arguments. We could possibly sink this down into `resolveCursor`. Or // improve the solver based so we don't need to run the old. auto ASTBasedReceiver = [this, CancellationToken, Invok, InputBuffer, fileSystem, Receiver, Offset, Actionables, SymbolGraph]( const RequestResult &Res) { if (Res.isCancelled()) { // If the AST-based result got cancelled, we don’t want to start // solver-based cursor info anymore. Receiver(Res); return; } // AST based completion *always* produces a result bool NoResults = Res.isError(); if (Res.isValue()) { NoResults = Res.value().Symbols.empty(); } if (!NoResults || !InputBuffer) { Receiver(Res); return; } CompilerInvocation CompInvok; Invok->applyTo(CompInvok); SmallVector Args; Args.reserve(Invok->getArgs().size()); for (const std::string &Arg : Invok->getArgs()) { Args.push_back(Arg.c_str()); } bool SolverProducedResults = false; auto SolverBasedReceiver = [&](const RequestResult &Res) { SolverProducedResults = true; Receiver(Res); }; performWithParamsToCompletionLikeOperation( InputBuffer.get(), Offset, /*InsertCodeCompletionToken=*/false, Args, fileSystem, CancellationToken, [&](CancellableResult ParmsResult) { ParmsResult.mapAsync( [&](auto &Params, auto DeliverTransformed) { getIDEInspectionInstance()->cursorInfo( Params.Invocation, Args, fileSystem, Params.completionBuffer, Offset, Params.DiagC, Params.CancellationFlag, DeliverTransformed); }, [&](auto Result) { deliverCursorInfoResults(SolverBasedReceiver, Result, *this, CompInvok, Actionables, SymbolGraph); }); }); // If the solver based cursor info produced no results, fallback to the // original AST based. if (!SolverProducedResults) { Receiver(Res); } }; resolveCursor(*this, PrimaryFilePath, InputBufferName, Offset, Length, Actionables, SymbolGraph, Invok, /*TryExistingAST=*/true, CancelOnSubsequentRequest, fileSystem, CancellationToken, ASTBasedReceiver); } void SwiftLangSupport::getDiagnostics( StringRef PrimaryFilePath, ArrayRef Args, llvm::Optional VfsOptions, SourceKitCancellationToken CancellationToken, std::function &)> Receiver) { std::string FileSystemError; auto FileSystem = getFileSystem(VfsOptions, PrimaryFilePath, FileSystemError); if (!FileSystem) { Receiver(RequestResult::fromError(FileSystemError)); return; } std::string InvocationError; SwiftInvocationRef Invok = ASTMgr->getTypecheckInvocation( Args, PrimaryFilePath, FileSystem, InvocationError); if (!InvocationError.empty()) { LOG_WARN_FUNC("error creating ASTInvocation: " << InvocationError); } if (!Invok) { Receiver(RequestResult::fromError(InvocationError)); return; } computeDiagnostics(*this, Invok, FileSystem, CancellationToken, Receiver); } void SwiftLangSupport::getRangeInfo( StringRef PrimaryFilePath, StringRef InputBufferName, unsigned Offset, unsigned Length, bool CancelOnSubsequentRequest, ArrayRef Args, SourceKitCancellationToken CancellationToken, std::function &)> Receiver) { if (IFaceGenContexts.get(InputBufferName)) { // FIXME: return range info for generated interfaces. Receiver(RequestResult::fromError( "Range info for generated interfaces is not implemented.")); return; } std::string Error; SwiftInvocationRef Invok = ASTMgr->getTypecheckInvocation(Args, PrimaryFilePath, Error); if (!Invok) { LOG_WARN_FUNC("failed to create an ASTInvocation: " << Error); Receiver(RequestResult::fromError(Error)); return; } if (Length == 0) { Receiver(RequestResult::fromError("Invalid range length.")); return; } resolveRange(*this, PrimaryFilePath, InputBufferName, Offset, Length, Invok, /*TryExistingAST=*/true, CancelOnSubsequentRequest, CancellationToken, Receiver); } void SwiftLangSupport::getNameInfo( StringRef PrimaryFilePath, StringRef InputBufferName, unsigned Offset, NameTranslatingInfo &Input, ArrayRef Args, SourceKitCancellationToken CancellationToken, std::function &)> Receiver) { if (auto IFaceGenRef = IFaceGenContexts.get(PrimaryFilePath)) { IFaceGenRef->accessASTAsync([IFaceGenRef, Offset, Input, Receiver] { SwiftInterfaceGenContext::ResolvedEntity Entity; Entity = IFaceGenRef->resolveEntityForOffset(Offset); if (Entity.isResolved()) { CompilerInvocation Invok; IFaceGenRef->applyTo(Invok); if (Entity.Mod) { // Module is ignored } else { // FIXME: Should pass the main module for the interface but currently // it's not necessary. } } else { NameTranslatingInfo Info; Info.InternalDiagnostic = "Unable to resolve entity from generated interface."; Receiver(RequestResult::fromResult(Info)); } }); return; } std::string Error; SwiftInvocationRef Invok = ASTMgr->getTypecheckInvocation(Args, PrimaryFilePath, Error); if (!Invok) { LOG_WARN_FUNC("failed to create an ASTInvocation: " << Error); Receiver(RequestResult::fromError(Error)); return; } resolveName(*this, PrimaryFilePath, InputBufferName, Offset, Invok, /*TryExistingAST=*/true, Input, CancellationToken, Receiver); } static void resolveCursorFromUSR( SwiftLangSupport &Lang, StringRef InputFile, StringRef USR, SwiftInvocationRef Invok, bool TryExistingAST, bool CancelOnSubsequentRequest, llvm::IntrusiveRefCntPtr fileSystem, SourceKitCancellationToken CancellationToken, std::function &)> Receiver) { assert(Invok); class CursorInfoConsumer : public SwiftASTConsumer { std::string InputFile; StringRef USR; SwiftLangSupport ⟪ SwiftInvocationRef ASTInvok; const bool TryExistingAST; bool CancelOnSubsequentRequest; SourceKitCancellationToken CancellationToken; std::function &)> Receiver; SmallVector PreviousASTSnaps; public: CursorInfoConsumer( StringRef InputFile, StringRef USR, SwiftLangSupport &Lang, SwiftInvocationRef ASTInvok, bool TryExistingAST, bool CancelOnSubsequentRequest, SourceKitCancellationToken CancellationToken, std::function &)> Receiver) : InputFile(InputFile), USR(USR), Lang(Lang), ASTInvok(std::move(ASTInvok)), TryExistingAST(TryExistingAST), CancelOnSubsequentRequest(CancelOnSubsequentRequest), CancellationToken(CancellationToken), Receiver(std::move(Receiver)) {} bool canUseASTWithSnapshots( ArrayRef Snapshots) override { if (!TryExistingAST) { LOG_INFO_FUNC(High, "will resolve using up-to-date AST"); return false; } if (!Snapshots.empty()) { PreviousASTSnaps.append(Snapshots.begin(), Snapshots.end()); LOG_INFO_FUNC(High, "will try existing AST"); return true; } LOG_INFO_FUNC(High, "will resolve using up-to-date AST"); return false; } void handlePrimaryAST(ASTUnitRef AstUnit) override { auto &CompIns = AstUnit->getCompilerInstance(); if (USR.startswith("c:")) { LOG_WARN_FUNC("lookup for C/C++/ObjC USRs not implemented"); CursorInfoData Info; Info.InternalDiagnostic = "Lookup for C/C++/ObjC USRs not implemented."; Receiver(RequestResult::fromResult(Info)); return; } auto &context = CompIns.getASTContext(); TypeDecl *D = Demangle::getTypeDeclForUSR(context, USR); if (!D) { CursorInfoData Info; Info.InternalDiagnostic = "Unable to resolve type from USR."; Receiver(RequestResult::fromResult(Info)); return; } CompilerInvocation CompInvok; ASTInvok->applyTo(CompInvok); if (auto *M = dyn_cast(D)) { passCursorInfoForModule(M, Lang.getIFaceGenContexts(), CompInvok, Receiver); } else { auto *DC = D->getDeclContext(); Type ContainerType; if (DC->isTypeContext()) { auto ContainerType = DC->getSelfInterfaceType(); ContainerType = D->getInnermostDeclContext()->mapTypeIntoContext(ContainerType); } ResolvedValueRefCursorInfoPtr Info = new ResolvedValueRefCursorInfo( /*SourceFile=*/nullptr, SourceLoc(), D, /*CtorTyRef=*/nullptr, /*ExtTyRef=*/nullptr, /*IsRef=*/false, /*Ty=*/Type(), ContainerType, /*CustomAttrRef=*/llvm::None, /*IsKeywordArgument=*/false, /*IsDynamic=*/false, /*ReceiverTypes=*/{}, /*ShorthandShadowedDecls=*/{}); Type selfTy; std::string Diagnostic; bool Success = passCursorInfoForDecl( Info, /*AddRefactorings*/ false, /*AddSymbolGraph*/ false, {}, Lang, CompInvok, Diagnostic, PreviousASTSnaps, /*DidReuseAST=*/false, Receiver); if (!Success) { if (!PreviousASTSnaps.empty()) { // Attempt again using the up-to-date AST. resolveCursorFromUSR(Lang, InputFile, USR, ASTInvok, /*TryExistingAST=*/false, CancelOnSubsequentRequest, CompIns.getSourceMgr().getFileSystem(), CancellationToken, Receiver); } else { CursorInfoData Info; Info.InternalDiagnostic = Diagnostic; Receiver(RequestResult::fromResult(Info)); } } } } void cancelled() override { Receiver(RequestResult::cancelled()); } void failed(StringRef Error) override { LOG_WARN_FUNC("cursor info failed: " << Error); Receiver(RequestResult::fromError(Error)); } }; auto Consumer = std::make_shared( InputFile, USR, Lang, Invok, TryExistingAST, CancelOnSubsequentRequest, CancellationToken, Receiver); /// FIXME: When request cancellation is implemented and Xcode adopts it, /// don't use 'OncePerASTToken'. static const char OncePerASTToken = 0; const void *Once = CancelOnSubsequentRequest ? &OncePerASTToken : nullptr; Lang.getASTManager()->processASTAsync(Invok, std::move(Consumer), Once, CancellationToken, fileSystem); } void SwiftLangSupport::getCursorInfoFromUSR( StringRef PrimaryFilePath, StringRef InputBufferName, StringRef USR, bool CancelOnSubsequentRequest, ArrayRef Args, llvm::Optional vfsOptions, SourceKitCancellationToken CancellationToken, std::function &)> Receiver) { std::string error; auto fileSystem = getFileSystem(vfsOptions, PrimaryFilePath, error); if (!fileSystem) return Receiver(RequestResult::fromError(error)); if (auto IFaceGenRef = IFaceGenContexts.get(PrimaryFilePath)) { LOG_WARN_FUNC("Info from usr for generated interface not implemented yet."); CursorInfoData Info; Info.InternalDiagnostic = "Info for generated interfaces not implemented."; Receiver(RequestResult::fromResult(Info)); return; } std::string Error; SwiftInvocationRef Invok = ASTMgr->getTypecheckInvocation(Args, PrimaryFilePath, fileSystem, Error); if (!Invok) { LOG_WARN_FUNC("failed to create an ASTInvocation: " << Error); Receiver(RequestResult::fromError(Error)); return; } resolveCursorFromUSR(*this, InputBufferName, USR, Invok, /*TryExistingAST=*/true, CancelOnSubsequentRequest, fileSystem, CancellationToken, Receiver); } //===----------------------------------------------------------------------===// // SwiftLangSupport::findUSRRange //===----------------------------------------------------------------------===// llvm::Optional> SwiftLangSupport::findUSRRange(StringRef DocumentName, StringRef USR) { if (auto IFaceGenRef = IFaceGenContexts.get(DocumentName)) return IFaceGenRef->findUSRRange(USR); // Only works for a module interface document currently. // FIXME: Report it as failed request. return llvm::None; } //===----------------------------------------------------------------------===// // SwiftLangSupport::findRelatedIdentifiersInFile //===----------------------------------------------------------------------===// void SwiftLangSupport::findRelatedIdentifiersInFile( StringRef PrimaryFilePath, StringRef InputBufferName, unsigned Offset, bool IncludeNonEditableBaseNames, bool CancelOnSubsequentRequest, ArrayRef Args, SourceKitCancellationToken CancellationToken, std::function &)> Receiver) { std::string Error; SwiftInvocationRef Invok = ASTMgr->getTypecheckInvocation(Args, PrimaryFilePath, Error); if (!Invok) { LOG_WARN_FUNC("failed to create an ASTInvocation: " << Error); Receiver(RequestResult::fromError(Error)); return; } class RelatedIdConsumer : public SwiftASTConsumer { std::string InputFile; unsigned Offset; bool IncludeNonEditableBaseNames; std::function &)> Receiver; SwiftInvocationRef Invok; #if SWIFT_BUILD_SWIFT_SYNTAX // FIXME: Don't silently eat errors here. RelatedIdentsResult getRelatedIdents(SourceFile *SrcFile, CompilerInstance &CompInst) { unsigned BufferID = SrcFile->getBufferID().value(); SourceLoc Loc = Lexer::getLocForStartOfToken(CompInst.getSourceMgr(), BufferID, Offset); if (Loc.isInvalid()) return RelatedIdentsResult::empty(); SourceManager &SrcMgr = CompInst.getASTContext().SourceMgr; ResolvedCursorInfoPtr CursorInfo = evaluateOrDefault(CompInst.getASTContext().evaluator, CursorInfoRequest{CursorInfoOwner(SrcFile, Loc)}, new ResolvedCursorInfo()); auto ValueRefCursorInfo = dyn_cast(CursorInfo); if (!ValueRefCursorInfo) return RelatedIdentsResult::empty(); if (ValueRefCursorInfo->isKeywordArgument()) return RelatedIdentsResult::empty(); ValueDecl *VD = ValueRefCursorInfo->typeOrValue(); if (!VD) return RelatedIdentsResult::empty(); // This was a module reference. // Only accept pointing to an identifier. if (!IncludeNonEditableBaseNames && !ValueRefCursorInfo->isRef() && (isa(VD) || isa(VD) || isa(VD))) return RelatedIdentsResult::empty(); llvm::Optional Info = getRenameInfo(CursorInfo); if (!Info) { return RelatedIdentsResult::empty(); } RenameLocs Locs = localRenameLocs(SrcFile, Info->VD); std::string OldName = Locs.getLocations().front().OldName.str(); #ifndef NDEBUG for (auto loc : Locs.getLocations()) { assert(loc.OldName == OldName && "Found related identfiers with different names?"); } #endif // Ignore any errors produced by `resolveRenameLocations` since, if some // symbol failed to resolve, we still want to return all the other // symbols. This makes related idents more fault-tolerant. DiagnosticEngine Diags(SrcMgr); std::vector ResolvedLocs = resolveRenameLocations( Locs.getLocations(), /*NewName=*/StringRef(), *SrcFile, Diags); SmallVector Ranges; assert(ResolvedLocs.size() == Locs.getLocations().size()); for (auto [RenameLoc, ResolvedLoc] : llvm::zip_equal(Locs.getLocations(), ResolvedLocs)) { if (ResolvedLoc.range.isInvalid()) { continue; } unsigned Offset = SrcMgr.getLocOffsetInBuffer(ResolvedLoc.range.getStart(), BufferID); Ranges.push_back( {Offset, ResolvedLoc.range.getByteLength(), RenameLoc.Usage}); } return RelatedIdentsResult(Ranges, OldName); } #endif public: RelatedIdConsumer( StringRef InputFile, unsigned Offset, bool IncludeNonEditableBaseNames, std::function &)> Receiver, SwiftInvocationRef Invok) : InputFile(InputFile.str()), Offset(Offset), IncludeNonEditableBaseNames(IncludeNonEditableBaseNames), Receiver(std::move(Receiver)), Invok(Invok) {} void handlePrimaryAST(ASTUnitRef AstUnit) override { using ResultType = RequestResult; #if !SWIFT_BUILD_SWIFT_SYNTAX ResultType::fromError( "relatedidents is not supported because sourcekitd was built without " "swift-syntax"); return; #else auto &CompInst = AstUnit->getCompilerInstance(); auto *SrcFile = retrieveInputFile(InputFile, CompInst); if (!SrcFile) { Receiver(RequestResult::fromError( "Unable to find input file")); return; } RelatedIdentsResult Result = getRelatedIdents(SrcFile, CompInst); Receiver(ResultType::fromResult(Result)); #endif } void cancelled() override { Receiver(RequestResult::cancelled()); } void failed(StringRef Error) override { LOG_WARN_FUNC("related idents failed: " << Error); Receiver(RequestResult::fromError(Error)); } static CaseStmt *getCaseStmtOfCanonicalVar(Decl *D) { assert(D); if (auto *VD = dyn_cast(D)) { if (auto *Canonical = VD->getCanonicalVarDecl()) { return dyn_cast_or_null(Canonical->getRecursiveParentPatternStmt()); } } return nullptr; } }; auto Consumer = std::make_shared( InputBufferName, Offset, IncludeNonEditableBaseNames, Receiver, Invok); /// FIXME: When request cancellation is implemented and Xcode adopts it, /// don't use 'OncePerASTToken'. static const char OncePerASTToken = 0; const void *Once = CancelOnSubsequentRequest ? &OncePerASTToken : nullptr; ASTMgr->processASTAsync(Invok, std::move(Consumer), Once, CancellationToken, llvm::vfs::getRealFileSystem()); } //===----------------------------------------------------------------------===// // SwiftLangSupport::findActiveRegionsInFile //===----------------------------------------------------------------------===// namespace { class IfConfigScanner : public SourceEntityWalker { unsigned BufferID = -1; SmallVectorImpl &Infos; bool Cancelled = false; public: explicit IfConfigScanner(unsigned BufferID, SmallVectorImpl &Infos) : BufferID(BufferID), Infos(Infos) {} private: bool walkToDeclPre(Decl *D, CharSourceRange Range) override { if (Cancelled) return false; if (auto *IfDecl = dyn_cast(D)) { for (auto &Clause : IfDecl->getClauses()) { unsigned Offset = D->getASTContext().SourceMgr.getLocOffsetInBuffer( Clause.Loc, BufferID); Infos.emplace_back(Offset, Clause.isActive); } } return true; } }; } // end anonymous namespace void SwiftLangSupport::findActiveRegionsInFile( StringRef PrimaryFilePath, StringRef InputBufferName, ArrayRef Args, SourceKitCancellationToken CancellationToken, std::function &)> Receiver) { std::string Error; SwiftInvocationRef Invok = ASTMgr->getTypecheckInvocation(Args, PrimaryFilePath, Error); if (!Invok) { LOG_WARN_FUNC("failed to create an ASTInvocation: " << Error); Receiver(RequestResult::fromError(Error)); return; } class IfConfigConsumer : public SwiftASTConsumer { std::string InputFile; std::function &)> Receiver; SwiftInvocationRef Invok; public: IfConfigConsumer( StringRef InputFile, std::function &)> Receiver, SwiftInvocationRef Invok) : InputFile(InputFile.str()), Receiver(std::move(Receiver)), Invok(Invok) {} void handlePrimaryAST(ASTUnitRef AstUnit) override { auto *SF = retrieveInputFile(InputFile, AstUnit->getCompilerInstance()); if (!SF) { Receiver(RequestResult::fromError( "Unable to find input file")); return; } SmallVector Configs; IfConfigScanner Scanner(*SF->getBufferID(), Configs); Scanner.walk(SF); // Sort by offset so nested decls are reported // in source order (not tree order). llvm::sort(Configs, [](const IfConfigInfo &LHS, const IfConfigInfo &RHS) -> bool { return LHS.Offset < RHS.Offset; }); ActiveRegionsInfo Info; Info.Configs = Configs; Receiver(RequestResult::fromResult(Info)); } void cancelled() override { Receiver(RequestResult::cancelled()); } void failed(StringRef Error) override { LOG_WARN_FUNC("inactive ranges failed: " << Error); Receiver(RequestResult::fromError(Error)); } }; auto Consumer = std::make_shared(InputBufferName, Receiver, Invok); ASTMgr->processASTAsync(Invok, std::move(Consumer), /*OncePerASTToken=*/nullptr, CancellationToken, llvm::vfs::getRealFileSystem()); } //===----------------------------------------------------------------------===// // SwiftLangSupport::semanticRefactoring //===----------------------------------------------------------------------===// static RefactoringKind getIDERefactoringKind(SemanticRefactoringInfo Info) { switch(Info.Kind) { case SemanticRefactoringKind::None: return RefactoringKind::None; #define SEMANTIC_REFACTORING(KIND, NAME, ID) \ case SemanticRefactoringKind::KIND: return RefactoringKind::KIND; #include "swift/Refactoring/RefactoringKinds.def" } } void SwiftLangSupport::semanticRefactoring( StringRef PrimaryFilePath, SemanticRefactoringInfo Info, ArrayRef Args, SourceKitCancellationToken CancellationToken, CategorizedEditsReceiver Receiver) { std::string Error; SwiftInvocationRef Invok = ASTMgr->getTypecheckInvocation(Args, PrimaryFilePath, Error); if (!Invok) { LOG_WARN_FUNC("failed to create an ASTInvocation: " << Error); Receiver(RequestResult>::fromError(Error)); return; } assert(Invok); class SemaRefactoringConsumer : public SwiftASTConsumer { SemanticRefactoringInfo Info; CategorizedEditsReceiver Receiver; public: SemaRefactoringConsumer(SemanticRefactoringInfo Info, CategorizedEditsReceiver Receiver) : Info(Info), Receiver(std::move(Receiver)) {} void handlePrimaryAST(ASTUnitRef AstUnit) override { RequestRefactoringEditConsumer EditConsumer(Receiver); auto &CompIns = AstUnit->getCompilerInstance(); RefactoringOptions Opts(getIDERefactoringKind(Info)); SourceFile *SF = retrieveInputFile(Info.InputBufferName, CompIns); if (!SF) { Receiver(RequestResult>::fromError( "Unable to find input file")); return; } Opts.Range.BufferID = *SF->getBufferID(); Opts.Range.Line = Info.Line; Opts.Range.Column = Info.Column; Opts.Range.Length = Info.Length; Opts.PreferredName = Info.PreferredName.str(); refactorSwiftModule(CompIns.getMainModule(), Opts, EditConsumer, EditConsumer); } void cancelled() override { Receiver(RequestResult>::cancelled()); } void failed(StringRef Error) override { Receiver(RequestResult>::fromError(Error)); } }; auto Consumer = std::make_shared(Info, Receiver); /// FIXME: When request cancellation is implemented and Xcode adopts it, /// don't use 'OncePerASTToken'. static const char OncePerASTToken = 0; getASTManager()->processASTAsync(Invok, std::move(Consumer), &OncePerASTToken, CancellationToken, llvm::vfs::getRealFileSystem()); } void SwiftLangSupport::collectExpressionTypes( StringRef PrimaryFilePath, StringRef InputBufferName, ArrayRef Args, ArrayRef ExpectedProtocols, bool FullyQualified, bool CanonicalType, SourceKitCancellationToken CancellationToken, std::function &)> Receiver) { std::string Error; SwiftInvocationRef Invok = ASTMgr->getTypecheckInvocation(Args, PrimaryFilePath, Error); if (!Invok) { LOG_WARN_FUNC("failed to create an ASTInvocation: " << Error); Receiver(RequestResult::fromError(Error)); return; } assert(Invok); class ExpressionTypeCollector: public SwiftASTConsumer { std::function &)> Receiver; std::string InputFile; std::vector ExpectedProtocols; bool FullyQualified; bool CanonicalType; public: ExpressionTypeCollector( std::function &)> Receiver, StringRef InputFile, ArrayRef ExpectedProtocols, bool FullyQualified, bool CanonicalType) : Receiver(std::move(Receiver)), InputFile(InputFile.str()), ExpectedProtocols(ExpectedProtocols.vec()), FullyQualified(FullyQualified), CanonicalType(CanonicalType) {} void handlePrimaryAST(ASTUnitRef AstUnit) override { SourceFile *SF = retrieveInputFile(InputFile, AstUnit->getCompilerInstance()); if (!SF) { Receiver(RequestResult::fromError( "Unable to find input file")); return; } std::vector Scratch; llvm::SmallString<256> TypeBuffer; llvm::raw_svector_ostream OS(TypeBuffer); ExpressionTypesInFile Result; for (auto Item : collectExpressionType(*SF, ExpectedProtocols, Scratch, FullyQualified, CanonicalType, OS)) { Result.Results.push_back({Item.offset, Item.length, Item.typeOffset, {}}); for (auto P: Item.protocols) { Result.Results.back().ProtocolOffsets.push_back(P.first); } } Result.TypeBuffer = OS.str(); Receiver(RequestResult::fromResult(Result)); } void cancelled() override { Receiver(RequestResult::cancelled()); } void failed(StringRef Error) override { Receiver(RequestResult::fromError(Error)); } }; auto Collector = std::make_shared( Receiver, InputBufferName, ExpectedProtocols, FullyQualified, CanonicalType); /// FIXME: When request cancellation is implemented and Xcode adopts it, /// don't use 'OncePerASTToken'. static const char OncePerASTToken = 0; getASTManager()->processASTAsync(Invok, std::move(Collector), &OncePerASTToken, CancellationToken, llvm::vfs::getRealFileSystem()); } void SwiftLangSupport::collectVariableTypes( StringRef PrimaryFilePath, StringRef InputBufferName, ArrayRef Args, llvm::Optional Offset, llvm::Optional Length, bool FullyQualified, SourceKitCancellationToken CancellationToken, std::function &)> Receiver) { std::string Error; SwiftInvocationRef Invok = ASTMgr->getTypecheckInvocation(Args, PrimaryFilePath, Error); if (!Invok) { LOG_WARN_FUNC("failed to create an ASTInvocation: " << Error); Receiver(RequestResult::fromError(Error)); return; } assert(Invok); class VariableTypeCollectorASTConsumer : public SwiftASTConsumer { private: std::function &)> Receiver; std::string InputFile; llvm::Optional Offset; llvm::Optional Length; bool FullyQualified; public: VariableTypeCollectorASTConsumer( std::function &)> Receiver, StringRef InputFile, llvm::Optional Offset, llvm::Optional Length, bool FullyQualified) : Receiver(std::move(Receiver)), InputFile(InputFile), Offset(Offset), Length(Length), FullyQualified(FullyQualified) {} void handlePrimaryAST(ASTUnitRef AstUnit) override { auto &CompInst = AstUnit->getCompilerInstance(); SourceFile *SF = retrieveInputFile(InputFile, CompInst); if (!SF) { Receiver(RequestResult::fromError( "Unable to find input file")); return; } // Construct the range for which variable types are to be queried. If // offset/length are unset, the (default) range will be used, which // corresponds to the entire document. SourceRange Range; if (Offset.has_value() && Length.has_value()) { auto &SM = CompInst.getSourceMgr(); unsigned BufferID = SF->getBufferID().value(); SourceLoc Start = Lexer::getLocForStartOfToken(SM, BufferID, *Offset); SourceLoc End = Lexer::getLocForStartOfToken(SM, BufferID, *Offset + *Length); Range = SourceRange(Start, End); } std::vector Infos; std::string TypeBuffer; llvm::raw_string_ostream OS(TypeBuffer); VariableTypesInFile Result; collectVariableType(*SF, Range, FullyQualified, Infos, OS); for (auto Info : Infos) { Result.Results.push_back({Info.Offset, Info.Length, Info.TypeOffset, Info.HasExplicitType}); } Result.TypeBuffer = OS.str(); Receiver(RequestResult::fromResult(Result)); } void cancelled() override { Receiver(RequestResult::cancelled()); } void failed(StringRef Error) override { Receiver(RequestResult::fromError(Error)); } }; auto Collector = std::make_shared( Receiver, InputBufferName, Offset, Length, FullyQualified); /// FIXME: When request cancellation is implemented and Xcode adopts it, /// don't use 'OncePerASTToken'. static const char OncePerASTToken = 0; getASTManager()->processASTAsync(Invok, std::move(Collector), &OncePerASTToken, CancellationToken, llvm::vfs::getRealFileSystem()); }