mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
3005 lines
111 KiB
C++
3005 lines
111 KiB
C++
//===--- 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 <numeric>
|
||
|
||
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("<Type usr=\"");
|
||
SwiftLangSupport::printUSR(TD, OS);
|
||
printXML("\">");
|
||
StreamPrinter::printTypeRef(T, TD, Name, NameContext);
|
||
printXML("</Type>");
|
||
}
|
||
};
|
||
} // 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().starts_with(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<Decl *>::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.
|
||
std::optional<PrintStructureKind> getPrintStructureKind() const {
|
||
if (!hasTag(PrintStructureKindTag))
|
||
return std::nullopt;
|
||
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
|
||
/// <decl.function.free>
|
||
/// func <decl.name>foo</decl.name>
|
||
/// (
|
||
/// <decl.var.parameter>
|
||
/// <decl.var.parameter.name>x</decl.var.parameter.name>:
|
||
/// <ref.struct usr="Si">Int</ref.struct>
|
||
/// </decl.var.parameter>
|
||
/// ) -> <decl.function.returntype>
|
||
/// <ref.struct usr="Si">Int</ref.struct></decl.function.returntype>
|
||
/// </decl.function.free>
|
||
/// \endverbatim
|
||
class FullyAnnotatedDeclarationPrinter final : public XMLEscapingPrinter {
|
||
public:
|
||
FullyAnnotatedDeclarationPrinter(raw_ostream &OS) : XMLEscapingPrinter(OS) {}
|
||
|
||
private:
|
||
|
||
// MARK: The ASTPrinter callback interface.
|
||
|
||
void printDeclPre(const Decl *D,
|
||
std::optional<BracketOptions> Bracket) override {
|
||
contextStack.emplace_back(PrintContext(D));
|
||
openTag(getTagForDecl(D, /*isRef=*/false));
|
||
}
|
||
void printDeclPost(const Decl *D,
|
||
std::optional<BracketOptions> 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<ValueDecl>(D) && "unexpected non-value decl for param");
|
||
openTagWithUSRForDecl(tag, cast<ValueDecl>(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 << "</" << tag << ">"; }
|
||
|
||
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<PrintContext, 3> 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<<"<Declaration>";
|
||
VD->print(Printer, PO);
|
||
OS<<"</Declaration>";
|
||
}
|
||
|
||
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 <typename FnTy>
|
||
static void walkRelatedDecls(const ValueDecl *VD, const FnTy &Fn) {
|
||
if (isa<ParamDecl>(VD))
|
||
return; // Parameters don't have interesting related declarations.
|
||
|
||
auto &ctx = VD->getASTContext();
|
||
|
||
llvm::SmallDenseMap<DeclName, unsigned, 16> NamesSeen;
|
||
++NamesSeen[VD->getName()];
|
||
|
||
|
||
auto *DC = VD->getDeclContext();
|
||
bool typeLookup = DC->isTypeContext();
|
||
|
||
SmallVector<ValueDecl *, 4> results;
|
||
|
||
if (typeLookup) {
|
||
auto type = DC->getDeclaredInterfaceType();
|
||
if (!type->is<ErrorType>()) {
|
||
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<ValueDecl *, 8> 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 std::optional<unsigned>
|
||
mapOffsetToOlderSnapshot(unsigned Offset, ImmutableTextSnapshotRef NewSnap,
|
||
ImmutableTextSnapshotRef OldSnap) {
|
||
SmallVector<ReplaceImmutableTextUpdateRef, 16> 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 std::nullopt; // 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 std::optional<unsigned>
|
||
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 std::nullopt;
|
||
}
|
||
|
||
/// 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<ImmutableTextSnapshotRef> 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<void(const RequestResult<CursorInfoData> &)> Receiver) {
|
||
std::string FullName = Mod.getFullName();
|
||
SmallVector<CursorSymbolInfo, 1> Symbols;
|
||
SmallVector<StringRef, 4> 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<ModuleDecl *>(MD), ModuleGroups);
|
||
Symbol.ModuleGroupArray = llvm::ArrayRef(ModuleGroups);
|
||
}
|
||
|
||
CursorInfoData Data;
|
||
Data.Symbols = Symbols;
|
||
Receiver(RequestResult<CursorInfoData>::fromResult(Data));
|
||
return false;
|
||
}
|
||
|
||
static void addRefactorings(
|
||
SmallVectorImpl<RefactoringInfo> &intoLangInfos,
|
||
const SmallVectorImpl<RefactorAvailabilityInfo> &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 std::optional<unsigned> getParamParentNameOffset(const ValueDecl *VD,
|
||
SourceLoc Cursor) {
|
||
if (Cursor.isInvalid())
|
||
return std::nullopt;
|
||
SourceLoc Loc;
|
||
if (auto PD = dyn_cast<ParamDecl>(VD)) {
|
||
|
||
// Avoid returning parent loc for internal-only names.
|
||
if (PD->getArgumentNameLoc().isValid() && PD->getArgumentNameLoc() != Cursor)
|
||
return std::nullopt;
|
||
auto *DC = PD->getDeclContext();
|
||
switch (DC->getContextKind()) {
|
||
case DeclContextKind::SubscriptDecl:
|
||
Loc = cast<SubscriptDecl>(DC)->getNameLoc();
|
||
break;
|
||
case DeclContextKind::AbstractFunctionDecl:
|
||
Loc = cast<AbstractFunctionDecl>(DC)->getNameLoc();
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
if (Loc.isInvalid())
|
||
return std::nullopt;
|
||
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<ClangImporter *>(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 {
|
||
ValueDecl *VD;
|
||
/// If not null, a solution-specific interface type of `VD`. This allows
|
||
/// us to produce results for declarations where the soluion has not been
|
||
/// applied to the AST, eg. because the declaration has an ambiguous type
|
||
/// or because it occurs inside a closure that has an error, which prevents
|
||
/// the constraint system solution to be applied to the AST.
|
||
Type SolutionSpecificInterfaceType;
|
||
Type ContainerType;
|
||
bool IsRef;
|
||
bool IsDynamic;
|
||
ArrayRef<NominalTypeDecl *> 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(ValueDecl *VD, Type SolutionSpecificInterfaceType,
|
||
Type ContainerType, bool IsRef, bool IsDynamic,
|
||
ArrayRef<NominalTypeDecl *> ReceiverTypes,
|
||
const CompilerInvocation &Invoc)
|
||
: VD(VD), SolutionSpecificInterfaceType(SolutionSpecificInterfaceType),
|
||
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<VarDecl>(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<char> &Str) {
|
||
auto Ref = StringRef(Str.data(), Str.size()).copy(Allocator);
|
||
Str.clear();
|
||
return Ref;
|
||
}
|
||
|
||
template <typename T>
|
||
static ArrayRef<T> copyAndClearArray(llvm::BumpPtrAllocator &Allocator,
|
||
SmallVectorImpl<T> &Array) {
|
||
auto Ref = copyArray(Allocator, llvm::ArrayRef(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<clang::ObjCMethodDecl>(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<clang::FileID, unsigned> Decomp =
|
||
ClangSM.getDecomposedLoc(CharRange.getBegin());
|
||
if (!Decomp.first.isInvalid()) {
|
||
if (auto FE = ClangSM.getFileEntryRefForID(Decomp.first)) {
|
||
Location.Filename = FE->getName();
|
||
|
||
std::pair<clang::FileID, unsigned> 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) -> std::optional<unsigned> {
|
||
if (auto FD = dyn_cast<AbstractFunctionDecl>(VD)) {
|
||
SourceRange R = FD->getSignatureSourceRange();
|
||
if (R.isValid())
|
||
return getCharLength(SM, R);
|
||
}
|
||
return std::nullopt;
|
||
};
|
||
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<ClangImporter*>(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<ImmutableTextSnapshotRef> PreviousSnaps,
|
||
llvm::BumpPtrAllocator &Allocator) {
|
||
// Override the type of `DInfo.VD` while retrieving symbol information.
|
||
// Ideally, we would pass a customizable `getInterfaceType` function to all
|
||
// functions that operate on VD but doing so is not viable - USR generation,
|
||
// AST printing, symbol graph and probably more have been designed with the
|
||
// assumption that VD->getInterfaceType() returns the fixed interface type
|
||
// of the declaration.
|
||
Type OriginalInterfaceType = DInfo.VD->getInterfaceType();
|
||
if (DInfo.SolutionSpecificInterfaceType) {
|
||
DInfo.VD->setInterfaceType(DInfo.SolutionSpecificInterfaceType);
|
||
}
|
||
SWIFT_DEFER { DInfo.VD->setInterfaceType(OriginalInterfaceType); };
|
||
|
||
SmallString<256> Buffer;
|
||
SmallVector<StringRef, 4> 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<MetatypeType>()) {
|
||
Ty = MetaTy->getInstanceType();
|
||
}
|
||
if (Ty && Ty->getCanonicalType()->is<ParameterizedProtocolType>()) {
|
||
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::getRawDocumentationComment(DInfo.OriginalProperty, OS);
|
||
Symbol.DocComment = copyAndClearString(Allocator, Buffer);
|
||
|
||
ide::getDocumentationCommentAsXML(DInfo.OriginalProperty, OS);
|
||
Symbol.DocCommentAsXML = 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<symbolgraphgen::PathComponent, 4> PathComponents;
|
||
SmallVector<symbolgraphgen::FragmentInfo, 8> FragmentInfos;
|
||
|
||
symbolgraphgen::SymbolGraphOptions Options;
|
||
Options.Target = Invoc.getLangOptions().Target;
|
||
Options.MinimumAccessLevel = AccessLevel::Private;
|
||
Options.IncludeSPISymbols = true;
|
||
Options.IncludeClangDocs = true;
|
||
Options.PrintPrivateSystemSymbols = true;
|
||
|
||
symbolgraphgen::printSymbolGraphForDecl(DInfo.VD, DInfo.BaseType,
|
||
DInfo.InSynthesizedExtension,
|
||
Options, OS, PathComponents,
|
||
FragmentInfos);
|
||
Symbol.SymbolGraph = copyAndClearString(Allocator, Buffer);
|
||
|
||
SmallVector<ParentInfo, 4> 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::ArrayRef(Parents));
|
||
|
||
SmallVector<ReferencedDeclInfo, 8> ReferencedDecls;
|
||
for (auto &FI: FragmentInfos) {
|
||
SmallVector<ParentInfo, 4> 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::ArrayRef(FIParents)));
|
||
}
|
||
Symbol.ReferencedSymbols =
|
||
copyArray(Allocator, llvm::ArrayRef(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<const ValueDecl *, const clang::NamedDecl *> D) {
|
||
// Could have junk in from previous failing USR print
|
||
Buffer.clear();
|
||
if (auto VD = D.dyn_cast<const ValueDecl *>()) {
|
||
if (SwiftLangSupport::printUSR(VD, OS))
|
||
return;
|
||
} else {
|
||
if (clang::index::generateUSRForDecl(
|
||
D.get<const clang::NamedDecl *>(), Buffer))
|
||
return;
|
||
}
|
||
Strings.push_back(copyAndClearString(Allocator, Buffer));
|
||
});
|
||
Symbol.OverrideUSRs = copyAndClearArray(Allocator, Strings);
|
||
|
||
walkRelatedDecls(DInfo.VD, [&](const ValueDecl *RelatedDecl,
|
||
bool UseOriginalBase, bool DuplicateName) {
|
||
OS << "<RelatedName usr=\"";
|
||
SwiftLangSupport::printUSR(RelatedDecl, OS);
|
||
OS << "\">";
|
||
if (isa<AbstractFunctionDecl>(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 << "</RelatedName>";
|
||
|
||
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<CollectionExpr>(E)) {
|
||
return CollectionLit->getInitializer().getDecl();
|
||
}
|
||
|
||
LiteralExpr* LitExpr = dyn_cast<LiteralExpr>(E);
|
||
if (!LitExpr) {
|
||
return nullptr;
|
||
}
|
||
|
||
bool IsObjectLiteral = isa<ObjectLiteralExpr>(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<BuiltinLiteralExpr>(E)) {
|
||
Type Ty = E->getType();
|
||
if (!Ty) {
|
||
return nullptr;
|
||
}
|
||
auto NominalTy = Ty->getAs<NominalOrBoundGenericNominalType>();
|
||
if (!NominalTy) {
|
||
return nullptr;
|
||
}
|
||
return NominalTy->getDecl();
|
||
}
|
||
return nullptr;
|
||
}
|
||
|
||
static bool addCursorInfoForLiteral(
|
||
CursorInfoData &Data, Expr *LitExpr, SwiftLangSupport &Lang,
|
||
const CompilerInvocation &CompInvoc, SourceLoc CursorLoc,
|
||
ArrayRef<ImmutableTextSnapshotRef> PreviousSnaps) {
|
||
if (!LitExpr) {
|
||
return false;
|
||
}
|
||
|
||
ValueDecl *Decl = getCursorInfoDeclForLiteral(LitExpr);
|
||
if (!Decl) {
|
||
return false;
|
||
}
|
||
|
||
auto &Symbol = Data.Symbols.emplace_back();
|
||
DeclInfo Info(Decl, nullptr, 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<ImmutableTextSnapshotRef> PreviousSnaps) {
|
||
DeclInfo OrigInfo(Info->getValueD(), Info->getSolutionSpecificInterfaceType(),
|
||
Info->getContainerType(), Info->isRef(), Info->isDynamic(),
|
||
Info->getReceiverTypes(), Invoc);
|
||
DeclInfo CtorTypeInfo(Info->getCtorTyRef(), Type(), Type(), true, false,
|
||
ArrayRef<NominalTypeDecl *>(), 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(), Type(), /*IsRef=*/true, /*IsDynamic=*/false,
|
||
ArrayRef<NominalTypeDecl *>(), 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<RefactoringInfo> KnownRefactoringInfo,
|
||
SwiftLangSupport &Lang, const CompilerInvocation &Invoc,
|
||
std::string &Diagnostic, ArrayRef<ImmutableTextSnapshotRef> PreviousSnaps,
|
||
bool DidReuseAST,
|
||
std::function<void(const RequestResult<CursorInfoData> &)> 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<CursorInfoData>::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<StringRef> Args = llvm::ArrayRef(Info.ArgNames);
|
||
std::vector<const clang::IdentifierInfo *> 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.ends_with(":") ? 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<Identifier, 8> 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::ArrayRef(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<void(const RequestResult<NameTranslatingInfo> &)> 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<NameTranslatingInfo>::fromResult(Result));
|
||
} else if (ObjCSelector Selector = ResultPair.second) {
|
||
Result.NameKind = SwiftLangSupport::getUIDForNameKind(NameKind::ObjC);
|
||
SmallString<64> Buffer;
|
||
StringRef Total = Selector.getString(Buffer);
|
||
SmallVector<StringRef, 4> 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<NameTranslatingInfo>::fromResult(Result));
|
||
} else {
|
||
Diagnostic = "Unable to resolve name info.";
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
case NameKind::ObjC: {
|
||
ClangImporter *Importer = static_cast<ClangImporter *>(VD->getDeclContext()->
|
||
getASTContext().getClangModuleLoader());
|
||
|
||
const clang::NamedDecl *Named = nullptr;
|
||
auto *BaseDecl = VD;
|
||
|
||
while (!Named && BaseDecl) {
|
||
Named = dyn_cast_or_null<clang::NamedDecl>(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<NameTranslatingInfo>::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<ImmutableTextSnapshotRef, 4> PreviousASTSnaps;
|
||
|
||
protected:
|
||
bool CancelOnSubsequentRequest;
|
||
protected:
|
||
ArrayRef<ImmutableTextSnapshotRef> getPreviousASTSnaps() {
|
||
return llvm::ArrayRef(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<ImmutableTextSnapshotRef> 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 = [&]() -> std::optional<unsigned> {
|
||
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 std::nullopt;
|
||
|
||
// Check that the new and old offset still point to the same token.
|
||
StringRef NewTok = getSourceToken(Offset, InputSnap);
|
||
if (NewTok.empty())
|
||
return std::nullopt;
|
||
if (NewTok == getSourceToken(OptOffset.value(), Snap))
|
||
return OptOffset;
|
||
|
||
return std::nullopt;
|
||
}
|
||
}
|
||
return std::nullopt;
|
||
};
|
||
|
||
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();
|
||
std::optional<unsigned> 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<llvm::vfs::FileSystem> fileSystem,
|
||
SourceKitCancellationToken CancellationToken,
|
||
std::function<void(const RequestResult<CursorInfoData> &)> Receiver) {
|
||
assert(Invok);
|
||
assert(fileSystem);
|
||
|
||
class CursorInfoConsumer : public CursorRangeInfoConsumer {
|
||
bool Actionables;
|
||
bool SymbolGraph;
|
||
SourceKitCancellationToken CancellationToken;
|
||
std::function<void(const RequestResult<CursorInfoData> &)> 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<void(const RequestResult<CursorInfoData> &)> 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<CursorInfoData>::fromError(
|
||
"Unable to find input file"));
|
||
return;
|
||
}
|
||
|
||
SourceManager &SM = CompIns.getSourceMgr();
|
||
unsigned BufferID = SF->getBufferID();
|
||
SourceLoc Loc =
|
||
Lexer::getLocForStartOfToken(SM, BufferID, Offset);
|
||
if (Loc.isInvalid()) {
|
||
Receiver(RequestResult<CursorInfoData>::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<RefactoringInfo, 8> 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<CursorInfoData>::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<ResolvedModuleRefCursorInfo>(CursorInfo);
|
||
passCursorInfoForModule(ModuleRefInfo->getMod(),
|
||
Lang.getIFaceGenContexts(), CompInvok,
|
||
Receiver);
|
||
return;
|
||
}
|
||
case CursorInfoKind::ValueRef: {
|
||
std::string Diagnostic;
|
||
bool Success = passCursorInfoForDecl(
|
||
cast<ResolvedValueRefCursorInfo>(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<CursorInfoData>::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<ResolvedExprStartCursorInfo>(CI);
|
||
if (!ExprInfo || !ExprInfo->getTrailingExpr()) {
|
||
return nullptr;
|
||
}
|
||
Expr *E = ExprInfo->getTrailingExpr();
|
||
if (dyn_cast<LiteralExpr>(E) || dyn_cast<CollectionExpr>(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<CursorInfoData>::fromResult(Data));
|
||
return;
|
||
}
|
||
case CursorInfoKind::Invalid:
|
||
CursorInfoData Data;
|
||
if (Actionables) {
|
||
Data.AvailableActions = Actions;
|
||
} else {
|
||
Data.InternalDiagnostic = "Unable to resolve cursor info.";
|
||
}
|
||
Receiver(RequestResult<CursorInfoData>::fromResult(Data));
|
||
return;
|
||
}
|
||
}
|
||
|
||
void cancelled() override {
|
||
Receiver(RequestResult<CursorInfoData>::cancelled());
|
||
}
|
||
|
||
void failed(StringRef Error) override {
|
||
LOG_WARN_FUNC("cursor info failed: " << Error);
|
||
Receiver(RequestResult<CursorInfoData>::fromError(Error));
|
||
}
|
||
};
|
||
|
||
auto Consumer = std::make_shared<CursorInfoConsumer>(
|
||
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<llvm::vfs::FileSystem> FileSystem,
|
||
SourceKitCancellationToken CancellationToken,
|
||
std::function<void(const RequestResult<DiagnosticsResult> &)> Receiver) {
|
||
|
||
class DiagnosticsConsumer : public SwiftASTConsumer {
|
||
std::function<void(const RequestResult<DiagnosticsResult> &)> Receiver;
|
||
|
||
public:
|
||
DiagnosticsConsumer(
|
||
std::function<void(const RequestResult<DiagnosticsResult> &)> Receiver)
|
||
: Receiver(Receiver) {}
|
||
|
||
void handlePrimaryAST(ASTUnitRef AstUnit) override {
|
||
unsigned BufferID = AstUnit->getPrimarySourceFile().getBufferID();
|
||
auto &DiagConsumer = AstUnit->getEditorDiagConsumer();
|
||
auto Diagnostics = DiagConsumer.getDiagnosticsForBuffer(BufferID);
|
||
Receiver(RequestResult<DiagnosticsResult>::fromResult(Diagnostics));
|
||
}
|
||
|
||
void cancelled() override {
|
||
Receiver(RequestResult<DiagnosticsResult>::cancelled());
|
||
}
|
||
|
||
void failed(StringRef Error) override {
|
||
LOG_WARN_FUNC("diagnostics failed: " << Error);
|
||
Receiver(RequestResult<DiagnosticsResult>::fromError(Error));
|
||
}
|
||
};
|
||
|
||
auto Consumer = std::make_shared<DiagnosticsConsumer>(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<void(const RequestResult<NameTranslatingInfo> &)> Receiver) {
|
||
assert(Invok);
|
||
|
||
class NameInfoConsumer : public CursorRangeInfoConsumer {
|
||
NameTranslatingInfo Input;
|
||
SourceKitCancellationToken CancellationToken;
|
||
std::function<void(const RequestResult<NameTranslatingInfo> &)> Receiver;
|
||
|
||
public:
|
||
NameInfoConsumer(
|
||
StringRef PrimaryFilePath, StringRef InputBufferName, unsigned Offset,
|
||
SwiftLangSupport &Lang, SwiftInvocationRef ASTInvok,
|
||
bool TryExistingAST, NameTranslatingInfo Input,
|
||
SourceKitCancellationToken CancellationToken,
|
||
std::function<void(const RequestResult<NameTranslatingInfo> &)>
|
||
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<NameTranslatingInfo>::fromError(
|
||
"Unable to find input file"));
|
||
return;
|
||
}
|
||
|
||
SourceLoc Loc = Lexer::getLocForStartOfToken(CompIns.getSourceMgr(),
|
||
SF->getBufferID(), Offset);
|
||
if (Loc.isInvalid()) {
|
||
Receiver(RequestResult<NameTranslatingInfo>::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<NameTranslatingInfo>::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<ResolvedValueRefCursorInfo>(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<NameTranslatingInfo>::fromResult(Info));
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
case CursorInfoKind::ExprStart:
|
||
case CursorInfoKind::StmtStart: {
|
||
NameTranslatingInfo Info;
|
||
Info.InternalDiagnostic =
|
||
"Resolved to incomplete expression or statement.";
|
||
Receiver(RequestResult<NameTranslatingInfo>::fromResult(Info));
|
||
return;
|
||
}
|
||
case CursorInfoKind::Invalid:
|
||
llvm_unreachable("bad sema token kind.");
|
||
}
|
||
}
|
||
|
||
void cancelled() override {
|
||
Receiver(RequestResult<NameTranslatingInfo>::cancelled());
|
||
}
|
||
|
||
void failed(StringRef Error) override {
|
||
LOG_WARN_FUNC("name info failed: " << Error);
|
||
Receiver(RequestResult<NameTranslatingInfo>::fromError(Error));
|
||
}
|
||
};
|
||
|
||
auto Consumer = std::make_shared<NameInfoConsumer>(
|
||
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<void(const RequestResult<RangeInfo> &)> Receiver) {
|
||
assert(Invok);
|
||
|
||
class RangeInfoConsumer : public CursorRangeInfoConsumer {
|
||
SourceKitCancellationToken CancellationToken;
|
||
std::function<void(const RequestResult<RangeInfo> &)> Receiver;
|
||
|
||
public:
|
||
RangeInfoConsumer(
|
||
StringRef PrimaryFilePath, StringRef InputBufferName, unsigned Offset,
|
||
unsigned Length, SwiftLangSupport &Lang, SwiftInvocationRef ASTInvok,
|
||
bool TryExistingAST, bool CancelOnSubsequentRequest,
|
||
SourceKitCancellationToken CancellationToken,
|
||
std::function<void(const RequestResult<RangeInfo> &)> 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<RangeInfo>::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<RangeInfo>::fromResult(Result));
|
||
return;
|
||
}
|
||
case RangeKind::SingleDecl:
|
||
case RangeKind::MultiTypeMemberDecl:
|
||
case RangeKind::MultiStatement:
|
||
case RangeKind::SingleStatement: {
|
||
Receiver(RequestResult<RangeInfo>::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<RangeInfo>::fromResult(Result));
|
||
}
|
||
return;
|
||
}
|
||
}
|
||
|
||
void cancelled() override {
|
||
Receiver(RequestResult<RangeInfo>::cancelled());
|
||
}
|
||
|
||
void failed(StringRef Error) override {
|
||
LOG_WARN_FUNC("range info failed: " << Error);
|
||
Receiver(RequestResult<RangeInfo>::fromError(Error));
|
||
}
|
||
};
|
||
|
||
auto Consumer = std::make_shared<RangeInfoConsumer>(
|
||
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<void(const RequestResult<CursorInfoData> &)> Receiver,
|
||
CancellableResult<CursorInfoResults> 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<ResolvedValueRefCursorInfo>(
|
||
ResolvedCursorInfo)) {
|
||
std::string Diagnostic; // Unused
|
||
addCursorInfoForDecl(Data, Result, AddRefactorings, AddSymbolGraph,
|
||
Lang, Invoc, Diagnostic, /*PreviousSnaps=*/{});
|
||
}
|
||
}
|
||
Data.DidReuseAST = Results->DidReuseAST;
|
||
if (!Data.Symbols.empty()) {
|
||
Receiver(RequestResult<CursorInfoData>::fromResult(Data));
|
||
}
|
||
break;
|
||
}
|
||
case CancellableResultKind::Failure:
|
||
Receiver(RequestResult<CursorInfoData>::fromError(Results.getError()));
|
||
break;
|
||
case CancellableResultKind::Cancelled:
|
||
Receiver(RequestResult<CursorInfoData>::cancelled());
|
||
break;
|
||
}
|
||
}
|
||
|
||
void SwiftLangSupport::getCursorInfo(
|
||
StringRef PrimaryFilePath, StringRef InputBufferName, unsigned Offset,
|
||
unsigned Length, bool Actionables, bool SymbolGraph,
|
||
bool CancelOnSubsequentRequest, ArrayRef<const char *> Args,
|
||
std::optional<VFSOptions> vfsOptions,
|
||
SourceKitCancellationToken CancellationToken,
|
||
std::function<void(const RequestResult<CursorInfoData> &)> Receiver) {
|
||
std::string error;
|
||
auto fileSystem = getFileSystem(vfsOptions, PrimaryFilePath, error);
|
||
if (!fileSystem)
|
||
return Receiver(RequestResult<CursorInfoData>::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(
|
||
/*SourceFile=*/nullptr, SourceLoc(),
|
||
const_cast<ValueDecl *>(Entity.Dcl),
|
||
/*CtorTyRef=*/nullptr,
|
||
/*ExtTyRef=*/nullptr, Entity.IsRef,
|
||
/*SolutionSpecificInterfaceType=*/Type(),
|
||
/*ContainerType=*/Type(),
|
||
/*CustomAttrRef=*/std::nullopt,
|
||
/*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<CursorInfoData>::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<CursorInfoData>::fromError(Error));
|
||
return;
|
||
}
|
||
|
||
// Solver based cursor info cannot handle generated buffers or range based
|
||
// cursor info.
|
||
std::shared_ptr<llvm::MemoryBuffer> InputBuffer;
|
||
if (InputBufferName.empty() && Length == 0) {
|
||
std::string InputFileError;
|
||
InputBuffer =
|
||
std::shared_ptr<llvm::MemoryBuffer>(getASTManager()->getMemoryBuffer(
|
||
PrimaryFilePath, 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<CursorInfoData> &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();
|
||
bool AllResultsHaveErrorType =
|
||
llvm::all_of(Res.value().Symbols, [](const CursorSymbolInfo &Symbol) {
|
||
// USR of the AST's error type if no type could be resolved.
|
||
return Symbol.TypeUSR == "$sXeD";
|
||
});
|
||
NoResults |= AllResultsHaveErrorType;
|
||
}
|
||
if (!NoResults || !InputBuffer) {
|
||
Receiver(Res);
|
||
return;
|
||
}
|
||
|
||
CompilerInvocation CompInvok;
|
||
Invok->applyTo(CompInvok);
|
||
|
||
SmallVector<const char *, 0> 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<CursorInfoData> &Res) {
|
||
SolverProducedResults = true;
|
||
Receiver(Res);
|
||
};
|
||
|
||
performWithParamsToCompletionLikeOperation(
|
||
InputBuffer.get(), Offset,
|
||
/*InsertCodeCompletionToken=*/false, Args, fileSystem,
|
||
CancellationToken,
|
||
[&](CancellableResult<CompletionLikeOperationParams> ParmsResult) {
|
||
ParmsResult.mapAsync<CursorInfoResults>(
|
||
[&](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<const char *> Args,
|
||
std::optional<VFSOptions> VfsOptions,
|
||
SourceKitCancellationToken CancellationToken,
|
||
std::function<void(const RequestResult<DiagnosticsResult> &)> Receiver) {
|
||
std::string FileSystemError;
|
||
auto FileSystem = getFileSystem(VfsOptions, PrimaryFilePath, FileSystemError);
|
||
if (!FileSystem) {
|
||
Receiver(RequestResult<DiagnosticsResult>::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<DiagnosticsResult>::fromError(InvocationError));
|
||
return;
|
||
}
|
||
|
||
computeDiagnostics(*this, Invok, FileSystem, CancellationToken, Receiver);
|
||
}
|
||
|
||
void SwiftLangSupport::getRangeInfo(
|
||
StringRef PrimaryFilePath, StringRef InputBufferName, unsigned Offset,
|
||
unsigned Length, bool CancelOnSubsequentRequest,
|
||
ArrayRef<const char *> Args, SourceKitCancellationToken CancellationToken,
|
||
std::function<void(const RequestResult<RangeInfo> &)> Receiver) {
|
||
if (IFaceGenContexts.get(InputBufferName)) {
|
||
// FIXME: return range info for generated interfaces.
|
||
Receiver(RequestResult<RangeInfo>::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<RangeInfo>::fromError(Error));
|
||
return;
|
||
}
|
||
if (Length == 0) {
|
||
Receiver(RequestResult<RangeInfo>::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<const char *> Args,
|
||
SourceKitCancellationToken CancellationToken,
|
||
std::function<void(const RequestResult<NameTranslatingInfo> &)> 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<NameTranslatingInfo>::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<NameTranslatingInfo>::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<llvm::vfs::FileSystem> fileSystem,
|
||
SourceKitCancellationToken CancellationToken,
|
||
std::function<void(const RequestResult<CursorInfoData> &)> Receiver) {
|
||
assert(Invok);
|
||
|
||
class CursorInfoConsumer : public SwiftASTConsumer {
|
||
std::string InputFile;
|
||
std::string USR;
|
||
SwiftLangSupport ⟪
|
||
SwiftInvocationRef ASTInvok;
|
||
const bool TryExistingAST;
|
||
bool CancelOnSubsequentRequest;
|
||
SourceKitCancellationToken CancellationToken;
|
||
std::function<void(const RequestResult<CursorInfoData> &)> Receiver;
|
||
SmallVector<ImmutableTextSnapshotRef, 4> PreviousASTSnaps;
|
||
|
||
public:
|
||
CursorInfoConsumer(
|
||
StringRef InputFile, StringRef USR, SwiftLangSupport &Lang,
|
||
SwiftInvocationRef ASTInvok, bool TryExistingAST,
|
||
bool CancelOnSubsequentRequest,
|
||
SourceKitCancellationToken CancellationToken,
|
||
std::function<void(const RequestResult<CursorInfoData> &)> Receiver)
|
||
: InputFile(InputFile), USR(USR), Lang(Lang),
|
||
ASTInvok(std::move(ASTInvok)), TryExistingAST(TryExistingAST),
|
||
CancelOnSubsequentRequest(CancelOnSubsequentRequest),
|
||
CancellationToken(CancellationToken), Receiver(std::move(Receiver)) {}
|
||
|
||
bool canUseASTWithSnapshots(
|
||
ArrayRef<ImmutableTextSnapshotRef> 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 (StringRef(USR).starts_with("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<CursorInfoData>::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<CursorInfoData>::fromResult(Info));
|
||
return;
|
||
}
|
||
|
||
CompilerInvocation CompInvok;
|
||
ASTInvok->applyTo(CompInvok);
|
||
|
||
if (auto *M = dyn_cast<ModuleDecl>(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,
|
||
/*SolutionSpecificInterfaceType=*/Type(), ContainerType,
|
||
/*CustomAttrRef=*/std::nullopt,
|
||
/*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<CursorInfoData>::fromResult(Info));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void cancelled() override {
|
||
Receiver(RequestResult<CursorInfoData>::cancelled());
|
||
}
|
||
|
||
void failed(StringRef Error) override {
|
||
LOG_WARN_FUNC("cursor info failed: " << Error);
|
||
Receiver(RequestResult<CursorInfoData>::fromError(Error));
|
||
}
|
||
};
|
||
|
||
auto Consumer = std::make_shared<CursorInfoConsumer>(
|
||
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<const char *> Args,
|
||
std::optional<VFSOptions> vfsOptions,
|
||
SourceKitCancellationToken CancellationToken,
|
||
std::function<void(const RequestResult<CursorInfoData> &)> Receiver) {
|
||
std::string error;
|
||
|
||
auto fileSystem = getFileSystem(vfsOptions, PrimaryFilePath, error);
|
||
if (!fileSystem)
|
||
return Receiver(RequestResult<CursorInfoData>::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<CursorInfoData>::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<CursorInfoData>::fromError(Error));
|
||
return;
|
||
}
|
||
|
||
resolveCursorFromUSR(*this, InputBufferName, USR, Invok,
|
||
/*TryExistingAST=*/true, CancelOnSubsequentRequest,
|
||
fileSystem, CancellationToken, Receiver);
|
||
}
|
||
|
||
//===----------------------------------------------------------------------===//
|
||
// SwiftLangSupport::findUSRRange
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
std::optional<std::pair<unsigned, unsigned>>
|
||
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 std::nullopt;
|
||
}
|
||
|
||
//===----------------------------------------------------------------------===//
|
||
// SwiftLangSupport::findRelatedIdentifiersInFile
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
void SwiftLangSupport::findRelatedIdentifiersInFile(
|
||
StringRef PrimaryFilePath, StringRef InputBufferName, unsigned Offset,
|
||
bool IncludeNonEditableBaseNames, bool CancelOnSubsequentRequest,
|
||
ArrayRef<const char *> Args, SourceKitCancellationToken CancellationToken,
|
||
std::function<void(const RequestResult<RelatedIdentsResult> &)> Receiver) {
|
||
|
||
std::string Error;
|
||
SwiftInvocationRef Invok =
|
||
ASTMgr->getTypecheckInvocation(Args, PrimaryFilePath, Error);
|
||
if (!Invok) {
|
||
LOG_WARN_FUNC("failed to create an ASTInvocation: " << Error);
|
||
Receiver(RequestResult<RelatedIdentsResult>::fromError(Error));
|
||
return;
|
||
}
|
||
|
||
class RelatedIdConsumer : public SwiftASTConsumer {
|
||
std::string InputFile;
|
||
unsigned Offset;
|
||
bool IncludeNonEditableBaseNames;
|
||
std::function<void(const RequestResult<RelatedIdentsResult> &)> Receiver;
|
||
SwiftInvocationRef Invok;
|
||
|
||
bool requiresDeepStack() override { return true; }
|
||
|
||
#if SWIFT_BUILD_SWIFT_SYNTAX
|
||
// FIXME: Don't silently eat errors here.
|
||
RelatedIdentsResult getRelatedIdents(SourceFile *SrcFile,
|
||
CompilerInstance &CompInst) {
|
||
unsigned BufferID = SrcFile->getBufferID();
|
||
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<ResolvedValueRefCursorInfo>(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<ConstructorDecl>(VD) || isa<DestructorDecl>(VD) ||
|
||
isa<SubscriptDecl>(VD)))
|
||
return RelatedIdentsResult::empty();
|
||
|
||
std::optional<RenameInfo> Info = getRenameInfo(CursorInfo);
|
||
|
||
if (!Info) {
|
||
return RelatedIdentsResult::empty();
|
||
}
|
||
|
||
RenameLocs Locs = localRenameLocs(SrcFile, Info->VD);
|
||
|
||
std::optional<std::string> OldName;
|
||
if (!Locs.getLocations().empty()) {
|
||
OldName = Locs.getLocations().front().OldName.str();
|
||
}
|
||
#ifndef NDEBUG
|
||
for (auto loc : Locs.getLocations()) {
|
||
assert(loc.OldName == OldName &&
|
||
"Found related identifiers 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<ResolvedAndRenameLoc> ResolvedAndRenameLocs =
|
||
resolveRenameLocations(Locs.getLocations(), /*NewName=*/StringRef(),
|
||
*SrcFile, Diags);
|
||
|
||
SmallVector<RelatedIdentInfo, 8> Ranges;
|
||
for (auto Loc : ResolvedAndRenameLocs) {
|
||
if (Loc.resolved.range.isInvalid()) {
|
||
continue;
|
||
}
|
||
unsigned Offset = SrcMgr.getLocOffsetInBuffer(
|
||
Loc.resolved.range.getStart(), BufferID);
|
||
Ranges.push_back(
|
||
{Offset, Loc.resolved.range.getByteLength(), Loc.renameLoc.Usage});
|
||
}
|
||
|
||
return RelatedIdentsResult(Ranges, OldName);
|
||
}
|
||
#endif
|
||
|
||
public:
|
||
RelatedIdConsumer(
|
||
StringRef InputFile, unsigned Offset, bool IncludeNonEditableBaseNames,
|
||
std::function<void(const RequestResult<RelatedIdentsResult> &)>
|
||
Receiver,
|
||
SwiftInvocationRef Invok)
|
||
: InputFile(InputFile.str()), Offset(Offset),
|
||
IncludeNonEditableBaseNames(IncludeNonEditableBaseNames),
|
||
Receiver(std::move(Receiver)), Invok(Invok) {}
|
||
|
||
void handlePrimaryAST(ASTUnitRef AstUnit) override {
|
||
using ResultType = RequestResult<RelatedIdentsResult>;
|
||
#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<RelatedIdentsResult>::fromError(
|
||
"Unable to find input file"));
|
||
return;
|
||
}
|
||
|
||
RelatedIdentsResult Result = getRelatedIdents(SrcFile, CompInst);
|
||
Receiver(ResultType::fromResult(Result));
|
||
#endif
|
||
}
|
||
|
||
void cancelled() override {
|
||
Receiver(RequestResult<RelatedIdentsResult>::cancelled());
|
||
}
|
||
|
||
void failed(StringRef Error) override {
|
||
LOG_WARN_FUNC("related idents failed: " << Error);
|
||
Receiver(RequestResult<RelatedIdentsResult>::fromError(Error));
|
||
}
|
||
|
||
static CaseStmt *getCaseStmtOfCanonicalVar(Decl *D) {
|
||
assert(D);
|
||
if (auto *VD = dyn_cast<VarDecl>(D)) {
|
||
if (auto *Canonical = VD->getCanonicalVarDecl()) {
|
||
return dyn_cast_or_null<CaseStmt>(Canonical->getRecursiveParentPatternStmt());
|
||
}
|
||
}
|
||
return nullptr;
|
||
}
|
||
};
|
||
|
||
auto Consumer = std::make_shared<RelatedIdConsumer>(
|
||
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
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
void SwiftLangSupport::findActiveRegionsInFile(
|
||
StringRef PrimaryFilePath, StringRef InputBufferName,
|
||
ArrayRef<const char *> Args, SourceKitCancellationToken CancellationToken,
|
||
std::function<void(const RequestResult<ActiveRegionsInfo> &)> Receiver) {
|
||
|
||
std::string Error;
|
||
SwiftInvocationRef Invok =
|
||
ASTMgr->getTypecheckInvocation(Args, PrimaryFilePath, Error);
|
||
if (!Invok) {
|
||
LOG_WARN_FUNC("failed to create an ASTInvocation: " << Error);
|
||
Receiver(RequestResult<ActiveRegionsInfo>::fromError(Error));
|
||
return;
|
||
}
|
||
|
||
class IfConfigConsumer : public SwiftASTConsumer {
|
||
std::string InputFile;
|
||
std::function<void(const RequestResult<ActiveRegionsInfo> &)> Receiver;
|
||
SwiftInvocationRef Invok;
|
||
|
||
public:
|
||
IfConfigConsumer(
|
||
StringRef InputFile,
|
||
std::function<void(const RequestResult<ActiveRegionsInfo> &)> 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<ActiveRegionsInfo>::fromError(
|
||
"Unable to find input file"));
|
||
return;
|
||
}
|
||
|
||
auto &SM = SF->getASTContext().SourceMgr;
|
||
auto BufferID = SF->getBufferID();
|
||
|
||
SmallVector<IfConfigInfo> Configs;
|
||
for (auto &range : SF->getIfConfigClauseRanges()) {
|
||
bool isActive = false;
|
||
switch (range.getKind()) {
|
||
case IfConfigClauseRangeInfo::ActiveClause:
|
||
isActive = true;
|
||
break;
|
||
case IfConfigClauseRangeInfo::InactiveClause:
|
||
isActive = false;
|
||
break;
|
||
case IfConfigClauseRangeInfo::EndDirective:
|
||
continue;
|
||
}
|
||
auto offset = SM.getLocOffsetInBuffer(range.getStartLoc(), BufferID);
|
||
Configs.emplace_back(offset, isActive);
|
||
}
|
||
ActiveRegionsInfo Info;
|
||
Info.Configs = Configs;
|
||
Receiver(RequestResult<ActiveRegionsInfo>::fromResult(Info));
|
||
}
|
||
|
||
void cancelled() override {
|
||
Receiver(RequestResult<ActiveRegionsInfo>::cancelled());
|
||
}
|
||
|
||
void failed(StringRef Error) override {
|
||
LOG_WARN_FUNC("inactive ranges failed: " << Error);
|
||
Receiver(RequestResult<ActiveRegionsInfo>::fromError(Error));
|
||
}
|
||
};
|
||
|
||
auto Consumer =
|
||
std::make_shared<IfConfigConsumer>(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<const char *> 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<ArrayRef<CategorizedEdits>>::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<ArrayRef<CategorizedEdits>>::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<ArrayRef<CategorizedEdits>>::cancelled());
|
||
}
|
||
|
||
void failed(StringRef Error) override {
|
||
Receiver(RequestResult<ArrayRef<CategorizedEdits>>::fromError(Error));
|
||
}
|
||
};
|
||
|
||
auto Consumer = std::make_shared<SemaRefactoringConsumer>(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<const char *> Args, ArrayRef<const char *> ExpectedProtocols,
|
||
bool FullyQualified, bool CanonicalType,
|
||
SourceKitCancellationToken CancellationToken,
|
||
std::function<void(const RequestResult<ExpressionTypesInFile> &)>
|
||
Receiver) {
|
||
std::string Error;
|
||
SwiftInvocationRef Invok =
|
||
ASTMgr->getTypecheckInvocation(Args, PrimaryFilePath, Error);
|
||
if (!Invok) {
|
||
LOG_WARN_FUNC("failed to create an ASTInvocation: " << Error);
|
||
Receiver(RequestResult<ExpressionTypesInFile>::fromError(Error));
|
||
return;
|
||
}
|
||
assert(Invok);
|
||
class ExpressionTypeCollector: public SwiftASTConsumer {
|
||
std::function<void(const RequestResult<ExpressionTypesInFile> &)> Receiver;
|
||
std::string InputFile;
|
||
std::vector<const char *> ExpectedProtocols;
|
||
bool FullyQualified;
|
||
bool CanonicalType;
|
||
public:
|
||
ExpressionTypeCollector(
|
||
std::function<void(const RequestResult<ExpressionTypesInFile> &)>
|
||
Receiver,
|
||
StringRef InputFile, ArrayRef<const char *> 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<ExpressionTypesInFile>::fromError(
|
||
"Unable to find input file"));
|
||
return;
|
||
}
|
||
|
||
std::vector<ExpressionTypeInfo> 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<ExpressionTypesInFile>::fromResult(Result));
|
||
}
|
||
|
||
void cancelled() override {
|
||
Receiver(RequestResult<ExpressionTypesInFile>::cancelled());
|
||
}
|
||
|
||
void failed(StringRef Error) override {
|
||
Receiver(RequestResult<ExpressionTypesInFile>::fromError(Error));
|
||
}
|
||
};
|
||
auto Collector = std::make_shared<ExpressionTypeCollector>(
|
||
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<const char *> Args, std::optional<unsigned> Offset,
|
||
std::optional<unsigned> Length, bool FullyQualified,
|
||
SourceKitCancellationToken CancellationToken,
|
||
std::function<void(const RequestResult<VariableTypesInFile> &)> Receiver) {
|
||
std::string Error;
|
||
SwiftInvocationRef Invok =
|
||
ASTMgr->getTypecheckInvocation(Args, PrimaryFilePath, Error);
|
||
if (!Invok) {
|
||
LOG_WARN_FUNC("failed to create an ASTInvocation: " << Error);
|
||
Receiver(RequestResult<VariableTypesInFile>::fromError(Error));
|
||
return;
|
||
}
|
||
assert(Invok);
|
||
|
||
class VariableTypeCollectorASTConsumer : public SwiftASTConsumer {
|
||
private:
|
||
std::function<void(const RequestResult<VariableTypesInFile> &)> Receiver;
|
||
std::string InputFile;
|
||
std::optional<unsigned> Offset;
|
||
std::optional<unsigned> Length;
|
||
bool FullyQualified;
|
||
|
||
public:
|
||
VariableTypeCollectorASTConsumer(
|
||
std::function<void(const RequestResult<VariableTypesInFile> &)>
|
||
Receiver,
|
||
StringRef InputFile, std::optional<unsigned> Offset,
|
||
std::optional<unsigned> 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<VariableTypesInFile>::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();
|
||
SourceLoc Start = Lexer::getLocForStartOfToken(SM, BufferID, *Offset);
|
||
SourceLoc End =
|
||
Lexer::getLocForStartOfToken(SM, BufferID, *Offset + *Length);
|
||
Range = SourceRange(Start, End);
|
||
}
|
||
|
||
std::vector<VariableTypeInfo> 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<VariableTypesInFile>::fromResult(Result));
|
||
}
|
||
|
||
void cancelled() override {
|
||
Receiver(RequestResult<VariableTypesInFile>::cancelled());
|
||
}
|
||
|
||
void failed(StringRef Error) override {
|
||
Receiver(RequestResult<VariableTypesInFile>::fromError(Error));
|
||
}
|
||
};
|
||
|
||
auto Collector = std::make_shared<VariableTypeCollectorASTConsumer>(
|
||
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());
|
||
}
|