Allow for emission of swift.extension symbols for extensions to external types in swiftSymbolGraphGen (#59047)

This includes:
 - bumping the SWIFT_SYMBOLGRAPH_FORMAT_MINOR version
 - introduction of the "swift.extension" symbol and "extensionTo" relationship
 - adding support for ExtensionDecl to the Symbol class
 - adding a "typeKind" field to the symbol's extension mixin which indicates what kind
   of symbol was extended
 - intoduction of the -emit-extension-block-symbols flag, which enables the behavior
   outlined below
 - adaptions to SymbolGraphASTWalker that ensure a swift.extension symbol is emitted
   for each extension to a type that does not exist in the local symbol graph
 - adaptions to SymbolGraph and SymbolGraphASTWalker that ensure member and conformance
   relationships are correctly associated with the swift.extension symbol instead of
   the original type declaration's (extended nominal's) symbol where applicable
 - adaptions to SymbolGraphASTWalker that ensure swift.extension symbols are connected
   to their respective extended nominal's symbol using an extensionTo relationship

Testing:
- adds SymbolGraph tests that test behavior only relevant in
  -emit-extension-block-symbols mode
- adapts some SymbolGraph tests to additionally test similar behavior for
  extensions to external types in -emit-extension-block-symbols mode
- adapts some SymbolGraph tests to (additionally or exclusively) test the
  behavior with -emit-extension-block-symbols mode enabled

Bugfixes:
- fixes a bug where some conformsTo relationships implicated by the conformances
  declared on an extension to an external type were not emitted
  (see test/SymbolGraph/Relationships/ConformsTo/Indirect.swift)

Further changes:
- documents the strategy for naming and associating children declared in extensions
  to typealiases (see test/SymbolGraph/Relationships/MemberOf/Typealias.swift,
  test/SymbolGraph/Symbols/Names.swift)
This commit is contained in:
Max Obermeier
2022-09-16 20:02:40 +02:00
committed by GitHub
parent 4b361d5bb6
commit 453fd2231b
43 changed files with 914 additions and 266 deletions

View File

@@ -1357,6 +1357,11 @@ def pretty_print: Flag<["-"], "pretty-print">,
Flags<[SwiftAPIExtractOption, SwiftSymbolGraphExtractOption]>,
HelpText<"Pretty-print the output JSON">;
def emit_extension_block_symbols: Flag<["-"], "emit-extension-block-symbols">,
Flags<[SwiftSymbolGraphExtractOption, FrontendOption,
NoInteractiveOption, SupplementaryOutput, HelpHidden]>,
HelpText<"Emit 'swift.extension' symbols for extensions to external types instead of directly associating members and conformances with the extended nominal when generating symbol graphs">;
// swift-symbolgraph-extract-only options
def output_dir : Separate<["-"], "output-dir">,
Flags<[NoDriverOption, SwiftSymbolGraphExtractOption, SwiftAPIDigesterOption,

View File

@@ -48,6 +48,11 @@ struct SymbolGraphOptions {
/// Whether to include documentation for clang nodes or not.
bool IncludeClangDocs = false;
/// Whether to emit "swift.extension" symbols for extensions to external types
/// along with "extensionTo" relationships instead of directly associating
/// members and conformances with the extended nominal.
bool EmitExtensionBlockSymbols = false;
};
} // end namespace symbolgraphgen

View File

