From 54eca225a64fd7e2528b8bc26da2361c2cc8c5f3 Mon Sep 17 00:00:00 2001 From: Max Obermeier Date: Wed, 11 May 2022 10:57:47 +0200 Subject: [PATCH] Allow for emission of swift.extension symbols for extensions to external types in swiftSymbolGraphGen 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) --- include/swift/Option/Options.td | 5 + .../swift/SymbolGraphGen/SymbolGraphOptions.h | 5 + lib/Driver/ToolChains.cpp | 2 + .../swift_symbolgraph_extract_main.cpp | 1 + lib/Frontend/CompilerInvocation.cpp | 2 + lib/SymbolGraphGen/Edge.h | 15 +- lib/SymbolGraphGen/FormatVersion.h | 4 +- lib/SymbolGraphGen/JSON.cpp | 5 +- lib/SymbolGraphGen/Symbol.cpp | 271 ++++++++++++------ lib/SymbolGraphGen/Symbol.h | 52 +++- lib/SymbolGraphGen/SymbolGraph.cpp | 84 +++--- lib/SymbolGraphGen/SymbolGraph.h | 6 +- lib/SymbolGraphGen/SymbolGraphASTWalker.cpp | 172 +++++++---- lib/SymbolGraphGen/SymbolGraphASTWalker.h | 6 + .../ClangImporter/ClangExtensions.swift | 23 ++ .../ClangImporter/ForeignExtensions.swift | 1 - .../Headers/EmitWhileBuilding.h | 2 + test/SymbolGraph/EmptyExtension.swift | 6 + test/SymbolGraph/Module/BasicExtension.swift | 64 +++-- test/SymbolGraph/Module/CrossImport.swift | 8 +- .../SymbolGraph/Module/NestedExtensions.swift | 18 +- .../ConformsTo/FilterImplicitlyPrivate.swift | 19 +- .../Relationships/ConformsTo/Indirect.swift | 26 +- .../ConformsTo/Inputs/ExternalIndirect.swift | 7 + .../Inputs/ExternalUnderscored.swift | 1 + .../MemberOf/Inputs/ExternalTypealias.swift | 3 + .../Relationships/MemberOf/Typealias.swift | 48 ++++ .../Synthesized/ConditionalConformance.swift | 30 +- .../Synthesized/EnablingDeclaration.swift | 25 ++ .../Synthesized/Inputs/RemoteP.swift | 10 + .../Synthesized/RemoteInheritedDocs.swift | 9 +- .../AccessLevelFilter/IncludeInternal.swift | 21 +- .../AccessLevelFilter/Inputs/Internal.swift | 1 + .../Symbols/Inputs/ExternalNames.swift | 7 + .../DeclarationFragments/SelfNotLinked.swift | 7 +- .../Symbols/Mixins/DocComment/Extension.swift | 17 ++ .../Symbols/Mixins/Inputs/SPIP.swift | 3 + .../SymbolGraph/Symbols/Mixins/Location.swift | 33 ++- test/SymbolGraph/Symbols/Mixins/SPI.swift | 37 ++- test/SymbolGraph/Symbols/Names.swift | 77 ++++- test/SymbolGraph/Symbols/PathComponents.swift | 27 +- test/SymbolGraph/Symbols/Unavailable.swift | 13 +- .../lib/SwiftLang/SwiftSourceDocInfo.cpp | 3 +- 43 files changed, 912 insertions(+), 264 deletions(-) create mode 100644 test/SymbolGraph/ClangImporter/ClangExtensions.swift create mode 100644 test/SymbolGraph/Relationships/ConformsTo/Inputs/ExternalIndirect.swift create mode 100644 test/SymbolGraph/Relationships/ConformsTo/Inputs/ExternalUnderscored.swift create mode 100644 test/SymbolGraph/Relationships/MemberOf/Inputs/ExternalTypealias.swift create mode 100644 test/SymbolGraph/Relationships/MemberOf/Typealias.swift create mode 100644 test/SymbolGraph/Relationships/Synthesized/EnablingDeclaration.swift create mode 100644 test/SymbolGraph/Symbols/AccessLevelFilter/Inputs/Internal.swift create mode 100644 test/SymbolGraph/Symbols/Inputs/ExternalNames.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/DocComment/Extension.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/Inputs/SPIP.swift diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index f01bf61453155..c3caf7fc4f904 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -1351,6 +1351,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, diff --git a/include/swift/SymbolGraphGen/SymbolGraphOptions.h b/include/swift/SymbolGraphGen/SymbolGraphOptions.h index a973a60f0cf51..c46e647ae701b 100644 --- a/include/swift/SymbolGraphGen/SymbolGraphOptions.h +++ b/include/swift/SymbolGraphGen/SymbolGraphOptions.h @@ -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 diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 31119078f170c..bb2a2b89719f8 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -616,6 +616,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; @@ -1112,6 +1113,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); diff --git a/lib/DriverTool/swift_symbolgraph_extract_main.cpp b/lib/DriverTool/swift_symbolgraph_extract_main.cpp index 65184f6540e31..316507b9999d1 100644 --- a/lib/DriverTool/swift_symbolgraph_extract_main.cpp +++ b/lib/DriverTool/swift_symbolgraph_extract_main.cpp @@ -172,6 +172,7 @@ int swift_symbolgraph_extract_main(ArrayRef 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)) { diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 98c228c9c11c5..9fdba8936a09f 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1284,6 +1284,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 = diff --git a/lib/SymbolGraphGen/Edge.h b/lib/SymbolGraphGen/Edge.h index 06291e1c81ef9..901961ba549b1 100644 --- a/lib/SymbolGraphGen/Edge.h +++ b/lib/SymbolGraphGen/Edge.h @@ -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; } diff --git a/lib/SymbolGraphGen/FormatVersion.h b/lib/SymbolGraphGen/FormatVersion.h index fd0ee9c07f6d4..4702e8dad03d1 100644 --- a/lib/SymbolGraphGen/FormatVersion.h +++ b/lib/SymbolGraphGen/FormatVersion.h @@ -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 diff --git a/lib/SymbolGraphGen/JSON.cpp b/lib/SymbolGraphGen/JSON.cpp index c0614dbaa7fd5..0d47224727efe 100644 --- a/lib/SymbolGraphGen/JSON.cpp +++ b/lib/SymbolGraphGen/JSON.cpp @@ -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 FilteredRequirements; diff --git a/lib/SymbolGraphGen/Symbol.cpp b/lib/SymbolGraphGen/Symbol.cpp index 9cea743addc89..471f9287b1ca7 100644 --- a/lib/SymbolGraphGen/Symbol.cpp +++ b/lib/SymbolGraphGen/Symbol.cpp @@ -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 Symbol::getKind(const ValueDecl *VD) const { +std::pair 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 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(D); + if (VD->isOperator()) return {"swift.func.op", "Operator"}; if (VD->isStatic()) @@ -78,29 +88,38 @@ std::pair 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(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(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 IDAndName = getKind(VD); + std::pair IDAndName = getKind(D); serializeKind(IDAndName.first, IDAndName.second, OS); } @@ -128,7 +147,14 @@ void Symbol::serializeNames(llvm::json::OStream &OS) const { SmallVector PathComponents; getPathComponents(PathComponents); - if (isa(VD) || isa(VD)) { + const ValueDecl *Decl = nullptr; + if (const auto *ED = dyn_cast(D)) { + Decl = ED->getExtendedNominal(); + } else if (const auto *VD = dyn_cast(D)) { + Decl = VD; + } + + if (isa(Decl) || isa(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( - getDocCommentProvidingDecl(VD, /*AllowSerialized=*/true)); - + const auto *DocCommentProvidingDecl = dyn_cast_or_null( + 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( - 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(VD)) { + if (const auto *FD = dyn_cast_or_null(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) + const auto *VD = dyn_cast(D); + + if (VD && BaseType) { SubMap = getSubMapForDecl(VD, BaseType); + } else { + SubMap = {}; + } - if (const auto *GC = VD->getAsGenericContext()) { + if (const auto *GC = D->getAsGenericContext()) { if (const auto Generics = GC->getGenericSignature()) { SmallVector FilteredParams; @@ -405,9 +434,9 @@ void Symbol::serializeSwiftGenericMixin(llvm::json::OStream &OS) const { filterGenericParams(Generics.getGenericParams(), FilteredParams, SubMap); - const auto *Self = dyn_cast(VD); + const auto *Self = dyn_cast(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(VD->getDeclContext())) { - ::serialize(Extension, OS); + if (const auto *ED = dyn_cast(D)) { + ::serialize(ED, OS); + } else if (const auto *VD = dyn_cast(D)) { + if (const auto *Extension = + dyn_cast_or_null(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(D)) { + OS.attribute("accessLevel", + getAccessLevelSpelling(getEffectiveAccessLevel(ED))); + } else if (const auto *VD = dyn_cast(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(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 &Availabilities) { void Symbol::serializeAvailabilityMixin(llvm::json::OStream &OS) const { llvm::StringMap 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(D)) { + return ED->getExtendedNominal()->getName(); + } else { + return cast(D)->getName(); + } +} + +const ValueDecl *Symbol::getSymbolDecl() const { + if (const auto *ED = dyn_cast(D)) { + return ED->getExtendedNominal(); + } else { + return cast(D); + } +} + void Symbol::getPathComponents(SmallVectorImpl &Components) const { + const ValueDecl *Decl = nullptr; + if (const auto *ED = dyn_cast(D)) { + Decl = ED->getExtendedNominal(); + } else if (const auto *VD = dyn_cast(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 &DeclComponents) { - // Collect the spellings, kinds, and decls of the fully qualified identifier - // components. - while (Decl && !isa(Decl)) { - SmallString<32> Scratch; - Decl->getName().getString(Scratch); - 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(DC->getAsDecl()); + auto collectPathComponents = + [&](const ValueDecl *Decl, + SmallVectorImpl &DeclComponents) { + // Collect the spellings, kinds, and decls of the fully qualified + // identifier components. + while (Decl && !isa(Decl)) { + SmallString<32> Scratch; + getName(Decl).getString(Scratch); + + 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(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 &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(Member)) { + maxPropertyAL = std::max(maxPropertyAL, VMember->getFormalAccess()); + } + } + + AccessLevel maxInheritedAL = AccessLevel::Private; + for (auto Inherited : ED->getInherited()) { + if (const auto *Proto = dyn_cast_or_null( + Inherited.getType()->getAnyNominal())) { + maxInheritedAL = std::max(maxInheritedAL, Proto->getFormalAccess()); + } + } + + return std::min(ED->getExtendedNominal()->getFormalAccess(), + std::max(maxPropertyAL, maxInheritedAL)); +} diff --git a/lib/SymbolGraphGen/Symbol.h b/lib/SymbolGraphGen/Symbol.h index 11eeacdf1a769..6dafc14a17f59 100644 --- a/lib/SymbolGraphGen/Symbol.h +++ b/lib/SymbolGraphGen/Symbol.h @@ -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 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 @@ class Symbol { 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 @@ class Symbol { 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 getKind(const Decl *D); }; } // end namespace symbolgraphgen @@ -151,18 +180,19 @@ template <> struct DenseMapInfo { static unsigned getHashValue(const Symbol S) { unsigned H = 0; H ^= DenseMapInfo::getHashValue(S.getGraph()); - H ^= DenseMapInfo::getHashValue(S.getSymbolDecl()); + H ^= + DenseMapInfo::getHashValue(S.getLocalSymbolDecl()); H ^= DenseMapInfo::getHashValue(S.getSynthesizedBaseTypeDecl()); H ^= DenseMapInfo::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:: - isEqual(LHS.getBaseType(), RHS.getBaseType()); + LHS.getLocalSymbolDecl() == RHS.getLocalSymbolDecl() && + LHS.getSynthesizedBaseTypeDecl() == + RHS.getSynthesizedBaseTypeDecl() && + DenseMapInfo::isEqual(LHS.getBaseType(), + RHS.getBaseType()); } }; } // end namespace llvm diff --git a/lib/SymbolGraphGen/SymbolGraph.cpp b/lib/SymbolGraphGen/SymbolGraph.cpp index 97d12177fbfd1..70edf5a93692c 100644 --- a/lib/SymbolGraphGen/SymbolGraph.cpp +++ b/lib/SymbolGraphGen/SymbolGraph.cpp @@ -29,26 +29,21 @@ using namespace swift; using namespace symbolgraphgen; -SymbolGraph::SymbolGraph(SymbolGraphASTWalker &Walker, - ModuleDecl &M, +SymbolGraph::SymbolGraph(SymbolGraphASTWalker &Walker, ModuleDecl &M, Optional ExtendedModule, markup::MarkupContext &Ctx, Optional ModuleVersion, bool IsForSingleNode) -: Walker(Walker), - M(M), - ExtendedModule(ExtendedModule), - Ctx(Ctx), - ModuleVersion(ModuleVersion), - IsForSingleNode(IsForSingleNode) { - if (auto *DM = M.getDeclaringModuleIfCrossImportOverlay()) { - DeclaringModule = DM; - SmallVector 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 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(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(VD)) { + if (const auto *ThisNominal = dyn_cast(D)) { OwningNominal = ThisNominal; - } else if (const auto *Extension = dyn_cast(VD)) { + } else if (const auto *Extension = dyn_cast(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(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(VD)) { +void SymbolGraph::recordConformanceRelationships(Symbol S) { + const auto D = S.getLocalSymbolDecl(); + if (const auto *NTD = dyn_cast(D)) { if (auto *PD = dyn_cast(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(Conformance->getDeclContext())); + recordEdge( + S, Symbol(this, Conformance->getProtocol(), nullptr), + RelationshipKind::ConformsTo(), + dyn_cast_or_null(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(S.getSymbolDecl())) { + if (const auto *TD = dyn_cast(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(D)) { if (const auto *Nominal = Extension->getExtendedNominal()) { - return isImplicitlyPrivate(Nominal, IgnoreContext); + return isImplicitlyPrivate(Nominal, IgnoreContext) || + Symbol::getEffectiveAccessLevel(Extension) < + Walker.Options.MinimumAccessLevel; } } diff --git a/lib/SymbolGraphGen/SymbolGraph.h b/lib/SymbolGraphGen/SymbolGraph.h index 50db059ff224e..b17ff056fdcd9 100644 --- a/lib/SymbolGraphGen/SymbolGraph.h +++ b/lib/SymbolGraphGen/SymbolGraph.h @@ -79,10 +79,8 @@ struct SymbolGraph { */ bool IsForSingleNode; - SymbolGraph(SymbolGraphASTWalker &Walker, - ModuleDecl &M, - Optional ExtendedModule, - markup::MarkupContext &Ctx, + SymbolGraph(SymbolGraphASTWalker &Walker, ModuleDecl &M, + Optional ExtendedModule, markup::MarkupContext &Ctx, Optional ModuleVersion = None, bool IsForSingleNode = false); diff --git a/lib/SymbolGraphGen/SymbolGraphASTWalker.cpp b/lib/SymbolGraphGen/SymbolGraphASTWalker.cpp index d8b12713e7c67..badb6075b359f 100644 --- a/lib/SymbolGraphGen/SymbolGraphASTWalker.cpp +++ b/lib/SymbolGraphGen/SymbolGraphASTWalker.cpp @@ -33,15 +33,14 @@ bool areModulesEqual(const ModuleDecl *lhs, const ModuleDecl *rhs) { } // anonymous namespace -SymbolGraphASTWalker::SymbolGraphASTWalker(ModuleDecl &M, - const SmallPtrSet ExportedImportedModules, - const llvm::SmallDenseMap, 4> QualifiedExportedImports, - const SymbolGraphOptions &Options) - : Options(Options), - M(M), - ExportedImportedModules(ExportedImportedModules), - QualifiedExportedImports(QualifiedExportedImports), - MainGraph(*this, M, None, Ctx) {} +SymbolGraphASTWalker::SymbolGraphASTWalker( + ModuleDecl &M, const SmallPtrSet ExportedImportedModules, + const llvm::SmallDenseMap, 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(M), - Ctx); + auto *Memory = Ctx.allocate(sizeof(SymbolGraph), alignof(SymbolGraph)); + auto *SG = new (Memory) + SymbolGraph(*this, MainGraph.M, Optional(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 Protocols; - SmallVector UnexpandedCompositions; + SmallVector 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(Ty->getAnyNominal())) { - Protocols.push_back(Proto); - } else if (const auto *Comp = Ty->getAs()) { + dyn_cast_or_null(Ty->getAnyNominal())) { + UnexpandedProtocols.push_back(Proto); + return; + } + + SmallVector UnexpandedCompositions; + + if (const auto *Comp = Ty->getAs()) { 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(Member->getAnyNominal())) { + Protocols.push_back(Proto); + UnexpandedProtocols.push_back(Proto); + } else if (const auto *Comp = + Member->getAs()) { + 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()); +} diff --git a/lib/SymbolGraphGen/SymbolGraphASTWalker.h b/lib/SymbolGraphGen/SymbolGraphASTWalker.h index 28982abf9948a..14d04b2385616 100644 --- a/lib/SymbolGraphGen/SymbolGraphASTWalker.h +++ b/lib/SymbolGraphGen/SymbolGraphASTWalker.h @@ -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 diff --git a/test/SymbolGraph/ClangImporter/ClangExtensions.swift b/test/SymbolGraph/ClangImporter/ClangExtensions.swift new file mode 100644 index 0000000000000..b6bf5bb45bdb8 --- /dev/null +++ b/test/SymbolGraph/ClangImporter/ClangExtensions.swift @@ -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 { } +} diff --git a/test/SymbolGraph/ClangImporter/ForeignExtensions.swift b/test/SymbolGraph/ClangImporter/ForeignExtensions.swift index ee0cd45a0170f..a9f84b46a1540 100644 --- a/test/SymbolGraph/ClangImporter/ForeignExtensions.swift +++ b/test/SymbolGraph/ClangImporter/ForeignExtensions.swift @@ -20,4 +20,3 @@ public extension String { case bar } } - diff --git a/test/SymbolGraph/ClangImporter/Inputs/EmitWhileBuilding/EmitWhileBuilding.framework/Headers/EmitWhileBuilding.h b/test/SymbolGraph/ClangImporter/Inputs/EmitWhileBuilding/EmitWhileBuilding.framework/Headers/EmitWhileBuilding.h index 97e9757f4261b..b2646a07c7b2b 100644 --- a/test/SymbolGraph/ClangImporter/Inputs/EmitWhileBuilding/EmitWhileBuilding.framework/Headers/EmitWhileBuilding.h +++ b/test/SymbolGraph/ClangImporter/Inputs/EmitWhileBuilding/EmitWhileBuilding.framework/Headers/EmitWhileBuilding.h @@ -1 +1,3 @@ double testVariable = 1.0; + +struct Foo { }; diff --git a/test/SymbolGraph/EmptyExtension.swift b/test/SymbolGraph/EmptyExtension.swift index df1f8d271d19b..6e4dcdb273003 100644 --- a/test/SymbolGraph/EmptyExtension.swift +++ b/test/SymbolGraph/EmptyExtension.swift @@ -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() {} } diff --git a/test/SymbolGraph/Module/BasicExtension.swift b/test/SymbolGraph/Module/BasicExtension.swift index a1290f8dcbcd6..811e112519bb6 100644 --- a/test/SymbolGraph/Module/BasicExtension.swift +++ b/test/SymbolGraph/Module/BasicExtension.swift @@ -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: + +// ALL-LABEL: module + +// ALL: {{"name": ?"BasicExtension"}} + -// EXTRACT: "precise": "s:SS14BasicExtensionE9somethingSSvp" +// Check for Symbols and Documentation Strings: -// BUILD: "precise":"s:SS14BasicExtensionE9somethingSSvp" +// Symbols and relationships are not ordered. Therefore, we have to use +// the `-DAG` variant if we have more than one symbol/relationship. -// EXTRACT: "kind": "memberOf" -// EXTRACT-NEXT: "source": "s:SS14BasicExtensionE9somethingSSvp" -// EXTRACT-NEXT: "target": "s:SS" +// ALL-LABEL: symbols -// BUILD: "kind":"memberOf" -// BUILD: "source":"s:SS14BasicExtensionE9somethingSSvp" -// BUILD: "target":"s:SS" +// 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"}} diff --git a/test/SymbolGraph/Module/CrossImport.swift b/test/SymbolGraph/Module/CrossImport.swift index 6de0a865df9de..fdf2ee94d762a 100644 --- a/test/SymbolGraph/Module/CrossImport.swift +++ b/test/SymbolGraph/Module/CrossImport.swift @@ -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 diff --git a/test/SymbolGraph/Module/NestedExtensions.swift b/test/SymbolGraph/Module/NestedExtensions.swift index 462bbc9caa6db..cbc37d7298693 100644 --- a/test/SymbolGraph/Module/NestedExtensions.swift +++ b/test/SymbolGraph/Module/NestedExtensions.swift @@ -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" diff --git a/test/SymbolGraph/Relationships/ConformsTo/FilterImplicitlyPrivate.swift b/test/SymbolGraph/Relationships/ConformsTo/FilterImplicitlyPrivate.swift index 39a0a51151ff1..620df49032e58 100644 --- a/test/SymbolGraph/Relationships/ConformsTo/FilterImplicitlyPrivate.swift +++ b/test/SymbolGraph/Relationships/ConformsTo/FilterImplicitlyPrivate.swift @@ -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": [] diff --git a/test/SymbolGraph/Relationships/ConformsTo/Indirect.swift b/test/SymbolGraph/Relationships/ConformsTo/Indirect.swift index 8f24c851c03be..0c1044c212e53 100644 --- a/test/SymbolGraph/Relationships/ConformsTo/Indirect.swift +++ b/test/SymbolGraph/Relationships/ConformsTo/Indirect.swift @@ -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" diff --git a/test/SymbolGraph/Relationships/ConformsTo/Inputs/ExternalIndirect.swift b/test/SymbolGraph/Relationships/ConformsTo/Inputs/ExternalIndirect.swift new file mode 100644 index 0000000000000..33738cc0afaef --- /dev/null +++ b/test/SymbolGraph/Relationships/ConformsTo/Inputs/ExternalIndirect.swift @@ -0,0 +1,7 @@ +public protocol EP { + func foo() +} + +public protocol EQ : EP {} + +public struct ES { } diff --git a/test/SymbolGraph/Relationships/ConformsTo/Inputs/ExternalUnderscored.swift b/test/SymbolGraph/Relationships/ConformsTo/Inputs/ExternalUnderscored.swift new file mode 100644 index 0000000000000..7a31ebc6940ed --- /dev/null +++ b/test/SymbolGraph/Relationships/ConformsTo/Inputs/ExternalUnderscored.swift @@ -0,0 +1 @@ +public struct _ExternalUnderscored {} diff --git a/test/SymbolGraph/Relationships/MemberOf/Inputs/ExternalTypealias.swift b/test/SymbolGraph/Relationships/MemberOf/Inputs/ExternalTypealias.swift new file mode 100644 index 0000000000000..f870544fc7dd6 --- /dev/null +++ b/test/SymbolGraph/Relationships/MemberOf/Inputs/ExternalTypealias.swift @@ -0,0 +1,3 @@ +public struct ExternalS {} + +public typealias ExternalA = ExternalS diff --git a/test/SymbolGraph/Relationships/MemberOf/Typealias.swift b/test/SymbolGraph/Relationships/MemberOf/Typealias.swift new file mode 100644 index 0000000000000..4ba80cb726a83 --- /dev/null +++ b/test/SymbolGraph/Relationships/MemberOf/Typealias.swift @@ -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" diff --git a/test/SymbolGraph/Relationships/Synthesized/ConditionalConformance.swift b/test/SymbolGraph/Relationships/Synthesized/ConditionalConformance.swift index f7ac63561f8aa..05a03251aa308 100644 --- a/test/SymbolGraph/Relationships/Synthesized/ConditionalConformance.swift +++ b/test/SymbolGraph/Relationships/Synthesized/ConditionalConformance.swift @@ -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() {} } diff --git a/test/SymbolGraph/Relationships/Synthesized/EnablingDeclaration.swift b/test/SymbolGraph/Relationships/Synthesized/EnablingDeclaration.swift new file mode 100644 index 0000000000000..bda0cbf3d3d12 --- /dev/null +++ b/test/SymbolGraph/Relationships/Synthesized/EnablingDeclaration.swift @@ -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\(\)"}} diff --git a/test/SymbolGraph/Relationships/Synthesized/Inputs/RemoteP.swift b/test/SymbolGraph/Relationships/Synthesized/Inputs/RemoteP.swift index 9c5450816ed49..852be4cc39ac1 100644 --- a/test/SymbolGraph/Relationships/Synthesized/Inputs/RemoteP.swift +++ b/test/SymbolGraph/Relationships/Synthesized/Inputs/RemoteP.swift @@ -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 {} diff --git a/test/SymbolGraph/Relationships/Synthesized/RemoteInheritedDocs.swift b/test/SymbolGraph/Relationships/Synthesized/RemoteInheritedDocs.swift index 3d5c55ed7b55f..22f4c4f86978d 100644 --- a/test/SymbolGraph/Relationships/Synthesized/RemoteInheritedDocs.swift +++ b/test/SymbolGraph/Relationships/Synthesized/RemoteInheritedDocs.swift @@ -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() {} } - diff --git a/test/SymbolGraph/Symbols/AccessLevelFilter/IncludeInternal.swift b/test/SymbolGraph/Symbols/AccessLevelFilter/IncludeInternal.swift index 6d83ca1a1a96d..b233fc4d834a2 100644 --- a/test/SymbolGraph/Symbols/AccessLevelFilter/IncludeInternal.swift +++ b/test/SymbolGraph/Symbols/AccessLevelFilter/IncludeInternal.swift @@ -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" diff --git a/test/SymbolGraph/Symbols/AccessLevelFilter/Inputs/Internal.swift b/test/SymbolGraph/Symbols/AccessLevelFilter/Inputs/Internal.swift new file mode 100644 index 0000000000000..3a1807ae16ff0 --- /dev/null +++ b/test/SymbolGraph/Symbols/AccessLevelFilter/Inputs/Internal.swift @@ -0,0 +1 @@ +struct S { } diff --git a/test/SymbolGraph/Symbols/Inputs/ExternalNames.swift b/test/SymbolGraph/Symbols/Inputs/ExternalNames.swift new file mode 100644 index 0000000000000..30c7ba878c53e --- /dev/null +++ b/test/SymbolGraph/Symbols/Inputs/ExternalNames.swift @@ -0,0 +1,7 @@ +public struct ExternalStruct { + public struct InnerStruct {} + + public typealias InnerTypeAlias = InnerStruct + + public enum InnerEnum {} +} diff --git a/test/SymbolGraph/Symbols/Mixins/DeclarationFragments/SelfNotLinked.swift b/test/SymbolGraph/Symbols/Mixins/DeclarationFragments/SelfNotLinked.swift index 63cdea78f401a..aa581411b6b8b 100644 --- a/test/SymbolGraph/Symbols/Mixins/DeclarationFragments/SelfNotLinked.swift +++ b/test/SymbolGraph/Symbols/Mixins/DeclarationFragments/SelfNotLinked.swift @@ -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: ], diff --git a/test/SymbolGraph/Symbols/Mixins/DocComment/Extension.swift b/test/SymbolGraph/Symbols/Mixins/DocComment/Extension.swift new file mode 100644 index 0000000000000..f7a6f07196e10 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/DocComment/Extension.swift @@ -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 diff --git a/test/SymbolGraph/Symbols/Mixins/Inputs/SPIP.swift b/test/SymbolGraph/Symbols/Mixins/Inputs/SPIP.swift new file mode 100644 index 0000000000000..ab521c5200695 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Inputs/SPIP.swift @@ -0,0 +1,3 @@ +/// SPI Protocol doc +@_spi(SPI) +public protocol P { } diff --git a/test/SymbolGraph/Symbols/Mixins/Location.swift b/test/SymbolGraph/Symbols/Mixins/Location.swift index 5fde2941b3856..8ae822056c89d 100644 --- a/test/SymbolGraph/Symbols/Mixins/Location.swift +++ b/test/SymbolGraph/Symbols/Mixins/Location.swift @@ -1,13 +1,32 @@ +// FYI: The lit commands and FileCheck statements are at the bottom of the file, to be resilient +// against changes to the doc comment format. + +/// location points here +/// v +public struct MyStruct {} + +/// 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 +// 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 -/// This is a struct. -public struct MyStruct {} -// CHECK: location -// CHECK-NEXT: uri -// CHECK-NEXT: position -// CHECK-NEXT: "line": 6 +// 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 diff --git a/test/SymbolGraph/Symbols/Mixins/SPI.swift b/test/SymbolGraph/Symbols/Mixins/SPI.swift index 4612d7d93e1e9..c5cff917b4f04 100644 --- a/test/SymbolGraph/Symbols/Mixins/SPI.swift +++ b/test/SymbolGraph/Symbols/Mixins/SPI.swift @@ -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 diff --git a/test/SymbolGraph/Symbols/Names.swift b/test/SymbolGraph/Symbols/Names.swift index bad344fa441c4..3e59b9fb4a4f2 100644 --- a/test/SymbolGraph/Symbols/Names.swift +++ b/test/SymbolGraph/Symbols/Names.swift @@ -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" diff --git a/test/SymbolGraph/Symbols/PathComponents.swift b/test/SymbolGraph/Symbols/PathComponents.swift index 14a794bd16c69..e73b67708d47e 100644 --- a/test/SymbolGraph/Symbols/PathComponents.swift +++ b/test/SymbolGraph/Symbols/PathComponents.swift @@ -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: ] diff --git a/test/SymbolGraph/Symbols/Unavailable.swift b/test/SymbolGraph/Symbols/Unavailable.swift index 732e7f945be97..34805d1eb04e8 100644 --- a/test/SymbolGraph/Symbols/Unavailable.swift +++ b/test/SymbolGraph/Symbols/Unavailable.swift @@ -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() {} +} diff --git a/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp b/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp index 5e733dcb14ef4..ef7d8e7461012 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp @@ -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,