Files
swift-mirror/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp
2015-12-31 23:28:40 +00:00

771 lines
26 KiB
C++

//===--- SwiftSourceDocInfo.cpp -------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "SwiftASTManager.h"
#include "SwiftLangSupport.h"
#include "SourceKit/Support/UIdent.h"
#include "SourceKit/Support/ImmutableTextBuffer.h"
#include "SourceKit/Support/Logging.h"
#include "swift/AST/ASTPrinter.h"
#include "swift/AST/Decl.h"
#include "swift/AST/NameLookup.h"
#include "swift/Basic/SourceManager.h"
#include "swift/Frontend/Frontend.h"
#include "swift/Frontend/PrintingDiagnosticConsumer.h"
#include "swift/IDE/SourceEntityWalker.h"
#include "swift/IDE/CommentConversion.h"
#include "swift/IDE/Utils.h"
#include "swift/Markup/XMLUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclObjC.h"
#include "clang/Basic/Module.h"
#include "clang/Index/USRGeneration.h"
#include "clang/Lex/Lexer.h"
#include "llvm/Support/MemoryBuffer.h"
using namespace SourceKit;
using namespace swift;
using namespace swift::ide;
class AnnotatedDeclarationPrinter : public XMLEscapingPrinter {
public:
AnnotatedDeclarationPrinter(raw_ostream &OS)
:XMLEscapingPrinter(OS) { }
private:
void printTypeRef(const TypeDecl *TD, Identifier Name) override {
printXML("<Type usr=\"");
SwiftLangSupport::printUSR(TD, OS);
printXML("\">");
StreamPrinter::printTypeRef(TD, Name);
printXML("</Type>");
}
};
static void printAnnotatedDeclaration(const ValueDecl *VD, raw_ostream &OS) {
AnnotatedDeclarationPrinter Printer(OS);
PrintOptions PO = PrintOptions::printQuickHelpDeclaration();
// 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();
// Wrap this up in XML, as that's what we'll use for documentation comments.
OS<<"<Declaration>";
VD->print(Printer, PO);
OS<<"</Declaration>";
}
template <typename FnTy>
void walkRelatedDecls(const ValueDecl *VD, const FnTy &Fn) {
llvm::SmallDenseMap<DeclName, unsigned, 16> NamesSeen;
++NamesSeen[VD->getFullName()];
SmallVector<ValueDecl *, 8> RelatedDecls;
// FIXME: Extract useful related declarations, overloaded functions,
// if VD is an initializer, we should extract other initializers etc.
// For now we use UnqualifiedLookup to fetch other declarations with the same
// base name.
auto TypeResolver = VD->getASTContext().getLazyResolver();
UnqualifiedLookup Lookup(VD->getName(), VD->getDeclContext(), TypeResolver);
for (auto result : Lookup.Results) {
ValueDecl *RelatedVD = result.getValueDecl();
if (RelatedVD->getAttrs().isUnavailable(VD->getASTContext()))
continue;
if (RelatedVD != VD) {
++NamesSeen[RelatedVD->getFullName()];
RelatedDecls.push_back(RelatedVD);
}
}
// Now provide the results along with whether the name is duplicate or not.
for (auto RelatedVD : RelatedDecls) {
Fn(RelatedVD, NamesSeen[RelatedVD->getFullName()] > 1);
}
}
//============================================================================//
// SwiftLangSupport::getCursorInfo
//============================================================================//
static StringRef getSourceToken(unsigned Offset,
ImmutableTextSnapshotRef Snap) {
auto MemBuf = Snap->getBuffer()->getInternalBuffer();
SourceManager SM;
auto MemBufRef = llvm::MemoryBuffer::getMemBuffer(MemBuf->getBuffer(),
MemBuf->getBufferIdentifier());
auto BufId = SM.addNewSourceBuffer(std::move(MemBufRef));
SourceLoc Loc = SM.getLocForOffset(BufId, Offset);
// Use fake language options; language options only affect validity
// and the exact token produced.
LangOptions FakeLangOpts;
Lexer L(FakeLangOpts, SM, BufId, nullptr, /*InSILMode=*/ false,
CommentRetentionMode::ReturnAsTokens);
return L.getTokenAt(Loc).getText();
}
static llvm::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 None; // Offset is part of newly inserted text.
if (Upd->getByteOffset() <= Offset) {
Offset += Upd->getLength(); // "bring back" what was removed.
Offset -= Upd->getText().size(); // "remove" what was added.
}
}
return Offset;
}
static llvm::Optional<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 None;
}
/// Tries to remap the location from a previous snapshot to the latest one.
static llvm::Optional<std::pair<unsigned, unsigned>>
tryRemappingLocToLatestSnapshot(SwiftLangSupport &Lang,
std::pair<unsigned, unsigned> Range,
StringRef Filename,
ArrayRef<ImmutableTextSnapshotRef> PreviousASTSnaps) {
ImmutableTextSnapshotRef LatestSnap;
if (auto EditorDoc = Lang.getEditorDocuments().findByPath(Filename))
LatestSnap = EditorDoc->getLatestSnapshot();
if (!LatestSnap)
return Range;
for (auto &PrevSnap : PreviousASTSnaps) {
if (PrevSnap->isFromSameBuffer(LatestSnap)) {
if (PrevSnap->getStamp() == LatestSnap->getStamp())
return Range;
auto OptBegin = mapOffsetToNewerSnapshot(Range.first,
PrevSnap, LatestSnap);
if (!OptBegin.hasValue())
return None;
auto OptEnd = mapOffsetToNewerSnapshot(Range.first+Range.second,
PrevSnap, LatestSnap);
if (!OptEnd.hasValue())
return None;
return std::make_pair(*OptBegin, *OptEnd-*OptBegin);
}
}
return Range;
}
/// Returns true for error.
static bool passCursorInfoForModule(ModuleEntity Mod,
SwiftInterfaceGenMap &IFaceGenContexts,
const CompilerInvocation &Invok,
std::function<void(const CursorInfo &)> Receiver) {
std::string Name = Mod.getName();
std::string FullName = Mod.getFullName();
CursorInfo Info;
Info.Kind = SwiftLangSupport::getUIDForModuleRef();
Info.Name = Name;
Info.ModuleName = FullName;
if (auto IFaceGenRef = IFaceGenContexts.find(Info.ModuleName, Invok))
Info.ModuleInterfaceName = IFaceGenRef->getDocumentName();
Info.IsSystem = Mod.isSystemModule();
Receiver(Info);
return false;
}
/// Returns true for failure to resolve.
static bool passCursorInfoForDecl(const ValueDecl *VD,
const Module *MainModule,
const Type Ty,
bool IsRef,
Optional<unsigned> OrigBufferID,
SwiftLangSupport &Lang,
const CompilerInvocation &Invok,
ArrayRef<ImmutableTextSnapshotRef> PreviousASTSnaps,
std::function<void(const CursorInfo &)> Receiver) {
if (AvailableAttr::isUnavailable(VD))
return true;
SmallString<64> SS;
unsigned NameBegin = SS.size();
{
llvm::raw_svector_ostream OS(SS);
SwiftLangSupport::printDisplayName(VD, OS);
}
unsigned NameEnd = SS.size();
unsigned USRBegin = SS.size();
{
llvm::raw_svector_ostream OS(SS);
SwiftLangSupport::printUSR(VD, OS);
}
unsigned USREnd = SS.size();
unsigned TypenameBegin = SS.size();
if (VD->hasType()) {
llvm::raw_svector_ostream OS(SS);
VD->getType().print(OS);
}
unsigned TypenameEnd = SS.size();
unsigned DocCommentBegin = SS.size();
{
llvm::raw_svector_ostream OS(SS);
ide::getDocumentationCommentAsXML(VD, OS);
}
unsigned DocCommentEnd = SS.size();
unsigned DeclBegin = SS.size();
{
llvm::raw_svector_ostream OS(SS);
printAnnotatedDeclaration(VD, OS);
}
unsigned DeclEnd = SS.size();
SmallVector<std::pair<unsigned, unsigned>, 4> OverUSROffs;
ide::walkOverriddenDecls(VD,
[&](llvm::PointerUnion<const ValueDecl*, const clang::NamedDecl*> D) {
unsigned OverUSRBegin = SS.size();
{
llvm::raw_svector_ostream OS(SS);
if (auto VD = D.dyn_cast<const ValueDecl*>()) {
if (SwiftLangSupport::printUSR(VD, OS))
return;
} else {
llvm::SmallString<128> Buf;
if (clang::index::generateUSRForDecl(
D.get<const clang::NamedDecl*>(), Buf))
return;
OS << Buf.str();
}
}
unsigned OverUSREnd = SS.size();
OverUSROffs.push_back(std::make_pair(OverUSRBegin, OverUSREnd));
});
SmallVector<std::pair<unsigned, unsigned>, 4> RelDeclOffs;
walkRelatedDecls(VD, [&](const ValueDecl *RelatedDecl, bool DuplicateName) {
unsigned RelatedDeclBegin = SS.size();
{
llvm::raw_svector_ostream OS(SS);
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.SkipIntroducerKeywords = true;
PO.ArgAndParamPrinting = PrintOptions::ArgAndParamPrintingMode::ArgumentOnly;
XMLEscapingPrinter Printer(OS);
RelatedDecl->print(Printer, PO);
} else {
llvm::SmallString<128> Buf;
{
llvm::raw_svector_ostream OSBuf(Buf);
SwiftLangSupport::printDisplayName(RelatedDecl, OSBuf);
}
llvm::markup::appendWithXMLEscaping(OS, Buf);
}
OS<<"</RelatedName>";
}
unsigned RelatedDeclEnd = SS.size();
RelDeclOffs.push_back(std::make_pair(RelatedDeclBegin, RelatedDeclEnd));
});
ASTContext &Ctx = VD->getASTContext();
ClangImporter *Importer = static_cast<ClangImporter*>(
Ctx.getClangModuleLoader());
std::string ModuleName;
auto ClangNode = VD->getClangNode();
if (ClangNode) {
auto ClangMod = Importer->getClangOwningModule(ClangNode);
ModuleName = ClangMod->getFullModuleName();
} else if (VD->getLoc().isInvalid() && VD->getModuleContext() != MainModule) {
ModuleName = VD->getModuleContext()->getName().str();
}
StringRef ModuleInterfaceName;
if (auto IFaceGenRef = Lang.getIFaceGenContexts().find(ModuleName, Invok))
ModuleInterfaceName = IFaceGenRef->getDocumentName();
UIdent Kind = SwiftLangSupport::getUIDForDecl(VD, IsRef);
StringRef Name = StringRef(SS.begin()+NameBegin, NameEnd-NameBegin);
StringRef USR = StringRef(SS.begin()+USRBegin, USREnd-USRBegin);
StringRef TypeName = StringRef(SS.begin()+TypenameBegin,
TypenameEnd-TypenameBegin);
StringRef DocComment = StringRef(SS.begin()+DocCommentBegin,
DocCommentEnd-DocCommentBegin);
StringRef AnnotatedDecl = StringRef(SS.begin()+DeclBegin,
DeclEnd-DeclBegin);
llvm::Optional<std::pair<unsigned, unsigned>> DeclarationLoc;
StringRef Filename;
getLocationInfo(VD, DeclarationLoc, Filename);
if (DeclarationLoc.hasValue()) {
DeclarationLoc = tryRemappingLocToLatestSnapshot(Lang,
*DeclarationLoc,
Filename,
PreviousASTSnaps);
if (!DeclarationLoc.hasValue())
return true; // failed to remap.
}
SmallVector<StringRef, 4> OverUSRs;
for (auto Offs : OverUSROffs) {
OverUSRs.push_back(StringRef(SS.begin()+Offs.first,
Offs.second-Offs.first));
}
SmallVector<StringRef, 4> AnnotatedRelatedDecls;
for (auto Offs : RelDeclOffs) {
AnnotatedRelatedDecls.push_back(StringRef(SS.begin() + Offs.first,
Offs.second - Offs.first));
}
bool IsSystem = VD->getModuleContext()->isSystemModule();
std::string TypeInterface;
CursorInfo Info;
Info.Kind = Kind;
Info.Name = Name;
Info.USR = USR;
Info.TypeName = TypeName;
Info.DocComment = DocComment;
Info.AnnotatedDeclaration = AnnotatedDecl;
Info.ModuleName = ModuleName;
Info.ModuleInterfaceName = ModuleInterfaceName;
Info.DeclarationLoc = DeclarationLoc;
Info.Filename = Filename;
Info.OverrideUSRs = OverUSRs;
Info.AnnotatedRelatedDeclarations = AnnotatedRelatedDecls;
Info.IsSystem = IsSystem;
Info.TypeInterface = ASTPrinter::printTypeInterface(Ty, VD->getDeclContext(),
TypeInterface) ?
StringRef(TypeInterface) : StringRef();
Receiver(Info);
return false;
}
static void resolveCursor(SwiftLangSupport &Lang,
StringRef InputFile, unsigned Offset,
SwiftInvocationRef Invok,
bool TryExistingAST,
std::function<void(const CursorInfo &)> Receiver) {
assert(Invok);
class CursorInfoConsumer : public SwiftASTConsumer {
std::string InputFile;
unsigned Offset;
SwiftLangSupport &Lang;
SwiftInvocationRef ASTInvok;
const bool TryExistingAST;
std::function<void(const CursorInfo &)> Receiver;
SmallVector<ImmutableTextSnapshotRef, 4> PreviousASTSnaps;
public:
CursorInfoConsumer(StringRef InputFile, unsigned Offset,
SwiftLangSupport &Lang,
SwiftInvocationRef ASTInvok,
bool TryExistingAST,
std::function<void(const CursorInfo &)> Receiver)
: InputFile(InputFile), Offset(Offset),
Lang(Lang),
ASTInvok(std::move(ASTInvok)),
TryExistingAST(TryExistingAST),
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 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(InputFile))
InputSnap = EditorDoc->getLatestSnapshot();
if (!InputSnap)
return false;
auto mappedBackOffset = [&]()->llvm::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.hasValue())
return None;
// Check that the new and old offset still point to the same token.
StringRef NewTok = getSourceToken(Offset, InputSnap);
if (NewTok.empty())
return None;
if (NewTok == getSourceToken(OptOffset.getValue(), Snap))
return OptOffset;
return None;
}
}
return None;
};
auto OldOffsetOpt = mappedBackOffset();
if (OldOffsetOpt.hasValue()) {
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;
}
void handlePrimaryAST(ASTUnitRef AstUnit) override {
auto &CompIns = AstUnit->getCompilerInstance();
Module *MainModule = CompIns.getMainModule();
unsigned BufferID = AstUnit->getPrimarySourceFile().getBufferID().getValue();
SourceLoc Loc =
Lexer::getLocForStartOfToken(CompIns.getSourceMgr(), BufferID, Offset);
if (Loc.isInvalid()) {
Receiver({});
return;
}
trace::TracedOperation TracedOp;
if (trace::enabled()) {
trace::SwiftInvocation SwiftArgs;
ASTInvok->raw(SwiftArgs.Args.Args, SwiftArgs.Args.PrimaryFile);
trace::initTraceFiles(SwiftArgs, CompIns);
TracedOp.start(trace::OperationKind::CursorInfoForSource, SwiftArgs,
{std::make_pair("Offset", std::to_string(Offset))});
}
SemaLocResolver Resolver(AstUnit->getPrimarySourceFile());
SemaToken SemaTok = Resolver.resolve(Loc);
if (SemaTok.isInvalid()) {
Receiver({});
return;
}
CompilerInvocation CompInvok;
ASTInvok->applyTo(CompInvok);
if (SemaTok.Mod) {
passCursorInfoForModule(SemaTok.Mod, Lang.getIFaceGenContexts(),
CompInvok, Receiver);
} else {
ValueDecl *VD = SemaTok.CtorTyRef ? SemaTok.CtorTyRef : SemaTok.ValueD;
bool Failed = passCursorInfoForDecl(VD, MainModule, SemaTok.Ty,
SemaTok.IsRef, BufferID, Lang,
CompInvok, PreviousASTSnaps,
Receiver);
if (Failed) {
if (!PreviousASTSnaps.empty()) {
// Attempt again using the up-to-date AST.
resolveCursor(Lang, InputFile, Offset, ASTInvok,
/*TryExistingAST=*/false, Receiver);
} else {
Receiver({});
}
}
}
}
void cancelled() override {
CursorInfo Info;
Info.IsCancelled = true;
Receiver(Info);
}
void failed(StringRef Error) override {
LOG_WARN_FUNC("cursor info failed: " << Error);
Receiver({});
}
};
auto Consumer = std::make_shared<CursorInfoConsumer>(
InputFile, Offset, Lang, Invok, TryExistingAST, Receiver);
/// FIXME: When request cancellation is implemented and Xcode adopts it,
/// don't use 'OncePerASTToken'.
static const char OncePerASTToken = 0;
Lang.getASTManager().processASTAsync(Invok, std::move(Consumer), &OncePerASTToken);
}
void SwiftLangSupport::getCursorInfo(
StringRef InputFile, unsigned Offset,
ArrayRef<const char *> Args,
std::function<void(const CursorInfo &)> Receiver) {
if (auto IFaceGenRef = IFaceGenContexts.get(InputFile)) {
trace::TracedOperation TracedOp;
if (trace::enabled()) {
trace::SwiftInvocation SwiftArgs;
trace::initTraceInfo(SwiftArgs, InputFile, Args);
// Do we need to record any files? If yes -- which ones?
trace::StringPairs OpArgs {
std::make_pair("DocumentName", IFaceGenRef->getDocumentName()),
std::make_pair("ModuleOrHeaderName", IFaceGenRef->getModuleOrHeaderName()),
std::make_pair("Offset", std::to_string(Offset))};
TracedOp.start(trace::OperationKind::CursorInfoForIFaceGen,
SwiftArgs, OpArgs);
}
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 {
// FIXME: Should pass the main module for the interface but currently
// it's not necessary.
passCursorInfoForDecl(Entity.Dcl, /*MainModule*/nullptr, Type(),
Entity.IsRef,
/*OrigBufferID=*/None, *this, Invok,
{}, Receiver);
}
} else {
Receiver({});
}
return;
}
std::string Error;
SwiftInvocationRef Invok = ASTMgr->getInvocation(Args, InputFile, Error);
if (!Invok) {
// FIXME: Report it as failed request.
LOG_WARN_FUNC("failed to create an ASTInvocation: " << Error);
Receiver({});
return;
}
resolveCursor(*this, InputFile, Offset, Invok, /*TryExistingAST=*/true,
Receiver);
}
//============================================================================//
// SwiftLangSupport::findUSRRange
//============================================================================//
llvm::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 None;
}
//============================================================================//
// SwiftLangSupport::findRelatedIdentifiersInFile
//============================================================================//
namespace {
class RelatedIdScanner : public ide::SourceEntityWalker {
ValueDecl *Dcl;
llvm::SmallVectorImpl<std::pair<unsigned, unsigned>> &Ranges;
SourceManager &SourceMgr;
unsigned BufferID = -1;
bool Cancelled = false;
public:
explicit RelatedIdScanner(SourceFile &SrcFile, unsigned BufferID,
ValueDecl *D,
llvm::SmallVectorImpl<std::pair<unsigned, unsigned>> &Ranges)
: Dcl(D), Ranges(Ranges),
SourceMgr(SrcFile.getASTContext().SourceMgr),
BufferID(BufferID) {
}
private:
bool walkToDeclPre(Decl *D, CharSourceRange Range) override {
if (Cancelled)
return false;
if (D == Dcl)
return passId(Range);
return true;
}
bool visitDeclReference(ValueDecl *D, CharSourceRange Range,
TypeDecl *CtorTyRef, Type T) override {
if (Cancelled)
return false;
if (CtorTyRef)
D = CtorTyRef;
if (D == Dcl)
return passId(Range);
return true;
}
bool passId(CharSourceRange Range) {
unsigned Offset = SourceMgr.getLocOffsetInBuffer(Range.getStart(),BufferID);
Ranges.push_back({ Offset, Range.getByteLength() });
return !Cancelled;
}
};
}
void SwiftLangSupport::findRelatedIdentifiersInFile(
StringRef InputFile, unsigned Offset,
ArrayRef<const char *> Args,
std::function<void(const RelatedIdentsInfo &)> Receiver) {
std::string Error;
SwiftInvocationRef Invok = ASTMgr->getInvocation(Args, InputFile, Error);
if (!Invok) {
// FIXME: Report it as failed request.
LOG_WARN_FUNC("failed to create an ASTInvocation: " << Error);
Receiver({});
return;
}
class RelatedIdConsumer : public SwiftASTConsumer {
unsigned Offset;
std::function<void(const RelatedIdentsInfo &)> Receiver;
SwiftInvocationRef Invok;
public:
RelatedIdConsumer(unsigned Offset,
std::function<void(const RelatedIdentsInfo &)> Receiver,
SwiftInvocationRef Invok)
: Offset(Offset), Receiver(std::move(Receiver)), Invok(Invok) { }
void handlePrimaryAST(ASTUnitRef AstUnit) override {
auto &CompInst = AstUnit->getCompilerInstance();
auto &SrcFile = AstUnit->getPrimarySourceFile();
trace::TracedOperation TracedOp;
SmallVector<std::pair<unsigned, unsigned>, 8> Ranges;
auto Action = [&]() {
if (trace::enabled()) {
trace::SwiftInvocation SwiftArgs;
Invok->raw(SwiftArgs.Args.Args, SwiftArgs.Args.PrimaryFile);
trace::initTraceFiles(SwiftArgs, CompInst);
TracedOp.start(trace::OperationKind::RelatedIdents, SwiftArgs,
{std::make_pair("Offset", std::to_string(Offset))});
}
unsigned BufferID = SrcFile.getBufferID().getValue();
SourceLoc Loc =
Lexer::getLocForStartOfToken(CompInst.getSourceMgr(), BufferID, Offset);
if (Loc.isInvalid())
return;
SemaLocResolver Resolver(SrcFile);
SemaToken SemaTok = Resolver.resolve(Loc);
if (SemaTok.isInvalid())
return;
if (SemaTok.IsKeywordArgument)
return;
ValueDecl *VD = SemaTok.CtorTyRef ? SemaTok.CtorTyRef : SemaTok.ValueD;
if (!VD)
return; // This was a module reference.
// Only accept pointing to an identifier.
if (!SemaTok.IsRef &&
(isa<ConstructorDecl>(VD) ||
isa<DestructorDecl>(VD) ||
isa<SubscriptDecl>(VD)))
return;
if (VD->getName().isOperator())
return;
RelatedIdScanner Scanner(SrcFile, BufferID, VD, Ranges);
if (DeclContext *LocalDC = VD->getDeclContext()->getLocalContext()) {
Scanner.walk(LocalDC);
} else {
Scanner.walk(SrcFile);
}
};
Action();
RelatedIdentsInfo Info;
Info.Ranges = Ranges;
Receiver(Info);
}
void cancelled() override {
RelatedIdentsInfo Info;
Info.IsCancelled = true;
Receiver(Info);
}
void failed(StringRef Error) override {
LOG_WARN_FUNC("related idents failed: " << Error);
Receiver({});
}
};
auto Consumer = std::make_shared<RelatedIdConsumer>(Offset, Receiver, Invok);
/// FIXME: When request cancellation is implemented and Xcode adopts it,
/// don't use 'OncePerASTToken'.
static const char OncePerASTToken = 0;
ASTMgr->processASTAsync(Invok, std::move(Consumer), &OncePerASTToken);
}