@@ -617,6 +617,7 @@ ToolChain::constructInvocation(const CompileJobAction &job,
context.Args.AddLastArg(Arguments, options::OPT_emit_symbol_graph_dir);
}
context.Args.AddLastArg(Arguments, options::OPT_include_spi_symbols);
context.Args.AddLastArg(Arguments, options::OPT_emit_extension_block_symbols);
context.Args.AddLastArg(Arguments, options::OPT_symbol_graph_minimum_access_level);
return II;
@@ -1113,6 +1114,7 @@ ToolChain::constructInvocation(const MergeModuleJobAction &job,
context.Args.AddLastArg(Arguments, options::OPT_emit_symbol_graph);
context.Args.AddLastArg(Arguments, options::OPT_emit_symbol_graph_dir);
context.Args.AddLastArg(Arguments, options::OPT_include_spi_symbols);
context.Args.AddLastArg(Arguments, options::OPT_emit_extension_block_symbols);
context.Args.AddLastArg(Arguments, options::OPT_symbol_graph_minimum_access_level);
context.Args.AddLastArg(Arguments, options::OPT_import_objc_header);

View File

@@ -172,6 +172,7 @@ int swift_symbolgraph_extract_main(ArrayRef<const char *> Args,
ParsedArgs.hasArg(OPT_skip_inherited_docs),
ParsedArgs.hasArg(OPT_include_spi_symbols),
/*IncludeClangDocs=*/false,
ParsedArgs.hasArg(OPT_emit_extension_block_symbols),
};
if (auto *A = ParsedArgs.getLastArg(OPT_minimum_access_level)) {

View File

@@ -1300,6 +1300,8 @@ static void ParseSymbolGraphArgs(symbolgraphgen::SymbolGraphOptions &Opts,
Opts.SkipInheritedDocs = Args.hasArg(OPT_skip_inherited_docs);
Opts.IncludeSPISymbols = Args.hasArg(OPT_include_spi_symbols);
Opts.EmitExtensionBlockSymbols =
Args.hasArg(OPT_emit_extension_block_symbols);
if (auto *A = Args.getLastArg(OPT_symbol_graph_minimum_access_level)) {
Opts.MinimumAccessLevel =

View File

@@ -104,7 +104,20 @@ struct RelationshipKind {
static inline RelationshipKind OptionalRequirementOf() {
return RelationshipKind { "optionalRequirementOf" };
}
/**
A symbol A extends a symbol B with members or conformances.
This relationship describes the connection between extension blocks
(swift.extension symbols) and the type they extend.
The implied inverse of this relationship is a symbol B that is extended
by an extension block symbol A.
*/
static inline RelationshipKind ExtensionTo() {
return RelationshipKind{"extensionTo"};
}
bool operator==(const RelationshipKind &Other) const {
return Name == Other.Name;
}

View File

@@ -14,7 +14,7 @@
#define SWIFT_SYMBOLGRAPHGEN_FORMATVERSION_H
#define SWIFT_SYMBOLGRAPH_FORMAT_MAJOR 0
#define SWIFT_SYMBOLGRAPH_FORMAT_MINOR 5
#define SWIFT_SYMBOLGRAPH_FORMAT_PATCH 3
#define SWIFT_SYMBOLGRAPH_FORMAT_MINOR 6
#define SWIFT_SYMBOLGRAPH_FORMAT_PATCH 0
#endif // SWIFT_SYMBOLGRAPHGEN_FORMATVERSION_H

View File

@@ -12,6 +12,8 @@
// Adds Symbol Graph JSON serialization to other types.
//===----------------------------------------------------------------------===//
#include "JSON.h"
#include "Symbol.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/Decl.h"
#include "swift/AST/FileUnit.h"
@@ -21,7 +23,6 @@
#include "swift/AST/USRGeneration.h"
#include "swift/ClangImporter/ClangModule.h"
#include "swift/Serialization/SerializedModuleLoader.h"
#include "JSON.h"
void swift::symbolgraphgen::serialize(const llvm::VersionTuple &VT,
llvm::json::OStream &OS) {
@@ -69,6 +70,8 @@ void swift::symbolgraphgen::serialize(const ExtensionDecl *Extension,
if (const auto *ExtendedModule = ExtendedNominal->getModuleContext()) {
OS.attribute("extendedModule", ExtendedModule->getNameStr());
}
OS.attribute("typeKind", Symbol::getKind(ExtendedNominal).first);
}
SmallVector<Requirement, 4> FilteredRequirements;

View File

@@ -33,16 +33,24 @@
using namespace swift;
using namespace symbolgraphgen;
Symbol::Symbol(SymbolGraph *Graph, const ExtensionDecl *ED,
const NominalTypeDecl *SynthesizedBaseTypeDecl, Type BaseType)
: Symbol::Symbol(Graph, nullptr, ED, SynthesizedBaseTypeDecl, BaseType) {}
Symbol::Symbol(SymbolGraph *Graph, const ValueDecl *VD,
const NominalTypeDecl *SynthesizedBaseTypeDecl,
Type BaseType)
: Graph(Graph),
VD(VD),
BaseType(BaseType),
SynthesizedBaseTypeDecl(SynthesizedBaseTypeDecl) {
if (!BaseType && SynthesizedBaseTypeDecl)
BaseType = SynthesizedBaseTypeDecl->getDeclaredInterfaceType();
const NominalTypeDecl *SynthesizedBaseTypeDecl, Type BaseType)
: Symbol::Symbol(Graph, VD, nullptr, SynthesizedBaseTypeDecl, BaseType) {}
Symbol::Symbol(SymbolGraph *Graph, const ValueDecl *VD, const ExtensionDecl *ED,
const NominalTypeDecl *SynthesizedBaseTypeDecl, Type BaseType)
: Graph(Graph), D(VD), BaseType(BaseType),
SynthesizedBaseTypeDecl(SynthesizedBaseTypeDecl) {
if (!BaseType && SynthesizedBaseTypeDecl)
BaseType = SynthesizedBaseTypeDecl->getDeclaredInterfaceType();
if (D == nullptr) {
D = ED;
}
}
void Symbol::serializeKind(StringRef Identifier, StringRef DisplayName,
llvm::json::OStream &OS) const {
@@ -52,10 +60,10 @@ void Symbol::serializeKind(StringRef Identifier, StringRef DisplayName,
});
}
std::pair<StringRef, StringRef> Symbol::getKind(const ValueDecl *VD) const {
std::pair<StringRef, StringRef> Symbol::getKind(const Decl *D) {
// Make sure supportsKind stays in sync with getKind.
assert(Symbol::supportsKind(VD->getKind()) && "unsupported decl kind");
switch (VD->getKind()) {
assert(Symbol::supportsKind(D->getKind()) && "unsupported decl kind");
switch (D->getKind()) {
case swift::DeclKind::Class:
return {"swift.class", "Class"};
case swift::DeclKind::Struct:
@@ -70,7 +78,9 @@ std::pair<StringRef, StringRef> Symbol::getKind(const ValueDecl *VD) const {
return {"swift.init", "Initializer"};
case swift::DeclKind::Destructor:
return {"swift.deinit", "Deinitializer"};
case swift::DeclKind::Func:
case swift::DeclKind::Func: {
const auto *VD = cast<ValueDecl>(D);
if (VD->isOperator())
return {"swift.func.op", "Operator"};
if (VD->isStatic())
@@ -78,29 +88,38 @@ std::pair<StringRef, StringRef> Symbol::getKind(const ValueDecl *VD) const {
if (VD->getDeclContext()->getSelfNominalTypeDecl())
return {"swift.method", "Instance Method"};
return {"swift.func", "Function"};
case swift::DeclKind::Var:
}
case swift::DeclKind::Var: {
const auto *VD = cast<ValueDecl>(D);
if (VD->isStatic())
return {"swift.type.property", "Type Property"};
if (VD->getDeclContext()->getSelfNominalTypeDecl())
return {"swift.property", "Instance Property"};
return {"swift.var", "Global Variable"};
case swift::DeclKind::Subscript:
}
case swift::DeclKind::Subscript: {
const auto *VD = cast<ValueDecl>(D);
if (VD->isStatic())
return {"swift.type.subscript", "Type Subscript"};
return {"swift.subscript", "Instance Subscript"};
}
case swift::DeclKind::TypeAlias:
return {"swift.typealias", "Type Alias"};
case swift::DeclKind::AssociatedType:
return {"swift.associatedtype", "Associated Type"};
case swift::DeclKind::Extension:
return {"swift.extension", "Extension"};
default:
llvm::errs() << "Unsupported kind: " << VD->getKindName(VD->getKind());
llvm::errs() << "Unsupported kind: " << D->getKindName(D->getKind());
llvm_unreachable("Unsupported declaration kind for symbol graph");
}
}
void Symbol::serializeKind(llvm::json::OStream &OS) const {
AttributeRAII A("kind", OS);
std::pair<StringRef, StringRef> IDAndName = getKind(VD);
std::pair<StringRef, StringRef> IDAndName = getKind(D);
serializeKind(IDAndName.first, IDAndName.second, OS);
}
@@ -128,7 +147,14 @@ void Symbol::serializeNames(llvm::json::OStream &OS) const {
SmallVector<PathComponent, 8> PathComponents;
getPathComponents(PathComponents);
if (isa<GenericTypeDecl>(VD) || isa<EnumElementDecl>(VD)) {
const ValueDecl *Decl = nullptr;
if (const auto *ED = dyn_cast<ExtensionDecl>(D)) {
Decl = ED->getExtendedNominal();
} else if (const auto *VD = dyn_cast<ValueDecl>(D)) {
Decl = VD;
}
if (isa<GenericTypeDecl>(Decl) || isa<EnumElementDecl>(Decl)) {
SmallString<64> FullyQualifiedTitle;
for (const auto *It = PathComponents.begin(); It != PathComponents.end(); ++It) {
@@ -180,15 +206,14 @@ void Symbol::serializeRange(size_t InitialIndentation,
const ValueDecl *Symbol::getDeclInheritingDocs() const {
// get the decl that would provide docs for this symbol
const auto *DocCommentProvidingDecl =
dyn_cast_or_null<ValueDecl>(
getDocCommentProvidingDecl(VD, /*AllowSerialized=*/true));
const auto *DocCommentProvidingDecl = dyn_cast_or_null<ValueDecl>(
getDocCommentProvidingDecl(D, /*AllowSerialized=*/true));
// if the decl is the same as the one for this symbol, we're not
// inheriting docs, so return null. however, if this symbol is
// a synthesized symbol, `VD` is actually the source symbol, and
// a synthesized symbol, `D` is actually the source symbol, and
// we should point to that one regardless.
if (DocCommentProvidingDecl == VD && !SynthesizedBaseTypeDecl) {
if (DocCommentProvidingDecl == D && !SynthesizedBaseTypeDecl) {
return nullptr;
} else {
// otherwise, return whatever `getDocCommentProvidingDecl` returned.
@@ -200,13 +225,13 @@ const ValueDecl *Symbol::getDeclInheritingDocs() const {
namespace {
StringRef getFileNameForDecl(const ValueDecl *VD) {
if (!VD) return StringRef{};
StringRef getFileNameForDecl(const Decl *D) {
if (!D) return StringRef{};
SourceLoc Loc = VD->getLoc(/*SerializedOK=*/true);
SourceLoc Loc = D->getLoc(/*SerializedOK=*/true);
if (Loc.isInvalid()) return StringRef{};
SourceManager &SourceM = VD->getASTContext().SourceMgr;
SourceManager &SourceM = D->getASTContext().SourceMgr;
return SourceM.getDisplayNameForLoc(Loc);
}
@@ -230,7 +255,7 @@ void serializeFileURI(llvm::json::OStream &OS, StringRef FileName) {
}
void Symbol::serializeDocComment(llvm::json::OStream &OS) const {
if (ClangNode ClangN = VD->getClangNode()) {
if (ClangNode ClangN = D->getClangNode()) {
if (!Graph->Walker.Options.IncludeClangDocs)
return;
@@ -256,7 +281,7 @@ void Symbol::serializeDocComment(llvm::json::OStream &OS) const {
StringRef FileName = getFileNameForDecl(ClangD);
if (!FileName.empty())
serializeFileURI(OS, FileName);
if (const auto *ModuleD = VD->getModuleContext()) {
if (const auto *ModuleD = D->getModuleContext()) {
OS.attribute("module", ModuleD->getNameStr());
}
OS.attributeArray("lines", [&]() {
@@ -271,12 +296,12 @@ void Symbol::serializeDocComment(llvm::json::OStream &OS) const {
return;
}
const auto *DocCommentProvidingDecl = VD;
const auto *DocCommentProvidingDecl = D;
if (!Graph->Walker.Options.SkipInheritedDocs) {
DocCommentProvidingDecl = dyn_cast_or_null<ValueDecl>(
getDocCommentProvidingDecl(VD, /*AllowSerialized=*/true));
getDocCommentProvidingDecl(D, /*AllowSerialized=*/true));
if (!DocCommentProvidingDecl) {
DocCommentProvidingDecl = VD;
DocCommentProvidingDecl = D;
}
}
auto RC = DocCommentProvidingDecl->getRawComment(/*SerializedOK=*/true);
@@ -323,7 +348,7 @@ void Symbol::serializeDocComment(llvm::json::OStream &OS) const {
}
void Symbol::serializeFunctionSignature(llvm::json::OStream &OS) const {
if (const auto *FD = dyn_cast_or_null<FuncDecl>(VD)) {
if (const auto *FD = dyn_cast_or_null<FuncDecl>(D)) {
OS.attributeObject("functionSignature", [&](){
// Parameters
@@ -392,12 +417,16 @@ static SubstitutionMap getSubMapForDecl(const ValueDecl *D, Type BaseType) {
}
void Symbol::serializeSwiftGenericMixin(llvm::json::OStream &OS) const {
SubstitutionMap SubMap;
if (BaseType)
SubMap = getSubMapForDecl(VD, BaseType);
const auto *VD = dyn_cast<ValueDecl>(D);
if (const auto *GC = VD->getAsGenericContext()) {
if (VD && BaseType) {
SubMap = getSubMapForDecl(VD, BaseType);
} else {
SubMap = {};
}
if (const auto *GC = D->getAsGenericContext()) {
if (const auto Generics = GC->getGenericSignature()) {
SmallVector<const GenericTypeParamType *, 4> FilteredParams;
@@ -405,9 +434,9 @@ void Symbol::serializeSwiftGenericMixin(llvm::json::OStream &OS) const {
filterGenericParams(Generics.getGenericParams(), FilteredParams,
SubMap);
const auto *Self = dyn_cast<NominalTypeDecl>(VD);
const auto *Self = dyn_cast<NominalTypeDecl>(D);
if (!Self) {
Self = VD->getDeclContext()->getSelfNominalTypeDecl();
Self = D->getDeclContext()->getSelfNominalTypeDecl();
}
filterGenericRequirements(Generics.getRequirements(), Self,
@@ -439,9 +468,13 @@ void Symbol::serializeSwiftGenericMixin(llvm::json::OStream &OS) const {
}
void Symbol::serializeSwiftExtensionMixin(llvm::json::OStream &OS) const {
if (const auto *Extension
= dyn_cast_or_null<ExtensionDecl>(VD->getDeclContext())) {
::serialize(Extension, OS);
if (const auto *ED = dyn_cast<ExtensionDecl>(D)) {
::serialize(ED, OS);
} else if (const auto *VD = dyn_cast<ValueDecl>(D)) {
if (const auto *Extension =
dyn_cast_or_null<ExtensionDecl>(VD->getDeclContext())) {
::serialize(Extension, OS);
}
}
}
@@ -450,41 +483,48 @@ void Symbol::serializeDeclarationFragmentMixin(llvm::json::OStream &OS) const {
}
void Symbol::serializeAccessLevelMixin(llvm::json::OStream &OS) const {
OS.attribute("accessLevel", getAccessLevelSpelling(VD->getFormalAccess()));
if (const auto *ED = dyn_cast<ExtensionDecl>(D)) {
OS.attribute("accessLevel",
getAccessLevelSpelling(getEffectiveAccessLevel(ED)));
} else if (const auto *VD = dyn_cast<ValueDecl>(D)) {
OS.attribute("accessLevel", getAccessLevelSpelling(VD->getFormalAccess()));
}
}
void Symbol::serializeMetadataMixin(llvm::json::OStream &OS) const {
StringRef Category = documentationMetadataForDecl(VD);
StringRef Category = documentationMetadataForDecl(D);
if (!Category.empty())
OS.attribute("metadata", Category);
}
void Symbol::serializeLocationMixin(llvm::json::OStream &OS) const {
if (ClangNode ClangN = VD->getClangNode()) {
if (!Graph->Walker.Options.IncludeClangDocs)
return;
if (const auto *VD = dyn_cast<ValueDecl>(D)) {
if (ClangNode ClangN = VD->getClangNode()) {
if (!Graph->Walker.Options.IncludeClangDocs)
return;
if (auto *ClangD = ClangN.getAsDecl()) {
StringRef FileName = getFileNameForDecl(ClangD);
if (!FileName.empty()) {
OS.attributeObject("location", [&](){
// TODO: We should use a common function to fill in the location
// information for both cursor info and symbol graph gen, then also
// include position here.
serializeFileURI(OS, FileName);
});
if (auto *ClangD = ClangN.getAsDecl()) {
StringRef FileName = getFileNameForDecl(ClangD);
if (!FileName.empty()) {
OS.attributeObject("location", [&](){
// TODO: We should use a common function to fill in the location
// information for both cursor info and symbol graph gen, then also
// include position here.
serializeFileURI(OS, FileName);
});
}
}
return;
}
return;
}
auto FileName = getFileNameForDecl(VD);
auto FileName = getFileNameForDecl(D);
if (FileName.empty()) {
return;
}
// TODO: Fold serializePosition into serializeFileURI so we don't need to load Loc twice?
auto Loc = VD->getLoc(/*SerializedOK=*/true);
auto Loc = D->getLoc(/*SerializedOK=*/true);
if (Loc.isInvalid()) {
return;
}
@@ -566,7 +606,7 @@ llvm::StringMap<Availability> &Availabilities) {
void Symbol::serializeAvailabilityMixin(llvm::json::OStream &OS) const {
llvm::StringMap<Availability> Availabilities;
getInheritedAvailabilities(VD, Availabilities);
getInheritedAvailabilities(D, Availabilities);
if (Availabilities.empty()) {
return;
@@ -580,7 +620,7 @@ void Symbol::serializeAvailabilityMixin(llvm::json::OStream &OS) const {
}
void Symbol::serializeSPIMixin(llvm::json::OStream &OS) const {
if (VD->isSPI())
if (D->isSPI())
OS.attribute("spi", true);
}
@@ -605,52 +645,78 @@ void Symbol::serialize(llvm::json::OStream &OS) const {
});
}
swift::DeclName Symbol::getName(const Decl *D) const {
if (const auto *ED = dyn_cast<ExtensionDecl>(D)) {
return ED->getExtendedNominal()->getName();
} else {
return cast<ValueDecl>(D)->getName();
}
}
const ValueDecl *Symbol::getSymbolDecl() const {
if (const auto *ED = dyn_cast<ExtensionDecl>(D)) {
return ED->getExtendedNominal();
} else {
return cast<ValueDecl>(D);
}
}
void
Symbol::getPathComponents(SmallVectorImpl<PathComponent> &Components) const {
const ValueDecl *Decl = nullptr;
if (const auto *ED = dyn_cast<ExtensionDecl>(D)) {
Decl = ED->getExtendedNominal();
} else if (const auto *VD = dyn_cast<ValueDecl>(D)) {
Decl = VD;
}
// Note: this is also used for sourcekit's cursor-info request, so can be
// called on local symbols too. For such symbols, the path contains all parent
// decl contexts that are currently representable in the symbol graph,
// skipping over the rest (e.g. containing closures and accessors).
auto collectPathComponents = [&](const ValueDecl *Decl,
SmallVectorImpl<PathComponent> &DeclComponents) {
// Collect the spellings, kinds, and decls of the fully qualified identifier
// components.
while (Decl && !isa<ModuleDecl>(Decl)) {
SmallString<32> Scratch;
Decl->getName().getString(Scratch);
if (supportsKind(Decl->getKind()))
DeclComponents.push_back({Scratch, getKind(Decl).first, Decl});
auto collectPathComponents =
[&](const ValueDecl *Decl,
SmallVectorImpl<PathComponent> &DeclComponents) {
// Collect the spellings, kinds, and decls of the fully qualified
// identifier components.
while (Decl && !isa<ModuleDecl>(Decl)) {
SmallString<32> Scratch;
getName(Decl).getString(Scratch);
// Find the next parent.
auto *DC = Decl->getDeclContext();
while (DC && DC->getContextKind() == DeclContextKind::AbstractClosureExpr)
DC = DC->getParent();
if (DC) {
if (const auto *Nominal = DC->getSelfNominalTypeDecl()) {
Decl = Nominal;
} else {
Decl = dyn_cast_or_null<ValueDecl>(DC->getAsDecl());
if (supportsKind(Decl->getKind()))
DeclComponents.push_back({Scratch, getKind(Decl).first, Decl});
// Find the next parent.
auto *DC = Decl->getDeclContext();
while (DC &&
DC->getContextKind() == DeclContextKind::AbstractClosureExpr)
DC = DC->getParent();
if (DC) {
if (const auto *Nominal = DC->getSelfNominalTypeDecl()) {
Decl = Nominal;
} else {
Decl = dyn_cast_or_null<ValueDecl>(DC->getAsDecl());
}
} else {
Decl = nullptr;
}
}
} else {
Decl = nullptr;
}
}
};
};
if (const auto BaseTypeDecl = getSynthesizedBaseTypeDecl()) {
// This is a synthesized member of some base type declaration, actually
// existing on another type, such as a default implementation of
// a protocol. Build a path as if it were defined in the base type.
SmallString<32> LastPathComponent;
VD->getName().getString(LastPathComponent);
if (supportsKind(VD->getKind()))
Components.push_back({LastPathComponent, getKind(VD).first, VD});
getName(Decl).getString(LastPathComponent);
if (supportsKind(Decl->getKind()))
Components.push_back({LastPathComponent, getKind(Decl).first, Decl});
collectPathComponents(BaseTypeDecl, Components);
} else {
// Otherwise, this is just a normal declaration, so we can build
// its path normally.
collectPathComponents(VD, Components);
collectPathComponents(Decl, Components);
}
// The list is leaf-to-root, but we want root-to-leaf, so reverse it.
@@ -697,7 +763,7 @@ void Symbol::printPath(llvm::raw_ostream &OS) const {
void Symbol::getUSR(SmallVectorImpl<char> &USR) const {
llvm::raw_svector_ostream OS(USR);
ide::printDeclUSR(VD, OS);
ide::printDeclUSR(D, OS);
if (SynthesizedBaseTypeDecl) {
OS << "::SYNTHESIZED::";
ide::printDeclUSR(SynthesizedBaseTypeDecl, OS);
@@ -717,9 +783,30 @@ bool Symbol::supportsKind(DeclKind Kind) {
case DeclKind::Var: LLVM_FALLTHROUGH;
case DeclKind::Subscript: LLVM_FALLTHROUGH;
case DeclKind::TypeAlias: LLVM_FALLTHROUGH;
case DeclKind::AssociatedType:
case DeclKind::AssociatedType: LLVM_FALLTHROUGH;
case DeclKind::Extension:
return true;
default:
return false;
}
}
AccessLevel Symbol::getEffectiveAccessLevel(const ExtensionDecl *ED) {
AccessLevel maxPropertyAL = AccessLevel::Private;
for (auto Member : ED->getMembers()) {
if (const auto *VMember = dyn_cast<ValueDecl>(Member)) {
maxPropertyAL = std::max(maxPropertyAL, VMember->getFormalAccess());
}
}
AccessLevel maxInheritedAL = AccessLevel::Private;
for (auto Inherited : ED->getInherited()) {
if (const auto *Proto = dyn_cast_or_null<ProtocolDecl>(
Inherited.getType()->getAnyNominal())) {
maxInheritedAL = std::max(maxInheritedAL, Proto->getFormalAccess());
}
}
return std::min(ED->getExtendedNominal()->getFormalAccess(),
std::max(maxPropertyAL, maxInheritedAL));
}

View File

@@ -32,11 +32,16 @@ struct SymbolGraph;
class Symbol {
/// The symbol graph in which this symbol resides.
SymbolGraph *Graph;
const ValueDecl *VD;
/// Either a ValueDecl* or ExtensionDecl*.
const Decl *D;
Type BaseType;
const NominalTypeDecl *SynthesizedBaseTypeDecl;
std::pair<StringRef, StringRef> getKind(const ValueDecl *VD) const;
Symbol(SymbolGraph *Graph, const ValueDecl *VD, const ExtensionDecl *ED,
const NominalTypeDecl *SynthesizedBaseTypeDecl,
Type BaseTypeForSubstitution = Type());
swift::DeclName getName(const Decl *D) const;
void serializeKind(StringRef Identifier, StringRef DisplayName,
llvm::json::OStream &OS) const;
@@ -81,6 +86,10 @@ class Symbol {
void serializeSPIMixin(llvm::json::OStream &OS) const;
public:
Symbol(SymbolGraph *Graph, const ExtensionDecl *ED,
const NominalTypeDecl *SynthesizedBaseTypeDecl,
Type BaseTypeForSubstitution = Type());
Symbol(SymbolGraph *Graph, const ValueDecl *VD,
const NominalTypeDecl *SynthesizedBaseTypeDecl,
Type BaseTypeForSubstitution = Type());
@@ -91,9 +100,9 @@ public:
return Graph;
}
const ValueDecl *getSymbolDecl() const {
return VD;
}
const ValueDecl *getSymbolDecl() const;
const Decl *getLocalSymbolDecl() const { return D; }
Type getBaseType() const {
return BaseType;
@@ -122,6 +131,26 @@ public:
const ValueDecl *getDeclInheritingDocs() const;
static bool supportsKind(DeclKind Kind);
/// Determines the effective access level of the given extension.
///
/// The effective access level is defined as the minimum of:
/// - the maximum access level of a property or conformance
/// - the access level of the extended nominal
///
/// The effective access level is defined this way so that the extension
/// symbol's access level equals the highest access level of any of the
/// symbols the extension symbol has a relationship to.
///
/// This function is not logically equivalent to
/// `ExtensionDecl.getMaxAccessLevel()`, which computes the maximum access
/// level any of the `ExtensionDecl`'s members
/// **can** have based on the extended type and types used in constraints.
static AccessLevel getEffectiveAccessLevel(const ExtensionDecl *ED);
/// Determines the kind of Symbol the given declaration produces and
/// returns the respective symbol kind identifier and kind name.
static std::pair<StringRef, StringRef> getKind(const Decl *D);
};
} // end namespace symbolgraphgen
@@ -151,18 +180,19 @@ template <> struct DenseMapInfo<Symbol> {
static unsigned getHashValue(const Symbol S) {
unsigned H = 0;
H ^= DenseMapInfo<SymbolGraph *>::getHashValue(S.getGraph());
H ^= DenseMapInfo<const swift::ValueDecl *>::getHashValue(S.getSymbolDecl());
H ^=
DenseMapInfo<const swift::Decl *>::getHashValue(S.getLocalSymbolDecl());
H ^= DenseMapInfo<const swift::NominalTypeDecl *>::getHashValue(S.getSynthesizedBaseTypeDecl());
H ^= DenseMapInfo<swift::Type>::getHashValue(S.getBaseType());
return H;
}
static bool isEqual(const Symbol LHS, const Symbol RHS) {
return LHS.getGraph() == RHS.getGraph() &&
LHS.getSymbolDecl() == RHS.getSymbolDecl() &&
LHS.getSynthesizedBaseTypeDecl() ==
RHS.getSynthesizedBaseTypeDecl() &&
DenseMapInfo<swift::Type>::
isEqual(LHS.getBaseType(), RHS.getBaseType());
LHS.getLocalSymbolDecl() == RHS.getLocalSymbolDecl() &&
LHS.getSynthesizedBaseTypeDecl() ==
RHS.getSynthesizedBaseTypeDecl() &&
DenseMapInfo<swift::Type>::isEqual(LHS.getBaseType(),
RHS.getBaseType());
}
};
} // end namespace llvm

View File

@@ -29,26 +29,21 @@
using namespace swift;
using namespace symbolgraphgen;
SymbolGraph::SymbolGraph(SymbolGraphASTWalker &Walker,
ModuleDecl &M,
SymbolGraph::SymbolGraph(SymbolGraphASTWalker &Walker, ModuleDecl &M,
Optional<ModuleDecl *> ExtendedModule,
markup::MarkupContext &Ctx,
Optional<llvm::VersionTuple> ModuleVersion,
bool IsForSingleNode)
: Walker(Walker),
M(M),
ExtendedModule(ExtendedModule),
Ctx(Ctx),
ModuleVersion(ModuleVersion),
IsForSingleNode(IsForSingleNode) {
if (auto *DM = M.getDeclaringModuleIfCrossImportOverlay()) {
DeclaringModule = DM;
SmallVector<Identifier, 1> Bystanders;
if (M.getRequiredBystandersIfCrossImportOverlay(DM, Bystanders)) {
BystanderModules = Bystanders;
}
: Walker(Walker), M(M), ExtendedModule(ExtendedModule), Ctx(Ctx),
ModuleVersion(ModuleVersion), IsForSingleNode(IsForSingleNode) {
if (auto *DM = M.getDeclaringModuleIfCrossImportOverlay()) {
DeclaringModule = DM;
SmallVector<Identifier, 1> Bystanders;
if (M.getRequiredBystandersIfCrossImportOverlay(DM, Bystanders)) {
BystanderModules = Bystanders;
}
}
}
// MARK: - Utilities
@@ -224,7 +219,7 @@ void SymbolGraph::recordEdge(Symbol Source,
}
void SymbolGraph::recordMemberRelationship(Symbol S) {
const auto *DC = S.getSymbolDecl()->getDeclContext();
const auto *DC = S.getLocalSymbolDecl()->getDeclContext();
switch (DC->getContextKind()) {
case DeclContextKind::GenericTypeDecl:
case DeclContextKind::ExtensionDecl:
@@ -242,12 +237,25 @@ void SymbolGraph::recordMemberRelationship(Symbol S) {
if (isRequirementOrDefaultImplementation(S.getSymbolDecl())) {
return;
}
if (DC->getSelfNominalTypeDecl() == nullptr) {
// If we couldn't look up the type the member is declared on (e.g.
// because the member is declared in an extension whose extended type
// doesn't exist), don't record a memberOf relationship.
return;
}
// If this is an extension to an external type, we use the extension
// symbol itself as the target.
if (auto const *Extension =
dyn_cast_or_null<ExtensionDecl>(DC->getAsDecl())) {
if (this->Walker.shouldBeRecordedAsExtension(Extension)) {
return recordEdge(S, Symbol(this, Extension, nullptr),
RelationshipKind::MemberOf());
}
}
return recordEdge(S,
Symbol(this, DC->getSelfNominalTypeDecl(), nullptr),
RelationshipKind::MemberOf());
@@ -289,11 +297,11 @@ void SymbolGraph::recordConformanceSynthesizedMemberRelationships(Symbol S) {
if (!Walker.Options.EmitSynthesizedMembers) {
return;
}
const auto VD = S.getSymbolDecl();
const auto D = S.getLocalSymbolDecl();
const NominalTypeDecl *OwningNominal = nullptr;
if (const auto *ThisNominal = dyn_cast<NominalTypeDecl>(VD)) {
if (const auto *ThisNominal = dyn_cast<NominalTypeDecl>(D)) {
OwningNominal = ThisNominal;
} else if (const auto *Extension = dyn_cast<ExtensionDecl>(VD)) {
} else if (const auto *Extension = dyn_cast<ExtensionDecl>(D)) {
if (const auto *ExtendedNominal = Extension->getExtendedNominal()) {
if (!ExtendedNominal->getModuleContext()->getNameStr()
.equals(M.getNameStr())) {
@@ -328,6 +336,12 @@ void SymbolGraph::recordConformanceSynthesizedMemberRelationships(Symbol S) {
continue;
}
// If D is not the OwningNominal, it is an ExtensionDecl. In that case
// we only want to get members that were enabled by this exact extension.
if (D != OwningNominal && Info.EnablingExt != D) {
continue;
}
for (const auto ExtensionMember : Info.Ext->getMembers()) {
if (const auto SynthMember = dyn_cast<ValueDecl>(ExtensionMember)) {
if (SynthMember->isObjC()) {
@@ -352,11 +366,10 @@ void SymbolGraph::recordConformanceSynthesizedMemberRelationships(Symbol S) {
auto ExtendedSG = Walker.getModuleSymbolGraph(OwningNominal);
Symbol Source(this, SynthMember, OwningNominal);
Symbol Target(this, OwningNominal, nullptr);
ExtendedSG->Nodes.insert(Source);
ExtendedSG->recordEdge(Source, Target, RelationshipKind::MemberOf());
ExtendedSG->recordEdge(Source, S, RelationshipKind::MemberOf());
}
}
}
@@ -447,27 +460,24 @@ void SymbolGraph::recordOptionalRequirementRelationships(Symbol S) {
}
}
void
SymbolGraph::recordConformanceRelationships(Symbol S) {
const auto VD = S.getSymbolDecl();
if (const auto *NTD = dyn_cast<NominalTypeDecl>(VD)) {
void SymbolGraph::recordConformanceRelationships(Symbol S) {
const auto D = S.getLocalSymbolDecl();
if (const auto *NTD = dyn_cast<NominalTypeDecl>(D)) {
if (auto *PD = dyn_cast<ProtocolDecl>(NTD)) {
PD->walkInheritedProtocols([&](ProtocolDecl *inherited) {
if (inherited != PD) {
recordEdge(Symbol(this, VD, nullptr),
Symbol(this, inherited, nullptr),
RelationshipKind::ConformsTo(),
nullptr);
recordEdge(S, Symbol(this, inherited, nullptr),
RelationshipKind::ConformsTo(), nullptr);
}
return TypeWalker::Action::Continue;
});
} else {
for (const auto *Conformance : NTD->getAllConformances()) {
recordEdge(Symbol(this, VD, nullptr),
Symbol(this, Conformance->getProtocol(), nullptr),
RelationshipKind::ConformsTo(),
dyn_cast_or_null<ExtensionDecl>(Conformance->getDeclContext()));
recordEdge(
S, Symbol(this, Conformance->getProtocol(), nullptr),
RelationshipKind::ConformsTo(),
dyn_cast_or_null<ExtensionDecl>(Conformance->getDeclContext()));
}
}
}
@@ -546,7 +556,7 @@ SymbolGraph::serializeDeclarationFragments(StringRef Key,
Options.setBaseType(S.getBaseType());
Options.PrintAsMember = true;
}
S.getSymbolDecl()->print(Printer, Options);
S.getLocalSymbolDecl()->print(Printer, Options);
}
void
@@ -565,7 +575,7 @@ SymbolGraph::serializeSubheadingDeclarationFragments(StringRef Key,
llvm::json::OStream &OS) {
DeclarationFragmentPrinter Printer(this, OS, Key);
if (const auto *TD = dyn_cast<GenericTypeDecl>(S.getSymbolDecl())) {
if (const auto *TD = dyn_cast<GenericTypeDecl>(S.getLocalSymbolDecl())) {
Printer.printAbridgedType(TD, /*PrintKeyword=*/true);
} else {
auto Options = getSubHeadingDeclarationFragmentsPrintOptions();
@@ -573,7 +583,7 @@ SymbolGraph::serializeSubheadingDeclarationFragments(StringRef Key,
Options.setBaseType(S.getBaseType());
Options.PrintAsMember = true;
}
S.getSymbolDecl()->print(Printer, Options);
S.getLocalSymbolDecl()->print(Printer, Options);
}
}
@@ -637,7 +647,9 @@ bool SymbolGraph::isImplicitlyPrivate(const Decl *D,
if (const auto *Extension = dyn_cast<ExtensionDecl>(D)) {
if (const auto *Nominal = Extension->getExtendedNominal()) {
return isImplicitlyPrivate(Nominal, IgnoreContext);
return isImplicitlyPrivate(Nominal, IgnoreContext) ||
Symbol::getEffectiveAccessLevel(Extension) <
Walker.Options.MinimumAccessLevel;
}
}

View File

@@ -79,10 +79,8 @@ struct SymbolGraph {
*/
bool IsForSingleNode;
SymbolGraph(SymbolGraphASTWalker &Walker,
ModuleDecl &M,
Optional<ModuleDecl *> ExtendedModule,
markup::MarkupContext &Ctx,
SymbolGraph(SymbolGraphASTWalker &Walker, ModuleDecl &M,
Optional<ModuleDecl *> ExtendedModule, markup::MarkupContext &Ctx,
Optional<llvm::VersionTuple> ModuleVersion = None,
bool IsForSingleNode = false);

View File

@@ -33,15 +33,14 @@ bool areModulesEqual(const ModuleDecl *lhs, const ModuleDecl *rhs) {
} // anonymous namespace
SymbolGraphASTWalker::SymbolGraphASTWalker(ModuleDecl &M,
const SmallPtrSet<ModuleDecl *, 4> ExportedImportedModules,
const llvm::SmallDenseMap<ModuleDecl *, SmallPtrSet<Decl *, 4>, 4> QualifiedExportedImports,
const SymbolGraphOptions &Options)
: Options(Options),
M(M),
ExportedImportedModules(ExportedImportedModules),
QualifiedExportedImports(QualifiedExportedImports),
MainGraph(*this, M, None, Ctx) {}
SymbolGraphASTWalker::SymbolGraphASTWalker(
ModuleDecl &M, const SmallPtrSet<ModuleDecl *, 4> ExportedImportedModules,
const llvm::SmallDenseMap<ModuleDecl *, SmallPtrSet<Decl *, 4>, 4>
QualifiedExportedImports,
const SymbolGraphOptions &Options)
: Options(Options), M(M), ExportedImportedModules(ExportedImportedModules),
QualifiedExportedImports(QualifiedExportedImports),
MainGraph(*this, M, None, Ctx) {}
/// Get a "sub" symbol graph for the parent module of a type that
/// the main module `M` is extending.
@@ -88,11 +87,9 @@ SymbolGraph *SymbolGraphASTWalker::getModuleSymbolGraph(const Decl *D) {
if (Found != ExtendedModuleGraphs.end()) {
return Found->getValue();
}
auto *Memory = Ctx.allocate(sizeof(SymbolGraph), alignof(SymbolGraph));
auto *SG = new (Memory) SymbolGraph(*this,
MainGraph.M,
Optional<ModuleDecl *>(M),
Ctx);
auto *Memory = Ctx.allocate(sizeof(SymbolGraph), alignof(SymbolGraph));
auto *SG = new (Memory)
SymbolGraph(*this, MainGraph.M, Optional<ModuleDecl *>(M), Ctx);
ExtendedModuleGraphs.insert({M->getNameStr(), SG});
return SG;
@@ -117,29 +114,29 @@ bool isUnavailableOrObsoleted(const Decl *D) {
} // end anonymous namespace
bool SymbolGraphASTWalker::walkToDeclPre(Decl *D, CharSourceRange Range) {
if (isUnavailableOrObsoleted(D)) {
return false;
}
if (isUnavailableOrObsoleted(D)) {
return false;
}
switch (D->getKind()) {
// We'll record nodes for the following kinds of declarations.
case swift::DeclKind::Class:
case swift::DeclKind::Struct:
case swift::DeclKind::Enum:
case swift::DeclKind::EnumElement:
case swift::DeclKind::Protocol:
case swift::DeclKind::Constructor:
case swift::DeclKind::Func:
case swift::DeclKind::Var:
case swift::DeclKind::Subscript:
case swift::DeclKind::TypeAlias:
case swift::DeclKind::AssociatedType:
case swift::DeclKind::Extension:
break;
// We'll descend into everything else.
default:
return true;
switch (D->getKind()) {
// We'll record nodes for the following kinds of declarations.
case swift::DeclKind::Class:
case swift::DeclKind::Struct:
case swift::DeclKind::Enum:
case swift::DeclKind::EnumElement:
case swift::DeclKind::Protocol:
case swift::DeclKind::Constructor:
case swift::DeclKind::Func:
case swift::DeclKind::Var:
case swift::DeclKind::Subscript:
case swift::DeclKind::TypeAlias:
case swift::DeclKind::AssociatedType:
case swift::DeclKind::Extension:
break;
// We'll descend into everything else.
default:
return true;
}
auto SG = getModuleSymbolGraph(D);
@@ -158,25 +155,83 @@ bool SymbolGraphASTWalker::walkToDeclPre(Decl *D, CharSourceRange Range) {
return false;
}
// We only treat extensions to external types as extensions. Extensions to
// local types are directly associated with the extended nominal.
auto const shouldBeRecordedAsExtension =
this->shouldBeRecordedAsExtension(Extension);
Symbol Source = shouldBeRecordedAsExtension
? Symbol(ExtendedSG, Extension, nullptr)
: Symbol(ExtendedSG, ExtendedNominal, nullptr);
// The extended nominal is recorded elsewhere for local types.
if (shouldBeRecordedAsExtension) {
ExtendedSG->recordNode(Source);
// Next to the extension symbol itself, we also introduce a relationship
// between the extension symbol and the extended nominal.
ExtendedSG->recordEdge(Source,
Symbol(ExtendedSG, ExtendedNominal, nullptr),
RelationshipKind::ExtensionTo());
}
// If there are some protocol conformances on this extension, we'll
// grab them for some new conformsTo relationships.
if (!Extension->getInherited().empty()) {
// We want to add conformsTo relationships for all protocols implicitly
// implied by those explicitly stated on the extension.
//
// Thus, we have to expand two syntactic constructs:
// * `protocol A: B, C { ... }` declarations, where those that still have
// to be expanded are stored in `UnexpandedProtocols`
// that still have to be expanded
// * `typealias A = B & C` declarations, which are directly expanded to
// unexpanded protocols in `HandleProtocolOrComposition`
//
// The expansion adds the base protocol to `Protocols` and calls
// `HandleProtocolOrComposition` for the implied protocols. This process
// continues until there is nothing left to expand (`UnexpandedProtocols`
// is empty), because `HandleProtocolOrComposition` didn't add any new
// unexpanded protocols. At that point, all direct and indirect
// conformances are stored in `Protocols`.
// The symbol graph to use to record these relationships.
SmallVector<const ProtocolDecl *, 4> Protocols;
SmallVector<const ProtocolCompositionType *, 4> UnexpandedCompositions;
SmallVector<const ProtocolDecl *, 4> UnexpandedProtocols;
// Unwrap `UnexpandedCompositions` and add all unexpanded protocols to the
// `UnexpandedProtocols` list for expansion.
auto HandleProtocolOrComposition = [&](Type Ty) {
if (const auto *Proto =
dyn_cast_or_null<ProtocolDecl>(Ty->getAnyNominal())) {
Protocols.push_back(Proto);
} else if (const auto *Comp = Ty->getAs<ProtocolCompositionType>()) {
dyn_cast_or_null<ProtocolDecl>(Ty->getAnyNominal())) {
UnexpandedProtocols.push_back(Proto);
return;
}
SmallVector<const ProtocolCompositionType *, 4> UnexpandedCompositions;
if (const auto *Comp = Ty->getAs<ProtocolCompositionType>()) {
UnexpandedCompositions.push_back(Comp);
} else {
abort();
llvm_unreachable("Expected ProtocolDecl or ProtocolCompositionType");
}
while (const auto *Comp = UnexpandedCompositions.pop_back_val()) {
for (const auto &Member : Comp->getMembers()) {
if (const auto *Proto =
dyn_cast_or_null<ProtocolDecl>(Member->getAnyNominal())) {
Protocols.push_back(Proto);
UnexpandedProtocols.push_back(Proto);
} else if (const auto *Comp =
Member->getAs<ProtocolCompositionType>()) {
UnexpandedCompositions.push_back(Comp);
} else {
abort();
}
}
}
};
// Start the process with the conformances stated
// explicitly on the extension.
for (const auto &InheritedLoc : Extension->getInherited()) {
auto InheritedTy = InheritedLoc.getType();
if (!InheritedTy) {
@@ -185,30 +240,31 @@ bool SymbolGraphASTWalker::walkToDeclPre(Decl *D, CharSourceRange Range) {
HandleProtocolOrComposition(InheritedTy);
}
while (!UnexpandedCompositions.empty()) {
const auto *Comp = UnexpandedCompositions.pop_back_val();
for (const auto &Member : Comp->getMembers()) {
HandleProtocolOrComposition(Member);
// "Recursively" expand the unexpanded list and populate
// the expanded `Protocols` list (in an iterative manner).
while (!UnexpandedProtocols.empty()) {
const auto *Proto = UnexpandedProtocols.pop_back_val();
for (const auto &InheritedEntry : Proto->getInherited()) {
auto InheritedTy = InheritedEntry.getType();
if (!InheritedTy) {
continue;
}
HandleProtocolOrComposition(InheritedTy);
}
Protocols.push_back(Proto);
}
Symbol Source(ExtendedSG, ExtendedNominal, nullptr);
// Record the expanded list of protocols.
for (const auto *Proto : Protocols) {
Symbol Target(&MainGraph, Proto, nullptr);
ExtendedSG->recordEdge(Source, Target, RelationshipKind::ConformsTo(),
Extension);
}
// While we won't record this node per se, or all of the other kinds of
// relationships, we might establish some synthesized members because we
// We also might establish some synthesized members because we
// extended an external type.
if (ExtendedNominal->getModuleContext() != &M) {
ExtendedSG->recordConformanceSynthesizedMemberRelationships({
ExtendedSG,
ExtendedNominal,
nullptr
});
ExtendedSG->recordConformanceSynthesizedMemberRelationships(Source);
}
}
@@ -310,3 +366,9 @@ bool SymbolGraphASTWalker::isExportedImportedModule(const ModuleDecl *M) const {
bool SymbolGraphASTWalker::isOurModule(const ModuleDecl *M) const {
return areModulesEqual(M, &this->M) || isExportedImportedModule(M);
}
bool SymbolGraphASTWalker::shouldBeRecordedAsExtension(
const ExtensionDecl *ED) const {
return Options.EmitExtensionBlockSymbols &&
!areModulesEqual(ED->getModuleContext(), ED->getExtendedNominal()->getModuleContext());
}

View File

@@ -113,6 +113,12 @@ struct SymbolGraphASTWalker : public SourceEntityWalker {
/// Returns whether the given module is the main module, or is an `@_exported import` module.
virtual bool isOurModule(const ModuleDecl *M) const;
public:
/// Returns whether the given ExtensionDecl is to be recorded as an extra
/// extension block symbol, or if its members should be directly associated
/// with its extended nominal.
virtual bool shouldBeRecordedAsExtension(const ExtensionDecl *ED) const;
};
} // end namespace symbolgraphgen

View File

@@ -0,0 +1,23 @@
// RUN: %empty-directory(%t)
// RUN: cp -r %S/Inputs/EmitWhileBuilding/EmitWhileBuilding.framework %t
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -emit-module-path %t/EmitWhileBuilding.framework/Modules/EmitWhileBuilding.swiftmodule/%target-swiftmodule-name -import-underlying-module -F %t -module-name EmitWhileBuilding -disable-objc-attr-requires-foundation-module %s %S/Inputs/EmitWhileBuilding/Extra.swift -emit-symbol-graph -emit-symbol-graph-dir %t -emit-extension-block-symbols
// RUN: %FileCheck %s --input-file %t/EmitWhileBuilding.symbols.json --check-prefix TYPE
// RUN: %FileCheck %s --input-file %t/EmitWhileBuilding.symbols.json --check-prefix EXTENSION
// RUN: %{python} -c 'import os.path; import sys; sys.exit(1 if os.path.exists(sys.argv[1]) else 0)' %t/EmitWhileBuilding@EmitWhileBuilding.symbols.json
// RUN: %target-swift-symbolgraph-extract -sdk %clang-importer-sdk -module-name EmitWhileBuilding -F %t -output-dir %t -pretty-print -v -emit-extension-block-symbols
// RUN: %FileCheck %s --input-file %t/EmitWhileBuilding.symbols.json --check-prefix TYPE
// RUN: %FileCheck %s --input-file %t/EmitWhileBuilding.symbols.json --check-prefix EXTENSION
// RUN: %{python} -c 'import os.path; import sys; sys.exit(1 if os.path.exists(sys.argv[1]) else 0)' %t/EmitWhileBuilding@EmitWhileBuilding.symbols.json
// REQUIRES: objc_interop
// ensure that the symbol `Foo.Bar` does appear in the base module's symbol graph
// and that there is no "swift.extension" symbol there
// TYPE: "s:So3FooV17EmitWhileBuildingE3BarO",
// EXTENSION-NOT: swift.extension
public extension Foo {
enum Bar { }
}

View File

@@ -20,4 +20,3 @@ public extension String {
case bar
}
}

View File

@@ -3,6 +3,12 @@
// RUN: %target-swift-symbolgraph-extract -module-name EmptyExtension -I %t -pretty-print -output-dir %t
// RUN: %{python} -c 'import os.path; import sys; sys.exit(1 if os.path.exists(sys.argv[1]) else 0)' %t/EmptyExtension@Swift.symbols.json
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %s -module-name EmptyExtension -emit-module -emit-module-path %t/
// RUN: %target-swift-symbolgraph-extract -module-name EmptyExtension -I %t -pretty-print -output-dir %t -emit-extension-block-symbols
// RUN: %{python} -c 'import os.path; import sys; sys.exit(1 if os.path.exists(sys.argv[1]) else 0)' %t/EmptyExtension@Swift.symbols.json
extension Sequence {
func foo() {}
}

View File

@@ -1,12 +1,27 @@
// Testing with Extension Block Symbols Off:
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %s -module-name BasicExtension -emit-module -emit-module-path %t/
// RUN: %target-swift-symbolgraph-extract -module-name BasicExtension -I %t -pretty-print -output-dir %t
// RUN: %FileCheck %s --input-file %t/BasicExtension@Swift.symbols.json --check-prefix EXTRACT
// RUN: %FileCheck %s --input-file %t/BasicExtension@Swift.symbols.json --check-prefixes ALL,EXTRACT,EBSOff,EBSOff_EXTRACT
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %s -module-name BasicExtension -emit-module -emit-module-path %t/ -emit-symbol-graph -emit-symbol-graph-dir %t
// RUN: %FileCheck %s --input-file %t/BasicExtension@Swift.symbols.json --check-prefix BUILD
// RUN: %FileCheck %s --input-file %t/BasicExtension@Swift.symbols.json --check-prefixes ALL,BUILD,EBSOff,EBSOff_BUILD
// Testing with Extension Block Symbols On:
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %s -module-name BasicExtension -emit-module -emit-module-path %t/
// RUN: %target-swift-symbolgraph-extract -module-name BasicExtension -I %t -pretty-print -output-dir %t -emit-extension-block-symbols
// RUN: %FileCheck %s --input-file %t/BasicExtension@Swift.symbols.json --check-prefixes ALL,EXTRACT,EBSOn,EBSOn_EXTRACT
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %s -module-name BasicExtension -emit-module -emit-module-path %t/ -emit-symbol-graph -emit-symbol-graph-dir %t -emit-extension-block-symbols
// RUN: %FileCheck %s --input-file %t/BasicExtension@Swift.symbols.json --check-prefixes ALL,BUILD,EBSOn,EBSOn_BUILD
/// We add some useless capabilities to ``Swift/String``.
extension String {
/// Return something.
public var something: String {
@@ -14,28 +29,43 @@ extension String {
}
}
// EXTRACT: module
// EXTRACT-NEXT: "name": "BasicExtension"
// BUILD: module
// BUILD: "name":"BasicExtension"
// Check for module name:
// EXTRACT: "precise": "s:SS14BasicExtensionE9somethingSSvp"
// ALL-LABEL: module
// BUILD: "precise":"s:SS14BasicExtensionE9somethingSSvp"
// ALL: {{"name": ?"BasicExtension"}}
// EXTRACT: "kind": "memberOf"
// EXTRACT-NEXT: "source": "s:SS14BasicExtensionE9somethingSSvp"
// EXTRACT-NEXT: "target": "s:SS"
// BUILD: "kind":"memberOf"
// BUILD: "source":"s:SS14BasicExtensionE9somethingSSvp"
// BUILD: "target":"s:SS"
// Check for Symbols and Documentation Strings:
// Symbols and relationships are not ordered. Therefore, we have to use
// the `-DAG` variant if we have more than one symbol/relationship.
// ALL-LABEL: symbols
// ALL-DAG: {{"precise": ?"s:SS14BasicExtensionE9somethingSSvp"}}
// EBSOn-DAG: {{"precise": ?"s:e:s:SS14BasicExtensionE9somethingSSvp"}}
// BUILD-DAG: Return something.
// EBSOn_BUILD-DAG: We add some useless capabilities to ``Swift/String``.
// EBSOn-DAG: {{"swiftExtension": ?{[[:space:]]*"extendedModule": ?"Swift",[[:space:]]*"typeKind": ?"swift.struct"[[:space:]]*}}}
// Check for Relationships:
// ALL-LABEL: relationships
// EBSOff: {{"kind": ?"memberOf",[[:space:]]*"source": ?"s:SS14BasicExtensionE9somethingSSvp",[[:space:]]*"target": ?"s:SS"}}
// EBSOn-DAG: {{"kind": ?"memberOf",[[:space:]]*"source": ?"s:SS14BasicExtensionE9somethingSSvp",[[:space:]]*"target": ?"s:e:s:SS14BasicExtensionE9somethingSSvp"}}
// EBSOn-DAG: {{"kind": ?"extensionTo",[[:space:]]*"source": ?"s:e:s:SS14BasicExtensionE9somethingSSvp",[[:space:]]*"target": ?"s:SS"}}
// Check for Symbols that should NOT be included:
// Extending `String` creates a memberOf relationship above.
// However, it should not be included as a node because `String`
// is owned by the Swift module.
// rdar://58876107
// EXTRACT-NOT: "precise": "s:SS"
// BUILD-NOT: "precise":"s:SS"
// ALL-NOT: {{"precise": ?"s:SS"}}

View File

@@ -3,7 +3,7 @@
// RUN: %target-build-swift %S/Inputs/CrossImport/B.swift -I %t -module-name B -emit-module -emit-module-path %t/
// RUN: %target-build-swift %s -module-name _A_B -I %t -emit-module -emit-module-path %t/
// RUN: cp -r %S/Inputs/CrossImport/A.swiftcrossimport %t/
// RUN: %target-swift-symbolgraph-extract -module-name A -I %t -pretty-print -output-dir %t
// RUN: %target-swift-symbolgraph-extract -module-name A -I %t -pretty-print -output-dir %t -emit-extension-block-symbols
// RUN: %FileCheck %s --input-file %t/_A_B@A.symbols.json --check-prefix CHECK-MOD
// RUN: %FileCheck %s --input-file %t/_A_B@A.symbols.json --check-prefix CHECK-A
// RUN: %FileCheck %s --input-file %t/_A_B@B.symbols.json --check-prefix CHECK-MOD
@@ -37,7 +37,11 @@ extension B {
// CHECK-A-NOT: s:1BAAV4_A_BE14untransmogrify1AAEVyF
// CHECK-A-DAG: s:1AAAV4_A_BE12transmogrify1BAEVyF
// CHECK-A-DAG: s:e:s:1AAAV4_A_BE12transmogrify1BAEVyF
// CHECK-A-DAG: s:4_A_B11LocalStructV
// CHECK-A-DAG: s:4_A_B11LocalStructV8someFuncyyF
// CHECK-A-NOT: s:e:s:4_A_B11LocalStructV8someFuncyyF
// CHECK-A-NOT: s:e:s:1BAAV4_A_BE14untransmogrify1AAEVyF
// CHECK-B: s:1BAAV4_A_BE14untransmogrify1AAEVyF
// CHECK-B-DAG: s:1BAAV4_A_BE14untransmogrify1AAEVyF
// CHECK-B-DAG: s:e:s:1BAAV4_A_BE14untransmogrify1AAEVyF

View File

@@ -3,17 +3,20 @@
// RUN: %target-build-swift %S/Inputs/NestedExtensions/B.swift -I %t -module-name B -emit-module -emit-module-path %t/
// RUN: %target-build-swift %s -module-name NestedExtensions -emit-module -I %t -emit-module-path %t/
// RUN: %target-swift-symbolgraph-extract -module-name A -I %t -pretty-print -output-dir %t
// RUN: %target-swift-symbolgraph-extract -module-name B -I %t -pretty-print -output-dir %t
// RUN: %target-swift-symbolgraph-extract -module-name NestedExtensions -I %t -pretty-print -output-dir %t
// RUN: %target-swift-symbolgraph-extract -module-name A -I %t -pretty-print -output-dir %t -emit-extension-block-symbols
// RUN: %target-swift-symbolgraph-extract -module-name B -I %t -pretty-print -output-dir %t -emit-extension-block-symbols
// RUN: %target-swift-symbolgraph-extract -module-name NestedExtensions -I %t -pretty-print -output-dir %t -emit-extension-block-symbols
// RUN: %FileCheck %s --input-file %t/B.symbols.json --check-prefix=MODULEB
// RUN: %FileCheck %s --input-file %t/B@A.symbols.json --check-prefix=MODULEBATA
// RUN: %FileCheck %s --input-file %t/NestedExtensions@A.symbols.json --check-prefix=NESTEDATA
// RUN: %{python} -c 'import os.path; import sys; sys.exit(1 if os.path.exists(sys.argv[1]) else 0)' %t/NestedExtensions@B.symbols.json
// RUN: %FileCheck %s --input-file %t/NestedExtensions.symbols.json --check-prefix=NESTED
import A
import B
@@ -35,10 +38,13 @@ extension AStruct.BStruct.CStruct where Thing: Equatable {
// BStruct belongs to AStruct and so should only ever appear in B@A extension symbol graph files.
// MODULEB-NOT: BStruct
// MODULEBATA: "precise": "s:1A7AStructV1BE7BStructV"
// MODULEB-NOT: "swift.extension"
// MODULEBATA-DAG: "precise": "s:1A7AStructV1BE7BStructV"
// MODULEBATA-DAG: "precise": "s:e:s:1A7AStructV1BE7BStructV"
// CStruct belongs to BStruct, and BStruct belongs to AStruct, so should only appear in NestedExtension@A.
// NESTED-NOT: BStruct
// NESTED-NOT: CStruct
// NESTEDATB-NOT: BStruct
// NESTEDATA: "precise": "s:1A7AStructV1BE7BStructV16NestedExtensionsE7CStructV"
// NESTED-NOT: "swift.extension"
// NESTEDATA-DAG: "precise": "s:1A7AStructV1BE7BStructV16NestedExtensionsE7CStructV"
// NESTEDATA-DAG: "precise": "s:e:s:1A7AStructV1BE7BStructV16NestedExtensionsE7CStructV"

View File

@@ -1,7 +1,16 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %s -module-name FilterImplicitlyPrivate -emit-module -emit-module-path %t/
// RUN: %target-build-swift %S/Inputs/ExternalUnderscored.swift -module-name ExternalUnderscored -emit-module -emit-module-path %t/
// RUN: %target-build-swift %s -module-name FilterImplicitlyPrivate -emit-module -emit-module-path %t/ -I %t
// RUN: %target-swift-symbolgraph-extract -module-name FilterImplicitlyPrivate -I %t -pretty-print -output-dir %t
// RUN: %FileCheck %s --input-file %t/FilterImplicitlyPrivate.symbols.json
// RUN: %{python} -c 'import os.path; import sys; sys.exit(1 if os.path.exists(sys.argv[1]) else 0)' %t/FilterImplicitlyPrivate@ExternalUnderscored.symbols.json
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %S/Inputs/ExternalUnderscored.swift -module-name ExternalUnderscored -emit-module -emit-module-path %t/
// RUN: %target-build-swift %s -module-name FilterImplicitlyPrivate -emit-module -emit-module-path %t/ -I %t
// RUN: %target-swift-symbolgraph-extract -module-name FilterImplicitlyPrivate -I %t -pretty-print -output-dir %t -emit-extension-block-symbols
// RUN: %FileCheck %s --input-file %t/FilterImplicitlyPrivate.symbols.json
// RUN: %{python} -c 'import os.path; import sys; sys.exit(1 if os.path.exists(sys.argv[1]) else 0)' %t/FilterImplicitlyPrivate@ExternalUnderscored.symbols.json
// Make sure extensions on implicitly private (< public or underscored, or inside one of those)
// don't emit relationships (or symbols)
@@ -46,5 +55,13 @@ extension _PublicUnderscored.InternalInner: CustomDebugStringConvertible {
}
}
import ExternalUnderscored
extension _ExternalUnderscored: CustomDebugStringConvertible {
public var debugDescription: String {
return ""
}
}
// CHECK: "symbols": []
// CHECK: "relationships": []

View File

@@ -1,7 +1,11 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %s -module-name Indirect -emit-module -emit-module-path %t/
// RUN: %target-swift-symbolgraph-extract -module-name Indirect -I %t -pretty-print -output-dir %t
// RUN: %target-build-swift %S/Inputs/ExternalIndirect.swift -module-name ExternalIndirect -emit-module -emit-module-path %t/
// RUN: %target-build-swift %s -module-name Indirect -emit-module -emit-module-path %t/ -I %t
// RUN: %target-swift-symbolgraph-extract -module-name Indirect -I %t -pretty-print -output-dir %t -emit-extension-block-symbols
// RUN: %target-swift-symbolgraph-extract -module-name ExternalIndirect -I %t -pretty-print -output-dir %t -emit-extension-block-symbols
// RUN: %FileCheck %s --input-file %t/Indirect.symbols.json
// RUN: %FileCheck %s --input-file %t/ExternalIndirect.symbols.json --check-prefix EXTERNAL
// RUN: %FileCheck %s --input-file %t/Indirect@ExternalIndirect.symbols.json --check-prefix EXTENSION
public protocol P {
func foo()
@@ -21,3 +25,21 @@ public struct S : Q {
// S : Q
// CHECK-DAG: "kind": "conformsTo",{{[[:space:]]*}}"source": "s:8Indirect1SV",{{[[:space:]]*}}"target": "s:8Indirect1QP"
import ExternalIndirect
extension ES: EQ {
public func foo() {}
}
// EQ : EP
// EXTERNAL-DAG: "kind": "conformsTo",{{[[:space:]]*}}"source": "s:16ExternalIndirect2EQP",{{[[:space:]]*}}"target": "s:16ExternalIndirect2EPP"
// extension ES : EP
// EXTENSION-DAG: "kind": "conformsTo",{{[[:space:]]*}}"source": "s:e:s:16ExternalIndirect2ESV0B0E3fooyyF",{{[[:space:]]*}}"target": "s:16ExternalIndirect2EPP"
// extension ES : EQ
// EXTENSION-DAG: "kind": "conformsTo",{{[[:space:]]*}}"source": "s:e:s:16ExternalIndirect2ESV0B0E3fooyyF",{{[[:space:]]*}}"target": "s:16ExternalIndirect2EQP"
// extension ES -> ES
// EXTENSION-DAG: "kind": "extensionTo",{{[[:space:]]*}}"source": "s:e:s:16ExternalIndirect2ESV0B0E3fooyyF",{{[[:space:]]*}}"target": "s:16ExternalIndirect2ESV"

View File

@@ -0,0 +1,7 @@
public protocol EP {
func foo()
}
public protocol EQ : EP {}
public struct ES { }

View File

@@ -0,0 +1 @@
public struct _ExternalUnderscored {}

View File

@@ -0,0 +1,3 @@
public struct ExternalS {}
public typealias ExternalA = ExternalS

View File

@@ -0,0 +1,48 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %S/Inputs/ExternalTypealias.swift -module-name ExternalTypealias -emit-module -emit-module-path %t/
// RUN: %target-build-swift %s -module-name Typealias -emit-module -emit-module-path %t/ -I %t
// RUN: %target-swift-symbolgraph-extract -module-name Typealias -I %t -pretty-print -output-dir %t
// RUN: %FileCheck %s --input-file %t/Typealias.symbols.json
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %S/Inputs/ExternalTypealias.swift -module-name ExternalTypealias -emit-module -emit-module-path %t/
// RUN: %target-build-swift %s -module-name Typealias -emit-module -emit-module-path %t/ -I %t
// RUN: %target-swift-symbolgraph-extract -module-name Typealias -I %t -pretty-print -output-dir %t
// RUN: %FileCheck %s --input-file %t/Typealias@ExternalTypealias.symbols.json --check-prefix EXTERNAL
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %S/Inputs/ExternalTypealias.swift -module-name ExternalTypealias -emit-module -emit-module-path %t/
// RUN: %target-build-swift %s -module-name Typealias -emit-module -emit-module-path %t/ -I %t
// RUN: %target-swift-symbolgraph-extract -module-name Typealias -I %t -pretty-print -output-dir %t -emit-extension-block-symbols
// RUN: %FileCheck %s --input-file %t/Typealias@ExternalTypealias.symbols.json --check-prefix EBS_EXTERNAL
public struct S {}
public typealias A = S
// Members of a typealias should be associated with the original type,
// not the typealias symbol
public extension A {
func foo() {}
}
// CHECK: "kind": "memberOf"
// CHECK-NEXT: "source": "s:9Typealias1SV3fooyyF"
// CHECK-NEXT: "target": "s:9Typealias1SV"
// This also applies to extensions to externally defined typealiases
import ExternalTypealias
public extension ExternalA {
func foo() {}
}
// EXTERNAL: "kind": "memberOf"
// EXTERNAL-NEXT: "source": "s:17ExternalTypealias0A1SV0B0E3fooyyF"
// EXTERNAL-NEXT: "target": "s:17ExternalTypealias0A1SV"
// EBS_EXTERNAL: "kind": "extensionTo"
// EBS_EXTERNAL-NEXT: "source": "s:e:s:17ExternalTypealias0A1SV0B0E3fooyyF"
// EBS_EXTERNAL-NEXT: "target": "s:17ExternalTypealias0A1SV"

View File

@@ -7,9 +7,22 @@
// RUN: %FileCheck %s --input-file %t/ConditionalConformance.symbols.json --check-prefix=CONFORMS
// RUN: %FileCheck %s --input-file %t/ConditionalConformance.symbols.json --check-prefix=MEMBER
// RUN: %FileCheck %s --input-file %t/ConditionalConformance@Swift.symbols.json --check-prefix=SYNTHEXT
// RUN: %FileCheck %s --input-file %t/ConditionalConformance@Swift.symbols.json --check-prefix=CONFORMSEXT
// RUN: %FileCheck %s --input-file %t/ConditionalConformance@Swift.symbols.json --check-prefix=MEMBEREXT
// RUN: %FileCheck %s --input-file %t/ConditionalConformance@Swift.symbols.json --check-prefixes=SYNTHEXT,EBSOff_SYNTHEXT
// RUN: %FileCheck %s --input-file %t/ConditionalConformance@Swift.symbols.json --check-prefixes=CONFORMSEXT,EBSOff_CONFORMSEXT
// RUN: %FileCheck %s --input-file %t/ConditionalConformance@Swift.symbols.json --check-prefixes=MEMBEREXT,EBSOff_MEMBEREXT
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %s -module-name ConditionalConformance -emit-module -emit-module-path %t/
// RUN: %target-swift-symbolgraph-extract -module-name ConditionalConformance -I %t -pretty-print -output-dir %t -emit-extension-block-symbols
// R\UN: %FileCheck %s --input-file %t/ConditionalConformance.symbols.json
// RUN: %FileCheck %s --input-file %t/ConditionalConformance.symbols.json --check-prefix=SYNTH
// RUN: %FileCheck %s --input-file %t/ConditionalConformance.symbols.json --check-prefix=CONFORMS
// RUN: %FileCheck %s --input-file %t/ConditionalConformance.symbols.json --check-prefix=MEMBER
// RUN: %FileCheck %s --input-file %t/ConditionalConformance@Swift.symbols.json --check-prefixes=SYNTHEXT,EBSOn_SYNTHEXT
// RUN: %FileCheck %s --input-file %t/ConditionalConformance@Swift.symbols.json --check-prefixes=CONFORMSEXT,EBSOn_CONFORMSEXT
// RUN: %FileCheck %s --input-file %t/ConditionalConformance@Swift.symbols.json --check-prefixes=MEMBEREXT,EBSOn_MEMBEREXT
// Relationships to Swift.Array should only go into the @Swift file.
// C\HECK-NOT: "s:Sa"
@@ -49,7 +62,8 @@ extension S: P where T == Int {
}
// CONFORMSEXT: "kind": "conformsTo"
// CONFORMSEXT-NEXT: "source": "s:Sa"
// EBSOff_CONFORMSEXT-NEXT: "source": "s:Sa"
// EBSOn_CONFORMSEXT-NEXT: "source": "s:e:s:Sa22ConditionalConformanceSiRszlE3baryyF"
// CONFORMSEXT-NEXT: "target": "s:22ConditionalConformance1PP"
// CONFORMSEXT-NEXT: swiftConstraints
// CONFORMSEXT: "kind": "sameType"
@@ -58,9 +72,11 @@ extension S: P where T == Int {
extension Array: P where Element == Int {
// SYNTHEXT: "source": "s:22ConditionalConformance1PPAAE3fooyyF::SYNTHESIZED::s:Sa"
// SYNTHEXT-NEXT: "target": "s:Sa"
// EBSOff_SYNTHEXT-NEXT: "target": "s:Sa"
// EBSOn_SYNTHEXT-NEXT: "target": "s:e:s:Sa22ConditionalConformanceSiRszlE3baryyF"
// MEMBEREXT: "source": "s:Sa22ConditionalConformanceSiRszlE3baryyF",
// MEMBEREXT-NEXT: "target": "s:Sa",
// MEMBEREXT: "source": "s:Sa22ConditionalConformanceSiRszlE3baryyF"
// EBSOff_MEMBEREXT-NEXT: "target": "s:Sa"
// EBSOn_MEMBEREXT-NEXT: "target": "s:e:s:Sa22ConditionalConformanceSiRszlE3baryyF"
public func bar() {}
}

View File

@@ -0,0 +1,25 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %S/Inputs/RemoteP.swift -module-name RemoteP -emit-module -emit-module-path %t/
// RUN: %target-build-swift %s -module-name EnablingDeclaration -emit-module -emit-module-path %t/ -I %t
// RUN: %target-swift-symbolgraph-extract -module-name EnablingDeclaration -I %t -pretty-print -output-dir %t
// RUN: %FileCheck %s --input-file %t/EnablingDeclaration@RemoteP.symbols.json --check-prefix=SYNTH
// RUN: %FileCheck %s --input-file %t/EnablingDeclaration@RemoteP.symbols.json --check-prefix=NOSYNTH
import RemoteP
// unrelated to the P protocol and P.extraFunc()
public extension PImpl {
func foo() {}
}
// NOSYNTH-NOT: {{"PImpl",[[:space:]]*"extraFunc\(\)"}}
// related to the P protocol and enables the synthesized member NoImpl.extraFunc()
extension NoPImpl: P {
public func someFunc() {}
public func otherFunc() {}
public func bonusFunc() {}
}
// SYNTH: {{"NoPImpl",[[:space:]]*"extraFunc\(\)"}}

View File

@@ -12,3 +12,13 @@ public extension P {
/// Extra default docs!
func extraFunc() {}
}
public struct PImpl: P {
public func someFunc() {}
public func otherFunc() {}
public func bonusFunc() {}
}
public struct NoPImpl {}

View File

@@ -2,13 +2,14 @@
// RUN: %target-swift-frontend %S/Inputs/RemoteP.swift -module-name RemoteP -emit-module -emit-module-path %t/RemoteP.swiftmodule -emit-module-source-info-path %t/RemoteP.swiftsourceinfo -emit-module-doc-path %t/RemoteP.swiftdoc
// RUN: %target-swift-frontend %s -module-name RemoteInheritedDocs -emit-module -emit-module-path %t/RemoteInheritedDocs.swiftmodule -emit-module-source-info-path %t/RemoteInheritedDocs.swiftsourceinfo -emit-module-doc-path %t/RemoteInheritedDocs.swiftdoc -I %t
// RUN: %target-swift-symbolgraph-extract -module-name RemoteInheritedDocs -I %t -pretty-print -output-dir %t
// RUN: %target-swift-symbolgraph-extract -module-name RemoteInheritedDocs -I %t -pretty-print -output-dir %t -emit-extension-block-symbols
// RUN: %FileCheck %s --input-file %t/RemoteInheritedDocs.symbols.json --check-prefix SOME
// RUN: %FileCheck %s --input-file %t/RemoteInheritedDocs.symbols.json --check-prefix OTHER
// RUN: %FileCheck %s --input-file %t/RemoteInheritedDocs.symbols.json --check-prefix BONUS
// RUN: %FileCheck %s --input-file %t/RemoteInheritedDocs.symbols.json --check-prefix INHERIT
// RUN: %FileCheck %s --input-file %t/RemoteInheritedDocs.symbols.json --check-prefix LOCAL
// RUN: %FileCheck %s --input-file %t/RemoteInheritedDocs.symbols.json --check-prefix OVERRIDE
// RUN: %FileCheck %s --input-file %t/RemoteInheritedDocs@RemoteP.symbols.json --check-prefix EXTENSION
// RUN: %target-swift-symbolgraph-extract -module-name RemoteInheritedDocs -I %t -pretty-print -output-dir %t -skip-inherited-docs
// RUN: %FileCheck %s --input-file %t/RemoteInheritedDocs.symbols.json --check-prefix SOME
@@ -44,6 +45,8 @@
// OVERRIDE-NOT: Extra default docs!
// OVERRIDE-NOT: Extension override!
// EXTENSION-NOT: Some Protocol
import RemoteP
// The `RemoteP.P` protocol has three methods: `someFunc` and `bonusFunc` don't have docs upstream,
@@ -53,6 +56,9 @@ import RemoteP
// `RemoteP.P` also has an extension with a default implementation for `extraFunc` that does have
// docs, but overriding it here should prevent those from appearing
// When emitting extension block symbols, local extension blocks should never inherit documentation
// from the original type declaration.
public struct S: P {
public func someFunc() {}
@@ -68,4 +74,3 @@ public extension P {
/// Extension override!
func someFunc() {}
}

View File

@@ -1,7 +1,10 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %s -module-name IncludeInternal -emit-module -emit-module-path %t/
// RUN: %target-swift-symbolgraph-extract -module-name IncludeInternal -I %t -pretty-print -output-dir %t -minimum-access-level internal
// RUN: %target-build-swift %S/Inputs/Internal.swift -module-name Internal -emit-module -emit-module-path %t/ -enable-testing
// RUN: %target-build-swift %s -module-name IncludeInternal -emit-module -emit-module-path %t/ -I %t
// RUN: %target-swift-symbolgraph-extract -module-name IncludeInternal -I %t -pretty-print -output-dir %t -minimum-access-level internal -emit-extension-block-symbols
// RUN: %FileCheck %s --input-file %t/IncludeInternal.symbols.json
// RUN: %FileCheck %s --input-file %t/IncludeInternal@Internal.symbols.json --check-prefix EXTENSION
// RUN: %FileCheck %s --input-file %t/IncludeInternal@Internal.symbols.json --check-prefix EXTENSION_EBS
public struct ShouldAppear {
public var x: Int
@@ -18,3 +21,17 @@ private struct ShouldntAppear {
// CHECK: ShouldAppear
// CHECK: ShouldAlsoAppear
// CHECK-NOT: ShouldntAppear
@testable import Internal
extension S {
func shouldAppear() { }
}
extension S {
private func shouldntAppear() { }
}
// EXTENSION: shouldAppear
// EXTENSION-NOT: shouldntAppear
// EXTENSION_EBS-COUNT-1: "swift.extension"

View File

@@ -0,0 +1 @@
struct S { }

View File

@@ -0,0 +1,7 @@
public struct ExternalStruct {
public struct InnerStruct {}
public typealias InnerTypeAlias = InnerStruct
public enum InnerEnum {}
}

View File

@@ -3,6 +3,11 @@
// RUN: %target-swift-symbolgraph-extract -module-name SelfNotLinked -I %t -pretty-print -output-dir %t
// RUN: %FileCheck %s --input-file %t/SelfNotLinked@Swift.symbols.json --match-full-lines --strict-whitespace
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %s -module-name SelfNotLinked -emit-module -emit-module-path %t/
// RUN: %target-swift-symbolgraph-extract -module-name SelfNotLinked -I %t -pretty-print -output-dir %t -emit-extension-block-symbols
// RUN: %FileCheck %s --input-file %t/SelfNotLinked@Swift.symbols.json --match-full-lines --strict-whitespace
extension Sequence where Self : Collection {
public func foo(x: Self) {}
}
@@ -43,5 +48,3 @@ extension Sequence where Self : Collection {
// CHECK-NEXT: }
// CHECK-NEXT: ],
// CHECK-NEXT: "accessLevel": "public"
// CHECK-NEXT: }
// CHECK-NEXT: ],

View File

@@ -0,0 +1,17 @@
// FYI: The lit commands and FileCheck statements are at the bottom of the file, to be resilient
// against changes to the doc comment format.
/// This should be captured
extension String: CustomDebugStringConvertible {
var debugDescription: String {
return ""
}
}
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %s -module-name Extension -emit-module-path %t/Extension.swiftmodule
// RUN: %target-swift-symbolgraph-extract -module-name Extension -I %t -pretty-print -output-dir %t -emit-extension-block-symbols
// RUN: %FileCheck %s --input-file %t/Extension@Swift.symbols.json
// CHECK: This should be captured

View File

@@ -0,0 +1,3 @@
/// SPI Protocol doc
@_spi(SPI)
public protocol P { }

View File

@@ -1,13 +1,32 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %s -module-name Location -emit-module-path %t/Location.swiftmodule
// RUN: %target-swift-symbolgraph-extract -module-name Location -I %t -pretty-print -output-dir %t
// RUN: %FileCheck %s --input-file %t/Location.symbols.json
// FYI: The lit commands and FileCheck statements are at the bottom of the file, to be resilient
// against changes to the doc comment format.
/// This is a struct.
/// location points here
/// v
public struct MyStruct {}
// CHECK: location
// CHECK-NEXT: uri
// CHECK-NEXT: position
// CHECK-NEXT: "line": 6
/// location points here
/// v
public extension String {
func foo() { }
}
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %s -module-name Location -emit-module-path %t/Location.swiftmodule
// RUN: %target-swift-symbolgraph-extract -module-name Location -I %t -pretty-print -output-dir %t -emit-extension-block-symbols
// RUN: %FileCheck %s --input-file %t/Location.symbols.json
// RUN: %FileCheck %s --input-file %t/Location@Swift.symbols.json --check-prefix EXTENSION
// CHECK: "location"
// CHECK-NEXT: "uri"
// CHECK-NEXT: "position"
// CHECK-NEXT: "line": 5
// CHECK-NEXT: "character": 14
// EXTENSION-LABEL: "swift.extension"
// EXTENSION: "location"
// EXTENSION-NEXT: "uri"
// EXTENSION-NEXT: "position"
// EXTENSION-NEXT: "line": 9
// EXTENSION-NEXT: "character": 7

View File

@@ -1,18 +1,28 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %s -module-name SPI -emit-module -emit-module-path %t/SPI.swiftmodule -include-spi-symbols
// RUN: %target-swift-symbolgraph-extract -module-name SPI -I %t -pretty-print -output-dir %t -include-spi-symbols
// RUN: %target-build-swift %S/Inputs/SPIP.swift -module-name SPIP -emit-module -emit-module-path %t/ -include-spi-symbols
// RUN: %target-build-swift %s -module-name SPI -emit-module -emit-module-path %t/SPI.swiftmodule -include-spi-symbols -I %t
// RUN: %target-swift-symbolgraph-extract -module-name SPI -I %t -pretty-print -output-dir %t -include-spi-symbols -emit-extension-block-symbols
// RUN: %FileCheck %s --input-file %t/SPI.symbols.json --check-prefix SPI
// RUN: %FileCheck %s --input-file %t/SPI@SPIP.symbols.json --check-prefix SPI_EXT_SPIP
// RUN: %FileCheck %s --input-file %t/SPI@Swift.symbols.json --check-prefix SPI_EXT_Swift
// RUN: %FileCheck %s --input-file %t/SPI.symbols.json --check-prefix SPIDOC
// RUN: %target-swift-symbolgraph-extract -module-name SPI -I %t -pretty-print -output-dir %t
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %S/Inputs/SPIP.swift -module-name SPIP -emit-module -emit-module-path %t/ -include-spi-symbols
// RUN: %target-build-swift %s -module-name SPI -emit-module -emit-module-path %t/SPI.swiftmodule -include-spi-symbols -I %t
// RUN: %target-swift-symbolgraph-extract -module-name SPI -I %t -pretty-print -output-dir %t -emit-extension-block-symbols
// RUN: %FileCheck %s --input-file %t/SPI.symbols.json --check-prefix NOSPI
// RUN: %{python} -c 'import os.path; import sys; sys.exit(1 if os.path.exists(sys.argv[1]) else 0)' %t/SPI@SPIP.symbols.json
// RUN: %{python} -c 'import os.path; import sys; sys.exit(1 if os.path.exists(sys.argv[1]) else 0)' %t/SPI@Swift.symbols.json
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %S/Inputs/SPIP.swift -module-name SPIP -emit-module -emit-module-path %t/
// RUN: %target-build-swift %s -module-name SPI -emit-module -emit-module-path %t/SPI.swiftmodule -emit-symbol-graph -emit-symbol-graph-dir %t/ -include-spi-symbols -v
// RUN: %FileCheck %s --input-file %t/SPI.symbols.json --check-prefix SPI-COMPILE
// RUN: %FileCheck %s --input-file %t/SPI.symbols.json --check-prefix SPIDOC
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %S/Inputs/SPIP.swift -module-name SPIP -emit-module -emit-module-path %t/
// RUN: %target-build-swift %s -module-name SPI -emit-module -emit-module-path %t/SPI.swiftmodule -emit-symbol-graph -emit-symbol-graph-dir %t/
// RUN: %FileCheck %s --input-file %t/SPI.symbols.json --check-prefix NOSPI-COMPILE
@@ -28,3 +38,24 @@
// NOSPI-COMPILE-NOT: s:3SPI10SomeStructV
// SPIDOC: This is some struct, there
#if canImport(SPIP)
@_spi(SPI) import SPIP
public extension P {
func foo() { }
}
@_spi(SPI)
extension String: P {}
#endif
// SPI_EXT_SPIP-DAG: "precise": "s:4SPIP1PP3SPIE3fooyyF"
// SPI_EXT_SPIP-DAG: "spi": true
// SPI_EXT_SPIP-DAG: "precise": "s:e:s:4SPIP1PP3SPIE3fooyyF"
// SPI_EXT_SPIP-DAG: "spi": true
// SPI_EXT_Swift-DAG: "precise": "s:e:s:SSs:4SPIP1PP"
// SPI_EXT_Swift-DAG: "spi": true
// SPI_EXT_Swift-DAG: "precise": "s:4SPIP1PP3SPIE3fooyyF::SYNTHESIZED::s:SS"
// SPI_EXT_Swift-DAG: "spi": true

View File

@@ -1,13 +1,22 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %s -module-name Names -emit-module -emit-module-path %t/
// RUN: %target-swift-symbolgraph-extract -module-name Names -I %t -pretty-print -output-dir %t
// RUN: %target-build-swift %S/Inputs/ExternalNames.swift -module-name ExternalNames -emit-module -emit-module-path %t/
// RUN: %target-build-swift %s -module-name Names -emit-module -emit-module-path %t/ -I %t
// RUN: %target-swift-symbolgraph-extract -module-name Names -I %t -pretty-print -output-dir %t -emit-extension-block-symbols
// RUN: %FileCheck %s --input-file %t/Names.symbols.json
// RUN: %FileCheck %s --input-file %t/Names.symbols.json --check-prefix=FUNC
// RUN: %FileCheck %s --input-file %t/Names.symbols.json --check-prefix=INNERTYPE
// RUN: %FileCheck %s --input-file %t/Names.symbols.json --check-prefix=INNERTYPEALIAS
// RUN: %FileCheck %s --input-file %t/Names.symbols.json --check-prefix=INNERENUM
// RUN: %FileCheck %s --input-file %t/Names.symbols.json --check-prefix=INNERCASE
// RUN: %FileCheck %s --input-file %t/Names@ExternalNames.symbols.json --check-prefix=EXT_INNERTYPE
// RUN: %FileCheck %s --input-file %t/Names@ExternalNames.symbols.json --check-prefix=EXT_INNERENUM
// RUN: %FileCheck %s --input-file %t/Names.symbols.json --check-prefix=INNERTYPEALIAS
// RUN: %FileCheck %s --input-file %t/Names.symbols.json --check-prefix=INNERTYPEALIAS_INNERINNERTYPE
// RUN: %FileCheck %s --input-file %t/Names@ExternalNames.symbols.json --check-prefix=EXT_INNERTYPEALIAS
// RUN: %FileCheck %s --input-file %t/Names@ExternalNames.symbols.json --check-prefix=EXT_INNERTYPEALIAS_INNERINNERTYPE
public struct MyStruct {
public struct InnerStruct {}
@@ -20,6 +29,28 @@ public struct MyStruct {
}
}
public extension MyStruct.InnerTypeAlias {
struct InnerInnerStruct {}
}
import ExternalNames
public extension ExternalStruct.InnerStruct {
func foo() {}
}
public extension ExternalStruct.InnerTypeAlias {
func bar() {}
}
public extension ExternalStruct.InnerEnum {
func foo() {}
}
public extension ExternalStruct.InnerTypeAlias {
struct InnerInnerStruct {}
}
// CHECK-LABEL: "precise": "s:5Names8MyStructV"
// CHECK: names
// CHECK-NEXT: "title": "MyStruct"
@@ -32,10 +63,6 @@ public struct MyStruct {
// INNERTYPE: names
// INNERTYPE-NEXT: "title": "MyStruct.InnerStruct"
// INNERTYPEALIAS-LABEL: "precise": "s:5Names8MyStructV14InnerTypeAliasa"
// INNERTYPEALIAS: names
// INNERTYPEALIAS-NEXT: "title": "MyStruct.InnerTypeAlias"
// INNERENUM-LABEL: "precise": "s:5Names8MyStructV9InnerEnumO",
// INNERENUM: names
// INNERENUM-NEXT: "title": "MyStruct.InnerEnum"
@@ -43,3 +70,39 @@ public struct MyStruct {
// INNERCASE-LABEL: "precise": "s:5Names8MyStructV9InnerEnumO0D4CaseyA2EmF",
// INNERCASE: names
// INNERCASE-NEXT: "title": "MyStruct.InnerEnum.InnerCase",
// EXT_INNERTYPE-LABEL: "precise": "s:e:s:13ExternalNames0A6StructV05InnerC0V0B0E3fooyyF"
// EXT_INNERTYPE: names
// EXT_INNERTYPE-NEXT: "title": "ExternalStruct.InnerStruct"
// EXT_INNERENUM-LABEL: "precise": "s:e:s:13ExternalNames0A6StructV9InnerEnumO0B0E3fooyyF",
// EXT_INNERENUM: names
// EXT_INNERENUM-NEXT: "title": "ExternalStruct.InnerEnum"
// The typealias symbol itself should use the name of the typealias
// INNERTYPEALIAS-LABEL: "precise": "s:5Names8MyStructV14InnerTypeAliasa"
// INNERTYPEALIAS: names
// INNERTYPEALIAS-NEXT: "title": "MyStruct.InnerTypeAlias"
// Types declared in extensions to typealiases should always use the name of their
// original (aliased) parent type
// INNERTYPEALIAS_INNERINNERTYPE-LABEL: "precise": "s:5Names8MyStructV05InnerC0V0ddC0V"
// INNERTYPEALIAS_INNERINNERTYPE: names
// INNERTYPEALIAS_INNERINNERTYPE-NEXT: "title": "MyStruct.InnerStruct.InnerInnerStruct"
// Extension symbols which extend a typealias should use the name of the original
// (aliased) type
// EXT_INNERTYPEALIAS-LABEL: "precise": "s:e:s:13ExternalNames0A6StructV05InnerC0V0B0E3baryyF"
// EXT_INNERTYPEALIAS: names
// EXT_INNERTYPEALIAS-NEXT: "title": "ExternalStruct.InnerStruct"
// Symbols declared in an extension to a typealias should also use the name of the original
// (aliased) type
// EXT_INNERTYPEALIAS_INNERINNERTYPE-LABEL: "precise": "s:13ExternalNames0A6StructV05InnerC0V0B0E0ddC0V"
// EXT_INNERTYPEALIAS_INNERINNERTYPE: names
// EXT_INNERTYPEALIAS_INNERINNERTYPE-NEXT: "title": "ExternalStruct.InnerStruct.InnerInnerStruct"

View File

@@ -1,7 +1,9 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %s -module-name PathComponents -emit-module -emit-module-path %t/
// RUN: %target-swift-symbolgraph-extract -module-name PathComponents -I %t -pretty-print -output-dir %t
// RUN: %target-swift-symbolgraph-extract -module-name PathComponents -I %t -pretty-print -output-dir %t -emit-extension-block-symbols
// RUN: %FileCheck %s --input-file %t/PathComponents.symbols.json
// RUN: %FileCheck %s --input-file %t/PathComponents@Swift.symbols.json --check-prefix EXTENSION
// RUN: %FileCheck %s --input-file %t/PathComponents@Swift.symbols.json --check-prefix EXTENSION_EBS
public struct Outer {
public struct Inner {
@@ -17,3 +19,26 @@ public struct Outer {
// CHECK-NEXT: "Inner"
// CHECK-NEXT: "x"
// CHECK-NEXT: ]
public extension String {
public struct Inner {
public var x: Int { 1 }
}
}
// EXTENSION: "precise": "s:SS14PathComponentsE5InnerV1xSivp"
// EXTENSION-NEXT: "interfaceLanguage": "swift"
// EXTENSION-NEXT: },
// EXTENSION-NEXT: "pathComponents": [
// EXTENSION-NEXT: "String"
// EXTENSION-NEXT: "Inner"
// EXTENSION-NEXT: "x"
// EXTENSION-NEXT: ]
// EXTENSION_EBS: "precise": "s:e:s:SS14PathComponentsE5InnerV"
// EXTENSION_EBS-NEXT: "interfaceLanguage": "swift"
// EXTENSION_EBS-NEXT: },
// EXTENSION_EBS-NEXT: "pathComponents": [
// EXTENSION_EBS-NEXT: "String"
// EXTENSION_EBS-NEXT: ]

View File

@@ -1,7 +1,8 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %s -module-name Unavailable -emit-module -emit-module-path %t/
// RUN: %target-swift-symbolgraph-extract -module-name Unavailable -I %t -pretty-print -output-dir %t
// RUN: %target-swift-symbolgraph-extract -module-name Unavailable -I %t -pretty-print -output-dir %t -emit-extension-block-symbols
// RUN: %FileCheck %s --input-file %t/Unavailable.symbols.json
// RUN: %{python} -c 'import os.path; import sys; sys.exit(1 if os.path.exists(sys.argv[1]) else 0)' %t/Unavailable@Swift.symbols.json
// REQUIRES: OS=macosx
@@ -26,3 +27,13 @@ extension ShouldAppear {
}
// CHECK-NOT: shouldntAppear
@available(OSX, unavailable)
extension String {
public func shouldntAppear1() { }
}
@available(OSX, obsoleted: 10.9)
extension String {
public func shouldntAppear2() {}
}

View File

@@ -1004,7 +1004,8 @@ fillSymbolInfo(CursorSymbolInfo &Symbol, const DeclInfo &DInfo,
/*PrintMessages=*/false,
/*SkipInheritedDocs=*/false,
/*IncludeSPISymbols=*/true,
/*IncludeClangDocs=*/true
/*IncludeClangDocs=*/true,
/*EmitExtensionBlockSymbols=*/false,
};
symbolgraphgen::printSymbolGraphForDecl(DInfo.VD, DInfo.BaseType,