mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Add a platform kind and availability attributes for macCatalyst. macCatalyst uses iOS version numbers and inherits availability from iOS attributes unless a macCatalyst attribute is explicitly provided.
1480 lines
51 KiB
C++
1480 lines
51 KiB
C++
//===--- SwiftDocSupport.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 "clang/AST/Decl.h"
|
|
#include "clang/Basic/Module.h"
|
|
#include "SwiftASTManager.h"
|
|
#include "SwiftEditorDiagConsumer.h"
|
|
#include "SwiftLangSupport.h"
|
|
#include "SourceKit/Support/Logging.h"
|
|
#include "SourceKit/Support/UIdent.h"
|
|
|
|
#include "swift/AST/ASTPrinter.h"
|
|
#include "swift/AST/ASTWalker.h"
|
|
#include "swift/AST/GenericSignature.h"
|
|
#include "swift/AST/TypeRepr.h"
|
|
#include "swift/Frontend/Frontend.h"
|
|
#include "swift/Frontend/PrintingDiagnosticConsumer.h"
|
|
#include "swift/IDE/CommentConversion.h"
|
|
#include "swift/IDE/ModuleInterfacePrinting.h"
|
|
#include "swift/IDE/SourceEntityWalker.h"
|
|
#include "swift/IDE/SyntaxModel.h"
|
|
#include "swift/IDE/Refactoring.h"
|
|
// This is included only for createLazyResolver(). Move to different header ?
|
|
#include "swift/Sema/IDETypeChecking.h"
|
|
#include "swift/Config.h"
|
|
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
#include "llvm/Support/Path.h"
|
|
|
|
using namespace SourceKit;
|
|
using namespace swift;
|
|
using namespace ide;
|
|
|
|
static ModuleDecl *getModuleByFullName(ASTContext &Ctx, StringRef ModuleName) {
|
|
SmallVector<Located<Identifier>, 4>
|
|
AccessPath;
|
|
while (!ModuleName.empty()) {
|
|
StringRef SubModuleName;
|
|
std::tie(SubModuleName, ModuleName) = ModuleName.split('.');
|
|
AccessPath.push_back({ Ctx.getIdentifier(SubModuleName), SourceLoc() });
|
|
}
|
|
return Ctx.getModule(AccessPath);
|
|
}
|
|
|
|
static ModuleDecl *getModuleByFullName(ASTContext &Ctx, Identifier ModuleName) {
|
|
return Ctx.getModule({ Located<Identifier>(ModuleName, SourceLoc()) });
|
|
}
|
|
|
|
namespace {
|
|
struct TextRange {
|
|
unsigned Offset;
|
|
unsigned Length;
|
|
};
|
|
|
|
struct TextEntity {
|
|
const Decl *Dcl = nullptr;
|
|
TypeOrExtensionDecl SynthesizeTarget;
|
|
const Decl *DefaultImplementationOf = nullptr;
|
|
StringRef Argument;
|
|
TextRange Range;
|
|
unsigned LocOffset = 0;
|
|
std::vector<TextEntity> SubEntities;
|
|
const bool IsSynthesizedExtension;
|
|
|
|
TextEntity(const Decl *D, TypeOrExtensionDecl SynthesizeTarget,
|
|
const Decl *DefaultImplementationOf, unsigned StartOffset,
|
|
bool IsSynthesizedExtension)
|
|
: Dcl(D), SynthesizeTarget(SynthesizeTarget),
|
|
DefaultImplementationOf(DefaultImplementationOf), Range{StartOffset, 0},
|
|
IsSynthesizedExtension(IsSynthesizedExtension) {}
|
|
|
|
TextEntity(const Decl *D, TypeOrExtensionDecl SynthesizeTarget,
|
|
const Decl *DefaultImplementationOf, TextRange TR,
|
|
unsigned LocOffset, bool IsSynthesizedExtension)
|
|
: Dcl(D), DefaultImplementationOf(DefaultImplementationOf), Range(TR),
|
|
LocOffset(LocOffset), IsSynthesizedExtension(IsSynthesizedExtension) {}
|
|
|
|
TextEntity(const Decl *D, TypeOrExtensionDecl SynthesizeTarget,
|
|
const Decl *DefaultImplementationOf, StringRef Arg, TextRange TR,
|
|
unsigned LocOffset, bool IsSynthesizedExtension)
|
|
: Dcl(D), SynthesizeTarget(SynthesizeTarget),
|
|
DefaultImplementationOf(DefaultImplementationOf), Argument(Arg),
|
|
Range(TR), LocOffset(LocOffset),
|
|
IsSynthesizedExtension(IsSynthesizedExtension) {}
|
|
};
|
|
|
|
struct TextReference {
|
|
const ValueDecl *Dcl = nullptr;
|
|
TextRange Range;
|
|
const Type Ty;
|
|
|
|
TextReference(const ValueDecl *D, unsigned Offset, unsigned Length,
|
|
const Type Ty = Type()) : Dcl(D), Range{Offset, Length}, Ty(Ty) {}
|
|
};
|
|
|
|
class AnnotatingPrinter : public StreamPrinter {
|
|
|
|
std::pair<const ExtensionDecl *, TypeOrExtensionDecl>
|
|
SynthesizedExtensionInfo = {nullptr, {}};
|
|
|
|
typedef llvm::SmallDenseMap<ValueDecl*, ValueDecl*> DefaultImplementMap;
|
|
llvm::SmallDenseMap<ProtocolDecl*, DefaultImplementMap> AllDefaultMaps;
|
|
DefaultImplementMap *DefaultMapToUse = nullptr;
|
|
|
|
void initDefaultMapToUse(const Decl *D) {
|
|
const auto *ED = dyn_cast<ExtensionDecl>(D);
|
|
if (!ED)
|
|
return;
|
|
if (auto NTD = ED->getExtendedNominal()) {
|
|
if (auto *PD = dyn_cast<ProtocolDecl>(NTD)) {
|
|
auto Pair = AllDefaultMaps.insert({PD, DefaultImplementMap()});
|
|
DefaultMapToUse = &Pair.first->getSecond();
|
|
if (Pair.second) {
|
|
swift::collectDefaultImplementationForProtocolMembers(PD,
|
|
Pair.first->getSecond());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void deinitDefaultMapToUse(const Decl*D) {
|
|
if (isa<ExtensionDecl>(D)) {
|
|
DefaultMapToUse = nullptr;
|
|
}
|
|
}
|
|
|
|
ValueDecl *getDefaultImplementation(const Decl *D) {
|
|
if (!DefaultMapToUse)
|
|
return nullptr;
|
|
auto *VD = const_cast<ValueDecl*>(dyn_cast<ValueDecl>(D));
|
|
auto Found = DefaultMapToUse->find(VD);
|
|
if (Found != DefaultMapToUse->end()) {
|
|
return Found->second;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
public:
|
|
std::vector<TextEntity> TopEntities;
|
|
std::vector<TextEntity> EntitiesStack;
|
|
std::vector<TextReference> References;
|
|
|
|
using StreamPrinter::StreamPrinter;
|
|
|
|
~AnnotatingPrinter() override {
|
|
assert(EntitiesStack.empty());
|
|
}
|
|
|
|
bool shouldContinuePre(const Decl *D, Optional<BracketOptions> Bracket) {
|
|
assert(Bracket.hasValue());
|
|
if (!Bracket.getValue().shouldOpenExtension(D) &&
|
|
isa<ExtensionDecl>(D))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool shouldContinuePost(const Decl *D, Optional<BracketOptions> Bracket) {
|
|
assert(Bracket.hasValue());
|
|
if (!Bracket.getValue().shouldCloseNominal(D) && isa<NominalTypeDecl>(D))
|
|
return false;
|
|
if (!Bracket.getValue().shouldCloseExtension(D) &&
|
|
isa<ExtensionDecl>(D))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
void printSynthesizedExtensionPre(const ExtensionDecl *ED,
|
|
TypeOrExtensionDecl Target,
|
|
Optional<BracketOptions> Bracket) override {
|
|
assert(!SynthesizedExtensionInfo.first);
|
|
SynthesizedExtensionInfo = {ED, Target};
|
|
if (!shouldContinuePre(ED, Bracket))
|
|
return;
|
|
unsigned StartOffset = OS.tell();
|
|
EntitiesStack.emplace_back(ED, Target, nullptr, StartOffset, true);
|
|
}
|
|
|
|
void
|
|
printSynthesizedExtensionPost(const ExtensionDecl *ED,
|
|
TypeOrExtensionDecl Target,
|
|
Optional<BracketOptions> Bracket) override {
|
|
assert(SynthesizedExtensionInfo.first);
|
|
SynthesizedExtensionInfo = {nullptr, {}};
|
|
if (!shouldContinuePost(ED, Bracket))
|
|
return;
|
|
TextEntity Entity = std::move(EntitiesStack.back());
|
|
EntitiesStack.pop_back();
|
|
unsigned EndOffset = OS.tell();
|
|
Entity.Range.Length = EndOffset - Entity.Range.Offset;
|
|
TopEntities.push_back(std::move(Entity));
|
|
}
|
|
|
|
void printDeclPre(const Decl *D, Optional<BracketOptions> Bracket) override {
|
|
if (isa<ParamDecl>(D))
|
|
return; // Parameters are handled specially in addParameters().
|
|
if (!shouldContinuePre(D, Bracket))
|
|
return;
|
|
unsigned StartOffset = OS.tell();
|
|
initDefaultMapToUse(D);
|
|
// If D is declared in the extension, then the synthesized target is valid.
|
|
TypeOrExtensionDecl SynthesizedTarget;
|
|
assert(D->getDeclContext()->isModuleScopeContext() == EntitiesStack.empty());
|
|
if (D->getDeclContext() == SynthesizedExtensionInfo.first)
|
|
SynthesizedTarget = SynthesizedExtensionInfo.second;
|
|
EntitiesStack.emplace_back(D, SynthesizedTarget,
|
|
getDefaultImplementation(D), StartOffset, false);
|
|
}
|
|
|
|
void printDeclLoc(const Decl *D) override {
|
|
if (EntitiesStack.back().Dcl == D) {
|
|
unsigned LocOffset = OS.tell();
|
|
EntitiesStack.back().LocOffset = LocOffset;
|
|
}
|
|
}
|
|
|
|
void printDeclPost(const Decl *D, Optional<BracketOptions> Bracket) override {
|
|
if (isa<ParamDecl>(D))
|
|
return; // Parameters are handled specially in addParameters().
|
|
if (!shouldContinuePost(D, Bracket))
|
|
return;
|
|
assert(!EntitiesStack.empty());
|
|
TextEntity Entity = std::move(EntitiesStack.back());
|
|
EntitiesStack.pop_back();
|
|
unsigned EndOffset = OS.tell();
|
|
Entity.Range.Length = EndOffset - Entity.Range.Offset;
|
|
if (EntitiesStack.empty()) {
|
|
assert (D->getDeclContext()->isModuleScopeContext());
|
|
TopEntities.push_back(std::move(Entity));
|
|
} else {
|
|
assert (!D->getDeclContext()->isModuleScopeContext());
|
|
EntitiesStack.back().SubEntities.push_back(std::move(Entity));
|
|
}
|
|
deinitDefaultMapToUse(D);
|
|
}
|
|
|
|
void printTypeRef(
|
|
Type T, const TypeDecl *TD, Identifier Name,
|
|
PrintNameContext NameContext = PrintNameContext::Normal) override {
|
|
unsigned StartOffset = OS.tell();
|
|
References.emplace_back(TD, StartOffset, Name.str().size());
|
|
StreamPrinter::printTypeRef(T, TD, Name, NameContext);
|
|
}
|
|
};
|
|
|
|
struct SourceTextInfo {
|
|
std::string Text;
|
|
std::vector<TextEntity> TopEntities;
|
|
std::vector<TextReference> References;
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
static void initDocGenericParams(const Decl *D, DocEntityInfo &Info) {
|
|
auto *DC = dyn_cast<DeclContext>(D);
|
|
if (DC == nullptr || !DC->isInnermostContextGeneric())
|
|
return;
|
|
|
|
GenericSignature GenericSig = DC->getGenericSignatureOfContext();
|
|
|
|
if (!GenericSig)
|
|
return;
|
|
|
|
// FIXME: Not right for extensions of nested generic types
|
|
for (auto *GP : GenericSig->getInnermostGenericParams()) {
|
|
if (GP->getDecl()->isImplicit())
|
|
continue;
|
|
DocGenericParam Param;
|
|
Param.Name = GP->getName().str();
|
|
Info.GenericParams.push_back(Param);
|
|
}
|
|
|
|
ProtocolDecl *proto = nullptr;
|
|
if (auto *typeDC = DC->getInnermostTypeContext())
|
|
proto = typeDC->getSelfProtocolDecl();
|
|
|
|
for (auto &Req : GenericSig->getRequirements()) {
|
|
// Skip protocol Self requirement.
|
|
if (proto &&
|
|
Req.getKind() == RequirementKind::Conformance &&
|
|
Req.getFirstType()->isEqual(proto->getSelfInterfaceType()) &&
|
|
Req.getSecondType()->getAnyNominal() == proto)
|
|
continue;
|
|
|
|
std::string ReqStr;
|
|
PrintOptions Opts;
|
|
llvm::raw_string_ostream OS(ReqStr);
|
|
Req.print(OS, Opts);
|
|
OS.flush();
|
|
Info.GenericRequirements.push_back(std::move(ReqStr));
|
|
}
|
|
}
|
|
|
|
static bool initDocEntityInfo(const Decl *D,
|
|
TypeOrExtensionDecl SynthesizedTarget,
|
|
const Decl *DefaultImplementationOf, bool IsRef,
|
|
bool IsSynthesizedExtension, DocEntityInfo &Info,
|
|
StringRef Arg = StringRef()) {
|
|
if (!IsRef && D->isImplicit())
|
|
return true;
|
|
if (!D || isa<ParamDecl>(D) ||
|
|
(isa<VarDecl>(D) && D->getDeclContext()->isLocalContext())) {
|
|
Info.Kind = SwiftLangSupport::getUIDForLocalVar(IsRef);
|
|
if (D) {
|
|
llvm::raw_svector_ostream OS(Info.Name);
|
|
SwiftLangSupport::printDisplayName(cast<ValueDecl>(D), OS);
|
|
} else {
|
|
Info.Name = "_";
|
|
}
|
|
|
|
if (!Arg.empty())
|
|
Info.Argument = Arg.str();
|
|
|
|
return false;
|
|
}
|
|
|
|
auto SynthesizedTargetNTD =
|
|
SynthesizedTarget ? SynthesizedTarget.getBaseNominal() : nullptr;
|
|
|
|
if (IsSynthesizedExtension) {
|
|
Info.Kind =
|
|
SwiftLangSupport::getUIDForExtensionOfDecl(SynthesizedTargetNTD);
|
|
} else
|
|
Info.Kind = SwiftLangSupport::getUIDForDecl(D, IsRef);
|
|
|
|
if (Info.Kind.isInvalid())
|
|
return true;
|
|
if (const auto *VD = dyn_cast<ValueDecl>(D)) {
|
|
llvm::raw_svector_ostream NameOS(Info.Name);
|
|
SwiftLangSupport::printDisplayName(VD, NameOS);
|
|
{
|
|
llvm::raw_svector_ostream OS(Info.USR);
|
|
SwiftLangSupport::printUSR(VD, OS);
|
|
if (SynthesizedTarget) {
|
|
OS << SwiftLangSupport::SynthesizedUSRSeparator;
|
|
SwiftLangSupport::printUSR(SynthesizedTargetNTD, OS);
|
|
{
|
|
llvm::raw_svector_ostream OS(Info.OriginalUSR);
|
|
SwiftLangSupport::printUSR(VD, OS);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (DefaultImplementationOf) {
|
|
llvm::raw_svector_ostream OS(Info.ProvideImplementationOfUSR);
|
|
SwiftLangSupport::printUSR((const ValueDecl*)DefaultImplementationOf, OS);
|
|
}
|
|
|
|
Info.IsUnavailable = AvailableAttr::isUnavailable(D);
|
|
Info.IsDeprecated = D->getAttrs().getDeprecated(D->getASTContext()) != nullptr;
|
|
Info.IsOptional = D->getAttrs().hasAttribute<OptionalAttr>();
|
|
|
|
if (!IsRef) {
|
|
llvm::raw_svector_ostream OS(Info.DocComment);
|
|
|
|
{
|
|
llvm::SmallString<128> DocBuffer;
|
|
{
|
|
llvm::raw_svector_ostream OSS(DocBuffer);
|
|
ide::getDocumentationCommentAsXML(D, OSS);
|
|
}
|
|
StringRef DocRef = (StringRef)DocBuffer;
|
|
if (IsSynthesizedExtension &&
|
|
DocRef.find("<Declaration>") != StringRef::npos) {
|
|
StringRef Open = "extension ";
|
|
assert(DocRef.find(Open) != StringRef::npos);
|
|
auto FirstPart = DocRef.substr(0, DocRef.find(Open) + (Open).size());
|
|
auto SecondPart = DocRef.substr(FirstPart.size());
|
|
auto ExtendedName = ((const ExtensionDecl*)D)->getExtendedNominal()
|
|
->getName().str();
|
|
assert(SecondPart.startswith(ExtendedName));
|
|
SecondPart = SecondPart.substr(ExtendedName.size());
|
|
llvm::SmallString<128> UpdatedDocBuffer;
|
|
UpdatedDocBuffer.append(FirstPart);
|
|
UpdatedDocBuffer.append(SynthesizedTargetNTD->getName().str());
|
|
UpdatedDocBuffer.append(SecondPart);
|
|
OS << UpdatedDocBuffer;
|
|
} else
|
|
OS << DocBuffer;
|
|
}
|
|
|
|
initDocGenericParams(D, Info);
|
|
|
|
llvm::raw_svector_ostream LocalizationKeyOS(Info.LocalizationKey);
|
|
ide::getLocalizationKey(D, LocalizationKeyOS);
|
|
|
|
if (auto *VD = dyn_cast<ValueDecl>(D)) {
|
|
llvm::raw_svector_ostream OS(Info.FullyAnnotatedDecl);
|
|
if (SynthesizedTarget)
|
|
SwiftLangSupport::printFullyAnnotatedSynthesizedDeclaration(
|
|
VD, SynthesizedTarget, OS);
|
|
else
|
|
SwiftLangSupport::printFullyAnnotatedDeclaration(VD, Type(), OS);
|
|
} else if (auto *E = dyn_cast<ExtensionDecl>(D)) {
|
|
if (auto Sig = E->getGenericSignature()) {
|
|
// The extension under printing is potentially part of a synthesized
|
|
// extension. Thus it's hard to print the fully annotated decl. We
|
|
// need to at least print the generic signature here.
|
|
llvm::raw_svector_ostream OS(Info.FullyAnnotatedGenericSig);
|
|
SwiftLangSupport::printFullyAnnotatedGenericReq(Sig, OS);
|
|
}
|
|
}
|
|
}
|
|
|
|
switch(D->getDeclContext()->getContextKind()) {
|
|
case DeclContextKind::AbstractClosureExpr:
|
|
case DeclContextKind::TopLevelCodeDecl:
|
|
case DeclContextKind::AbstractFunctionDecl:
|
|
case DeclContextKind::SubscriptDecl:
|
|
case DeclContextKind::EnumElementDecl:
|
|
case DeclContextKind::Initializer:
|
|
case DeclContextKind::SerializedLocal:
|
|
case DeclContextKind::ExtensionDecl:
|
|
case DeclContextKind::GenericTypeDecl:
|
|
break;
|
|
|
|
// We report sub-module information only for top-level decls.
|
|
case DeclContextKind::Module:
|
|
case DeclContextKind::FileUnit: {
|
|
if (auto *CD = D->getClangDecl()) {
|
|
if (auto *M = CD->getImportedOwningModule()) {
|
|
if (M->isSubModule()) {
|
|
llvm::raw_svector_ostream OS(Info.SubModuleName);
|
|
ModuleDecl::ReverseFullNameIterator(M).printForward(OS);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool initDocEntityInfo(const TextEntity &Entity,
|
|
DocEntityInfo &Info) {
|
|
if (initDocEntityInfo(Entity.Dcl, Entity.SynthesizeTarget,
|
|
Entity.DefaultImplementationOf,
|
|
/*IsRef=*/false, Entity.IsSynthesizedExtension,
|
|
Info, Entity.Argument))
|
|
return true;
|
|
Info.Offset = Entity.Range.Offset;
|
|
Info.Length = Entity.Range.Length;
|
|
return false;
|
|
}
|
|
|
|
static const TypeDecl *getTypeDeclFromType(Type Ty) {
|
|
if (auto alias = dyn_cast<TypeAliasType>(Ty.getPointer()))
|
|
return alias->getDecl();
|
|
return Ty->getAnyNominal();
|
|
}
|
|
|
|
static void passInherits(const ValueDecl *D, DocInfoConsumer &Consumer) {
|
|
DocEntityInfo EntInfo;
|
|
if (initDocEntityInfo(D, {}, nullptr, /*IsRef=*/true, false, EntInfo))
|
|
return;
|
|
Consumer.handleInheritsEntity(EntInfo);
|
|
}
|
|
static void passConforms(const ValueDecl *D, DocInfoConsumer &Consumer) {
|
|
DocEntityInfo EntInfo;
|
|
if (initDocEntityInfo(D, {}, nullptr, /*IsRef=*/true, false, EntInfo))
|
|
return;
|
|
Consumer.handleConformsToEntity(EntInfo);
|
|
}
|
|
static void passInherits(ArrayRef<TypeLoc> InheritedTypes,
|
|
DocInfoConsumer &Consumer) {
|
|
for (auto Inherited : InheritedTypes) {
|
|
if (!Inherited.getType())
|
|
continue;
|
|
|
|
if (auto Proto = Inherited.getType()->getAs<ProtocolType>()) {
|
|
passConforms(Proto->getDecl(), Consumer);
|
|
continue;
|
|
}
|
|
|
|
if (auto ProtoComposition
|
|
= Inherited.getType()->getAs<ProtocolCompositionType>()) {
|
|
for (auto T : ProtoComposition->getMembers())
|
|
passInherits(TypeLoc::withoutLoc(T), Consumer);
|
|
continue;
|
|
}
|
|
|
|
if (auto TD = getTypeDeclFromType(Inherited.getType())) {
|
|
passInherits(TD, Consumer);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void passConforms(ArrayRef<ValueDecl *> Dcls,
|
|
DocInfoConsumer &Consumer) {
|
|
for (auto D : Dcls)
|
|
passConforms(D, Consumer);
|
|
}
|
|
static void passExtends(const ValueDecl *D, DocInfoConsumer &Consumer) {
|
|
DocEntityInfo EntInfo;
|
|
if (initDocEntityInfo(D, {}, nullptr, /*IsRef=*/true, false, EntInfo))
|
|
return;
|
|
Consumer.handleExtendsEntity(EntInfo);
|
|
}
|
|
|
|
static void passInheritsAndConformancesForValueDecl(const ValueDecl *VD,
|
|
DocInfoConsumer &Consumer) {
|
|
if (auto Overridden = VD->getOverriddenDecl())
|
|
passInherits(Overridden, Consumer);
|
|
passConforms(VD->getSatisfiedProtocolRequirements(/*Sorted=*/true),
|
|
Consumer);
|
|
}
|
|
|
|
static void reportRelated(ASTContext &Ctx, const Decl *D,
|
|
TypeOrExtensionDecl SynthesizedTarget,
|
|
DocInfoConsumer &Consumer) {
|
|
if (!D || isa<ParamDecl>(D))
|
|
return;
|
|
if (const auto *ED = dyn_cast<ExtensionDecl>(D)) {
|
|
if (SynthesizedTarget) {
|
|
auto Extends = SynthesizedTarget.getBaseNominal();
|
|
passExtends(Extends, Consumer);
|
|
} else if (Type T = ED->getExtendedType()) {
|
|
if (auto TD = getTypeDeclFromType(T))
|
|
passExtends(TD, Consumer);
|
|
}
|
|
|
|
passInherits(ED->getInherited(), Consumer);
|
|
|
|
} else if (auto *TAD = dyn_cast<TypeAliasDecl>(D)) {
|
|
|
|
if (auto Ty = TAD->getDeclaredInterfaceType()) {
|
|
// If underlying type exists, report the inheritance and conformance of the
|
|
// underlying type.
|
|
if (auto NM = Ty->getAnyNominal()) {
|
|
passInherits(NM->getInherited(), Consumer);
|
|
passConforms(NM->getSatisfiedProtocolRequirements(/*Sorted=*/true),
|
|
Consumer);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Otherwise, report the inheritance of the type alias itself.
|
|
passInheritsAndConformancesForValueDecl(TAD, Consumer);
|
|
} else if (const auto *TD = dyn_cast<TypeDecl>(D)) {
|
|
llvm::SmallVector<TypeLoc, 4> AllInherits;
|
|
getInheritedForPrinting(TD, PrintOptions(), AllInherits);
|
|
passInherits(AllInherits, Consumer);
|
|
passConforms(TD->getSatisfiedProtocolRequirements(/*Sorted=*/true),
|
|
Consumer);
|
|
} else if (auto *VD = dyn_cast<ValueDecl>(D)) {
|
|
passInheritsAndConformancesForValueDecl(VD, Consumer);
|
|
}
|
|
}
|
|
|
|
static ArrayRef<const DeclAttribute*>
|
|
getDeclAttributes(const Decl *D, std::vector<const DeclAttribute*> &Scratch) {
|
|
for (auto Attr : D->getAttrs()) {
|
|
Scratch.push_back(Attr);
|
|
}
|
|
// For enum elements, inherit their parent enum decls' deprecated attributes.
|
|
if (auto *DE = dyn_cast<EnumElementDecl>(D)) {
|
|
for (auto Attr : DE->getParentEnum()->getAttrs()) {
|
|
if (auto Avail = dyn_cast<AvailableAttr>(Attr)) {
|
|
if (Avail->Deprecated || Avail->isUnconditionallyDeprecated()) {
|
|
Scratch.push_back(Attr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return llvm::makeArrayRef(Scratch);
|
|
}
|
|
|
|
// Only reports @available.
|
|
// FIXME: Handle all attributes.
|
|
static void reportAttributes(ASTContext &Ctx,
|
|
const Decl *D,
|
|
DocInfoConsumer &Consumer) {
|
|
static UIdent AvailableAttrKind("source.lang.swift.attribute.availability");
|
|
static UIdent PlatformIOS("source.availability.platform.ios");
|
|
static UIdent PlatformMacCatalyst("source.availability.platform.maccatalyst");
|
|
static UIdent PlatformOSX("source.availability.platform.osx");
|
|
static UIdent PlatformtvOS("source.availability.platform.tvos");
|
|
static UIdent PlatformWatchOS("source.availability.platform.watchos");
|
|
static UIdent PlatformIOSAppExt("source.availability.platform.ios_app_extension");
|
|
static UIdent PlatformMacCatalystAppExt("source.availability.platform.maccatalyst_app_extension");
|
|
static UIdent PlatformOSXAppExt("source.availability.platform.osx_app_extension");
|
|
static UIdent PlatformtvOSAppExt("source.availability.platform.tvos_app_extension");
|
|
static UIdent PlatformWatchOSAppExt("source.availability.platform.watchos_app_extension");
|
|
std::vector<const DeclAttribute*> Scratch;
|
|
|
|
for (auto Attr : getDeclAttributes(D, Scratch)) {
|
|
if (auto Av = dyn_cast<AvailableAttr>(Attr)) {
|
|
UIdent PlatformUID;
|
|
switch (Av->Platform) {
|
|
case PlatformKind::none:
|
|
PlatformUID = UIdent(); break;
|
|
case PlatformKind::iOS:
|
|
PlatformUID = PlatformIOS; break;
|
|
case PlatformKind::macCatalyst:
|
|
PlatformUID = PlatformIOS; break;
|
|
case PlatformKind::OSX:
|
|
PlatformUID = PlatformOSX; break;
|
|
case PlatformKind::tvOS:
|
|
PlatformUID = PlatformtvOS; break;
|
|
case PlatformKind::watchOS:
|
|
PlatformUID = PlatformWatchOS; break;
|
|
case PlatformKind::iOSApplicationExtension:
|
|
PlatformUID = PlatformIOSAppExt; break;
|
|
case PlatformKind::macCatalystApplicationExtension:
|
|
PlatformUID = PlatformMacCatalystAppExt; break;
|
|
case PlatformKind::OSXApplicationExtension:
|
|
PlatformUID = PlatformOSXAppExt; break;
|
|
case PlatformKind::tvOSApplicationExtension:
|
|
PlatformUID = PlatformtvOSAppExt; break;
|
|
case PlatformKind::watchOSApplicationExtension:
|
|
PlatformUID = PlatformWatchOSAppExt; break;
|
|
}
|
|
|
|
AvailableAttrInfo Info;
|
|
Info.AttrKind = AvailableAttrKind;
|
|
Info.IsUnavailable = Av->isUnconditionallyUnavailable();
|
|
Info.IsDeprecated = Av->isUnconditionallyDeprecated();
|
|
Info.Platform = PlatformUID;
|
|
Info.Message = Av->Message;
|
|
if (Av->Introduced)
|
|
Info.Introduced = *Av->Introduced;
|
|
if (Av->Deprecated)
|
|
Info.Deprecated = *Av->Deprecated;
|
|
if (Av->Obsoleted)
|
|
Info.Obsoleted = *Av->Obsoleted;
|
|
|
|
Consumer.handleAvailableAttribute(Info);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void reportDocEntities(ASTContext &Ctx,
|
|
ArrayRef<TextEntity> Entities,
|
|
DocInfoConsumer &Consumer) {
|
|
for (auto &Entity : Entities) {
|
|
DocEntityInfo EntInfo;
|
|
if (initDocEntityInfo(Entity, EntInfo))
|
|
continue;
|
|
Consumer.startSourceEntity(EntInfo);
|
|
reportRelated(Ctx, Entity.Dcl,
|
|
Entity.IsSynthesizedExtension ? Entity.SynthesizeTarget
|
|
: TypeOrExtensionDecl(),
|
|
Consumer);
|
|
reportDocEntities(Ctx, Entity.SubEntities, Consumer);
|
|
reportAttributes(Ctx, Entity.Dcl, Consumer);
|
|
Consumer.finishSourceEntity(EntInfo.Kind);
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
class DocSyntaxWalker : public SyntaxModelWalker {
|
|
SourceManager &SM;
|
|
unsigned BufferID;
|
|
ArrayRef<TextReference> References;
|
|
DocInfoConsumer &Consumer;
|
|
SourceLoc LastArgLoc;
|
|
SourceLoc LastParamLoc;
|
|
|
|
public:
|
|
DocSyntaxWalker(SourceManager &SM, unsigned BufferID,
|
|
ArrayRef<TextReference> References,
|
|
DocInfoConsumer &Consumer)
|
|
: SM(SM), BufferID(BufferID), References(References), Consumer(Consumer) {}
|
|
|
|
bool walkToNodePre(SyntaxNode Node) override {
|
|
unsigned Offset = SM.getLocOffsetInBuffer(Node.Range.getStart(), BufferID);
|
|
unsigned Length = Node.Range.getByteLength();
|
|
|
|
reportRefsUntil(Offset);
|
|
if (!References.empty() && References.front().Range.Offset == Offset)
|
|
return true;
|
|
|
|
switch (Node.Kind) {
|
|
case SyntaxNodeKind::EditorPlaceholder:
|
|
return true;
|
|
|
|
case SyntaxNodeKind::Keyword:
|
|
case SyntaxNodeKind::Identifier:
|
|
if (Node.Range.getStart() == LastArgLoc ||
|
|
Node.Range.getStart() == LastParamLoc)
|
|
return true;
|
|
break;
|
|
|
|
case SyntaxNodeKind::DollarIdent:
|
|
case SyntaxNodeKind::Integer:
|
|
case SyntaxNodeKind::Floating:
|
|
case SyntaxNodeKind::String:
|
|
case SyntaxNodeKind::StringInterpolationAnchor:
|
|
case SyntaxNodeKind::CommentLine:
|
|
case SyntaxNodeKind::CommentBlock:
|
|
case SyntaxNodeKind::CommentMarker:
|
|
case SyntaxNodeKind::CommentURL:
|
|
case SyntaxNodeKind::DocCommentLine:
|
|
case SyntaxNodeKind::DocCommentBlock:
|
|
case SyntaxNodeKind::DocCommentField:
|
|
case SyntaxNodeKind::TypeId:
|
|
case SyntaxNodeKind::BuildConfigKeyword:
|
|
case SyntaxNodeKind::BuildConfigId:
|
|
case SyntaxNodeKind::PoundDirectiveKeyword:
|
|
case SyntaxNodeKind::AttributeId:
|
|
case SyntaxNodeKind::AttributeBuiltin:
|
|
case SyntaxNodeKind::ObjectLiteral:
|
|
break;
|
|
}
|
|
|
|
DocEntityInfo Info;
|
|
Info.Kind = SwiftLangSupport::getUIDForSyntaxNodeKind(Node.Kind);
|
|
Info.Offset = Offset;
|
|
Info.Length = Length;
|
|
Consumer.handleAnnotation(Info);
|
|
return true;
|
|
}
|
|
|
|
void finished() {
|
|
reportRefsUntil(std::numeric_limits<unsigned>::max());
|
|
}
|
|
|
|
bool walkToSubStructurePre(SyntaxStructureNode Node) override {
|
|
if (Node.Kind == SyntaxStructureKind::Parameter) {
|
|
auto Param = dyn_cast<ParamDecl>(Node.Dcl);
|
|
|
|
auto passAnnotation = [&](UIdent Kind, SourceLoc Loc, Identifier Name) {
|
|
if (Loc.isInvalid())
|
|
return;
|
|
unsigned Offset = SM.getLocOffsetInBuffer(Loc, BufferID);
|
|
unsigned Length = Name.empty() ? 1 : Name.getLength();
|
|
reportRefsUntil(Offset);
|
|
|
|
DocEntityInfo Info;
|
|
Info.Kind = Kind;
|
|
Info.Offset = Offset;
|
|
Info.Length = Length;
|
|
Consumer.handleAnnotation(Info);
|
|
};
|
|
|
|
// Argument
|
|
static UIdent KindArgument("source.lang.swift.syntaxtype.argument");
|
|
passAnnotation(KindArgument, Param->getArgumentNameLoc(),
|
|
Param->getArgumentName());
|
|
LastArgLoc = Param->getArgumentNameLoc();
|
|
|
|
// Parameter
|
|
static UIdent KindParameter("source.lang.swift.syntaxtype.parameter");
|
|
passAnnotation(KindParameter, Param->getNameLoc(), Param->getName());
|
|
LastParamLoc = Param->getNameLoc();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
void reportRefsUntil(unsigned Offset) {
|
|
while (!References.empty() && References.front().Range.Offset < Offset) {
|
|
const TextReference &Ref = References.front();
|
|
References = References.slice(1);
|
|
DocEntityInfo Info;
|
|
if (initDocEntityInfo(Ref.Dcl, {}, nullptr, /*IsRef=*/true, false, Info))
|
|
continue;
|
|
Info.Offset = Ref.Range.Offset;
|
|
Info.Length = Ref.Range.Length;
|
|
Info.Ty = Ref.Ty;
|
|
Consumer.handleAnnotation(Info);
|
|
}
|
|
}
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
static bool makeParserAST(CompilerInstance &CI, StringRef Text,
|
|
CompilerInvocation Invocation) {
|
|
Invocation.getFrontendOptions().InputsAndOutputs.clearInputs();
|
|
Invocation.setModuleName("main");
|
|
Invocation.setInputKind(InputFileKind::Swift);
|
|
|
|
std::unique_ptr<llvm::MemoryBuffer> Buf;
|
|
Buf = llvm::MemoryBuffer::getMemBuffer(Text, "<module-interface>");
|
|
Invocation.getFrontendOptions().InputsAndOutputs.addInput(
|
|
InputFile(Buf.get()->getBufferIdentifier(), false, Buf.get()));
|
|
if (CI.setup(Invocation))
|
|
return true;
|
|
CI.performParseOnly();
|
|
return false;
|
|
}
|
|
|
|
static void collectFuncEntities(std::vector<TextEntity> &Ents,
|
|
std::vector<TextEntity*> &FuncEntities) {
|
|
for (TextEntity &Ent : Ents) {
|
|
if (isa<AbstractFunctionDecl>(Ent.Dcl) || isa<SubscriptDecl>(Ent.Dcl)) {
|
|
// We are getting the entities via a pointer and later adding to their
|
|
// subentities; make sure it doesn't have subentities now or we are going
|
|
// to invalidate the pointers.
|
|
assert(Ent.SubEntities.empty());
|
|
FuncEntities.push_back(&Ent);
|
|
}
|
|
collectFuncEntities(Ent.SubEntities, FuncEntities);
|
|
}
|
|
}
|
|
|
|
static void addParameters(ArrayRef<Identifier> &ArgNames,
|
|
const ParameterList *paramList,
|
|
TextEntity &Ent,
|
|
SourceManager &SM,
|
|
unsigned BufferID) {
|
|
for (auto ¶m : *paramList) {
|
|
StringRef Arg;
|
|
if (!ArgNames.empty()) {
|
|
Identifier Id = ArgNames.front();
|
|
Arg = Id.empty() ? "_" : Id.str();
|
|
ArgNames = ArgNames.slice(1);
|
|
}
|
|
|
|
if (auto typeRepr = param->getTypeRepr()) {
|
|
SourceRange TypeRange = typeRepr->getSourceRange();
|
|
if (auto InOutTyR = dyn_cast_or_null<InOutTypeRepr>(typeRepr))
|
|
TypeRange = InOutTyR->getBase()->getSourceRange();
|
|
if (TypeRange.isInvalid())
|
|
continue;
|
|
|
|
unsigned StartOffs = SM.getLocOffsetInBuffer(TypeRange.Start, BufferID);
|
|
unsigned EndOffs =
|
|
SM.getLocOffsetInBuffer(Lexer::getLocForEndOfToken(SM, TypeRange.End),
|
|
BufferID);
|
|
TextRange TR{ StartOffs, EndOffs-StartOffs };
|
|
TextEntity Param(param, {}, nullptr, Arg, TR, StartOffs, false);
|
|
Ent.SubEntities.push_back(std::move(Param));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void addParameters(const AbstractFunctionDecl *FD,
|
|
TextEntity &Ent,
|
|
SourceManager &SM,
|
|
unsigned BufferID) {
|
|
ArrayRef<Identifier> ArgNames;
|
|
DeclName Name = FD->getFullName();
|
|
if (Name) {
|
|
ArgNames = Name.getArgumentNames();
|
|
}
|
|
auto paramList = FD->getParameters();
|
|
addParameters(ArgNames, paramList, Ent, SM, BufferID);
|
|
}
|
|
|
|
static void addParameters(const SubscriptDecl *D,
|
|
TextEntity &Ent,
|
|
SourceManager &SM,
|
|
unsigned BufferID) {
|
|
ArrayRef<Identifier> ArgNames;
|
|
DeclName Name = D->getFullName();
|
|
if (Name) {
|
|
ArgNames = Name.getArgumentNames();
|
|
}
|
|
addParameters(ArgNames, D->getIndices(), Ent, SM, BufferID);
|
|
}
|
|
|
|
namespace {
|
|
class FuncWalker : public ASTWalker {
|
|
SourceManager &SM;
|
|
unsigned BufferID;
|
|
llvm::MutableArrayRef<TextEntity*> FuncEnts;
|
|
|
|
public:
|
|
FuncWalker(SourceManager &SM, unsigned BufferID,
|
|
llvm::MutableArrayRef<TextEntity*> FuncEnts)
|
|
: SM(SM), BufferID(BufferID), FuncEnts(FuncEnts) {}
|
|
|
|
bool walkToDeclPre(Decl *D) override {
|
|
if (D->isImplicit())
|
|
return false; // Skip body.
|
|
|
|
if (FuncEnts.empty())
|
|
return false;
|
|
|
|
if (!isa<AbstractFunctionDecl>(D) && !isa<SubscriptDecl>(D))
|
|
return true;
|
|
|
|
unsigned Offset = SM.getLocOffsetInBuffer(D->getLoc(), BufferID);
|
|
auto Found = FuncEnts.end();
|
|
if (FuncEnts.front()->LocOffset == Offset) {
|
|
Found = FuncEnts.begin();
|
|
} else {
|
|
Found = std::lower_bound(FuncEnts.begin(), FuncEnts.end(), Offset,
|
|
[](TextEntity *Ent, unsigned Offs) {
|
|
return Ent->LocOffset < Offs;
|
|
});
|
|
}
|
|
if (Found == FuncEnts.end() || (*Found)->LocOffset != Offset)
|
|
return false;
|
|
if (auto FD = dyn_cast<AbstractFunctionDecl>(D)) {
|
|
addParameters(FD, **Found, SM, BufferID);
|
|
} else {
|
|
addParameters(cast<SubscriptDecl>(D), **Found, SM, BufferID);
|
|
}
|
|
FuncEnts = llvm::MutableArrayRef<TextEntity*>(Found+1, FuncEnts.end());
|
|
return false; // skip body.
|
|
}
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
static void addParameterEntities(CompilerInstance &CI,
|
|
SourceTextInfo &IFaceInfo) {
|
|
std::vector<TextEntity*> FuncEntities;
|
|
collectFuncEntities(IFaceInfo.TopEntities, FuncEntities);
|
|
llvm::MutableArrayRef<TextEntity*> FuncEnts(FuncEntities.data(),
|
|
FuncEntities.size());
|
|
for (auto Unit : CI.getMainModule()->getFiles()) {
|
|
auto SF = dyn_cast<SourceFile>(Unit);
|
|
if (!SF)
|
|
continue;
|
|
FuncWalker Walker(CI.getSourceMgr(), *SF->getBufferID(), FuncEnts);
|
|
SF->walk(Walker);
|
|
}
|
|
}
|
|
|
|
static void reportSourceAnnotations(const SourceTextInfo &IFaceInfo,
|
|
CompilerInstance &CI,
|
|
DocInfoConsumer &Consumer) {
|
|
for (auto Unit : CI.getMainModule()->getFiles()) {
|
|
auto SF = dyn_cast<SourceFile>(Unit);
|
|
if (!SF)
|
|
continue;
|
|
|
|
SyntaxModelContext SyntaxContext(*SF);
|
|
DocSyntaxWalker SyntaxWalker(CI.getSourceMgr(), *SF->getBufferID(),
|
|
IFaceInfo.References, Consumer);
|
|
SyntaxContext.walk(SyntaxWalker);
|
|
SyntaxWalker.finished();
|
|
}
|
|
}
|
|
|
|
static bool getModuleInterfaceInfo(ASTContext &Ctx, StringRef ModuleName,
|
|
SourceTextInfo &Info) {
|
|
// Load standard library so that Clang importer can use it.
|
|
auto *Stdlib = getModuleByFullName(Ctx, Ctx.StdlibModuleName);
|
|
if (!Stdlib)
|
|
return true;
|
|
|
|
auto *M = getModuleByFullName(Ctx, ModuleName);
|
|
if (!M)
|
|
return true;
|
|
|
|
PrintOptions Options = PrintOptions::printDocInterface();
|
|
ModuleTraversalOptions TraversalOptions = None;
|
|
TraversalOptions |= ModuleTraversal::VisitSubmodules;
|
|
TraversalOptions |= ModuleTraversal::VisitHidden;
|
|
|
|
SmallString<128> Text;
|
|
llvm::raw_svector_ostream OS(Text);
|
|
AnnotatingPrinter Printer(OS);
|
|
printModuleInterface(M, None, TraversalOptions, Printer, Options,
|
|
true);
|
|
Info.Text = OS.str();
|
|
Info.TopEntities = std::move(Printer.TopEntities);
|
|
Info.References = std::move(Printer.References);
|
|
return false;
|
|
}
|
|
|
|
static bool reportModuleDocInfo(CompilerInvocation Invocation,
|
|
StringRef ModuleName,
|
|
DocInfoConsumer &Consumer) {
|
|
CompilerInstance CI;
|
|
// Display diagnostics to stderr.
|
|
PrintingDiagnosticConsumer PrintDiags;
|
|
CI.addDiagnosticConsumer(&PrintDiags);
|
|
|
|
if (CI.setup(Invocation))
|
|
return true;
|
|
|
|
ASTContext &Ctx = CI.getASTContext();
|
|
registerIDERequestFunctions(Ctx.evaluator);
|
|
(void)createTypeChecker(Ctx);
|
|
|
|
SourceTextInfo IFaceInfo;
|
|
if (getModuleInterfaceInfo(Ctx, ModuleName, IFaceInfo))
|
|
return true;
|
|
|
|
CompilerInstance ParseCI;
|
|
if (makeParserAST(ParseCI, IFaceInfo.Text, Invocation))
|
|
return true;
|
|
addParameterEntities(ParseCI, IFaceInfo);
|
|
|
|
Consumer.handleSourceText(IFaceInfo.Text);
|
|
reportDocEntities(Ctx, IFaceInfo.TopEntities, Consumer);
|
|
reportSourceAnnotations(IFaceInfo, ParseCI, Consumer);
|
|
|
|
return false;
|
|
}
|
|
|
|
namespace {
|
|
class SourceDocASTWalker : public SourceEntityWalker {
|
|
public:
|
|
SourceManager &SM;
|
|
unsigned BufferID;
|
|
|
|
std::vector<TextEntity> TopEntities;
|
|
std::vector<TextEntity> EntitiesStack;
|
|
std::vector<TextReference> References;
|
|
|
|
SourceDocASTWalker(SourceManager &SM, unsigned BufferID)
|
|
: SM(SM), BufferID(BufferID) {}
|
|
|
|
~SourceDocASTWalker() override {
|
|
assert(EntitiesStack.empty());
|
|
}
|
|
|
|
bool walkToDeclPre(Decl *D, CharSourceRange Range) override {
|
|
if (!isa<ValueDecl>(D) && !isa<ExtensionDecl>(D))
|
|
return true;
|
|
if (isLocal(D))
|
|
return true;
|
|
TextRange TR = getTextRange(D->getSourceRange());
|
|
unsigned LocOffset = getOffset(Range.getStart());
|
|
EntitiesStack.emplace_back(D, TypeOrExtensionDecl(), nullptr, TR, LocOffset,
|
|
false);
|
|
return true;
|
|
}
|
|
|
|
bool walkToDeclPost(Decl *D) override {
|
|
if (EntitiesStack.empty() || EntitiesStack.back().Dcl != D)
|
|
return true;
|
|
|
|
TextEntity Entity = std::move(EntitiesStack.back());
|
|
EntitiesStack.pop_back();
|
|
if (EntitiesStack.empty())
|
|
TopEntities.push_back(Entity);
|
|
else
|
|
EntitiesStack.back().SubEntities.push_back(Entity);
|
|
return true;
|
|
}
|
|
|
|
bool visitDeclReference(ValueDecl *D, CharSourceRange Range,
|
|
TypeDecl *CtorTyRef, ExtensionDecl *ExtTyRef, Type Ty,
|
|
ReferenceMetaData Data) override {
|
|
if (Data.isImplicit)
|
|
return true;
|
|
unsigned StartOffset = getOffset(Range.getStart());
|
|
References.emplace_back(D, StartOffset, Range.getByteLength(), Ty);
|
|
return true;
|
|
}
|
|
|
|
bool visitSubscriptReference(ValueDecl *D, CharSourceRange Range,
|
|
ReferenceMetaData Data,
|
|
bool IsOpenBracket) override {
|
|
// Treat both open and close brackets equally
|
|
return visitDeclReference(D, Range, nullptr, nullptr, Type(), Data);
|
|
}
|
|
|
|
bool isLocal(Decl *D) const {
|
|
return isa<ParamDecl>(D) || D->getDeclContext()->getLocalContext();
|
|
}
|
|
|
|
unsigned getOffset(SourceLoc Loc) const {
|
|
return SM.getLocOffsetInBuffer(Loc, BufferID);
|
|
}
|
|
|
|
TextRange getTextRange(SourceRange R) const {
|
|
unsigned Start = getOffset(R.Start);
|
|
unsigned End = getOffset(R.End);
|
|
return TextRange{ Start, End-Start };
|
|
}
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
static bool getSourceTextInfo(CompilerInstance &CI,
|
|
SourceTextInfo &Info) {
|
|
SourceManager &SM = CI.getSourceMgr();
|
|
unsigned BufID = CI.getInputBufferIDs().back();
|
|
|
|
SourceDocASTWalker Walker(SM, BufID);
|
|
Walker.walk(*CI.getMainModule());
|
|
|
|
CharSourceRange FullRange = SM.getRangeForBuffer(BufID);
|
|
Info.Text = SM.extractText(FullRange);
|
|
Info.TopEntities = std::move(Walker.TopEntities);
|
|
Info.References = std::move(Walker.References);
|
|
return false;
|
|
}
|
|
|
|
static bool reportSourceDocInfo(CompilerInvocation Invocation,
|
|
llvm::MemoryBuffer *InputBuf,
|
|
DocInfoConsumer &Consumer) {
|
|
CompilerInstance CI;
|
|
// Display diagnostics to stderr.
|
|
PrintingDiagnosticConsumer PrintDiags;
|
|
CI.addDiagnosticConsumer(&PrintDiags);
|
|
|
|
EditorDiagConsumer DiagConsumer;
|
|
CI.addDiagnosticConsumer(&DiagConsumer);
|
|
Invocation.getFrontendOptions().InputsAndOutputs.addInput(
|
|
InputFile(InputBuf->getBufferIdentifier(), false, InputBuf));
|
|
if (CI.setup(Invocation))
|
|
return true;
|
|
DiagConsumer.setInputBufferIDs(CI.getInputBufferIDs());
|
|
|
|
ASTContext &Ctx = CI.getASTContext();
|
|
CloseClangModuleFiles scopedCloseFiles(*Ctx.getClangModuleLoader());
|
|
CI.performSema();
|
|
|
|
// Setup a typechecker for protocol conformance resolving.
|
|
(void)createTypeChecker(Ctx);
|
|
|
|
SourceTextInfo SourceInfo;
|
|
if (getSourceTextInfo(CI, SourceInfo))
|
|
return true;
|
|
addParameterEntities(CI, SourceInfo);
|
|
|
|
reportDocEntities(Ctx, SourceInfo.TopEntities, Consumer);
|
|
reportSourceAnnotations(SourceInfo, CI, Consumer);
|
|
for (auto &Diag : DiagConsumer.getDiagnosticsForBuffer(
|
|
CI.getInputBufferIDs().back()))
|
|
Consumer.handleDiagnostic(Diag);
|
|
|
|
return false;
|
|
}
|
|
|
|
class RequestRefactoringEditConsumer::Implementation {
|
|
public:
|
|
CategorizedEditsReceiver Receiver;
|
|
std::vector<Edit> AllEdits;
|
|
std::vector<std::pair<unsigned, unsigned>> StartEnds;
|
|
std::vector<UIdent> UIds;
|
|
SmallString<64> ErrBuffer;
|
|
llvm::raw_svector_ostream OS;
|
|
PrintingDiagnosticConsumer DiagConsumer;
|
|
Implementation(CategorizedEditsReceiver Receiver):
|
|
Receiver(std::move(Receiver)), OS(ErrBuffer), DiagConsumer(OS) {}
|
|
~Implementation() {
|
|
if (DiagConsumer.didErrorOccur()) {
|
|
Receiver(RequestResult<ArrayRef<CategorizedEdits>>::fromError(OS.str()));
|
|
return;
|
|
}
|
|
assert(UIds.size() == StartEnds.size());
|
|
std::vector<CategorizedEdits> Results;
|
|
for (unsigned I = 0, N = UIds.size(); I < N; I ++) {
|
|
auto Pair = StartEnds[I];
|
|
Results.push_back({UIds[I],
|
|
llvm::makeArrayRef(AllEdits.data() + Pair.first,
|
|
Pair.second - Pair.first)});
|
|
}
|
|
Receiver(RequestResult<ArrayRef<CategorizedEdits>>::fromResult(Results));
|
|
}
|
|
void accept(SourceManager &SM, RegionType RegionType,
|
|
ArrayRef<Replacement> Replacements) {
|
|
unsigned Start = AllEdits.size();
|
|
std::transform(Replacements.begin(), Replacements.end(),
|
|
std::back_inserter(AllEdits),
|
|
[&](const Replacement &R) -> Edit {
|
|
std::pair<unsigned, unsigned>
|
|
Start = SM.getLineAndColumn(R.Range.getStart()),
|
|
End = SM.getLineAndColumn(R.Range.getEnd());
|
|
SmallVector<NoteRegion, 4> SubRanges;
|
|
auto RawRanges = R.RegionsWorthNote;
|
|
std::transform(RawRanges.begin(), RawRanges.end(),
|
|
std::back_inserter(SubRanges),
|
|
[](swift::ide::NoteRegion R) -> SourceKit::NoteRegion {
|
|
return {
|
|
SwiftLangSupport::getUIDForRefactoringRangeKind(R.Kind),
|
|
R.StartLine, R.StartColumn, R.EndLine, R.EndColumn,
|
|
R.ArgIndex
|
|
}; });
|
|
return {Start.first, Start.second, End.first, End.second, R.Text,
|
|
std::move(SubRanges)};
|
|
});
|
|
unsigned End = AllEdits.size();
|
|
StartEnds.emplace_back(Start, End);
|
|
UIds.push_back(SwiftLangSupport::getUIDForRegionType(RegionType));
|
|
}
|
|
};
|
|
|
|
RequestRefactoringEditConsumer::
|
|
RequestRefactoringEditConsumer(CategorizedEditsReceiver Receiver) :
|
|
Impl(*new Implementation(Receiver)) {}
|
|
|
|
RequestRefactoringEditConsumer::
|
|
~RequestRefactoringEditConsumer() { delete &Impl; };
|
|
|
|
void RequestRefactoringEditConsumer::
|
|
accept(SourceManager &SM, RegionType RegionType,
|
|
ArrayRef<Replacement> Replacements) {
|
|
Impl.accept(SM, RegionType, Replacements);
|
|
}
|
|
|
|
void RequestRefactoringEditConsumer::handleDiagnostic(
|
|
SourceManager &SM, const DiagnosticInfo &Info) {
|
|
Impl.DiagConsumer.handleDiagnostic(SM, Info);
|
|
}
|
|
|
|
class RequestRenameRangeConsumer::Implementation {
|
|
CategorizedRenameRangesReceiver Receiver;
|
|
std::string ErrBuffer;
|
|
llvm::raw_string_ostream OS;
|
|
std::vector<CategorizedRenameRanges> CategorizedRanges;
|
|
|
|
public:
|
|
PrintingDiagnosticConsumer DiagConsumer;
|
|
|
|
public:
|
|
Implementation(CategorizedRenameRangesReceiver Receiver)
|
|
: Receiver(Receiver), OS(ErrBuffer), DiagConsumer(OS) {}
|
|
|
|
~Implementation() {
|
|
if (DiagConsumer.didErrorOccur()) {
|
|
Receiver(RequestResult<ArrayRef<CategorizedRenameRanges>>::fromError(OS.str()));
|
|
return;
|
|
}
|
|
Receiver(RequestResult<ArrayRef<CategorizedRenameRanges>>::fromResult(CategorizedRanges));
|
|
}
|
|
|
|
void accept(SourceManager &SM, RegionType RegionType,
|
|
ArrayRef<ide::RenameRangeDetail> Ranges) {
|
|
CategorizedRenameRanges Results;
|
|
Results.Category = SwiftLangSupport::getUIDForRegionType(RegionType);
|
|
for (const auto &R : Ranges) {
|
|
SourceKit::RenameRangeDetail Result;
|
|
std::tie(Result.StartLine, Result.StartColumn) =
|
|
SM.getLineAndColumn(R.Range.getStart());
|
|
std::tie(Result.EndLine, Result.EndColumn) =
|
|
SM.getLineAndColumn(R.Range.getEnd());
|
|
Result.ArgIndex = R.Index;
|
|
Result.Kind =
|
|
SwiftLangSupport::getUIDForRefactoringRangeKind(R.RangeKind);
|
|
Results.Ranges.push_back(std::move(Result));
|
|
}
|
|
CategorizedRanges.push_back(std::move(Results));
|
|
}
|
|
};
|
|
|
|
RequestRenameRangeConsumer::RequestRenameRangeConsumer(
|
|
CategorizedRenameRangesReceiver Receiver)
|
|
: Impl(*new Implementation(Receiver)) {}
|
|
RequestRenameRangeConsumer::~RequestRenameRangeConsumer() { delete &Impl; }
|
|
|
|
void RequestRenameRangeConsumer::accept(
|
|
SourceManager &SM, RegionType RegionType,
|
|
ArrayRef<ide::RenameRangeDetail> Ranges) {
|
|
Impl.accept(SM, RegionType, Ranges);
|
|
}
|
|
|
|
void RequestRenameRangeConsumer::handleDiagnostic(SourceManager &SM,
|
|
const DiagnosticInfo &Info) {
|
|
Impl.DiagConsumer.handleDiagnostic(SM, Info);
|
|
}
|
|
|
|
static NameUsage getNameUsage(RenameType Type) {
|
|
switch (Type) {
|
|
case RenameType::Definition:
|
|
return NameUsage::Definition;
|
|
case RenameType::Reference:
|
|
return NameUsage::Reference;
|
|
case RenameType::Call:
|
|
return NameUsage::Call;
|
|
case RenameType::Unknown:
|
|
return NameUsage::Unknown;
|
|
}
|
|
}
|
|
|
|
static std::vector<RenameLoc>
|
|
getSyntacticRenameLocs(ArrayRef<RenameLocations> RenameLocations);
|
|
|
|
void SwiftLangSupport::
|
|
syntacticRename(llvm::MemoryBuffer *InputBuf,
|
|
ArrayRef<RenameLocations> RenameLocations,
|
|
ArrayRef<const char*> Args,
|
|
CategorizedEditsReceiver Receiver) {
|
|
std::string Error;
|
|
CompilerInstance ParseCI;
|
|
PrintingDiagnosticConsumer PrintDiags;
|
|
ParseCI.addDiagnosticConsumer(&PrintDiags);
|
|
SourceFile *SF = getSyntacticSourceFile(InputBuf, Args, ParseCI, Error);
|
|
if (!SF) {
|
|
Receiver(RequestResult<ArrayRef<CategorizedEdits>>::fromError(Error));
|
|
return;
|
|
}
|
|
|
|
auto RenameLocs = getSyntacticRenameLocs(RenameLocations);
|
|
RequestRefactoringEditConsumer EditConsumer(Receiver);
|
|
swift::ide::syntacticRename(SF, RenameLocs, EditConsumer, EditConsumer);
|
|
}
|
|
|
|
void SwiftLangSupport::findRenameRanges(
|
|
llvm::MemoryBuffer *InputBuf, ArrayRef<RenameLocations> RenameLocations,
|
|
ArrayRef<const char *> Args, CategorizedRenameRangesReceiver Receiver) {
|
|
std::string Error;
|
|
CompilerInstance ParseCI;
|
|
PrintingDiagnosticConsumer PrintDiags;
|
|
ParseCI.addDiagnosticConsumer(&PrintDiags);
|
|
SourceFile *SF = getSyntacticSourceFile(InputBuf, Args, ParseCI, Error);
|
|
if (!SF) {
|
|
Receiver(RequestResult<ArrayRef<CategorizedRenameRanges>>::fromError(Error));
|
|
return;
|
|
}
|
|
|
|
auto RenameLocs = getSyntacticRenameLocs(RenameLocations);
|
|
RequestRenameRangeConsumer Consumer(Receiver);
|
|
swift::ide::findSyntacticRenameRanges(SF, RenameLocs, Consumer, Consumer);
|
|
}
|
|
|
|
void SwiftLangSupport::findLocalRenameRanges(
|
|
StringRef Filename, unsigned Line, unsigned Column, unsigned Length,
|
|
ArrayRef<const char *> Args, CategorizedRenameRangesReceiver Receiver) {
|
|
std::string Error;
|
|
SwiftInvocationRef Invok = ASTMgr->getInvocation(Args, Filename, Error);
|
|
if (!Invok) {
|
|
LOG_WARN_FUNC("failed to create an ASTInvocation: " << Error);
|
|
Receiver(RequestResult<ArrayRef<CategorizedRenameRanges>>::fromError(Error));
|
|
return;
|
|
}
|
|
|
|
struct LocalRenameRangeASTConsumer : public SwiftASTConsumer {
|
|
unsigned Line, Column, Length;
|
|
CategorizedRenameRangesReceiver Receiver;
|
|
|
|
LocalRenameRangeASTConsumer(unsigned Line, unsigned Column, unsigned Length,
|
|
CategorizedRenameRangesReceiver Receiver)
|
|
: Line(Line), Column(Column), Length(Length),
|
|
Receiver(std::move(Receiver)) {}
|
|
|
|
void handlePrimaryAST(ASTUnitRef AstUnit) override {
|
|
auto &SF = AstUnit->getPrimarySourceFile();
|
|
swift::ide::RangeConfig Range{*SF.getBufferID(), Line, Column, Length};
|
|
RequestRenameRangeConsumer Consumer(std::move(Receiver));
|
|
swift::ide::findLocalRenameRanges(&SF, Range, Consumer, Consumer);
|
|
}
|
|
|
|
void cancelled() override {
|
|
Receiver(RequestResult<ArrayRef<CategorizedRenameRanges>>::cancelled());
|
|
}
|
|
|
|
void failed(StringRef Error) override {
|
|
Receiver(RequestResult<ArrayRef<CategorizedRenameRanges>>::fromError(Error));
|
|
}
|
|
};
|
|
|
|
auto ASTConsumer = std::make_shared<LocalRenameRangeASTConsumer>(
|
|
Line, Column, Length, std::move(Receiver));
|
|
/// FIXME: When request cancellation is implemented and Xcode adopts it,
|
|
/// don't use 'OncePerASTToken'.
|
|
static const char OncePerASTToken = 0;
|
|
getASTManager()->processASTAsync(Invok, ASTConsumer, &OncePerASTToken,
|
|
llvm::vfs::getRealFileSystem());
|
|
}
|
|
|
|
SourceFile *SwiftLangSupport::getSyntacticSourceFile(
|
|
llvm::MemoryBuffer *InputBuf, ArrayRef<const char *> Args,
|
|
CompilerInstance &ParseCI, std::string &Error) {
|
|
CompilerInvocation Invocation;
|
|
|
|
bool Failed = getASTManager()->initCompilerInvocationNoInputs(
|
|
Invocation, Args, ParseCI.getDiags(), Error);
|
|
if (Failed) {
|
|
Error = "Compiler invocation init failed";
|
|
return nullptr;
|
|
}
|
|
Invocation.setInputKind(InputFileKind::Swift);
|
|
Invocation.getFrontendOptions().InputsAndOutputs.addInput(
|
|
InputFile(InputBuf->getBufferIdentifier(), false, InputBuf));
|
|
|
|
if (ParseCI.setup(Invocation)) {
|
|
Error = "Compiler invocation set up failed";
|
|
return nullptr;
|
|
}
|
|
ParseCI.performParseOnly(/*EvaluateConditionals*/true);
|
|
|
|
SourceFile *SF = nullptr;
|
|
unsigned BufferID = ParseCI.getInputBufferIDs().back();
|
|
for (auto Unit : ParseCI.getMainModule()->getFiles()) {
|
|
if (auto Current = dyn_cast<SourceFile>(Unit)) {
|
|
if (Current->getBufferID().getValue() == BufferID) {
|
|
SF = Current;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!SF)
|
|
Error = "Failed to determine SourceFile for input buffer";
|
|
return SF;
|
|
}
|
|
|
|
static std::vector<RenameLoc>
|
|
getSyntacticRenameLocs(ArrayRef<RenameLocations> RenameLocations) {
|
|
std::vector<RenameLoc> RenameLocs;
|
|
for(const auto &Locations: RenameLocations) {
|
|
for(const auto &Location: Locations.LineColumnLocs) {
|
|
RenameLocs.push_back({Location.Line, Location.Column,
|
|
getNameUsage(Location.Type), Locations.OldName, Locations.NewName,
|
|
Locations.IsFunctionLike, Locations.IsNonProtocolType});
|
|
}
|
|
}
|
|
return RenameLocs;
|
|
}
|
|
|
|
void SwiftLangSupport::getDocInfo(llvm::MemoryBuffer *InputBuf,
|
|
StringRef ModuleName,
|
|
ArrayRef<const char *> Args,
|
|
DocInfoConsumer &Consumer) {
|
|
CompilerInstance CI;
|
|
// Display diagnostics to stderr.
|
|
PrintingDiagnosticConsumer PrintDiags;
|
|
CI.addDiagnosticConsumer(&PrintDiags);
|
|
|
|
CompilerInvocation Invocation;
|
|
std::string Error;
|
|
bool Failed = getASTManager()->initCompilerInvocationNoInputs(
|
|
Invocation, Args, CI.getDiags(), Error, /*AllowInputs=*/false);
|
|
|
|
if (Failed) {
|
|
Consumer.failed(Error);
|
|
return;
|
|
}
|
|
|
|
Invocation.getClangImporterOptions().ImportForwardDeclarations = true;
|
|
|
|
if (!ModuleName.empty()) {
|
|
bool Error = reportModuleDocInfo(Invocation, ModuleName, Consumer);
|
|
if (Error)
|
|
Consumer.failed("Error occurred");
|
|
return;
|
|
}
|
|
|
|
Failed = reportSourceDocInfo(Invocation, InputBuf, Consumer);
|
|
if (Failed)
|
|
Consumer.failed("Error occurred");
|
|
}
|
|
|
|
void SwiftLangSupport::
|
|
findModuleGroups(StringRef ModuleName, ArrayRef<const char *> Args,
|
|
std::function<void(const RequestResult<ArrayRef<StringRef>> &)> Receiver) {
|
|
CompilerInvocation Invocation;
|
|
Invocation.getClangImporterOptions().ImportForwardDeclarations = true;
|
|
Invocation.getFrontendOptions().InputsAndOutputs.clearInputs();
|
|
|
|
CompilerInstance CI;
|
|
// Display diagnostics to stderr.
|
|
PrintingDiagnosticConsumer PrintDiags;
|
|
CI.addDiagnosticConsumer(&PrintDiags);
|
|
std::vector<StringRef> Groups;
|
|
std::string Error;
|
|
if (getASTManager()->initCompilerInvocationNoInputs(Invocation, Args,
|
|
CI.getDiags(), Error)) {
|
|
Receiver(RequestResult<ArrayRef<StringRef>>::fromError(Error));
|
|
return;
|
|
}
|
|
if (CI.setup(Invocation)) {
|
|
Error = "Compiler invocation set up fails.";
|
|
Receiver(RequestResult<ArrayRef<StringRef>>::fromError(Error));
|
|
return;
|
|
}
|
|
|
|
ASTContext &Ctx = CI.getASTContext();
|
|
// Setup a typechecker for protocol conformance resolving.
|
|
(void)createTypeChecker(Ctx);
|
|
|
|
// Load standard library so that Clang importer can use it.
|
|
auto *Stdlib = getModuleByFullName(Ctx, Ctx.StdlibModuleName);
|
|
if (!Stdlib) {
|
|
Error = "Cannot load stdlib.";
|
|
Receiver(RequestResult<ArrayRef<StringRef>>::fromError(Error));
|
|
return;
|
|
}
|
|
auto *M = getModuleByFullName(Ctx, ModuleName);
|
|
if (!M) {
|
|
Error = "Cannot find the module.";
|
|
Receiver(RequestResult<ArrayRef<StringRef>>::fromError(Error));
|
|
return;
|
|
}
|
|
std::vector<StringRef> Scratch;
|
|
Receiver(RequestResult<ArrayRef<StringRef>>::fromResult(
|
|
collectModuleGroups(M, Scratch)));
|
|
}
|