mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
2914 lines
99 KiB
C++
2914 lines
99 KiB
C++
#include "llvm/ADT/STLExtras.h"
|
|
#include "swift/AST/ASTMangler.h"
|
|
#include "swift/Basic/Defer.h"
|
|
#include "swift/Sema/IDETypeChecking.h"
|
|
#include "swift/SIL/SILDeclRef.h"
|
|
#include <swift/APIDigester/ModuleAnalyzerNodes.h>
|
|
#include <algorithm>
|
|
|
|
using namespace swift;
|
|
using namespace ide;
|
|
using namespace api;
|
|
|
|
namespace fs = llvm::sys::fs;
|
|
namespace path = llvm::sys::path;
|
|
|
|
namespace {
|
|
static PrintOptions getTypePrintOpts(CheckerOptions CheckerOpts) {
|
|
PrintOptions Opts;
|
|
Opts.SynthesizeSugarOnTypes = true;
|
|
if (!CheckerOpts.Migrator) {
|
|
// We should always print fully qualified type names for checking either
|
|
// API or ABI stability.
|
|
Opts.FullyQualifiedTypes = true;
|
|
}
|
|
return Opts;
|
|
}
|
|
} // End of anonymous namespace.
|
|
|
|
struct swift::ide::api::SDKNodeInitInfo {
|
|
SDKContext &Ctx;
|
|
DeclKind DKind;
|
|
AccessorKind AccKind;
|
|
SourceLoc Loc;
|
|
#define KEY_STRING(X, Y) StringRef X;
|
|
#include "swift/IDE/DigesterEnums.def"
|
|
#define KEY_BOOL(X, Y) bool X = false;
|
|
#include "swift/IDE/DigesterEnums.def"
|
|
#define KEY_UINT(X, Y) Optional<uint8_t> X;
|
|
#include "swift/IDE/DigesterEnums.def"
|
|
#define KEY_STRING_ARR(X, Y) std::vector<StringRef> X;
|
|
#include "swift/IDE/DigesterEnums.def"
|
|
|
|
swift::ReferenceOwnership ReferenceOwnership = ReferenceOwnership::Strong;
|
|
std::vector<DeclAttrKind> DeclAttrs;
|
|
std::vector<TypeAttrKind> TypeAttrs;
|
|
|
|
SDKNodeInitInfo(SDKContext &Ctx) : Ctx(Ctx) {}
|
|
SDKNodeInitInfo(SDKContext &Ctx, Decl *D);
|
|
SDKNodeInitInfo(SDKContext &Ctx, ValueDecl *VD);
|
|
SDKNodeInitInfo(SDKContext &Ctx, OperatorDecl *D);
|
|
SDKNodeInitInfo(SDKContext &Ctx, ImportDecl *ID);
|
|
SDKNodeInitInfo(SDKContext &Ctx, ProtocolConformance *Conform);
|
|
SDKNodeInitInfo(SDKContext &Ctx, Type Ty, TypeInitInfo Info = TypeInitInfo());
|
|
SDKNode* createSDKNode(SDKNodeKind Kind);
|
|
};
|
|
|
|
bool swift::ide::api::hasValidParentPtr(SDKNodeKind kind) {
|
|
switch(kind) {
|
|
case SDKNodeKind::Conformance:
|
|
case SDKNodeKind::DeclAccessor:
|
|
return false;
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
SDKContext::SDKContext(CheckerOptions Opts): Diags(SourceMgr), Opts(Opts) {}
|
|
|
|
DiagnosticEngine &SDKContext::getDiags(SourceLoc Loc) {
|
|
// If the location is invalid, we just use the locally created DiagEngine.
|
|
if (Loc.isInvalid())
|
|
return Diags;
|
|
// If the Loc is valid, it may belong to any of the SourceManagers owned by
|
|
// the ASTContexts we created, thus we should go through the ASTContxts to find
|
|
// the right DiagnosticEngine to use.
|
|
for (auto &CI: CIs) {
|
|
if (CI->getSourceMgr().isOwning(Loc))
|
|
return CI->getDiags();
|
|
}
|
|
llvm_unreachable("cannot find diagnostic engine to use");
|
|
}
|
|
|
|
void SDKContext::addDiagConsumer(DiagnosticConsumer &Consumer) {
|
|
// we may emit diagnostics via any of the diagnostic engine, so add the consumer
|
|
// to all of them.
|
|
Diags.addConsumer(Consumer);
|
|
for (auto &CI: CIs) {
|
|
CI->getDiags().addConsumer(Consumer);
|
|
}
|
|
}
|
|
|
|
void SDKNodeRoot::registerDescendant(SDKNode *D) {
|
|
// Operator doesn't have usr
|
|
if (isa<SDKNodeDeclOperator>(D))
|
|
return;
|
|
// Import doesn't have usr
|
|
if (isa<SDKNodeDeclImport>(D))
|
|
return;
|
|
if (auto DD = dyn_cast<SDKNodeDecl>(D)) {
|
|
assert(!DD->getUsr().empty());
|
|
DescendantDeclTable[DD->getUsr()].insert(DD);
|
|
}
|
|
}
|
|
|
|
SDKNode::SDKNode(SDKNodeInitInfo Info, SDKNodeKind Kind): Ctx(Info.Ctx),
|
|
Name(Info.Name), PrintedName(Info.PrintedName), TheKind(unsigned(Kind)) {}
|
|
|
|
SDKNodeRoot::SDKNodeRoot(SDKNodeInitInfo Info): SDKNode(Info, SDKNodeKind::Root),
|
|
ToolArgs(Info.ToolArgs),
|
|
JsonFormatVer(Info.JsonFormatVer.hasValue() ? *Info.JsonFormatVer : DIGESTER_JSON_DEFAULT_VERSION) {}
|
|
|
|
SDKNodeDecl::SDKNodeDecl(SDKNodeInitInfo Info, SDKNodeKind Kind)
|
|
: SDKNode(Info, Kind), DKind(Info.DKind), Usr(Info.Usr),
|
|
MangledName(Info.MangledName), Loc(Info.Loc),
|
|
Location(Info.Location), ModuleName(Info.ModuleName),
|
|
DeclAttributes(Info.DeclAttrs), IsImplicit(Info.IsImplicit),
|
|
IsStatic(Info.IsStatic), IsDeprecated(Info.IsDeprecated),
|
|
IsProtocolReq(Info.IsProtocolReq),
|
|
IsOverriding(Info.IsOverriding),
|
|
IsOpen(Info.IsOpen),
|
|
IsInternal(Info.IsInternal), IsABIPlaceholder(Info.IsABIPlaceholder),
|
|
IsFromExtension(Info.IsFromExtension),
|
|
ReferenceOwnership(uint8_t(Info.ReferenceOwnership)),
|
|
GenericSig(Info.GenericSig),
|
|
SugaredGenericSig(Info.SugaredGenericSig),
|
|
FixedBinaryOrder(Info.FixedBinaryOrder),
|
|
introVersions({Info.IntromacOS, Info.IntroiOS, Info.IntrotvOS,
|
|
Info.IntrowatchOS, Info.Introswift}),
|
|
ObjCName(Info.ObjCName) {}
|
|
|
|
SDKNodeType::SDKNodeType(SDKNodeInitInfo Info, SDKNodeKind Kind):
|
|
SDKNode(Info, Kind), TypeAttributes(Info.TypeAttrs),
|
|
HasDefaultArg(Info.HasDefaultArg),
|
|
ParamValueOwnership(Info.ParamValueOwnership) {}
|
|
|
|
SDKNodeTypeNominal::SDKNodeTypeNominal(SDKNodeInitInfo Info):
|
|
SDKNodeType(Info, SDKNodeKind::TypeNominal), USR(Info.Usr),
|
|
MangledName(Info.MangledName) {}
|
|
|
|
SDKNodeTypeFunc::SDKNodeTypeFunc(SDKNodeInitInfo Info):
|
|
SDKNodeType(Info, SDKNodeKind::TypeFunc) {}
|
|
|
|
SDKNodeTypeAlias::SDKNodeTypeAlias(SDKNodeInitInfo Info):
|
|
SDKNodeType(Info, SDKNodeKind::TypeAlias) {}
|
|
|
|
SDKNodeDeclType::SDKNodeDeclType(SDKNodeInitInfo Info):
|
|
SDKNodeDecl(Info, SDKNodeKind::DeclType), SuperclassUsr(Info.SuperclassUsr),
|
|
SuperclassNames(Info.SuperclassNames),
|
|
EnumRawTypeName(Info.EnumRawTypeName),
|
|
IsExternal(Info.IsExternal),
|
|
IsEnumExhaustive(Info.IsEnumExhaustive),
|
|
HasMissingDesignatedInitializers(Info.HasMissingDesignatedInitializers),
|
|
InheritsConvenienceInitializers(Info.InheritsConvenienceInitializers) {}
|
|
|
|
SDKNodeConformance::SDKNodeConformance(SDKNodeInitInfo Info):
|
|
SDKNode(Info, SDKNodeKind::Conformance),
|
|
Usr(Info.Usr), MangledName(Info.MangledName),
|
|
IsABIPlaceholder(Info.IsABIPlaceholder) {}
|
|
|
|
SDKNodeTypeWitness::SDKNodeTypeWitness(SDKNodeInitInfo Info):
|
|
SDKNode(Info, SDKNodeKind::TypeWitness) {}
|
|
|
|
SDKNodeDeclOperator::SDKNodeDeclOperator(SDKNodeInitInfo Info):
|
|
SDKNodeDecl(Info, SDKNodeKind::DeclOperator) {}
|
|
|
|
SDKNodeDeclTypeAlias::SDKNodeDeclTypeAlias(SDKNodeInitInfo Info):
|
|
SDKNodeDecl(Info, SDKNodeKind::DeclTypeAlias) {}
|
|
|
|
SDKNodeDeclVar::SDKNodeDeclVar(SDKNodeInitInfo Info):
|
|
SDKNodeDecl(Info, SDKNodeKind::DeclVar), IsLet(Info.IsLet),
|
|
HasStorage(Info.HasStorage) {}
|
|
|
|
SDKNodeDeclAbstractFunc::SDKNodeDeclAbstractFunc(SDKNodeInitInfo Info,
|
|
SDKNodeKind Kind): SDKNodeDecl(Info, Kind), IsThrowing(Info.IsThrowing),
|
|
ReqNewWitnessTableEntry(Info.ReqNewWitnessTableEntry),
|
|
SelfIndex(Info.SelfIndex) {}
|
|
|
|
SDKNodeDeclFunction::SDKNodeDeclFunction(SDKNodeInitInfo Info):
|
|
SDKNodeDeclAbstractFunc(Info, SDKNodeKind::DeclFunction),
|
|
FuncSelfKind(Info.FuncSelfKind) {}
|
|
|
|
SDKNodeDeclConstructor::SDKNodeDeclConstructor(SDKNodeInitInfo Info):
|
|
SDKNodeDeclAbstractFunc(Info, SDKNodeKind::DeclConstructor), InitKind(Info.InitKind) {}
|
|
|
|
SDKNodeDeclAccessor::SDKNodeDeclAccessor(SDKNodeInitInfo Info):
|
|
SDKNodeDeclAbstractFunc(Info, SDKNodeKind::DeclAccessor),
|
|
AccKind(Info.AccKind) {}
|
|
|
|
SDKNodeDeclImport::SDKNodeDeclImport(SDKNodeInitInfo Info):
|
|
SDKNodeDecl(Info, SDKNodeKind::DeclImport) {}
|
|
|
|
SDKNodeDeclAssociatedType::SDKNodeDeclAssociatedType(SDKNodeInitInfo Info):
|
|
SDKNodeDecl(Info, SDKNodeKind::DeclAssociatedType) {}
|
|
|
|
SDKNodeDeclSubscript::SDKNodeDeclSubscript(SDKNodeInitInfo Info):
|
|
SDKNodeDeclAbstractFunc(Info, SDKNodeKind::DeclSubscript),
|
|
HasStorage(Info.HasStorage) {}
|
|
|
|
StringRef SDKNodeDecl::getHeaderName() const {
|
|
if (Location.empty())
|
|
return StringRef();
|
|
return llvm::sys::path::filename(Location.split(":").first);
|
|
}
|
|
|
|
static SDKNodeDeclAccessor *getAccessorInternal(ArrayRef<SDKNode*> Accessors,
|
|
AccessorKind Kind) {
|
|
for (auto *AC: Accessors) {
|
|
if (cast<SDKNodeDeclAccessor>(AC)->getAccessorKind() == Kind) {
|
|
return cast<SDKNodeDeclAccessor>(AC);
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
SDKNodeDeclAccessor *SDKNodeDeclVar::getAccessor(AccessorKind Kind) const {
|
|
return getAccessorInternal(Accessors, Kind);
|
|
}
|
|
|
|
SDKNodeDeclAccessor *SDKNodeDeclSubscript::getAccessor(AccessorKind Kind) const {
|
|
return getAccessorInternal(Accessors, Kind);
|
|
}
|
|
|
|
SDKNodeType *SDKNodeDeclVar::getType() const {
|
|
return cast<SDKNodeType>(childAt(0));
|
|
}
|
|
|
|
NodePtr UpdatedNodesMap::findUpdateCounterpart(const SDKNode *Node) const {
|
|
assert(Node->isAnnotatedAs(NodeAnnotation::Updated) && "Not update operation.");
|
|
auto FoundPair = std::find_if(MapImpl.begin(), MapImpl.end(),
|
|
[&](std::pair<NodePtr, NodePtr> Pair) {
|
|
return Pair.second == Node || Pair.first == Node;
|
|
});
|
|
assert(FoundPair != MapImpl.end() && "Cannot find update counterpart.");
|
|
return Node == FoundPair->first ? FoundPair->second : FoundPair->first;
|
|
}
|
|
|
|
#define NODE_KIND_RANGE(ID, FIRST, LAST) \
|
|
bool SDKNode##ID::classof(const SDKNode *N) { \
|
|
return N->getKind() >= SDKNodeKind::FIRST && \
|
|
N->getKind() <= SDKNodeKind::LAST; \
|
|
}
|
|
#include "swift/IDE/DigesterEnums.def"
|
|
|
|
unsigned SDKNode::getChildIndex(const SDKNode* Child) const {
|
|
auto It = std::find(Children.begin(), Children.end(), Child);
|
|
assert(It != Children.end() && "cannot find the child");
|
|
return It - Children.begin();
|
|
}
|
|
|
|
SDKNode* SDKNode::getOnlyChild() const {
|
|
assert(Children.size() == 1 && "more that one child.");
|
|
return *Children.begin();
|
|
}
|
|
|
|
SDKNodeRoot *SDKNode::getRootNode() const {
|
|
for (auto *Root = const_cast<SDKNode*>(this); Root;) {
|
|
if (auto Result = dyn_cast<SDKNodeRoot>(Root))
|
|
return Result;
|
|
if (auto *Conf = dyn_cast<SDKNodeConformance>(Root)) {
|
|
Root = Conf->getNominalTypeDecl();
|
|
} else if (auto *Acc = dyn_cast<SDKNodeDeclAccessor>(Root)) {
|
|
Root = Acc->getStorage();
|
|
} else {
|
|
Root = Root->getParent();
|
|
}
|
|
}
|
|
llvm_unreachable("Unhandled SDKNodeKind in switch.");
|
|
}
|
|
|
|
uint8_t SDKNode::getJsonFormatVersion() const {
|
|
return getRootNode()->getJsonFormatVersion();
|
|
}
|
|
|
|
void SDKNode::addChild(SDKNode *Child) {
|
|
Child->Parent = this;
|
|
Children.push_back(Child);
|
|
if (auto *Root = dyn_cast<SDKNodeRoot>(this)) {
|
|
struct DeclCollector: public SDKNodeVisitor {
|
|
SDKNodeRoot &Root;
|
|
DeclCollector(SDKNodeRoot &Root): Root(Root) {}
|
|
void visit(NodePtr Node) override {
|
|
Root.registerDescendant(Node);
|
|
}
|
|
} Collector(*Root);
|
|
SDKNode::preorderVisit(Child, Collector);
|
|
}
|
|
}
|
|
|
|
NodePtr SDKNode::childAt(unsigned I) const {
|
|
assert(I < getChildrenCount());
|
|
return getChildren()[I];
|
|
}
|
|
|
|
void SDKNode::removeChild(NodePtr C) {
|
|
Children.erase(std::find(Children.begin(), Children.end(), C));
|
|
}
|
|
|
|
void SDKNode::annotate(NodeAnnotation Anno, StringRef Comment) {
|
|
assert(!Comment.empty());
|
|
if(isAnnotatedAs(Anno))
|
|
return;
|
|
annotate(Anno);
|
|
AnnotateComments[Anno] = Comment;
|
|
}
|
|
|
|
void SDKNode::removeAnnotate(NodeAnnotation Anno) {
|
|
assert(isAnnotatedAs(Anno));
|
|
Annotations.erase(Anno);
|
|
AnnotateComments.erase(Anno);
|
|
assert(!isAnnotatedAs(Anno));
|
|
assert(AnnotateComments.count(Anno) == 0);
|
|
}
|
|
|
|
StringRef SDKNode::getAnnotateComment(NodeAnnotation Anno) const {
|
|
return AnnotateComments.find(Anno)->second;
|
|
}
|
|
|
|
ArrayRef<NodeAnnotation> SDKNode::
|
|
getAnnotations(std::vector<NodeAnnotation> &Scratch) const {
|
|
for (auto Ann : Annotations)
|
|
Scratch.push_back(Ann);
|
|
return llvm::makeArrayRef(Scratch);
|
|
}
|
|
|
|
bool SDKNode::isAnnotatedAs(NodeAnnotation Anno) const {
|
|
return Annotations.find(Anno) != Annotations.end();;
|
|
}
|
|
|
|
void SDKNode::preorderVisit(NodePtr Root, SDKNodeVisitor &Visitor) {
|
|
Visitor.visit(Root);
|
|
Visitor.Ancestors.push_back(Root);
|
|
for (auto Child : Root->Children)
|
|
preorderVisit(Child, Visitor);
|
|
Visitor.Ancestors.pop_back();
|
|
}
|
|
|
|
void SDKNode::postorderVisit(NodePtr Root, SDKNodeVisitor &Visitor) {
|
|
Visitor.Ancestors.push_back(Root);
|
|
for (auto Child : Root->Children)
|
|
postorderVisit(Child, Visitor);
|
|
Visitor.Ancestors.pop_back();
|
|
Visitor.visit(Root);
|
|
}
|
|
|
|
SDKNodeVectorViewer::VectorIt
|
|
SDKNodeVectorViewer::getNext(VectorIt Start) {
|
|
for (auto It = Start; It != Collection.end(); ++ It)
|
|
if (Selector(*It))
|
|
return It;
|
|
return Collection.end();
|
|
}
|
|
|
|
SDKNodeVectorViewer::ViewerIterator&
|
|
SDKNodeVectorViewer::ViewerIterator::operator++() {
|
|
P = Viewer.getNext(P + 1);
|
|
return *this;
|
|
}
|
|
|
|
SDKNodeVectorViewer::ViewerIterator SDKNodeVectorViewer::begin() {
|
|
return ViewerIterator(*this, getNext(Collection.begin()));
|
|
}
|
|
|
|
SDKNodeVectorViewer::ViewerIterator SDKNodeVectorViewer::end() {
|
|
return ViewerIterator(*this, Collection.end());
|
|
}
|
|
|
|
KnownTypeKind SDKNodeType::getTypeKind() const {
|
|
#define KNOWN_TYPE(NAME) if (getName() == #NAME) return KnownTypeKind::NAME;
|
|
#include "swift/IDE/DigesterEnums.def"
|
|
return KnownTypeKind::Unknown;
|
|
}
|
|
|
|
ArrayRef<TypeAttrKind> SDKNodeType::getTypeAttributes() const {
|
|
return llvm::makeArrayRef(TypeAttributes.data(), TypeAttributes.size());
|
|
}
|
|
|
|
void SDKNodeType::addTypeAttribute(TypeAttrKind AttrKind) {
|
|
TypeAttributes.push_back(AttrKind);
|
|
}
|
|
|
|
bool SDKNodeType::hasTypeAttribute(TypeAttrKind DAKind) const {
|
|
return std::find(TypeAttributes.begin(), TypeAttributes.end(), DAKind) !=
|
|
TypeAttributes.end();
|
|
}
|
|
|
|
StringRef SDKNodeType::getParamValueOwnership() const {
|
|
return ParamValueOwnership.empty() ? "Default" : ParamValueOwnership;
|
|
}
|
|
|
|
StringRef SDKNodeType::getTypeRoleDescription() const {
|
|
assert(isTopLevelType());
|
|
auto P = getParent();
|
|
switch(P->getKind()) {
|
|
case SDKNodeKind::Root:
|
|
case SDKNodeKind::TypeNominal:
|
|
case SDKNodeKind::TypeFunc:
|
|
case SDKNodeKind::TypeAlias:
|
|
case SDKNodeKind::DeclType:
|
|
case SDKNodeKind::DeclOperator:
|
|
case SDKNodeKind::Conformance:
|
|
case SDKNodeKind::DeclImport:
|
|
llvm_unreachable("Type Parent is wrong");
|
|
case SDKNodeKind::DeclFunction:
|
|
case SDKNodeKind::DeclConstructor:
|
|
case SDKNodeKind::DeclAccessor:
|
|
case SDKNodeKind::DeclSubscript:
|
|
return SDKNodeDeclAbstractFunc::getTypeRoleDescription(Ctx,
|
|
P->getChildIndex(this));
|
|
case SDKNodeKind::DeclVar:
|
|
return "declared";
|
|
case SDKNodeKind::DeclTypeAlias:
|
|
return "underlying";
|
|
case SDKNodeKind::DeclAssociatedType:
|
|
return "default";
|
|
case SDKNodeKind::TypeWitness:
|
|
return "type witness type";
|
|
}
|
|
llvm_unreachable("Unhandled SDKNodeKind in switch");
|
|
}
|
|
|
|
SDKNode *SDKNodeRoot::getInstance(SDKContext &Ctx) {
|
|
SDKNodeInitInfo Info(Ctx);
|
|
Info.Name = Ctx.buffer("TopLevel");
|
|
Info.PrintedName = Ctx.buffer("TopLevel");
|
|
llvm::transform(Ctx.getOpts().ToolArgs, std::back_inserter(Info.ToolArgs),
|
|
[&](std::string s) { return Ctx.buffer(s); });
|
|
Info.JsonFormatVer = DIGESTER_JSON_VERSION;
|
|
return Info.createSDKNode(SDKNodeKind::Root);
|
|
}
|
|
|
|
StringRef SDKNodeDecl::getScreenInfo() const {
|
|
auto ModuleName = getModuleName();
|
|
auto HeaderName = getHeaderName();
|
|
auto &Ctx = getSDKContext();
|
|
llvm::SmallString<64> SS;
|
|
llvm::raw_svector_ostream OS(SS);
|
|
if (Ctx.getOpts().CompilerStyle) {
|
|
// Compiler style we don't need source info
|
|
OS << (Ctx.checkingABI() ? "ABI breakage" : "API breakage");
|
|
} else {
|
|
// Print more source info.
|
|
if (Ctx.getOpts().PrintModule)
|
|
OS << ModuleName;
|
|
if (!HeaderName.empty())
|
|
OS << "(" << HeaderName << ")";
|
|
}
|
|
if (!OS.str().empty())
|
|
OS << ": ";
|
|
bool IsExtension = false;
|
|
if (auto *TD = dyn_cast<SDKNodeDeclType>(this)) {
|
|
IsExtension = TD->isExternal();
|
|
}
|
|
|
|
// There is no particular reasons why we don't use lower-cased keyword names
|
|
// in non-CompilerStyle mode. This is to be backward compatible so clients
|
|
// don't need to update existing known breakages.
|
|
OS << getDeclKindStr(IsExtension? DeclKind::Extension : getDeclKind(),
|
|
getSDKContext().getOpts().CompilerStyle);
|
|
OS << " " << getFullyQualifiedName();
|
|
return Ctx.buffer(OS.str());
|
|
}
|
|
|
|
void SDKNodeDecl::printFullyQualifiedName(llvm::raw_ostream &OS) const {
|
|
if (auto *ACC = dyn_cast<SDKNodeDeclAccessor>(this)) {
|
|
ACC->getStorage()->printFullyQualifiedName(OS);
|
|
OS << "." << getPrintedName();
|
|
return;
|
|
}
|
|
std::vector<NodePtr> Parent;
|
|
for (auto *P = getParent(); isa<SDKNodeDecl>(P); P = P->getParent())
|
|
Parent.push_back(P);
|
|
for (auto It = Parent.rbegin(); It != Parent.rend(); ++ It)
|
|
OS << (*It)->getPrintedName() << ".";
|
|
OS << getPrintedName();
|
|
}
|
|
|
|
StringRef SDKNodeDecl::getFullyQualifiedName() const {
|
|
llvm::SmallString<32> Buffer;
|
|
llvm::raw_svector_ostream OS(Buffer);
|
|
printFullyQualifiedName(OS);
|
|
return getSDKContext().buffer(OS.str());
|
|
}
|
|
|
|
bool SDKNodeDecl::isNonOptionalProtocolRequirement() const {
|
|
return isProtocolRequirement() && !hasDeclAttribute(DAK_Optional);
|
|
}
|
|
|
|
bool SDKNodeDecl::hasDeclAttribute(DeclAttrKind DAKind) const {
|
|
return std::find(DeclAttributes.begin(), DeclAttributes.end(), DAKind) !=
|
|
DeclAttributes.end();
|
|
}
|
|
|
|
ArrayRef<DeclAttrKind> SDKNodeDecl::getDeclAttributes() const {
|
|
return llvm::makeArrayRef(DeclAttributes.data(), DeclAttributes.size());
|
|
}
|
|
|
|
bool SDKNodeDecl::hasAttributeChange(const SDKNodeDecl &Another) const {
|
|
std::set<DeclAttrKind> Left(getDeclAttributes().begin(),
|
|
getDeclAttributes().end());
|
|
std::set<DeclAttrKind> Right(Another.getDeclAttributes().begin(),
|
|
Another.getDeclAttributes().end());
|
|
return Left != Right;
|
|
}
|
|
|
|
bool SDKNodeType::hasAttributeChange(const SDKNodeType &Another) const {
|
|
std::set<TypeAttrKind> Left(getTypeAttributes().begin(),
|
|
getTypeAttributes().end());
|
|
std::set<TypeAttrKind> Right(Another.getTypeAttributes().begin(),
|
|
Another.getTypeAttributes().end());
|
|
return Left != Right;
|
|
}
|
|
|
|
SDKNodeDecl *SDKNodeType::getClosestParentDecl() const {
|
|
auto *Result = getParent();
|
|
for (; !isa<SDKNodeDecl>(Result); Result = Result->getParent());
|
|
return Result->getAs<SDKNodeDecl>();
|
|
}
|
|
|
|
void SDKNodeDeclType::addConformance(SDKNode *Conf) {
|
|
cast<SDKNodeConformance>(Conf)->TypeDecl = this;
|
|
Conformances.push_back(Conf);
|
|
}
|
|
|
|
void SDKNodeDeclSubscript::addAccessor(SDKNode *AC) {
|
|
cast<SDKNodeDeclAccessor>(AC)->Owner = this;
|
|
Accessors.push_back(AC);
|
|
}
|
|
|
|
void SDKNodeDeclVar::addAccessor(SDKNode *AC) {
|
|
cast<SDKNodeDeclAccessor>(AC)->Owner = this;
|
|
Accessors.push_back(AC);
|
|
}
|
|
|
|
SDKNodeType *SDKNodeTypeWitness::getUnderlyingType() const {
|
|
return getOnlyChild()->getAs<SDKNodeType>();
|
|
}
|
|
|
|
StringRef SDKNodeTypeWitness::getWitnessedTypeName() const {
|
|
return Ctx.buffer((llvm::Twine(getParent()->getAs<SDKNodeConformance>()->
|
|
getName()) + "." + getName()).str());
|
|
}
|
|
|
|
Optional<SDKNodeDeclType*> SDKNodeDeclType::getSuperclass() const {
|
|
if (SuperclassUsr.empty())
|
|
return None;
|
|
auto Descendants = getRootNode()->getDescendantsByUsr(SuperclassUsr);
|
|
if (!Descendants.empty()) {
|
|
return Descendants.front()->getAs<SDKNodeDeclType>();
|
|
}
|
|
return None;
|
|
}
|
|
|
|
/// Finding the node through all children, including the inherited ones,
|
|
/// whose printed name matches with the given name.
|
|
Optional<SDKNodeDecl*>
|
|
SDKNodeDeclType::lookupChildByPrintedName(StringRef Name) const {
|
|
for (auto C : getChildren()) {
|
|
if (C->getPrintedName() == Name)
|
|
return C->getAs<SDKNodeDecl>();
|
|
}
|
|
// Finding from the inheritance chain.
|
|
if (auto Super = getSuperclass()) {
|
|
return (*Super)->lookupChildByPrintedName(Name);
|
|
}
|
|
return None;
|
|
}
|
|
|
|
SDKNodeType *SDKNodeDeclType::getRawValueType() const {
|
|
if (isConformingTo(KnownProtocolKind::RawRepresentable)) {
|
|
if (auto RV = lookupChildByPrintedName("rawValue")) {
|
|
return (*(*RV)->getChildBegin())->getAs<SDKNodeType>();
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool SDKNodeDeclType::isConformingTo(swift::ide::api::KnownProtocolKind Kind) const {
|
|
switch (Kind) {
|
|
#define KNOWN_PROTOCOL(NAME) \
|
|
case KnownProtocolKind::NAME: \
|
|
return std::find_if(Conformances.begin(), Conformances.end(), \
|
|
[](SDKNode *Conf) { return Conf->getName() == #NAME; }) != \
|
|
Conformances.end();
|
|
#include "swift/IDE/DigesterEnums.def"
|
|
}
|
|
llvm_unreachable("covered switch");
|
|
}
|
|
|
|
StringRef SDKNodeDeclAbstractFunc::getTypeRoleDescription(SDKContext &Ctx,
|
|
unsigned Index) {
|
|
if (Index == 0) {
|
|
return Ctx.buffer("return");
|
|
} else {
|
|
llvm::SmallString<4> Buffer;
|
|
Buffer += "parameter ";
|
|
Buffer += std::to_string(Index - 1);
|
|
return Ctx.buffer(Buffer.str());
|
|
}
|
|
}
|
|
|
|
#define NODE_KIND(X, NAME) \
|
|
bool SDKNode##X::classof(const SDKNode *N) { \
|
|
return N->getKind() == SDKNodeKind::X; \
|
|
}
|
|
#include "swift/IDE/DigesterEnums.def"
|
|
|
|
static Optional<KeyKind> parseKeyKind(StringRef Content) {
|
|
return llvm::StringSwitch<Optional<KeyKind>>(Content)
|
|
#define KEY(NAME) .Case(#NAME, KeyKind::KK_##NAME)
|
|
#include "swift/IDE/DigesterEnums.def"
|
|
.Default(None);
|
|
}
|
|
|
|
static StringRef getKeyContent(SDKContext &Ctx, KeyKind Kind) {
|
|
switch (Kind) {
|
|
#define KEY(NAME) case KeyKind::KK_##NAME: return Ctx.buffer(#NAME);
|
|
#include "swift/IDE/DigesterEnums.def"
|
|
}
|
|
llvm_unreachable("Unhandled KeyKind in switch.");
|
|
}
|
|
|
|
SDKNode* SDKNode::constructSDKNode(SDKContext &Ctx,
|
|
llvm::yaml::MappingNode *Node) {
|
|
static auto GetScalarString = [&](llvm::yaml::Node *N) -> StringRef {
|
|
SmallString<64> Buffer;
|
|
return Ctx.buffer(cast<llvm::yaml::ScalarNode>(N)->getValue(Buffer));
|
|
};
|
|
|
|
static auto getAsInt = [&](llvm::yaml::Node *N) -> int {
|
|
return std::stoi(cast<llvm::yaml::ScalarNode>(N)->getRawValue().str());
|
|
};
|
|
static auto getAsBool = [&](llvm::yaml::Node *N) -> bool {
|
|
auto txt = cast<llvm::yaml::ScalarNode>(N)->getRawValue();
|
|
assert(txt.startswith("false") || txt.startswith("true"));
|
|
return txt.startswith("true");
|
|
};
|
|
SDKNodeKind Kind;
|
|
SDKNodeInitInfo Info(Ctx);
|
|
NodeVector Children;
|
|
NodeVector Conformances;
|
|
NodeVector Accessors;
|
|
SDKNode *Result = nullptr;
|
|
|
|
for (auto &Pair : *Node) {
|
|
auto keyString = GetScalarString(Pair.getKey());
|
|
if (auto keyKind = parseKeyKind(keyString)) {
|
|
switch(*keyKind) {
|
|
case KeyKind::KK_kind:
|
|
if (auto parsedKind = parseSDKNodeKind(GetScalarString(Pair.getValue()))) {
|
|
Kind = *parsedKind;
|
|
} else {
|
|
Ctx.diagnose(Pair.getValue(), diag::sdk_node_unrecognized_node_kind,
|
|
GetScalarString(Pair.getValue()));
|
|
}
|
|
break;
|
|
#define KEY_UINT(X, Y) \
|
|
case KeyKind::KK_##Y: Info.X = getAsInt(Pair.getValue()); break;
|
|
#include "swift/IDE/DigesterEnums.def"
|
|
#define KEY_STRING(X, Y) \
|
|
case KeyKind::KK_##Y: Info.X = GetScalarString(Pair.getValue()); break;
|
|
#include "swift/IDE/DigesterEnums.def"
|
|
#define KEY_BOOL(X, Y) case KeyKind::KK_##Y: Info.X = getAsBool(Pair.getValue()); break;
|
|
#include "swift/IDE/DigesterEnums.def"
|
|
case KeyKind::KK_children:
|
|
for (auto &Mapping : *cast<llvm::yaml::SequenceNode>(Pair.getValue())) {
|
|
Children.push_back(constructSDKNode(Ctx,
|
|
cast<llvm::yaml::MappingNode>(&Mapping)));
|
|
}
|
|
break;
|
|
case KeyKind::KK_conformances:
|
|
for (auto &Mapping : *cast<llvm::yaml::SequenceNode>(Pair.getValue())) {
|
|
Conformances.push_back(constructSDKNode(Ctx,
|
|
cast<llvm::yaml::MappingNode>(&Mapping)));
|
|
}
|
|
break;
|
|
#define KEY_STRING_ARR(X, Y) \
|
|
case KeyKind::KK_##Y: \
|
|
assert(Info.X.empty()); \
|
|
for (auto &Name : *cast<llvm::yaml::SequenceNode>(Pair.getValue())) { \
|
|
Info.X.push_back(GetScalarString(&Name)); \
|
|
} \
|
|
break;
|
|
#include "swift/IDE/DigesterEnums.def"
|
|
case KeyKind::KK_ownership:
|
|
Info.ReferenceOwnership =
|
|
swift::ReferenceOwnership(getAsInt(Pair.getValue()));
|
|
assert(Info.ReferenceOwnership != swift::ReferenceOwnership::Strong &&
|
|
"Strong is implied.");
|
|
break;
|
|
|
|
case KeyKind::KK_typeAttributes: {
|
|
auto *Seq = cast<llvm::yaml::SequenceNode>(Pair.getValue());
|
|
std::transform(Seq->begin(), Seq->end(),
|
|
std::back_inserter(Info.TypeAttrs),
|
|
[&](llvm::yaml::Node &N) {
|
|
auto Result = llvm::StringSwitch<TypeAttrKind>(GetScalarString(&N))
|
|
#define TYPE_ATTR(X) .Case(#X, TypeAttrKind::TAK_##X)
|
|
#include "swift/AST/Attr.def"
|
|
.Default(TypeAttrKind::TAK_Count);
|
|
if (Result == TAK_Count)
|
|
Ctx.diagnose(&N, diag::sdk_node_unrecognized_type_attr_kind,
|
|
GetScalarString(&N));
|
|
return Result;
|
|
});
|
|
break;
|
|
}
|
|
case KeyKind::KK_declAttributes: {
|
|
auto *Seq = cast<llvm::yaml::SequenceNode>(Pair.getValue());
|
|
std::transform(Seq->begin(), Seq->end(), std::back_inserter(Info.DeclAttrs),
|
|
[&](llvm::yaml::Node &N) {
|
|
auto Result = llvm::StringSwitch<DeclAttrKind>(GetScalarString(&N))
|
|
#define DECL_ATTR(_, NAME, ...) .Case(#NAME, DeclAttrKind::DAK_##NAME)
|
|
#include "swift/AST/Attr.def"
|
|
.Default(DeclAttrKind::DAK_Count);
|
|
if (Result == DAK_Count)
|
|
Ctx.diagnose(&N, diag::sdk_node_unrecognized_decl_attr_kind,
|
|
GetScalarString(&N));
|
|
return Result;
|
|
});
|
|
break;
|
|
}
|
|
case KeyKind::KK_accessors: {
|
|
for (auto &Mapping : *cast<llvm::yaml::SequenceNode>(Pair.getValue())) {
|
|
Accessors.push_back(constructSDKNode(Ctx,
|
|
cast<llvm::yaml::MappingNode>(&Mapping)));
|
|
}
|
|
break;
|
|
}
|
|
case KeyKind::KK_accessorKind: {
|
|
AccessorKind unknownKind = (AccessorKind)((uint8_t)(AccessorKind::Last) + 1);
|
|
Info.AccKind = llvm::StringSwitch<AccessorKind>(
|
|
GetScalarString(Pair.getValue()))
|
|
#define ACCESSOR(ID)
|
|
#define SINGLETON_ACCESSOR(ID, KEYWORD) .Case(#KEYWORD, AccessorKind::ID)
|
|
#include "swift/AST/AccessorKinds.def"
|
|
.Default(unknownKind);
|
|
if (Info.AccKind == unknownKind) {
|
|
Ctx.diagnose(Pair.getValue(), diag::sdk_node_unrecognized_accessor_kind,
|
|
GetScalarString(Pair.getValue()));
|
|
}
|
|
break;
|
|
}
|
|
case KeyKind::KK_declKind: {
|
|
auto dKind = llvm::StringSwitch<Optional<DeclKind>>(
|
|
GetScalarString(Pair.getValue()))
|
|
#define DECL(X, PARENT) .Case(#X, DeclKind::X)
|
|
#include "swift/AST/DeclNodes.def"
|
|
.Default(None);
|
|
if (dKind)
|
|
Info.DKind = *dKind;
|
|
else
|
|
Ctx.diagnose(Pair.getValue(), diag::sdk_node_unrecognized_decl_kind,
|
|
GetScalarString(Pair.getValue()));
|
|
break;
|
|
}
|
|
}
|
|
} else if (keyString == ABIRootKey) {
|
|
Result = constructSDKNode(Ctx,
|
|
cast<llvm::yaml::MappingNode>(Pair.getValue()));
|
|
} else if (keyString == ConstValuesKey) {
|
|
// We don't need to consume the const values from the compiler-side
|
|
Pair.skip();
|
|
} else {
|
|
Ctx.diagnose(Pair.getKey(), diag::sdk_node_unrecognized_key,
|
|
keyString);
|
|
Pair.skip();
|
|
}
|
|
};
|
|
if (Result)
|
|
return Result;
|
|
Result = Info.createSDKNode(Kind);
|
|
for (auto C : Children) {
|
|
Result->addChild(C);
|
|
}
|
|
for (auto *Conf: Conformances) {
|
|
cast<SDKNodeDeclType>(Result)->addConformance(Conf);
|
|
}
|
|
for (auto *Acc: Accessors) {
|
|
if (auto *SD = dyn_cast<SDKNodeDeclSubscript>(Result)) {
|
|
SD->addAccessor(Acc);
|
|
} else if (auto *VD = dyn_cast<SDKNodeDeclVar>(Result)) {
|
|
VD->addAccessor(Acc);
|
|
}
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
bool SDKNode::hasSameChildren(const SDKNode &Other) const {
|
|
if (Children.size() != Other.Children.size())
|
|
return false;
|
|
for (unsigned I = 0; I < Children.size(); ++ I) {
|
|
if (*Children[I] != *Other.Children[I])
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void swift::ide::api::nodeSetDifference(ArrayRef<SDKNode*> Left,
|
|
ArrayRef<SDKNode*> Right,
|
|
NodeVector &LeftMinusRight,
|
|
NodeVector &RightMinusLeft) {
|
|
llvm::SmallPtrSet<NodePtr, 16> LeftToRemove;
|
|
llvm::SmallPtrSet<NodePtr, 16> RightToRemove;
|
|
for (auto LC: Left) {
|
|
for (auto RC: Right) {
|
|
if (!RightToRemove.count(RC) && *LC == *RC) {
|
|
LeftToRemove.insert(LC);
|
|
RightToRemove.insert(RC);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
std::for_each(Left.begin(), Left.end(), [&] (SDKNode *N) {
|
|
if (!LeftToRemove.count(N))
|
|
LeftMinusRight.push_back(N);
|
|
});
|
|
std::for_each(Right.begin(), Right.end(), [&] (SDKNode *N) {
|
|
if (!RightToRemove.count(N))
|
|
RightMinusLeft.push_back(N);
|
|
});
|
|
}
|
|
|
|
|
|
static bool hasSameContents(ArrayRef<SDKNode*> Left,
|
|
ArrayRef<SDKNode*> Right) {
|
|
NodeVector LeftMinusRight, RightMinusLeft;
|
|
nodeSetDifference(Left, Right, LeftMinusRight, RightMinusLeft);
|
|
return LeftMinusRight.empty() && RightMinusLeft.empty();
|
|
}
|
|
|
|
static bool hasSameParameterFlags(const SDKNodeType *Left, const SDKNodeType *Right) {
|
|
if (Left->hasDefaultArgument() != Right->hasDefaultArgument())
|
|
return false;
|
|
if (Left->getParamValueOwnership() != Right->getParamValueOwnership())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
// Return whether a decl has been moved in/out to an extension
|
|
static Optional<bool> isFromExtensionChanged(const SDKNode &L, const SDKNode &R) {
|
|
assert(L.getKind() == R.getKind());
|
|
// Version 8 starts to include whether a decl is from an extension.
|
|
if (L.getJsonFormatVersion() + R.getJsonFormatVersion() < 2 * 8) {
|
|
return llvm::None;
|
|
}
|
|
auto *Left = dyn_cast<SDKNodeDecl>(&L);
|
|
auto *Right = dyn_cast<SDKNodeDecl>(&R);
|
|
if (!Left) {
|
|
return llvm::None;
|
|
}
|
|
if (Left->isFromExtension() == Right->isFromExtension()) {
|
|
return llvm::None;
|
|
} else {
|
|
return Right->isFromExtension();
|
|
}
|
|
}
|
|
|
|
static bool isSDKNodeEqual(SDKContext &Ctx, const SDKNode &L, const SDKNode &R) {
|
|
auto *LeftAlias = dyn_cast<SDKNodeTypeAlias>(&L);
|
|
auto *RightAlias = dyn_cast<SDKNodeTypeAlias>(&R);
|
|
if (LeftAlias || RightAlias) {
|
|
auto Left = L.getAs<SDKNodeType>();
|
|
auto Right = R.getAs<SDKNodeType>();
|
|
|
|
// First compare the parameter attributes.
|
|
if (!hasSameParameterFlags(Left, Right))
|
|
return false;
|
|
|
|
// Comparing the underlying types if any of the inputs are alias.
|
|
Left = LeftAlias ? LeftAlias->getUnderlyingType() : Left;
|
|
Right = RightAlias ? RightAlias->getUnderlyingType() : Right;
|
|
return *Left == *Right;
|
|
}
|
|
|
|
if (L.getKind() != R.getKind())
|
|
return false;
|
|
|
|
switch(L.getKind()) {
|
|
case SDKNodeKind::TypeAlias:
|
|
llvm_unreachable("Should be handled above.");
|
|
case SDKNodeKind::TypeNominal:
|
|
case SDKNodeKind::TypeFunc: {
|
|
auto Left = L.getAs<SDKNodeType>();
|
|
auto Right = R.getAs<SDKNodeType>();
|
|
if (Left->hasAttributeChange(*Right))
|
|
return false;
|
|
if (!hasSameParameterFlags(Left, Right))
|
|
return false;
|
|
if (Left->getPrintedName() == Right->getPrintedName())
|
|
return true;
|
|
if (Ctx.checkingABI()) {
|
|
// For abi checking where we don't have sugar types at all, the printed
|
|
// name difference is enough to indicate these two types differ.
|
|
return false;
|
|
} else {
|
|
return Left->getName() == Right->getName() &&
|
|
Left->hasSameChildren(*Right);
|
|
}
|
|
}
|
|
|
|
case SDKNodeKind::DeclFunction: {
|
|
auto Left = L.getAs<SDKNodeDeclFunction>();
|
|
auto Right = R.getAs<SDKNodeDeclFunction>();
|
|
if (Left->getSelfAccessKind() != Right->getSelfAccessKind())
|
|
return false;
|
|
LLVM_FALLTHROUGH;
|
|
}
|
|
case SDKNodeKind::DeclConstructor: {
|
|
auto Left = L.getAs<SDKNodeDeclAbstractFunc>();
|
|
auto Right = R.getAs<SDKNodeDeclAbstractFunc>();
|
|
if (Left->isThrowing() ^ Right->isThrowing())
|
|
return false;
|
|
if (Left->reqNewWitnessTableEntry() != Right->reqNewWitnessTableEntry())
|
|
return false;
|
|
LLVM_FALLTHROUGH;
|
|
}
|
|
case SDKNodeKind::DeclAccessor: {
|
|
if (auto *LA = dyn_cast<SDKNodeDeclAccessor>(&L)) {
|
|
if (auto *RA = dyn_cast<SDKNodeDeclAccessor>(&R)) {
|
|
if (LA->getAccessorKind() != RA->getAccessorKind())
|
|
return false;
|
|
}
|
|
}
|
|
LLVM_FALLTHROUGH;
|
|
}
|
|
case SDKNodeKind::DeclVar: {
|
|
if (auto *LV = dyn_cast<SDKNodeDeclVar>(&L)) {
|
|
if (auto *RV = dyn_cast<SDKNodeDeclVar>(&R)) {
|
|
if (Ctx.checkingABI()) {
|
|
if (LV->hasStorage() != RV->hasStorage())
|
|
return false;
|
|
}
|
|
if (!hasSameContents(LV->getAllAccessors(), RV->getAllAccessors()))
|
|
return false;
|
|
}
|
|
}
|
|
LLVM_FALLTHROUGH;
|
|
}
|
|
case SDKNodeKind::DeclType: {
|
|
auto *Left = dyn_cast<SDKNodeDeclType>(&L);
|
|
auto *Right = dyn_cast<SDKNodeDeclType>(&R);
|
|
if (Left && Right) {
|
|
if (!hasSameContents(Left->getConformances(), Right->getConformances())) {
|
|
return false;
|
|
}
|
|
if (Left->getSuperClassName() != Right->getSuperClassName()) {
|
|
return false;
|
|
}
|
|
if (Left->getDeclKind() != Right->getDeclKind()) {
|
|
return false;
|
|
}
|
|
}
|
|
LLVM_FALLTHROUGH;
|
|
}
|
|
case SDKNodeKind::DeclAssociatedType:
|
|
case SDKNodeKind::DeclSubscript: {
|
|
if (auto *Left = dyn_cast<SDKNodeDeclSubscript>(&L)) {
|
|
if (auto *Right = dyn_cast<SDKNodeDeclSubscript>(&R)) {
|
|
if (!hasSameContents(Left->getAllAccessors(), Right->getAllAccessors()))
|
|
return false;
|
|
}
|
|
}
|
|
LLVM_FALLTHROUGH;
|
|
}
|
|
case SDKNodeKind::DeclOperator:
|
|
case SDKNodeKind::DeclTypeAlias: {
|
|
auto Left = L.getAs<SDKNodeDecl>();
|
|
auto Right = R.getAs<SDKNodeDecl>();
|
|
if (Left->isStatic() ^ Right->isStatic())
|
|
return false;
|
|
if (Left->getReferenceOwnership() != Right->getReferenceOwnership())
|
|
return false;
|
|
if (Left->hasAttributeChange(*Right))
|
|
return false;
|
|
if (Left->getGenericSignature() != Right->getGenericSignature())
|
|
return false;
|
|
if (Left->isOpen() != Right->isOpen())
|
|
return false;
|
|
if (Left->isInternal() != Right->isInternal())
|
|
return false;
|
|
if (Left->getObjCName() != Right->getObjCName())
|
|
return false;
|
|
if (Left->hasFixedBinaryOrder() != Right->hasFixedBinaryOrder())
|
|
return false;
|
|
if (Left->hasFixedBinaryOrder()) {
|
|
if (Left->getFixedBinaryOrder() != Right->getFixedBinaryOrder())
|
|
return false;
|
|
}
|
|
if (Left->getUsr() != Right->getUsr())
|
|
return false;
|
|
LLVM_FALLTHROUGH;
|
|
}
|
|
case SDKNodeKind::Conformance:
|
|
case SDKNodeKind::TypeWitness:
|
|
case SDKNodeKind::DeclImport:
|
|
case SDKNodeKind::Root: {
|
|
if (isFromExtensionChanged(L, R))
|
|
return false;
|
|
return L.getPrintedName() == R.getPrintedName() &&
|
|
L.hasSameChildren(R);
|
|
}
|
|
}
|
|
|
|
llvm_unreachable("Unhandled SDKNodeKind in switch.");
|
|
}
|
|
|
|
bool SDKContext::isEqual(const SDKNode &Left, const SDKNode &Right) {
|
|
return isSDKNodeEqual(*this, Left, Right);
|
|
}
|
|
|
|
AccessLevel SDKContext::getAccessLevel(const ValueDecl *VD) const {
|
|
return checkingABI() ? VD->getEffectiveAccess() : VD->getFormalAccess();
|
|
}
|
|
|
|
bool SDKNode::operator==(const SDKNode &Other) const {
|
|
return Ctx.isEqual(*this, Other);
|
|
}
|
|
|
|
// The pretty printer of a tree of SDKNode
|
|
class SDKNodeDumpVisitor : public SDKNodeVisitor {
|
|
void dumpSpace(int Num) {
|
|
while (Num != 0) {
|
|
llvm::outs() << "\t";
|
|
Num --;
|
|
}
|
|
}
|
|
void visit(NodePtr Node) override {
|
|
dumpSpace(depth());
|
|
llvm::outs() << "[" << Node->getKind() << "]" << Node->getName() << "\n";
|
|
}
|
|
public:
|
|
SDKNodeDumpVisitor() {};
|
|
};
|
|
|
|
static StringRef getPrintedName(SDKContext &Ctx, Type Ty,
|
|
bool IsImplicitlyUnwrappedOptional = false) {
|
|
std::string S;
|
|
llvm::raw_string_ostream OS(S);
|
|
PrintOptions PO = getTypePrintOpts(Ctx.getOpts());
|
|
PO.SkipAttributes = true;
|
|
if (IsImplicitlyUnwrappedOptional)
|
|
PO.PrintOptionalAsImplicitlyUnwrapped = true;
|
|
|
|
Ty.print(OS, PO);
|
|
return Ctx.buffer(OS.str());
|
|
}
|
|
|
|
static StringRef getTypeName(SDKContext &Ctx, Type Ty,
|
|
bool IsImplicitlyUnwrappedOptional) {
|
|
if (Ty->getKind() == TypeKind::Paren) {
|
|
return Ctx.buffer("Paren");
|
|
}
|
|
if (Ty->isVoid()) {
|
|
return Ctx.buffer("Void");
|
|
}
|
|
if (auto *NAT = dyn_cast<TypeAliasType>(Ty.getPointer())) {
|
|
return NAT->getDecl()->getNameStr();
|
|
}
|
|
|
|
if (auto existential = Ty->getAs<ExistentialType>()) {
|
|
return getTypeName(Ctx, existential->getConstraintType(),
|
|
IsImplicitlyUnwrappedOptional);
|
|
}
|
|
|
|
if (Ty->getAnyNominal()) {
|
|
if (IsImplicitlyUnwrappedOptional) {
|
|
assert(Ty->getOptionalObjectType());
|
|
return StringRef("ImplicitlyUnwrappedOptional");
|
|
}
|
|
return Ty->getAnyNominal()->getNameStr();
|
|
}
|
|
#define TYPE(id, parent) \
|
|
if (Ty->getKind() == TypeKind::id) { \
|
|
return Ctx.buffer(#id); \
|
|
}
|
|
#include "swift/AST/TypeNodes.def"
|
|
llvm_unreachable("Unhandled type name.");
|
|
}
|
|
|
|
static StringRef calculateUsr(SDKContext &Ctx, ValueDecl *VD) {
|
|
llvm::SmallString<64> SS;
|
|
llvm::raw_svector_ostream OS(SS);
|
|
if (!ide::printValueDeclUSR(VD, OS)) {
|
|
return Ctx.buffer(SS.str());
|
|
}
|
|
return StringRef();
|
|
}
|
|
|
|
static StringRef calculateMangledName(SDKContext &Ctx, ValueDecl *VD) {
|
|
if (isFromClang(VD)) {
|
|
// Don't mangle clang symbols.
|
|
return StringRef();
|
|
}
|
|
if (auto *attr = VD->getAttrs().getAttribute<SILGenNameAttr>()) {
|
|
return Ctx.buffer(attr->Name);
|
|
}
|
|
Mangle::ASTMangler NewMangler;
|
|
return Ctx.buffer(NewMangler.mangleAnyDecl(VD, true));
|
|
}
|
|
|
|
static StringRef calculateLocation(SDKContext &SDKCtx, Decl *D) {
|
|
if (SDKCtx.getOpts().AvoidLocation)
|
|
return StringRef();
|
|
auto &Ctx = D->getASTContext();
|
|
auto &Importer = static_cast<ClangImporter &>(*Ctx.getClangModuleLoader());
|
|
|
|
clang::SourceManager &SM = Importer.getClangPreprocessor().getSourceManager();
|
|
if (ClangNode CN = D->getClangNode()) {
|
|
clang::SourceLocation Loc = CN.getLocation();
|
|
Loc = SM.getFileLoc(Loc);
|
|
if (Loc.isValid())
|
|
return SDKCtx.buffer(Loc.printToString(SM));
|
|
}
|
|
|
|
return StringRef();
|
|
}
|
|
|
|
static bool isFunctionTypeNoEscape(Type Ty) {
|
|
if (auto *AFT = Ty->getAs<AnyFunctionType>()) {
|
|
return AFT->getExtInfo().isNoEscape();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static StringRef getSimpleName(ValueDecl *VD) {
|
|
if (VD->hasName()) {
|
|
auto name = VD->getBaseName();
|
|
switch (name.getKind()) {
|
|
case DeclBaseName::Kind::Subscript:
|
|
return "subscript";
|
|
case DeclBaseName::Kind::Constructor:
|
|
return "init";
|
|
case DeclBaseName::Kind::Destructor:
|
|
return "deinit";
|
|
case DeclBaseName::Kind::Normal:
|
|
return llvm::StringSwitch<StringRef>(name.getIdentifier().str())
|
|
.Case("subscript", "`subscript`")
|
|
.Case("init", "`init`")
|
|
.Case("deinit", "`deinit`")
|
|
.Default(name.getIdentifier().str());
|
|
}
|
|
}
|
|
if (auto *AD = dyn_cast<AccessorDecl>(VD)) {
|
|
switch(AD->getAccessorKind()) {
|
|
#define ACCESSOR(ID) \
|
|
case AccessorKind::ID: return #ID;
|
|
#include "swift/AST/AccessorKinds.def"
|
|
}
|
|
}
|
|
return "_";
|
|
}
|
|
|
|
static StringRef getPrintedName(SDKContext &Ctx, ValueDecl *VD) {
|
|
if (isa<AbstractFunctionDecl>(VD) || isa<SubscriptDecl>(VD)) {
|
|
llvm::SmallString<32> Result;
|
|
Result.append(getSimpleName(VD));
|
|
Result.append("(");
|
|
for (auto Arg : VD->getName().getArgumentNames()) {
|
|
Result.append(Arg.empty() ? "_" : Arg.str());
|
|
Result.append(":");
|
|
}
|
|
Result.append(")");
|
|
return Ctx.buffer(Result.str());
|
|
}
|
|
return getSimpleName(VD);
|
|
}
|
|
|
|
static bool isFuncThrowing(ValueDecl *VD) {
|
|
if (auto AF = dyn_cast<AbstractFunctionDecl>(VD)) {
|
|
return AF->hasThrows();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static Optional<uint8_t> getSelfIndex(ValueDecl *VD) {
|
|
if (auto AF = dyn_cast<AbstractFunctionDecl>(VD)) {
|
|
if (AF->isImportAsInstanceMember())
|
|
return AF->getSelfIndex();
|
|
}
|
|
return None;
|
|
}
|
|
|
|
static ReferenceOwnership getReferenceOwnership(ValueDecl *VD) {
|
|
if (auto OA = VD->getAttrs().getAttribute<ReferenceOwnershipAttr>()) {
|
|
return OA->get();
|
|
}
|
|
return ReferenceOwnership::Strong;
|
|
}
|
|
|
|
// Get a requirement with all types canonicalized.
|
|
Requirement getCanonicalRequirement(Requirement &Req) {
|
|
auto kind = Req.getKind();
|
|
if (kind == RequirementKind::Layout) {
|
|
return Requirement(kind, Req.getFirstType()->getCanonicalType(),
|
|
Req.getLayoutConstraint());
|
|
} else {
|
|
return Requirement(kind, Req.getFirstType()->getCanonicalType(),
|
|
Req.getSecondType()->getCanonicalType());
|
|
}
|
|
}
|
|
|
|
static
|
|
StringRef printGenericSignature(SDKContext &Ctx, ArrayRef<Requirement> AllReqs,
|
|
bool Canonical) {
|
|
llvm::SmallString<32> Result;
|
|
llvm::raw_svector_ostream OS(Result);
|
|
if (AllReqs.empty())
|
|
return StringRef();
|
|
OS << "<";
|
|
bool First = true;
|
|
PrintOptions Opts = getTypePrintOpts(Ctx.getOpts());
|
|
for (auto Req: AllReqs) {
|
|
if (!First) {
|
|
OS << ", ";
|
|
} else {
|
|
First = false;
|
|
}
|
|
if (Canonical)
|
|
getCanonicalRequirement(Req).print(OS, Opts);
|
|
else
|
|
Req.print(OS, Opts);
|
|
}
|
|
OS << ">";
|
|
return Ctx.buffer(OS.str());
|
|
}
|
|
|
|
static StringRef printGenericSignature(SDKContext &Ctx, Decl *D, bool Canonical) {
|
|
llvm::SmallString<32> Result;
|
|
llvm::raw_svector_ostream OS(Result);
|
|
if (auto *PD = dyn_cast<ProtocolDecl>(D)) {
|
|
return printGenericSignature(Ctx, PD->getRequirementSignature().getRequirements(),
|
|
Canonical);
|
|
}
|
|
PrintOptions Opts = getTypePrintOpts(Ctx.getOpts());
|
|
if (auto *GC = D->getAsGenericContext()) {
|
|
if (auto Sig = GC->getGenericSignature()) {
|
|
if (Canonical)
|
|
Sig.getCanonicalSignature()->print(OS, Opts);
|
|
else
|
|
Sig->print(OS, Opts);
|
|
return Ctx.buffer(OS.str());
|
|
}
|
|
}
|
|
return StringRef();
|
|
}
|
|
|
|
static
|
|
StringRef printGenericSignature(SDKContext &Ctx, ProtocolConformance *Conf, bool Canonical) {
|
|
return printGenericSignature(Ctx, Conf->getConditionalRequirements(), Canonical);
|
|
}
|
|
|
|
static Optional<uint8_t> getSimilarMemberCount(NominalTypeDecl *NTD,
|
|
ValueDecl *VD,
|
|
llvm::function_ref<bool(Decl*)> Check) {
|
|
if (!Check(VD))
|
|
return None;
|
|
auto Members = NTD->getMembers();
|
|
auto End = std::find(Members.begin(), Members.end(), VD);
|
|
assert(End != Members.end());
|
|
return std::count_if(Members.begin(), End, Check);
|
|
}
|
|
|
|
Optional<uint8_t> SDKContext::getFixedBinaryOrder(ValueDecl *VD) const {
|
|
// We don't need fixed binary order when checking API stability.
|
|
if (!checkingABI())
|
|
return None;
|
|
auto *NTD = dyn_cast_or_null<NominalTypeDecl>(VD->getDeclContext()->
|
|
getAsDecl());
|
|
|
|
if (!NTD || isa<ProtocolDecl>(NTD) || NTD->isResilient())
|
|
return None;
|
|
|
|
// The relative order of stored properties matters for non-resilient type.
|
|
auto isStored = [](Decl *M) {
|
|
if (auto *STD = dyn_cast<AbstractStorageDecl>(M)) {
|
|
return STD->hasStorage() && !STD->isStatic();
|
|
}
|
|
return false;
|
|
};
|
|
|
|
switch (NTD->getKind()) {
|
|
case DeclKind::Enum: {
|
|
return getSimilarMemberCount(NTD, VD, [](Decl *M) {
|
|
return isa<EnumElementDecl>(M);
|
|
});
|
|
}
|
|
case DeclKind::Class:
|
|
case DeclKind::Struct: {
|
|
return getSimilarMemberCount(NTD, VD, isStored);
|
|
}
|
|
default:
|
|
llvm_unreachable("bad nominal type kind.");
|
|
}
|
|
}
|
|
|
|
// check for if it has @available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
|
|
static bool isABIPlaceHolder(Decl *D) {
|
|
llvm::SmallSet<PlatformKind, 4> Platforms;
|
|
for (auto *ATT: D->getAttrs()) {
|
|
if (auto *AVA = dyn_cast<AvailableAttr>(ATT)) {
|
|
if (AVA->Platform != PlatformKind::none && AVA->Introduced &&
|
|
AVA->Introduced->getMajor() == 9999) {
|
|
Platforms.insert(AVA->Platform);
|
|
}
|
|
}
|
|
}
|
|
return Platforms.size() == 4;
|
|
}
|
|
|
|
static bool isABIPlaceholderRecursive(Decl *D) {
|
|
for (auto *CD = D; CD; CD = CD->getDeclContext()->getAsDecl()) {
|
|
if (isABIPlaceHolder(CD))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
StringRef SDKContext::getPlatformIntroVersion(Decl *D, PlatformKind Kind) {
|
|
if (!D)
|
|
return StringRef();
|
|
for (auto *ATT: D->getAttrs()) {
|
|
if (auto *AVA = dyn_cast<AvailableAttr>(ATT)) {
|
|
if (AVA->Platform == Kind && AVA->Introduced) {
|
|
return buffer(AVA->Introduced->getAsString());
|
|
}
|
|
}
|
|
}
|
|
return StringRef();
|
|
}
|
|
|
|
StringRef SDKContext::getLanguageIntroVersion(Decl *D) {
|
|
if (!D)
|
|
return StringRef();
|
|
for (auto *ATT: D->getAttrs()) {
|
|
if (auto *AVA = dyn_cast<AvailableAttr>(ATT)) {
|
|
if (AVA->isLanguageVersionSpecific() && AVA->Introduced) {
|
|
return buffer(AVA->Introduced->getAsString());
|
|
}
|
|
}
|
|
}
|
|
return getLanguageIntroVersion(D->getDeclContext()->getAsDecl());
|
|
}
|
|
|
|
StringRef SDKContext::getObjcName(Decl *D) {
|
|
if (auto *OC = D->getAttrs().getAttribute<ObjCAttr>()) {
|
|
if (OC->getName().hasValue()) {
|
|
SmallString<32> Buffer;
|
|
return buffer(OC->getName()->getString(Buffer));
|
|
}
|
|
}
|
|
return StringRef();
|
|
}
|
|
|
|
SDKNodeInitInfo::SDKNodeInitInfo(SDKContext &Ctx, Type Ty, TypeInitInfo Info) :
|
|
Ctx(Ctx), Name(getTypeName(Ctx, Ty, Info.IsImplicitlyUnwrappedOptional)),
|
|
PrintedName(getPrintedName(Ctx, Ty, Info.IsImplicitlyUnwrappedOptional)),
|
|
ParamValueOwnership(Info.ValueOwnership),
|
|
HasDefaultArg(Info.hasDefaultArgument) {
|
|
if (isFunctionTypeNoEscape(Ty))
|
|
TypeAttrs.push_back(TypeAttrKind::TAK_noescape);
|
|
// If this is a nominal type, get its Usr.
|
|
if (auto *ND = Ty->getAnyNominal()) {
|
|
Usr = calculateUsr(Ctx, ND);
|
|
MangledName = calculateMangledName(Ctx, ND);
|
|
}
|
|
}
|
|
|
|
static std::vector<DeclAttrKind> collectDeclAttributes(Decl *D) {
|
|
std::vector<DeclAttrKind> Results;
|
|
for (auto *Attr: D->getAttrs())
|
|
Results.push_back(Attr->getKind());
|
|
if (auto *VD = dyn_cast<ValueDecl>(D)) {
|
|
#define HANDLE(COND, KIND_NAME) \
|
|
if (VD->COND && !llvm::is_contained(Results, DeclAttrKind::KIND_NAME)) \
|
|
Results.emplace_back(DeclAttrKind::KIND_NAME);
|
|
// These attributes may be semantically applicable to the current decl but absent from
|
|
// the actual AST. Populating them to the nodes ensure we don't have false positives.
|
|
HANDLE(isObjC(), DAK_ObjC)
|
|
HANDLE(isFinal(), DAK_Final)
|
|
HANDLE(isDynamic(), DAK_Dynamic)
|
|
#undef HANDLE
|
|
}
|
|
return Results;
|
|
}
|
|
|
|
CtorInitializerKind SDKNodeDeclConstructor::getInitKind() const {
|
|
#define CASE(KIND) if (InitKind == #KIND) return CtorInitializerKind::KIND;
|
|
CASE(Designated)
|
|
CASE(Convenience)
|
|
CASE(ConvenienceFactory)
|
|
CASE(Factory)
|
|
#undef CASE
|
|
llvm_unreachable("unhandled init kind");
|
|
}
|
|
|
|
StringRef SDKContext::getInitKind(Decl *D) {
|
|
if (auto *CD = dyn_cast<ConstructorDecl>(D)) {
|
|
switch(CD->getInitKind()) {
|
|
#define CASE(KIND) case CtorInitializerKind::KIND: return #KIND;
|
|
CASE(Designated)
|
|
CASE(Convenience)
|
|
CASE(ConvenienceFactory)
|
|
CASE(Factory)
|
|
#undef CASE
|
|
}
|
|
}
|
|
return StringRef();
|
|
}
|
|
|
|
static bool isDeclaredInExtension(Decl *D) {
|
|
if (auto *DC = D->getDeclContext()->getAsDecl()) {
|
|
return isa<ExtensionDecl>(DC);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
SDKNodeInitInfo::SDKNodeInitInfo(SDKContext &Ctx, Decl *D):
|
|
Ctx(Ctx), DKind(D->getKind()), Loc(D->getLoc()),
|
|
Location(calculateLocation(Ctx, D)),
|
|
ModuleName(D->getModuleContext()->getName().str()),
|
|
GenericSig(printGenericSignature(Ctx, D, /*Canonical*/Ctx.checkingABI())),
|
|
SugaredGenericSig(Ctx.checkingABI()?
|
|
printGenericSignature(Ctx, D, /*Canonical*/false):
|
|
StringRef()),
|
|
IntromacOS(Ctx.getPlatformIntroVersion(D, PlatformKind::macOS)),
|
|
IntroiOS(Ctx.getPlatformIntroVersion(D, PlatformKind::iOS)),
|
|
IntrotvOS(Ctx.getPlatformIntroVersion(D, PlatformKind::tvOS)),
|
|
IntrowatchOS(Ctx.getPlatformIntroVersion(D, PlatformKind::watchOS)),
|
|
Introswift(Ctx.getLanguageIntroVersion(D)),
|
|
ObjCName(Ctx.getObjcName(D)),
|
|
InitKind(Ctx.getInitKind(D)),
|
|
IsImplicit(D->isImplicit()),
|
|
IsDeprecated(D->getAttrs().getDeprecated(D->getASTContext())),
|
|
IsABIPlaceholder(isABIPlaceholderRecursive(D)),
|
|
IsFromExtension(isDeclaredInExtension(D)),
|
|
DeclAttrs(collectDeclAttributes(D)) {}
|
|
|
|
SDKNodeInitInfo::SDKNodeInitInfo(SDKContext &Ctx, OperatorDecl *OD):
|
|
SDKNodeInitInfo(Ctx, cast<Decl>(OD)) {
|
|
Name = OD->getName().str();
|
|
PrintedName = OD->getName().str();
|
|
}
|
|
|
|
SDKNodeInitInfo::SDKNodeInitInfo(SDKContext &Ctx, ImportDecl *ID):
|
|
SDKNodeInitInfo(Ctx, cast<Decl>(ID)) {
|
|
std::string content;
|
|
llvm::raw_string_ostream OS(content);
|
|
ID->getModulePath().print(OS);
|
|
Name = PrintedName = Ctx.buffer(content);
|
|
}
|
|
|
|
SDKNodeInitInfo::SDKNodeInitInfo(SDKContext &Ctx, ProtocolConformance *Conform):
|
|
SDKNodeInitInfo(Ctx, Conform->getProtocol()) {
|
|
// The conformance can be conditional. The generic signature keeps track of
|
|
// the requirements.
|
|
GenericSig = printGenericSignature(Ctx, Conform, Ctx.checkingABI());
|
|
SugaredGenericSig = Ctx.checkingABI() ?
|
|
printGenericSignature(Ctx, Conform, false): StringRef();
|
|
// Whether this conformance is ABI placeholder depends on the decl context
|
|
// of this conformance.
|
|
IsABIPlaceholder = isABIPlaceholderRecursive(Conform->getDeclContext()->
|
|
getAsDecl());
|
|
}
|
|
|
|
static bool isProtocolRequirement(ValueDecl *VD) {
|
|
if (isa<ProtocolDecl>(VD->getDeclContext()) && VD->isProtocolRequirement())
|
|
return true;
|
|
// If the VD is an accessor of the property declaration that is a protocol
|
|
// requirement, we consider this accessor as a protocol requirement too.
|
|
if (auto *AD = dyn_cast<AccessorDecl>(VD)) {
|
|
if (auto *ST = AD->getStorage()) {
|
|
return isProtocolRequirement(ST);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool requireWitnessTableEntry(ValueDecl *VD) {
|
|
if (auto *FD = dyn_cast<AbstractFunctionDecl>(VD)) {
|
|
return SILDeclRef::requiresNewWitnessTableEntry(FD);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
SDKNodeInitInfo::SDKNodeInitInfo(SDKContext &Ctx, ValueDecl *VD)
|
|
: SDKNodeInitInfo(Ctx, cast<Decl>(VD)) {
|
|
Name = getSimpleName(VD);
|
|
PrintedName = getPrintedName(Ctx, VD);
|
|
Usr = calculateUsr(Ctx, VD);
|
|
MangledName = calculateMangledName(Ctx, VD);
|
|
IsThrowing = isFuncThrowing(VD);
|
|
IsStatic = VD->isStatic();
|
|
IsOverriding = VD->getOverriddenDecl();
|
|
IsProtocolReq = isProtocolRequirement(VD);
|
|
IsOpen = Ctx.getAccessLevel(VD) == AccessLevel::Open;
|
|
IsInternal = Ctx.getAccessLevel(VD) < AccessLevel::Public;
|
|
SelfIndex = getSelfIndex(VD);
|
|
FixedBinaryOrder = Ctx.getFixedBinaryOrder(VD);
|
|
ReferenceOwnership = getReferenceOwnership(VD);
|
|
ReqNewWitnessTableEntry = IsProtocolReq && requireWitnessTableEntry(VD);
|
|
|
|
// Calculate usr for its super class.
|
|
if (auto *CD = dyn_cast_or_null<ClassDecl>(VD)) {
|
|
if (auto *Super = CD->getSuperclassDecl()) {
|
|
SuperclassUsr = calculateUsr(Ctx, Super);
|
|
for (auto T = CD->getSuperclass(); T; T = T->getSuperclass()) {
|
|
SuperclassNames.push_back(getPrintedName(Ctx, T->getCanonicalType()));
|
|
}
|
|
}
|
|
HasMissingDesignatedInitializers = CD->hasMissingDesignatedInitializers();
|
|
InheritsConvenienceInitializers = CD->inheritsSuperclassInitializers();
|
|
}
|
|
|
|
if (auto *FD = dyn_cast<FuncDecl>(VD)) {
|
|
switch(FD->getSelfAccessKind()) {
|
|
case SelfAccessKind::Mutating:
|
|
FuncSelfKind = "Mutating";
|
|
break;
|
|
case SelfAccessKind::Consuming:
|
|
// FIXME: Stay consistent with earlier digests that had underscores here.
|
|
FuncSelfKind = "__Consuming";
|
|
break;
|
|
case SelfAccessKind::NonMutating:
|
|
FuncSelfKind = "NonMutating";
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Get enum raw type name if this is an enum.
|
|
if (auto *ED = dyn_cast<EnumDecl>(VD)) {
|
|
IsEnumExhaustive = ED->isFormallyExhaustive(nullptr);
|
|
if (auto RT = ED->getRawType()) {
|
|
if (auto *D = RT->getNominalOrBoundGenericNominal()) {
|
|
EnumRawTypeName = D->getName().str();
|
|
}
|
|
}
|
|
}
|
|
if (auto *VAD = dyn_cast<VarDecl>(VD)) {
|
|
IsLet = VAD->isLet();
|
|
}
|
|
if (auto *VAR = dyn_cast<AbstractStorageDecl>(VD)) {
|
|
HasStorage = VAR->hasStorage();
|
|
}
|
|
if (auto *ACC = dyn_cast<AccessorDecl>(VD)) {
|
|
AccKind = ACC->getAccessorKind();
|
|
}
|
|
}
|
|
|
|
SDKNode *SDKNodeInitInfo::createSDKNode(SDKNodeKind Kind) {
|
|
switch(Kind) {
|
|
#define NODE_KIND(X, NAME) \
|
|
case SDKNodeKind::X: \
|
|
return static_cast<SDKNode*>(new (Ctx.allocator().Allocate<SDKNode##X>()) \
|
|
SDKNode##X(*this)); \
|
|
break;
|
|
#include "swift/IDE/DigesterEnums.def"
|
|
}
|
|
llvm_unreachable("covered switch");
|
|
}
|
|
|
|
// Recursively construct a node that represents a type, for instance,
|
|
// representing the return value type of a function decl.
|
|
SDKNode *swift::ide::api::
|
|
SwiftDeclCollector::constructTypeNode(Type T, TypeInitInfo Info) {
|
|
if (Ctx.checkingABI()) {
|
|
T = T->getCanonicalType();
|
|
|
|
if (T->hasOpaqueArchetype()) {
|
|
// When the type contains an opaque result type and we're in the ABI mode,
|
|
// we should substitute the opaque result type to its underlying type.
|
|
// Notice this only works if the opaque result type is from an inlinable
|
|
// function where the function body is present in the swift module file,
|
|
// thus allowing us to know the concrete type.
|
|
ReplaceOpaqueTypesWithUnderlyingTypes replacer(
|
|
/*inContext=*/nullptr, ResilienceExpansion::Maximal,
|
|
/*isWholeModuleContext=*/false);
|
|
T = T.subst(replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes)
|
|
->getCanonicalType();
|
|
}
|
|
}
|
|
|
|
if (auto NAT = dyn_cast<TypeAliasType>(T.getPointer())) {
|
|
SDKNode* Root = SDKNodeInitInfo(Ctx, T, Info).createSDKNode(SDKNodeKind::TypeAlias);
|
|
Root->addChild(constructTypeNode(NAT->getSinglyDesugaredType()));
|
|
return Root;
|
|
}
|
|
|
|
if (auto Fun = T->getAs<AnyFunctionType>()) {
|
|
SDKNode* Root = SDKNodeInitInfo(Ctx, T, Info).createSDKNode(SDKNodeKind::TypeFunc);
|
|
|
|
// Still, return type first
|
|
Root->addChild(constructTypeNode(Fun->getResult()));
|
|
|
|
auto Input = AnyFunctionType::composeTuple(Fun->getASTContext(),
|
|
Fun->getParams());
|
|
Root->addChild(constructTypeNode(Input));
|
|
return Root;
|
|
}
|
|
|
|
SDKNode* Root = SDKNodeInitInfo(Ctx, T, Info).createSDKNode(SDKNodeKind::TypeNominal);
|
|
|
|
// Keep paren type as a stand-alone level.
|
|
if (auto *PT = dyn_cast<ParenType>(T.getPointer())) {
|
|
Root->addChild(constructTypeNode(PT->getSinglyDesugaredType()));
|
|
return Root;
|
|
}
|
|
|
|
// Handle the case where Type has sub-types.
|
|
if (auto BGT = T->getAs<BoundGenericType>()) {
|
|
for (auto Arg : BGT->getGenericArgs()) {
|
|
Root->addChild(constructTypeNode(Arg));
|
|
}
|
|
} else if (auto Tup = T->getAs<TupleType>()) {
|
|
for (auto Elt : Tup->getElementTypes())
|
|
Root->addChild(constructTypeNode(Elt));
|
|
} else if (auto MTT = T->getAs<AnyMetatypeType>()) {
|
|
Root->addChild(constructTypeNode(MTT->getInstanceType()));
|
|
} else if (auto ATT = T->getAs<ArchetypeType>()) {
|
|
for (auto Pro : ATT->getConformsTo()) {
|
|
Root->addChild(constructTypeNode(Pro->getDeclaredInterfaceType()));
|
|
}
|
|
}
|
|
return Root;
|
|
}
|
|
|
|
std::vector<SDKNode*> swift::ide::api::
|
|
SwiftDeclCollector::createParameterNodes(ParameterList *PL) {
|
|
std::vector<SDKNode*> Result;
|
|
for (auto param: *PL) {
|
|
TypeInitInfo Info;
|
|
Info.IsImplicitlyUnwrappedOptional = param->isImplicitlyUnwrappedOptional();
|
|
Info.hasDefaultArgument = param->getDefaultArgumentKind() !=
|
|
DefaultArgumentKind::None;
|
|
switch (param->getValueOwnership()) {
|
|
#define CASE(KIND) case ValueOwnership::KIND: Info.ValueOwnership = #KIND; break;
|
|
CASE(Owned)
|
|
CASE(InOut)
|
|
CASE(Shared)
|
|
case ValueOwnership::Default: break;
|
|
#undef CASE
|
|
}
|
|
Result.push_back(constructTypeNode(param->getInterfaceType(), Info));
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
// Construct a node for a function decl. The first child of the function decl
|
|
// is guaranteed to be the return value type of this function.
|
|
// We sometimes skip the first parameter because it can be metatype of dynamic
|
|
// this if the function is a member function.
|
|
SDKNode *swift::ide::api::
|
|
SwiftDeclCollector::constructFunctionNode(FuncDecl* FD,
|
|
SDKNodeKind Kind) {
|
|
auto Func = SDKNodeInitInfo(Ctx, FD).createSDKNode(Kind);
|
|
TypeInitInfo Info;
|
|
Info.IsImplicitlyUnwrappedOptional = FD->isImplicitlyUnwrappedOptional();
|
|
Func->addChild(constructTypeNode(FD->getResultInterfaceType(), Info));
|
|
for (auto *Node : createParameterNodes(FD->getParameters()))
|
|
Func->addChild(Node);
|
|
return Func;
|
|
}
|
|
|
|
SDKNode* swift::ide::api::
|
|
SwiftDeclCollector::constructInitNode(ConstructorDecl *CD) {
|
|
auto Func = SDKNodeInitInfo(Ctx, CD).createSDKNode(SDKNodeKind::DeclConstructor);
|
|
Func->addChild(constructTypeNode(CD->getResultInterfaceType()));
|
|
for (auto *Node : createParameterNodes(CD->getParameters()))
|
|
Func->addChild(Node);
|
|
return Func;
|
|
}
|
|
|
|
bool swift::ide::api::
|
|
SDKContext::shouldIgnore(Decl *D, const Decl* Parent) const {
|
|
// Exclude all clang nodes if we're comparing Swift decls specifically.
|
|
// FIXME: isFromClang also excludes Swift decls with @objc. We should allow those.
|
|
if (Opts.SwiftOnly && isFromClang(D)) {
|
|
return true;
|
|
}
|
|
if (auto *ACC = dyn_cast<AccessorDecl>(D)) {
|
|
// Only include accessors if they are part of var and subscript decl.
|
|
if (!isa<AbstractStorageDecl>(Parent)) {
|
|
return true;
|
|
}
|
|
// Only include getter/setter if we are checking source compatibility.
|
|
if (!checkingABI()) {
|
|
switch (ACC->getAccessorKind()) {
|
|
case AccessorKind::Get:
|
|
case AccessorKind::Set:
|
|
break;
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
if (checkingABI()) {
|
|
if (auto *VD = dyn_cast<ValueDecl>(D)) {
|
|
// Private vars with fixed binary orders can have ABI-impact, so we should
|
|
// allowlist them if we're checking ABI.
|
|
if (getFixedBinaryOrder(VD).hasValue())
|
|
return false;
|
|
// Typealias should have no impact on ABI.
|
|
if (isa<TypeAliasDecl>(VD))
|
|
return true;
|
|
}
|
|
// Exclude decls with @_alwaysEmitIntoClient if we are checking ABI.
|
|
// These decls are considered effectively public because they are usable
|
|
// from inline, so we have to manually exclude them here.
|
|
if (D->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>())
|
|
return true;
|
|
} else {
|
|
if (D->isPrivateStdlibDecl(false))
|
|
return true;
|
|
}
|
|
if (AvailableAttr::isUnavailable(D))
|
|
return true;
|
|
if (auto VD = dyn_cast<ValueDecl>(D)) {
|
|
switch (getAccessLevel(VD)) {
|
|
case AccessLevel::Internal:
|
|
case AccessLevel::Private:
|
|
case AccessLevel::FilePrivate:
|
|
return true;
|
|
case AccessLevel::Public:
|
|
case AccessLevel::Open:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (auto *ClangD = D->getClangDecl()) {
|
|
if (isa<clang::ObjCIvarDecl>(ClangD))
|
|
return true;
|
|
if (isa<clang::FieldDecl>(ClangD))
|
|
return true;
|
|
if (ClangD->hasAttr<clang::SwiftPrivateAttr>())
|
|
return true;
|
|
|
|
// If this decl is a synthesized member from a conformed clang protocol, we
|
|
// should ignore this member to reduce redundancy.
|
|
if (Parent &&
|
|
!isa<swift::ProtocolDecl>(Parent) &&
|
|
isa<clang::ObjCProtocolDecl>(ClangD->getDeclContext()))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
SDKNode *swift::ide::api::
|
|
SwiftDeclCollector::constructTypeDeclNode(NominalTypeDecl *NTD) {
|
|
auto TypeNode = SDKNodeInitInfo(Ctx, NTD).createSDKNode(SDKNodeKind::DeclType);
|
|
addConformancesToTypeDecl(cast<SDKNodeDeclType>(TypeNode), NTD);
|
|
addMembersToRoot(TypeNode, NTD);
|
|
for (auto Ext : NTD->getExtensions()) {
|
|
HandledExtensions.insert(Ext);
|
|
addMembersToRoot(TypeNode, Ext);
|
|
}
|
|
return TypeNode;
|
|
}
|
|
|
|
/// Create a node for stand-alone extensions. In the sdk dump, we don't have
|
|
/// a specific node for extension. Members in extensions are inlined to the
|
|
/// extended types. If the extended types are from a different module, we have to
|
|
/// synthesize this type node to include those extension members, since these
|
|
/// extension members are legit members of the module.
|
|
SDKNode *swift::ide::api::
|
|
SwiftDeclCollector::constructExternalExtensionNode(NominalTypeDecl *NTD,
|
|
ArrayRef<ExtensionDecl*> AllExts) {
|
|
SDKNodeInitInfo initInfo(Ctx, NTD);
|
|
initInfo.IsExternal = true;
|
|
auto *TypeNode = initInfo.createSDKNode(SDKNodeKind::DeclType);
|
|
addConformancesToTypeDecl(cast<SDKNodeDeclType>(TypeNode), NTD);
|
|
|
|
bool anyConformancesAdded = false;
|
|
// The members of the extensions are the only members of this synthesized type.
|
|
for (auto *Ext: AllExts) {
|
|
HandledExtensions.insert(Ext);
|
|
addMembersToRoot(TypeNode, Ext);
|
|
|
|
// Keep track if we've declared any conformances in this extension.
|
|
// FIXME: This is too conservative. We only _really_ care if this extension
|
|
// declares a conformance to any public protocols outside the module
|
|
// where the extended type originated. Eventually this should be
|
|
// updated to filter extensions that declare conformances to internal
|
|
// protocols that either don't inherit from any protocols or only
|
|
// inherit from other internal protocols. It should also consider
|
|
// conditional conformances with internal requirements that are still
|
|
// part of the ABI.
|
|
if (!Ext->getInherited().empty())
|
|
anyConformancesAdded = true;
|
|
}
|
|
|
|
// If none of the extensions added any public members or conformances, don't
|
|
// synthesize the type node.
|
|
if (TypeNode->getChildrenCount() == 0 && !anyConformancesAdded)
|
|
return nullptr;
|
|
|
|
return TypeNode;
|
|
}
|
|
|
|
SDKNode *swift::ide::api::
|
|
SwiftDeclCollector::constructVarNode(ValueDecl *VD) {
|
|
auto Var = cast<SDKNodeDeclVar>(SDKNodeInitInfo(Ctx, VD).createSDKNode(SDKNodeKind::DeclVar));
|
|
TypeInitInfo Info;
|
|
Info.IsImplicitlyUnwrappedOptional = VD->isImplicitlyUnwrappedOptional();
|
|
Var->addChild(constructTypeNode(VD->getInterfaceType(), Info));
|
|
if (auto VAD = dyn_cast<AbstractStorageDecl>(VD)) {
|
|
llvm::SmallVector<AccessorDecl*, 4> scratch;
|
|
for(auto *AC: VAD->getOpaqueAccessors(scratch)) {
|
|
if (!Ctx.shouldIgnore(AC, VAD)) {
|
|
Var->addAccessor(constructFunctionNode(AC, SDKNodeKind::DeclAccessor));
|
|
}
|
|
}
|
|
}
|
|
return Var;
|
|
}
|
|
|
|
SDKNode *swift::ide::api::
|
|
SwiftDeclCollector::constructTypeAliasNode(TypeAliasDecl *TAD) {
|
|
auto Alias = SDKNodeInitInfo(Ctx, TAD).createSDKNode(SDKNodeKind::DeclTypeAlias);
|
|
Alias->addChild(constructTypeNode(TAD->getUnderlyingType()));
|
|
return Alias;
|
|
}
|
|
|
|
SDKNode *swift::ide::api::
|
|
SwiftDeclCollector::constructAssociatedTypeNode(AssociatedTypeDecl *ATD) {
|
|
auto Asso = SDKNodeInitInfo(Ctx, ATD).
|
|
createSDKNode(SDKNodeKind::DeclAssociatedType);
|
|
if (auto DT = ATD->getDefaultDefinitionType()) {
|
|
Asso->addChild(constructTypeNode(DT));
|
|
}
|
|
return Asso;
|
|
}
|
|
|
|
SDKNode *swift::ide::api::
|
|
SwiftDeclCollector::constructSubscriptDeclNode(SubscriptDecl *SD) {
|
|
auto *Subs = cast<SDKNodeDeclSubscript>(SDKNodeInitInfo(Ctx, SD).
|
|
createSDKNode(SDKNodeKind::DeclSubscript));
|
|
Subs->addChild(constructTypeNode(SD->getElementInterfaceType()));
|
|
for (auto *Node: createParameterNodes(SD->getIndices())) {
|
|
Subs->addChild(Node);
|
|
}
|
|
llvm::SmallVector<AccessorDecl*, 4> scratch;
|
|
for(auto *AC: SD->getOpaqueAccessors(scratch)) {
|
|
if (!Ctx.shouldIgnore(AC, SD)) {
|
|
Subs->addAccessor(constructFunctionNode(AC, SDKNodeKind::DeclAccessor));
|
|
}
|
|
}
|
|
return Subs;
|
|
}
|
|
|
|
void swift::ide::api::
|
|
SwiftDeclCollector::addMembersToRoot(SDKNode *Root, IterableDeclContext *Context) {
|
|
for (auto *Member : Context->getMembers()) {
|
|
if (Ctx.shouldIgnore(Member, Context->getDecl()))
|
|
continue;
|
|
if (auto Func = dyn_cast<FuncDecl>(Member)) {
|
|
Root->addChild(constructFunctionNode(Func, SDKNodeKind::DeclFunction));
|
|
} else if (auto CD = dyn_cast<ConstructorDecl>(Member)) {
|
|
Root->addChild(constructInitNode(CD));
|
|
} else if (auto VD = dyn_cast<VarDecl>(Member)) {
|
|
Root->addChild(constructVarNode(VD));
|
|
} else if (auto TAD = dyn_cast<TypeAliasDecl>(Member)) {
|
|
Root->addChild(constructTypeAliasNode(TAD));
|
|
} else if (auto EED = dyn_cast<EnumElementDecl>(Member)) {
|
|
Root->addChild(constructVarNode(EED));
|
|
} else if (auto NTD = dyn_cast<NominalTypeDecl>(Member)) {
|
|
Root->addChild(constructTypeDeclNode(NTD));
|
|
} else if (auto ATD = dyn_cast<AssociatedTypeDecl>(Member)) {
|
|
Root->addChild(constructAssociatedTypeNode(ATD));
|
|
} else if (auto SD = dyn_cast<SubscriptDecl>(Member)) {
|
|
Root->addChild(constructSubscriptDeclNode(SD));
|
|
} else if (isa<PatternBindingDecl>(Member)) {
|
|
// All containing variables should have been handled.
|
|
} else if (isa<EnumCaseDecl>(Member)) {
|
|
// All containing variables should have been handled.
|
|
} else if (isa<IfConfigDecl>(Member)) {
|
|
// All containing members should have been handled.
|
|
} else if (isa<PoundDiagnosticDecl>(Member)) {
|
|
// All containing members should have been handled.
|
|
} else if (isa<DestructorDecl>(Member)) {
|
|
// deinit has no impact.
|
|
} else if (isa<MissingMemberDecl>(Member)) {
|
|
// avoid adding MissingMemberDecl
|
|
} else {
|
|
llvm::errs() << "Unhandled decl:\n";
|
|
Member->dump(llvm::errs());
|
|
}
|
|
}
|
|
}
|
|
|
|
SDKNode *swift::ide::api::
|
|
SwiftDeclCollector::constructTypeWitnessNode(AssociatedTypeDecl *Assoc,
|
|
Type Ty) {
|
|
auto *Witness = SDKNodeInitInfo(Ctx, Assoc).createSDKNode(SDKNodeKind::TypeWitness);
|
|
Witness->addChild(constructTypeNode(Ty));
|
|
return Witness;
|
|
}
|
|
|
|
SDKNode *swift::ide::api::
|
|
SwiftDeclCollector::constructConformanceNode(ProtocolConformance *Conform) {
|
|
if (Ctx.checkingABI())
|
|
Conform = Conform->getCanonicalConformance();
|
|
auto ConfNode = cast<SDKNodeConformance>(SDKNodeInitInfo(Ctx,
|
|
Conform).createSDKNode(SDKNodeKind::Conformance));
|
|
Conform->forEachTypeWitness(
|
|
[&](AssociatedTypeDecl *assoc, Type ty, TypeDecl *typeDecl) -> bool {
|
|
ConfNode->addChild(constructTypeWitnessNode(assoc, ty));
|
|
return false;
|
|
});
|
|
return ConfNode;
|
|
}
|
|
|
|
void swift::ide::api::
|
|
SwiftDeclCollector::addConformancesToTypeDecl(SDKNodeDeclType *Root,
|
|
NominalTypeDecl *NTD) {
|
|
// Avoid adding the same conformance twice.
|
|
SmallPtrSet<ProtocolConformance*, 4> Seen;
|
|
for (auto &Conf: NTD->getAllConformances()) {
|
|
if (!Ctx.shouldIgnore(Conf->getProtocol()) && !Seen.count(Conf))
|
|
Root->addConformance(constructConformanceNode(Conf));
|
|
Seen.insert(Conf);
|
|
}
|
|
}
|
|
|
|
void SwiftDeclCollector::printTopLevelNames() {
|
|
for (auto &Node : RootNode->getChildren()) {
|
|
llvm::outs() << Node->getKind() << ": " << Node->getName() << '\n';
|
|
}
|
|
}
|
|
|
|
void SwiftDeclCollector::lookupVisibleDecls(ArrayRef<ModuleDecl *> Modules) {
|
|
for (auto M: Modules) {
|
|
llvm::SmallVector<Decl*, 512> Decls;
|
|
swift::getTopLevelDeclsForDisplay(M, Decls);
|
|
for (auto D : Decls) {
|
|
if (Ctx.shouldIgnore(D))
|
|
continue;
|
|
if (KnownDecls.count(D))
|
|
continue;
|
|
KnownDecls.insert(D);
|
|
if (auto VD = dyn_cast<ValueDecl>(D))
|
|
foundDecl(VD, DeclVisibilityKind::DynamicLookup,
|
|
DynamicLookupInfo::AnyObject);
|
|
else
|
|
processDecl(D);
|
|
}
|
|
}
|
|
|
|
// Now sort the macros before processing so that we can have deterministic
|
|
// output.
|
|
llvm::array_pod_sort(ClangMacros.begin(), ClangMacros.end(),
|
|
[](ValueDecl * const *lhs,
|
|
ValueDecl * const *rhs) -> int {
|
|
return (*lhs)->getBaseName().userFacingName().compare(
|
|
(*rhs)->getBaseName().userFacingName());
|
|
});
|
|
|
|
for (auto *VD : ClangMacros)
|
|
processValueDecl(VD);
|
|
|
|
// Collect extensions to types from other modules and synthesize type nodes
|
|
// for them.
|
|
llvm::MapVector<NominalTypeDecl*, llvm::SmallVector<ExtensionDecl*, 4>> ExtensionMap;
|
|
for (auto *D: KnownDecls) {
|
|
if (auto *Ext = dyn_cast<ExtensionDecl>(D)) {
|
|
if (HandledExtensions.find(Ext) == HandledExtensions.end()) {
|
|
if (auto *NTD = Ext->getExtendedNominal()) {
|
|
// Check if the extension is from other modules.
|
|
if (!llvm::is_contained(Modules, NTD->getModuleContext())) {
|
|
ExtensionMap[NTD].push_back(Ext);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (auto Pair: ExtensionMap) {
|
|
if (auto child = constructExternalExtensionNode(Pair.first, Pair.second))
|
|
RootNode->addChild(child);
|
|
}
|
|
}
|
|
|
|
SDKNode *SwiftDeclCollector::constructOperatorDeclNode(OperatorDecl *OD) {
|
|
return SDKNodeInitInfo(Ctx, OD).createSDKNode(SDKNodeKind::DeclOperator);
|
|
}
|
|
|
|
void SwiftDeclCollector::processDecl(Decl *D) {
|
|
assert(!isa<ValueDecl>(D));
|
|
if (auto *OD = dyn_cast<OperatorDecl>(D)) {
|
|
RootNode->addChild(constructOperatorDeclNode(OD));
|
|
}
|
|
if (auto *IM = dyn_cast<ImportDecl>(D)) {
|
|
RootNode->addChild(SDKNodeInitInfo(Ctx, IM)
|
|
.createSDKNode(SDKNodeKind::DeclImport));
|
|
}
|
|
}
|
|
|
|
void SwiftDeclCollector::processValueDecl(ValueDecl *VD) {
|
|
if (auto FD = dyn_cast<FuncDecl>(VD)) {
|
|
RootNode->addChild(constructFunctionNode(FD, SDKNodeKind::DeclFunction));
|
|
} else if (auto NTD = dyn_cast<NominalTypeDecl>(VD)) {
|
|
RootNode->addChild(constructTypeDeclNode(NTD));
|
|
} else if (auto VAD = dyn_cast<VarDecl>(VD)) {
|
|
RootNode->addChild(constructVarNode(VAD));
|
|
} else if (auto TAD = dyn_cast<TypeAliasDecl>(VD)) {
|
|
RootNode->addChild(constructTypeAliasNode(TAD));
|
|
} else {
|
|
llvm_unreachable("unhandled value decl");
|
|
}
|
|
}
|
|
|
|
void SwiftDeclCollector::foundDecl(ValueDecl *VD, DeclVisibilityKind Reason,
|
|
DynamicLookupInfo) {
|
|
if (VD->getClangMacro()) {
|
|
// Collect macros, we will sort them afterwards.
|
|
ClangMacros.push_back(VD);
|
|
return;
|
|
}
|
|
|
|
processValueDecl(VD);
|
|
}
|
|
|
|
void SDKNode::output(json::Output &out, KeyKind Key, bool Value) {
|
|
if (Value)
|
|
out.mapRequired(getKeyContent(Ctx, Key).data(), Value);
|
|
}
|
|
|
|
void SDKNode::output(json::Output &out, KeyKind Key, StringRef Value) {
|
|
if (!Value.empty())
|
|
out.mapRequired(getKeyContent(Ctx, Key).data(), Value);
|
|
}
|
|
|
|
void SDKNode::jsonize(json::Output &out) {
|
|
auto Kind = getKind();
|
|
out.mapRequired(getKeyContent(Ctx, KeyKind::KK_kind).data(), Kind);
|
|
output(out, KeyKind::KK_name, Name);
|
|
output(out, KeyKind::KK_printedName, PrintedName);
|
|
out.mapOptional(getKeyContent(Ctx, KeyKind::KK_children).data(), Children);
|
|
}
|
|
|
|
void SDKNodeRoot::jsonize(json::Output &out) {
|
|
SDKNode::jsonize(out);
|
|
out.mapRequired(getKeyContent(Ctx, KeyKind::KK_json_format_version).data(), JsonFormatVer);
|
|
if (!Ctx.getOpts().AvoidToolArgs)
|
|
out.mapOptional(getKeyContent(Ctx, KeyKind::KK_tool_arguments).data(), ToolArgs);
|
|
}
|
|
|
|
void SDKNodeConformance::jsonize(json::Output &out) {
|
|
SDKNode::jsonize(out);
|
|
output(out, KeyKind::KK_usr, Usr);
|
|
output(out, KeyKind::KK_mangledName, MangledName);
|
|
output(out, KeyKind::KK_isABIPlaceholder, IsABIPlaceholder);
|
|
}
|
|
|
|
void SDKNodeDecl::jsonize(json::Output &out) {
|
|
SDKNode::jsonize(out);
|
|
out.mapRequired(getKeyContent(Ctx, KeyKind::KK_declKind).data(), DKind);
|
|
output(out, KeyKind::KK_usr, Usr);
|
|
output(out, KeyKind::KK_mangledName, MangledName);
|
|
output(out, KeyKind::KK_location, Location);
|
|
output(out, KeyKind::KK_moduleName, ModuleName);
|
|
output(out, KeyKind::KK_genericSig, GenericSig);
|
|
output(out, KeyKind::KK_sugared_genericSig, SugaredGenericSig);
|
|
output(out, KeyKind::KK_static, IsStatic);
|
|
output(out, KeyKind::KK_deprecated,IsDeprecated);
|
|
output(out, KeyKind::KK_protocolReq, IsProtocolReq);
|
|
output(out, KeyKind::KK_overriding, IsOverriding);
|
|
output(out, KeyKind::KK_implicit, IsImplicit);
|
|
output(out, KeyKind::KK_isOpen, IsOpen);
|
|
output(out, KeyKind::KK_isInternal, IsInternal);
|
|
output(out, KeyKind::KK_isABIPlaceholder, IsABIPlaceholder);
|
|
output(out, KeyKind::KK_intro_Macosx, introVersions.macos);
|
|
output(out, KeyKind::KK_intro_iOS, introVersions.ios);
|
|
output(out, KeyKind::KK_intro_tvOS, introVersions.tvos);
|
|
output(out, KeyKind::KK_intro_watchOS, introVersions.watchos);
|
|
output(out, KeyKind::KK_intro_swift, introVersions.swift);
|
|
output(out, KeyKind::KK_objc_name, ObjCName);
|
|
out.mapOptional(getKeyContent(Ctx, KeyKind::KK_declAttributes).data(), DeclAttributes);
|
|
out.mapOptional(getKeyContent(Ctx, KeyKind::KK_fixedbinaryorder).data(), FixedBinaryOrder);
|
|
// Strong reference is implied, no need for serialization.
|
|
if (getReferenceOwnership() != ReferenceOwnership::Strong) {
|
|
uint8_t Raw = uint8_t(getReferenceOwnership());
|
|
out.mapRequired(getKeyContent(Ctx, KeyKind::KK_ownership).data(), Raw);
|
|
}
|
|
output(out, KeyKind::KK_isFromExtension, IsFromExtension);
|
|
}
|
|
|
|
void SDKNodeDeclAbstractFunc::jsonize(json::Output &out) {
|
|
SDKNodeDecl::jsonize(out);
|
|
output(out, KeyKind::KK_throwing, IsThrowing);
|
|
output(out, KeyKind::KK_reqNewWitnessTableEntry, ReqNewWitnessTableEntry);
|
|
out.mapOptional(getKeyContent(Ctx, KeyKind::KK_selfIndex).data(), SelfIndex);
|
|
}
|
|
|
|
void SDKNodeDeclFunction::jsonize(json::Output &out) {
|
|
SDKNodeDeclAbstractFunc::jsonize(out);
|
|
output(out, KeyKind::KK_funcSelfKind, FuncSelfKind);
|
|
}
|
|
|
|
void SDKNodeDeclConstructor::jsonize(json::Output &out) {
|
|
SDKNodeDeclAbstractFunc::jsonize(out);
|
|
output(out, KeyKind::KK_init_kind, InitKind);
|
|
}
|
|
|
|
void SDKNodeDeclType::jsonize(json::Output &out) {
|
|
SDKNodeDecl::jsonize(out);
|
|
output(out, KeyKind::KK_superclassUsr, SuperclassUsr);
|
|
output(out, KeyKind::KK_enumRawTypeName, EnumRawTypeName);
|
|
output(out, KeyKind::KK_isExternal, IsExternal);
|
|
output(out, KeyKind::KK_isEnumExhaustive, IsEnumExhaustive);
|
|
output(out, KeyKind::KK_hasMissingDesignatedInitializers,
|
|
HasMissingDesignatedInitializers);
|
|
output(out, KeyKind::KK_inheritsConvenienceInitializers,
|
|
InheritsConvenienceInitializers);
|
|
out.mapOptional(getKeyContent(Ctx, KeyKind::KK_superclassNames).data(), SuperclassNames);
|
|
out.mapOptional(getKeyContent(Ctx, KeyKind::KK_conformances).data(), Conformances);
|
|
}
|
|
|
|
void SDKNodeType::jsonize(json::Output &out) {
|
|
SDKNode::jsonize(out);
|
|
out.mapOptional(getKeyContent(Ctx, KeyKind::KK_typeAttributes).data(), TypeAttributes);
|
|
output(out, KeyKind::KK_hasDefaultArg, HasDefaultArg);
|
|
output(out, KeyKind::KK_paramValueOwnership, ParamValueOwnership);
|
|
}
|
|
|
|
void SDKNodeTypeNominal::jsonize(json::Output &out) {
|
|
SDKNodeType::jsonize(out);
|
|
output(out, KeyKind::KK_usr, USR);
|
|
}
|
|
|
|
void SDKNodeDeclSubscript::jsonize(json::Output &out) {
|
|
SDKNodeDeclAbstractFunc::jsonize(out);
|
|
output(out, KeyKind::KK_hasStorage, HasStorage);
|
|
out.mapOptional(getKeyContent(Ctx, KeyKind::KK_accessors).data(), Accessors);
|
|
}
|
|
|
|
void SDKNodeDeclVar::jsonize(json::Output &out) {
|
|
SDKNodeDecl::jsonize(out);
|
|
output(out, KeyKind::KK_isLet, IsLet);
|
|
output(out, KeyKind::KK_hasStorage, HasStorage);
|
|
out.mapOptional(getKeyContent(Ctx, KeyKind::KK_accessors).data(), Accessors);
|
|
}
|
|
|
|
void SDKNodeDeclAccessor::jsonize(json::Output &out) {
|
|
SDKNodeDeclAbstractFunc::jsonize(out);
|
|
out.mapRequired(getKeyContent(Ctx, KeyKind::KK_accessorKind).data(), AccKind);
|
|
}
|
|
|
|
|
|
namespace swift {
|
|
namespace json {
|
|
// In the namespace of swift::json, we define several functions so that the
|
|
// JSON serializer will know how to interpret and dump types defined in this
|
|
// file.
|
|
template<>
|
|
struct ScalarEnumerationTraits<TypeAttrKind> {
|
|
static void enumeration(Output &out, TypeAttrKind &value) {
|
|
#define TYPE_ATTR(X) out.enumCase(value, #X, TypeAttrKind::TAK_##X);
|
|
#include "swift/AST/Attr.def"
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct ScalarEnumerationTraits<DeclAttrKind> {
|
|
static void enumeration(Output &out, DeclAttrKind &value) {
|
|
#define DECL_ATTR(_, Name, ...) out.enumCase(value, #Name, DeclAttrKind::DAK_##Name);
|
|
#include "swift/AST/Attr.def"
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct ScalarEnumerationTraits<DeclKind> {
|
|
static void enumeration(Output &out, DeclKind &value) {
|
|
#define DECL(X, PARENT) out.enumCase(value, #X, DeclKind::X);
|
|
#include "swift/AST/DeclNodes.def"
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct ScalarEnumerationTraits<AccessorKind> {
|
|
static void enumeration(Output &out, AccessorKind &value) {
|
|
#define ACCESSOR(ID)
|
|
#define SINGLETON_ACCESSOR(ID, KEYWORD) \
|
|
out.enumCase(value, #KEYWORD, AccessorKind::ID);
|
|
#include "swift/AST/AccessorKinds.def"
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct ObjectTraits<SDKNode *> {
|
|
static void mapping(Output &out, SDKNode *&value) {
|
|
value->jsonize(out);
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct ArrayTraits<ArrayRef<SDKNode*>> {
|
|
static size_t size(Output &out, ArrayRef<SDKNode *> &seq) {
|
|
return seq.size();
|
|
}
|
|
static SDKNode *&element(Output &, ArrayRef<SDKNode *> &seq,
|
|
size_t index) {
|
|
return const_cast<SDKNode *&>(seq[index]);
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct ArrayTraits<ArrayRef<TypeAttrKind>> {
|
|
static size_t size(Output &out, ArrayRef<TypeAttrKind> &seq) {
|
|
return seq.size();
|
|
}
|
|
static TypeAttrKind& element(Output &, ArrayRef<TypeAttrKind> &seq,
|
|
size_t index) {
|
|
return const_cast<TypeAttrKind&>(seq[index]);
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct ArrayTraits<ArrayRef<DeclAttrKind>> {
|
|
static size_t size(Output &out, ArrayRef<DeclAttrKind> &seq) {
|
|
return seq.size();
|
|
}
|
|
static DeclAttrKind& element(Output &, ArrayRef<DeclAttrKind> &seq,
|
|
size_t index) {
|
|
return const_cast<DeclAttrKind&>(seq[index]);
|
|
}
|
|
};
|
|
template<>
|
|
struct ArrayTraits<ArrayRef<StringRef>> {
|
|
static size_t size(Output &out, ArrayRef<StringRef> &seq) {
|
|
return seq.size();
|
|
}
|
|
static StringRef& element(Output &, ArrayRef<StringRef> &seq,
|
|
size_t index) {
|
|
return const_cast<StringRef&>(seq[index]);
|
|
}
|
|
};
|
|
} // namespace json
|
|
} // namespace swift
|
|
|
|
namespace {// Anonymous namespace.
|
|
// Deserialize an SDKNode tree.
|
|
std::pair<std::unique_ptr<llvm::MemoryBuffer>, SDKNode*>
|
|
static parseJsonEmit(SDKContext &Ctx, StringRef FileName) {
|
|
namespace yaml = llvm::yaml;
|
|
|
|
// Load the input file.
|
|
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr =
|
|
vfs::getFileOrSTDIN(*Ctx.getSourceMgr().getFileSystem(), FileName,
|
|
/*FileSize*/-1, /*RequiresNullTerminator*/true,
|
|
/*IsVolatile*/false, /*RetryCount*/30);
|
|
if (!FileBufOrErr) {
|
|
llvm_unreachable("Failed to read JSON file");
|
|
}
|
|
StringRef Buffer = FileBufOrErr->get()->getBuffer();
|
|
yaml::Stream Stream(llvm::MemoryBufferRef(Buffer, FileName),
|
|
Ctx.getSourceMgr().getLLVMSourceMgr());
|
|
SDKNode *Result = nullptr;
|
|
for (auto DI = Stream.begin(); DI != Stream.end(); ++ DI) {
|
|
assert(DI != Stream.end() && "Failed to read a document");
|
|
yaml::Node *N = DI->getRoot();
|
|
assert(N && "Failed to find a root");
|
|
Result = SDKNode::constructSDKNode(Ctx, cast<yaml::MappingNode>(N));
|
|
}
|
|
return {std::move(FileBufOrErr.get()), Result};
|
|
}
|
|
enum class ConstKind: uint8_t {
|
|
StringLiteral = 0,
|
|
IntegerLiteral,
|
|
FloatLiteral,
|
|
BooleanLiteral,
|
|
Array,
|
|
Dictionary,
|
|
};
|
|
|
|
struct ConstExprInfo {
|
|
StringRef filePath;
|
|
ConstKind kind;
|
|
unsigned offset = 0;
|
|
unsigned length = 0;
|
|
StringRef value;
|
|
StringRef referencedD;
|
|
ConstExprInfo(StringRef filePath, ConstKind kind, unsigned offset,
|
|
unsigned length, StringRef value, StringRef referencedD):
|
|
filePath(filePath), kind(kind), offset(offset), length(length), value(value),
|
|
referencedD(referencedD) {}
|
|
ConstExprInfo() = default;
|
|
};
|
|
|
|
class ConstExtractor: public ASTWalker {
|
|
SDKContext &SCtx;
|
|
SourceManager &SM;
|
|
std::vector<ConstExprInfo> allConsts;
|
|
|
|
void record(Expr *E, ConstKind kind, StringRef Value, StringRef ReferencedD) {
|
|
auto startLoc = E->getStartLoc();
|
|
// Asserts?
|
|
if (startLoc.isInvalid())
|
|
return;
|
|
auto endLoc = E->getEndLoc();
|
|
assert(endLoc.isValid());
|
|
endLoc = Lexer::getLocForEndOfToken(SM, endLoc);
|
|
auto bufferId = SM.findBufferContainingLoc(startLoc);
|
|
auto length = SM.getByteDistance(startLoc, endLoc);
|
|
auto file = SM.getIdentifierForBuffer(bufferId);
|
|
auto offset = SM.getLocOffsetInBuffer(startLoc, bufferId);
|
|
allConsts.emplace_back(file, kind, offset, length, Value, ReferencedD);
|
|
}
|
|
|
|
void record(Expr *E, Expr *ValueProvider, StringRef ReferencedD = "") {
|
|
std::string content;
|
|
llvm::raw_string_ostream os(content);
|
|
ValueProvider->printConstExprValue(&os, nullptr);
|
|
assert(!content.empty());
|
|
auto buffered = SCtx.buffer(content);
|
|
switch(ValueProvider->getKind()) {
|
|
#define CASE(X) case ExprKind::X: record(E, ConstKind::X, buffered, ReferencedD); break;
|
|
CASE(StringLiteral)
|
|
CASE(IntegerLiteral)
|
|
CASE(FloatLiteral)
|
|
CASE(BooleanLiteral)
|
|
CASE(Dictionary)
|
|
CASE(Array)
|
|
#undef CASE
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
StringRef getDeclName(Decl *D) {
|
|
if (auto *VD = dyn_cast<ValueDecl>(D)) {
|
|
std::string content;
|
|
llvm::raw_string_ostream os(content);
|
|
VD->getName().print(os);
|
|
return SCtx.buffer(content);
|
|
}
|
|
return StringRef();
|
|
}
|
|
|
|
bool handleSimpleReference(Expr *E) {
|
|
assert(E);
|
|
Decl *ReferencedDecl = nullptr;
|
|
if (auto *MRE = dyn_cast<MemberRefExpr>(E)) {
|
|
ReferencedDecl = MRE->getDecl().getDecl();
|
|
} else if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
|
|
ReferencedDecl = DRE->getDecl();
|
|
} else {
|
|
return false;
|
|
}
|
|
assert(ReferencedDecl);
|
|
if (auto *VAR = dyn_cast<VarDecl>(ReferencedDecl)) {
|
|
if (!VAR->getAttrs().hasAttribute<CompileTimeConstAttr>()) {
|
|
return false;
|
|
}
|
|
if (auto *PD = VAR->getParentPatternBinding()) {
|
|
if (auto *init = PD->getInit(PD->getPatternEntryIndexForVarDecl(VAR))) {
|
|
record(E, init, getDeclName(ReferencedDecl));
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
|
|
if (E->isSemanticallyConstExpr()) {
|
|
record(E, E);
|
|
return { false, E };
|
|
}
|
|
if (handleSimpleReference(E)) {
|
|
return { false, E };
|
|
}
|
|
return { true, E };
|
|
}
|
|
public:
|
|
ConstExtractor(SDKContext &SCtx, ASTContext &Ctx): SCtx(SCtx),
|
|
SM(Ctx.SourceMgr) {}
|
|
void extract(ModuleDecl *MD) { MD->walk(*this); }
|
|
std::vector<ConstExprInfo> &getAllConstValues() { return allConsts; }
|
|
};
|
|
} // End of anonymous namespace
|
|
|
|
template <> struct swift::json::ObjectTraits<ConstExprInfo> {
|
|
static void mapping(Output &out, ConstExprInfo &info) {
|
|
out.mapRequired("filePath", info.filePath);
|
|
StringRef kind;
|
|
switch(info.kind) {
|
|
#define CASE(X) case ConstKind::X: kind = #X; break;
|
|
CASE(StringLiteral)
|
|
CASE(IntegerLiteral)
|
|
CASE(FloatLiteral)
|
|
CASE(BooleanLiteral)
|
|
CASE(Dictionary)
|
|
CASE(Array)
|
|
#undef CASE
|
|
}
|
|
out.mapRequired("kind", kind);
|
|
out.mapRequired("offset", info.offset);
|
|
out.mapRequired("length", info.length);
|
|
out.mapRequired("value", info.value);
|
|
if (!info.referencedD.empty())
|
|
out.mapRequired("decl", info.referencedD);
|
|
}
|
|
};
|
|
|
|
struct swift::ide::api::PayLoad {
|
|
std::vector<ConstExprInfo> *allContsValues = nullptr;
|
|
};
|
|
|
|
// Construct all roots vector from a given file where a forest was
|
|
// previously dumped.
|
|
void SwiftDeclCollector::deSerialize(StringRef Filename) {
|
|
auto Pair = parseJsonEmit(Ctx, Filename);
|
|
RootNode = std::move(Pair.second);
|
|
}
|
|
|
|
// Serialize the content of all roots to a given file using JSON format.
|
|
void SwiftDeclCollector::serialize(StringRef Filename, SDKNode *Root,
|
|
PayLoad OtherInfo) {
|
|
std::error_code EC;
|
|
llvm::raw_fd_ostream fs(Filename, EC, llvm::sys::fs::OF_None);
|
|
json::Output yout(fs);
|
|
assert(Root->getKind() == SDKNodeKind::Root);
|
|
SDKNodeRoot &root = *static_cast<SDKNodeRoot*>(Root);
|
|
yout.beginObject();
|
|
yout.mapRequired(ABIRootKey, root);
|
|
if (auto *constValues = OtherInfo.allContsValues) {
|
|
yout.mapRequired(ConstValuesKey, *constValues);
|
|
}
|
|
yout.endObject();
|
|
}
|
|
|
|
// Serialize the content of all roots to a given file using JSON format.
|
|
void SwiftDeclCollector::serialize(StringRef Filename) {
|
|
SwiftDeclCollector::serialize(Filename, RootNode, PayLoad());
|
|
}
|
|
|
|
SDKNodeRoot *
|
|
swift::ide::api::getEmptySDKNodeRoot(SDKContext &SDKCtx) {
|
|
SwiftDeclCollector Collector(SDKCtx);
|
|
return Collector.getSDKRoot();
|
|
}
|
|
|
|
SDKNodeRoot*
|
|
swift::ide::api::getSDKNodeRoot(SDKContext &SDKCtx,
|
|
const CompilerInvocation &InitInvoke,
|
|
const llvm::StringSet<> &ModuleNames) {
|
|
CheckerOptions Opts = SDKCtx.getOpts();
|
|
CompilerInvocation Invocation(InitInvoke);
|
|
|
|
CompilerInstance &CI = SDKCtx.newCompilerInstance();
|
|
// Display diagnostics to stderr.
|
|
PrintingDiagnosticConsumer PrintDiags(llvm::errs());
|
|
if (llvm::errs().has_colors())
|
|
PrintDiags.forceColors();
|
|
CI.addDiagnosticConsumer(&PrintDiags);
|
|
// The PrintDiags is only responsible compiler errors, we should remove the
|
|
// consumer immediately after importing is done.
|
|
SWIFT_DEFER { CI.getDiags().removeConsumer(PrintDiags); };
|
|
std::string InstanceSetupError;
|
|
if (CI.setup(Invocation, InstanceSetupError)) {
|
|
llvm::errs() << InstanceSetupError << '\n';
|
|
return nullptr;
|
|
}
|
|
|
|
auto &Ctx = CI.getASTContext();
|
|
|
|
// Load standard library so that Clang importer can use it.
|
|
auto *Stdlib = Ctx.getStdlibModule(/*loadIfAbsent=*/true);
|
|
if (!Stdlib) {
|
|
llvm::errs() << "Failed to load Swift stdlib\n";
|
|
return nullptr;
|
|
}
|
|
|
|
std::vector<ModuleDecl *> Modules;
|
|
for (auto &Entry : ModuleNames) {
|
|
StringRef Name = Entry.getKey();
|
|
if (Opts.Verbose)
|
|
llvm::errs() << "Loading module: " << Name << "...\n";
|
|
auto *M = Ctx.getModuleByName(Name);
|
|
if (!M || M->failedToLoad() || Ctx.Diags.hadAnyError()) {
|
|
llvm::errs() << "Failed to load module: " << Name << '\n';
|
|
if (Opts.AbortOnModuleLoadFailure)
|
|
return nullptr;
|
|
} else {
|
|
Modules.push_back(M);
|
|
}
|
|
}
|
|
if (Opts.Verbose)
|
|
llvm::errs() << "Scanning symbols...\n";
|
|
|
|
SwiftDeclCollector Collector(SDKCtx);
|
|
Collector.lookupVisibleDecls(Modules);
|
|
return Collector.getSDKRoot();
|
|
}
|
|
|
|
void swift::ide::api::dumpSDKRoot(SDKNodeRoot *Root, PayLoad load,
|
|
StringRef OutputFile) {
|
|
assert(Root);
|
|
auto Opts = Root->getSDKContext().getOpts();
|
|
if (Opts.Verbose)
|
|
llvm::errs() << "Dumping SDK...\n";
|
|
SwiftDeclCollector::serialize(OutputFile, Root, load);
|
|
if (Opts.Verbose)
|
|
llvm::errs() << "Dumped to "<< OutputFile << "\n";
|
|
}
|
|
|
|
void swift::ide::api::dumpSDKRoot(SDKNodeRoot *Root, StringRef OutputFile) {
|
|
dumpSDKRoot(Root, PayLoad(), OutputFile);
|
|
}
|
|
|
|
int swift::ide::api::dumpSDKContent(const CompilerInvocation &InitInvoke,
|
|
const llvm::StringSet<> &ModuleNames,
|
|
StringRef OutputFile, CheckerOptions Opts) {
|
|
SDKContext SDKCtx(Opts);
|
|
SDKNodeRoot *Root = getSDKNodeRoot(SDKCtx, InitInvoke, ModuleNames);
|
|
if (!Root)
|
|
return 1;
|
|
dumpSDKRoot(Root, OutputFile);
|
|
return 0;
|
|
}
|
|
|
|
int swift::ide::api::deserializeSDKDump(StringRef dumpPath, StringRef OutputPath,
|
|
CheckerOptions Opts) {
|
|
std::error_code EC;
|
|
llvm::raw_fd_ostream FS(OutputPath, EC, llvm::sys::fs::OF_None);
|
|
if (!fs::exists(dumpPath)) {
|
|
llvm::errs() << dumpPath << " does not exist\n";
|
|
return 1;
|
|
}
|
|
PrintingDiagnosticConsumer PDC;
|
|
SDKContext Ctx(Opts);
|
|
Ctx.addDiagConsumer(PDC);
|
|
|
|
SwiftDeclCollector Collector(Ctx);
|
|
Collector.deSerialize(dumpPath);
|
|
Collector.serialize(OutputPath);
|
|
return 0;
|
|
}
|
|
|
|
void swift::ide::api::dumpModuleContent(ModuleDecl *MD, StringRef OutputFile,
|
|
bool ABI, bool Empty) {
|
|
CheckerOptions opts;
|
|
opts.ABI = ABI;
|
|
opts.SwiftOnly = true;
|
|
opts.AvoidLocation = true;
|
|
opts.AvoidToolArgs = true;
|
|
opts.Migrator = false;
|
|
opts.SkipOSCheck = false;
|
|
opts.Verbose = false;
|
|
SDKContext ctx(opts);
|
|
SwiftDeclCollector collector(ctx);
|
|
ConstExtractor extractor(ctx, MD->getASTContext());
|
|
PayLoad payload;
|
|
SWIFT_DEFER {
|
|
payload.allContsValues = &extractor.getAllConstValues();
|
|
dumpSDKRoot(collector.getSDKRoot(), payload, OutputFile);
|
|
};
|
|
if (Empty) {
|
|
return;
|
|
}
|
|
collector.lookupVisibleDecls({MD});
|
|
extractor.extract(MD);
|
|
}
|
|
|
|
int swift::ide::api::findDeclUsr(StringRef dumpPath, CheckerOptions Opts) {
|
|
std::error_code EC;
|
|
if (!fs::exists(dumpPath)) {
|
|
llvm::errs() << dumpPath << " does not exist\n";
|
|
return 1;
|
|
}
|
|
PrintingDiagnosticConsumer PDC;
|
|
SDKContext Ctx(Opts);
|
|
Ctx.addDiagConsumer(PDC);
|
|
|
|
SwiftDeclCollector Collector(Ctx);
|
|
Collector.deSerialize(dumpPath);
|
|
struct FinderByLocation: SDKNodeVisitor {
|
|
StringRef Location;
|
|
FinderByLocation(StringRef Location): Location(Location) {}
|
|
void visit(SDKNode* Node) override {
|
|
if (auto *D = dyn_cast<SDKNodeDecl>(Node)) {
|
|
if (D->getLocation().contains(Location) && !D->getUsr().empty()) {
|
|
llvm::outs() << D->getFullyQualifiedName() << ": " << D->getUsr() << "\n";
|
|
}
|
|
}
|
|
}
|
|
};
|
|
if (!Opts.LocationFilter.empty()) {
|
|
FinderByLocation Finder(Opts.LocationFilter);
|
|
Collector.visitAllRoots(Finder);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void swift::ide::api::SDKNodeDeclType::diagnose(SDKNode *Right) {
|
|
SDKNodeDecl::diagnose(Right);
|
|
auto *R = dyn_cast<SDKNodeDeclType>(Right);
|
|
if (!R)
|
|
return;
|
|
auto Loc = R->getLoc();
|
|
if (getDeclKind() != R->getDeclKind()) {
|
|
emitDiag(Loc, diag::decl_kind_changed, getDeclKindStr(R->getDeclKind(),
|
|
getSDKContext().getOpts().CompilerStyle));
|
|
return;
|
|
}
|
|
|
|
assert(getDeclKind() == R->getDeclKind());
|
|
auto DKind = getDeclKind();
|
|
switch (DKind) {
|
|
case DeclKind::Class: {
|
|
auto LSuperClass = getSuperClassName();
|
|
auto RSuperClass = R->getSuperClassName();
|
|
if (!LSuperClass.empty() && LSuperClass != RSuperClass) {
|
|
if (RSuperClass.empty()) {
|
|
emitDiag(Loc, diag::super_class_removed, LSuperClass);
|
|
} else if (!llvm::is_contained(R->getClassInheritanceChain(), LSuperClass)) {
|
|
emitDiag(Loc, diag::super_class_changed, LSuperClass, RSuperClass);
|
|
}
|
|
}
|
|
|
|
// Check for @_hasMissingDesignatedInitializers and
|
|
// @_inheritsConvenienceInitializers changes.
|
|
if (isOpen() && R->isOpen()) {
|
|
// It's not safe to add new, invisible designated inits to open
|
|
// classes.
|
|
if (!hasMissingDesignatedInitializers() &&
|
|
R->hasMissingDesignatedInitializers())
|
|
R->emitDiag(R->getLoc(), diag::added_invisible_designated_init);
|
|
}
|
|
|
|
// It's not safe to stop inheriting convenience inits, it changes
|
|
// the set of initializers that are available.
|
|
if (!Ctx.checkingABI() &&
|
|
inheritsConvenienceInitializers() &&
|
|
!R->inheritsConvenienceInitializers())
|
|
R->emitDiag(R->getLoc(), diag::not_inheriting_convenience_inits);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void swift::ide::api::SDKNodeDeclAbstractFunc::diagnose(SDKNode *Right) {
|
|
SDKNodeDecl::diagnose(Right);
|
|
auto *R = dyn_cast<SDKNodeDeclAbstractFunc>(Right);
|
|
if (!R)
|
|
return;
|
|
auto Loc = R->getLoc();
|
|
if (!isThrowing() && R->isThrowing()) {
|
|
emitDiag(Loc, diag::decl_new_attr, Ctx.buffer("throwing"));
|
|
}
|
|
if (Ctx.checkingABI()) {
|
|
if (reqNewWitnessTableEntry() != R->reqNewWitnessTableEntry()) {
|
|
emitDiag(Loc, diag::decl_new_witness_table_entry, reqNewWitnessTableEntry());
|
|
}
|
|
|
|
// Diagnose moving a non-final class member to an extension.
|
|
if (hasValidParentPtr(getKind())) {
|
|
while(auto *parent = dyn_cast<SDKNodeDecl>(getParent())) {
|
|
if (parent->getDeclKind() != DeclKind::Class) {
|
|
break;
|
|
}
|
|
if (hasDeclAttribute(DeclAttrKind::DAK_Final)) {
|
|
break;
|
|
}
|
|
auto result = isFromExtensionChanged(*this, *Right);
|
|
if (result.hasValue() && *result) {
|
|
emitDiag(Loc, diag::class_member_moved_to_extension);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void swift::ide::api::SDKNodeDeclFunction::diagnose(SDKNode *Right) {
|
|
SDKNodeDeclAbstractFunc::diagnose(Right);
|
|
auto *R = dyn_cast<SDKNodeDeclFunction>(Right);
|
|
if (!R)
|
|
return;
|
|
auto Loc = R->getLoc();
|
|
if (getSelfAccessKind() != R->getSelfAccessKind()) {
|
|
emitDiag(Loc, diag::func_self_access_change, getSelfAccessKind(),
|
|
R->getSelfAccessKind());
|
|
}
|
|
if (Ctx.checkingABI()) {
|
|
if (hasFixedBinaryOrder() != R->hasFixedBinaryOrder()) {
|
|
emitDiag(Loc, diag::func_has_fixed_order_change, hasFixedBinaryOrder());
|
|
}
|
|
}
|
|
}
|
|
|
|
static StringRef getAttrName(DeclAttrKind Kind) {
|
|
switch (Kind) {
|
|
#define DECL_ATTR(NAME, CLASS, ...) \
|
|
case DAK_##CLASS: \
|
|
return DeclAttribute::isDeclModifier(DAK_##CLASS) ? #NAME : "@"#NAME;
|
|
#include "swift/AST/Attr.def"
|
|
case DAK_Count:
|
|
llvm_unreachable("unrecognized attribute kind.");
|
|
}
|
|
llvm_unreachable("covered switch");
|
|
}
|
|
|
|
static bool shouldDiagnoseAddingAttribute(SDKNodeDecl *D, DeclAttrKind Kind) {
|
|
return true;
|
|
}
|
|
|
|
static bool shouldDiagnoseRemovingAttribute(SDKNodeDecl *D, DeclAttrKind Kind) {
|
|
return true;
|
|
}
|
|
|
|
static bool isOwnershipEquivalent(ReferenceOwnership Left,
|
|
ReferenceOwnership Right) {
|
|
if (Left == Right)
|
|
return true;
|
|
if (Left == ReferenceOwnership::Unowned && Right == ReferenceOwnership::Weak)
|
|
return true;
|
|
if (Left == ReferenceOwnership::Weak && Right == ReferenceOwnership::Unowned)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
void swift::ide::api::detectRename(SDKNode *L, SDKNode *R) {
|
|
if (L->getKind() == R->getKind() && isa<SDKNodeDecl>(L) &&
|
|
L->getPrintedName() != R->getPrintedName()) {
|
|
L->annotate(NodeAnnotation::Rename);
|
|
L->annotate(NodeAnnotation::RenameOldName, L->getPrintedName());
|
|
L->annotate(NodeAnnotation::RenameNewName, R->getPrintedName());
|
|
}
|
|
}
|
|
|
|
void swift::ide::api::SDKNodeDecl::diagnose(SDKNode *Right) {
|
|
SDKNode::diagnose(Right);
|
|
auto *RD = dyn_cast<SDKNodeDecl>(Right);
|
|
if (!RD)
|
|
return;
|
|
detectRename(this, RD);
|
|
auto Loc = RD->getLoc();
|
|
if (isOpen() && !RD->isOpen()) {
|
|
emitDiag(Loc, diag::no_longer_open);
|
|
}
|
|
|
|
// Diagnose static attribute change.
|
|
if (isStatic() ^ RD->isStatic()) {
|
|
emitDiag(Loc, diag::decl_new_attr, Ctx.buffer(isStatic() ? "not static" :
|
|
"static"));
|
|
}
|
|
|
|
// Diagnose ownership change.
|
|
if (!isOwnershipEquivalent(getReferenceOwnership(),
|
|
RD->getReferenceOwnership())) {
|
|
auto getOwnershipDescription = [&](swift::ReferenceOwnership O) {
|
|
if (O == ReferenceOwnership::Strong)
|
|
return Ctx.buffer("strong");
|
|
return keywordOf(O);
|
|
};
|
|
emitDiag(Loc, diag::decl_attr_change,
|
|
getOwnershipDescription(getReferenceOwnership()),
|
|
getOwnershipDescription(RD->getReferenceOwnership()));
|
|
}
|
|
// Diagnose generic signature change
|
|
if (getGenericSignature() != RD->getGenericSignature()) {
|
|
// Prefer sugared signature in diagnostics to be more user-friendly.
|
|
if (Ctx.commonVersionAtLeast(2) &&
|
|
getSugaredGenericSignature() != RD->getSugaredGenericSignature()) {
|
|
emitDiag(Loc, diag::generic_sig_change,
|
|
getSugaredGenericSignature(), RD->getSugaredGenericSignature());
|
|
} else {
|
|
emitDiag(Loc, diag::generic_sig_change,
|
|
getGenericSignature(), RD->getGenericSignature());
|
|
}
|
|
}
|
|
|
|
// ObjC name changes are considered breakage
|
|
if (getObjCName() != RD->getObjCName()) {
|
|
if (Ctx.commonVersionAtLeast(4)) {
|
|
emitDiag(Loc, diag::objc_name_change, getObjCName(), RD->getObjCName());
|
|
}
|
|
}
|
|
|
|
if (isOptional() != RD->isOptional()) {
|
|
if (Ctx.checkingABI()) {
|
|
// Both adding/removing optional is ABI-breaking.
|
|
emitDiag(Loc, diag::optional_req_changed, isOptional());
|
|
} else if (isOptional()) {
|
|
// Removing optional is source-breaking.
|
|
emitDiag(Loc, diag::optional_req_changed, isOptional());
|
|
}
|
|
}
|
|
|
|
// Diagnose removing attributes.
|
|
for (auto Kind: getDeclAttributes()) {
|
|
if (!RD->hasDeclAttribute(Kind)) {
|
|
if ((Ctx.checkingABI() ? DeclAttribute::isRemovingBreakingABI(Kind) :
|
|
DeclAttribute::isRemovingBreakingAPI(Kind)) &&
|
|
shouldDiagnoseRemovingAttribute(this, Kind)) {
|
|
emitDiag(Loc, diag::decl_new_attr,
|
|
Ctx.buffer((llvm::Twine("without ") + getAttrName(Kind)).str()));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Diagnose adding attributes.
|
|
for (auto Kind: RD->getDeclAttributes()) {
|
|
if (!hasDeclAttribute(Kind)) {
|
|
if ((Ctx.checkingABI() ? DeclAttribute::isAddingBreakingABI(Kind) :
|
|
DeclAttribute::isAddingBreakingAPI(Kind)) &&
|
|
shouldDiagnoseAddingAttribute(this, Kind)) {
|
|
emitDiag(Loc, diag::decl_new_attr,
|
|
Ctx.buffer((llvm::Twine("with ") + getAttrName(Kind)).str()));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Ctx.checkingABI()) {
|
|
if (hasFixedBinaryOrder() && RD->hasFixedBinaryOrder() &&
|
|
getFixedBinaryOrder() != RD->getFixedBinaryOrder()) {
|
|
emitDiag(Loc, diag::decl_reorder, getFixedBinaryOrder(),
|
|
RD->getFixedBinaryOrder());
|
|
}
|
|
if (getUsr() != RD->getUsr()) {
|
|
auto left = demangleUSR(getUsr());
|
|
auto right = demangleUSR(RD->getUsr());
|
|
if (left != right) {
|
|
emitDiag(Loc, diag::demangled_name_changed, left, right);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void swift::ide::api::SDKNodeDeclOperator::diagnose(SDKNode *Right) {
|
|
SDKNodeDecl::diagnose(Right);
|
|
auto *RO = dyn_cast<SDKNodeDeclOperator>(Right);
|
|
if (!RO)
|
|
return;
|
|
auto Loc = RO->getLoc();
|
|
if (getDeclKind() != RO->getDeclKind()) {
|
|
emitDiag(Loc, diag::decl_kind_changed, getDeclKindStr(RO->getDeclKind(),
|
|
getSDKContext().getOpts().CompilerStyle));
|
|
}
|
|
}
|
|
|
|
void swift::ide::api::SDKNodeDeclVar::diagnose(SDKNode *Right) {
|
|
SDKNodeDecl::diagnose(Right);
|
|
auto *RV = dyn_cast<SDKNodeDeclVar>(Right);
|
|
if (!RV)
|
|
return;
|
|
auto Loc = RV->getLoc();
|
|
if (Ctx.checkingABI()) {
|
|
if (hasFixedBinaryOrder() != RV->hasFixedBinaryOrder()) {
|
|
emitDiag(Loc, diag::var_has_fixed_order_change, hasFixedBinaryOrder());
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool shouldDiagnoseType(SDKNodeType *T) {
|
|
return T->isTopLevelType();
|
|
}
|
|
|
|
void swift::ide::api::SDKNodeType::diagnose(SDKNode *Right) {
|
|
SDKNode::diagnose(Right);
|
|
auto *RT = dyn_cast<SDKNodeType>(Right);
|
|
if (!RT || !shouldDiagnoseType(this))
|
|
return;
|
|
assert(isTopLevelType());
|
|
|
|
// Diagnose type witness changes when diagnosing ABI breakages.
|
|
if (auto *Wit = dyn_cast<SDKNodeTypeWitness>(getParent())) {
|
|
auto *Conform = Wit->getParent()->getAs<SDKNodeConformance>();
|
|
if (Ctx.checkingABI() && getPrintedName() != RT->getPrintedName()) {
|
|
auto *LD = Conform->getNominalTypeDecl();
|
|
LD->emitDiag(SourceLoc(), diag::type_witness_change,
|
|
Wit->getWitnessedTypeName(),
|
|
getPrintedName(), RT->getPrintedName());
|
|
}
|
|
return;
|
|
}
|
|
|
|
StringRef Descriptor = getTypeRoleDescription();
|
|
assert(isa<SDKNodeDecl>(getParent()));
|
|
auto LParent = cast<SDKNodeDecl>(getParent());
|
|
assert(LParent->getKind() == RT->getParent()->getAs<SDKNodeDecl>()->getKind());
|
|
auto Loc = RT->getParent()->getAs<SDKNodeDecl>()->getLoc();
|
|
if (getPrintedName() != RT->getPrintedName()) {
|
|
LParent->emitDiag(Loc, diag::decl_type_change,
|
|
Descriptor, getPrintedName(), RT->getPrintedName());
|
|
}
|
|
|
|
if (hasDefaultArgument() && !RT->hasDefaultArgument()) {
|
|
LParent->emitDiag(Loc, diag::default_arg_removed, Descriptor);
|
|
}
|
|
if (getParamValueOwnership() != RT->getParamValueOwnership()) {
|
|
LParent->emitDiag(Loc, diag::param_ownership_change,
|
|
getTypeRoleDescription(),
|
|
getParamValueOwnership(),
|
|
RT->getParamValueOwnership());
|
|
}
|
|
}
|
|
|
|
void swift::ide::api::SDKNodeTypeFunc::diagnose(SDKNode *Right) {
|
|
SDKNodeType::diagnose(Right);
|
|
auto *RT = dyn_cast<SDKNodeTypeFunc>(Right);
|
|
if (!RT || !shouldDiagnoseType(this))
|
|
return;
|
|
assert(isTopLevelType());
|
|
auto Loc = RT->getParent()->getAs<SDKNodeDecl>()->getLoc();
|
|
if (Ctx.checkingABI() && isEscaping() != RT->isEscaping()) {
|
|
getParent()->getAs<SDKNodeDecl>()->emitDiag(Loc,
|
|
diag::func_type_escaping_changed,
|
|
getTypeRoleDescription(),
|
|
isEscaping());
|
|
}
|
|
}
|