diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index c04a95a55484b..9950f2bc4b6ea 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -1774,6 +1774,16 @@ def symbol_graph_minimum_access_level: Separate<["-"], "symbol-graph-minimum-acc HelpText<"Include symbols with this access level or more when emitting a symbol graph">, MetaVarName<"">; +def symbol_graph_allow_availability_platforms: Separate<["-"], "symbol-graph-allow-availability-platforms">, + Flags<[FrontendOption, NoInteractiveOption, SupplementaryOutput, HelpHidden]>, + HelpText<"Restrict availability metadata to the given platforms, e.g. 'macOS,Swift'">, + MetaVarName<"">; + +def symbol_graph_block_availability_platforms: Separate<["-"], "symbol-graph-block-availability-platforms">, + Flags<[FrontendOption, NoInteractiveOption, SupplementaryOutput, HelpHidden]>, + HelpText<"Remove the given platforms from symbol graph availability metadata, e.g. 'macOS,Swift'">, + MetaVarName<"">; + def pretty_print: Flag<["-"], "pretty-print">, Flags<[SwiftSymbolGraphExtractOption]>, HelpText<"Pretty-print the output JSON">; @@ -1788,6 +1798,16 @@ def omit_extension_block_symbols: Flag<["-"], "omit-extension-block-symbols">, NoInteractiveOption, SupplementaryOutput, HelpHidden]>, HelpText<"Directly associate members and conformances with the extended nominal when generating symbol graphs instead of emitting 'swift.extension' symbols for extensions to external types">; +def allow_availability_platforms: Separate<["-"], "allow-availability-platforms">, + Flags<[SwiftSymbolGraphExtractOption]>, + HelpText<"Restrict availability metadata to the given platforms, e.g. 'macOS,Swift'">, + MetaVarName<"">; + +def block_availability_platforms: Separate<["-"], "block-availability-platforms">, + Flags<[SwiftSymbolGraphExtractOption]>, + HelpText<"Remove the given platforms from symbol graph availability metadata, e.g. 'macOS,Swift'">, + MetaVarName<"">; + // swift-synthesize-interface-only options def include_submodules : Flag<["-"], "include-submodules">, Flags<[NoDriverOption, SwiftSynthesizeInterfaceOption]>, diff --git a/include/swift/SymbolGraphGen/SymbolGraphOptions.h b/include/swift/SymbolGraphGen/SymbolGraphOptions.h index 168e345d4c4e3..179c2c142ded9 100644 --- a/include/swift/SymbolGraphGen/SymbolGraphOptions.h +++ b/include/swift/SymbolGraphGen/SymbolGraphOptions.h @@ -10,8 +10,9 @@ // //===----------------------------------------------------------------------===// -#include "llvm/TargetParser/Triple.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/TargetParser/Triple.h" #include "swift/AST/AttrKind.h" @@ -69,6 +70,13 @@ struct SymbolGraphOptions { /// If this has a value specifies an explicit allow list of reexported module /// names that should be included symbol graph. std::optional> AllowedReexportedModules = {}; + + /// If set, a list of availability platforms to restrict (or block) when + /// rendering symbol graphs. + std::optional> AvailabilityPlatforms = {}; + + /// Whether `AvailabilityPlatforms` is an allow list or a block list. + bool AvailabilityIsBlockList = false; }; } // end namespace symbolgraphgen diff --git a/lib/DriverTool/swift_symbolgraph_extract_main.cpp b/lib/DriverTool/swift_symbolgraph_extract_main.cpp index 1dedd9bb72202..8648672cae9ee 100644 --- a/lib/DriverTool/swift_symbolgraph_extract_main.cpp +++ b/lib/DriverTool/swift_symbolgraph_extract_main.cpp @@ -203,6 +203,25 @@ int swift_symbolgraph_extract_main(ArrayRef Args, .Default(AccessLevel::Public); } + if (auto *A = ParsedArgs.getLastArg(OPT_allow_availability_platforms)) { + llvm::SmallVector AvailabilityPlatforms; + StringRef(A->getValue()) + .split(AvailabilityPlatforms, ',', /*MaxSplits*/ -1, + /*KeepEmpty*/ false); + Options.AvailabilityPlatforms = llvm::DenseSet( + AvailabilityPlatforms.begin(), AvailabilityPlatforms.end()); + Options.AvailabilityIsBlockList = false; + } else if (auto *A = + ParsedArgs.getLastArg(OPT_block_availability_platforms)) { + llvm::SmallVector AvailabilityPlatforms; + StringRef(A->getValue()) + .split(AvailabilityPlatforms, ',', /*MaxSplits*/ -1, + /*KeepEmpty*/ false); + Options.AvailabilityPlatforms = llvm::DenseSet( + AvailabilityPlatforms.begin(), AvailabilityPlatforms.end()); + Options.AvailabilityIsBlockList = true; + } + Invocation.getLangOptions().setCxxInteropFromArgs(ParsedArgs, Diags); std::string InstanceSetupError; diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 08a02869acb83..d3698206435c6 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -2221,6 +2221,25 @@ static void ParseSymbolGraphArgs(symbolgraphgen::SymbolGraphOptions &Opts, Opts.MinimumAccessLevel = AccessLevel::Public; } + if (auto *A = Args.getLastArg(OPT_symbol_graph_allow_availability_platforms)) { + llvm::SmallVector AvailabilityPlatforms; + StringRef(A->getValue()) + .split(AvailabilityPlatforms, ',', /*MaxSplits*/ -1, + /*KeepEmpty*/ false); + Opts.AvailabilityPlatforms = llvm::DenseSet( + AvailabilityPlatforms.begin(), AvailabilityPlatforms.end()); + Opts.AvailabilityIsBlockList = false; + } else if (auto *A = Args.getLastArg( + OPT_symbol_graph_block_availability_platforms)) { + llvm::SmallVector AvailabilityPlatforms; + StringRef(A->getValue()) + .split(AvailabilityPlatforms, ',', /*MaxSplits*/ -1, + /*KeepEmpty*/ false); + Opts.AvailabilityPlatforms = llvm::DenseSet( + AvailabilityPlatforms.begin(), AvailabilityPlatforms.end()); + Opts.AvailabilityIsBlockList = true; + } + // default values for generating symbol graphs during a build Opts.PrettyPrint = false; Opts.EmitSynthesizedMembers = true; diff --git a/lib/SymbolGraphGen/Symbol.cpp b/lib/SymbolGraphGen/Symbol.cpp index 993c47bb8af54..10adbb6de68fc 100644 --- a/lib/SymbolGraphGen/Symbol.cpp +++ b/lib/SymbolGraphGen/Symbol.cpp @@ -697,6 +697,26 @@ void Symbol::serializeAvailabilityMixin(llvm::json::OStream &OS) const { llvm::StringMap Availabilities; getInheritedAvailabilities(D, Availabilities); + // If we were asked to filter the availability platforms for the output graph, + // perform that filtering here. + if (Graph->Walker.Options.AvailabilityPlatforms) { + auto AvailabilityPlatforms = + Graph->Walker.Options.AvailabilityPlatforms.value(); + if (Graph->Walker.Options.AvailabilityIsBlockList) { + for (const auto Availability : Availabilities.keys()) { + if (Availability != "*" && AvailabilityPlatforms.contains(Availability)) { + Availabilities.erase(Availability); + } + } + } else { + for (const auto Availability : Availabilities.keys()) { + if (Availability != "*" && !AvailabilityPlatforms.contains(Availability)) { + Availabilities.erase(Availability); + } + } + } + } + if (Availabilities.empty()) { return; } diff --git a/stdlib/cmake/modules/AddSwiftStdlib.cmake b/stdlib/cmake/modules/AddSwiftStdlib.cmake index b149ee969517c..28829186bc4e4 100644 --- a/stdlib/cmake/modules/AddSwiftStdlib.cmake +++ b/stdlib/cmake/modules/AddSwiftStdlib.cmake @@ -932,11 +932,14 @@ function(add_swift_target_library_single target name) endif() endif() - # FIXME: swiftDarwin currently trips an assertion in SymbolGraphGen - if (SWIFTLIB_IS_STDLIB AND SWIFT_STDLIB_BUILD_SYMBOL_GRAPHS AND NOT ${name} STREQUAL "swiftDarwin") + # FIXME: swiftDarwin and swiftDifferentiationUnittest currently trip an assertion in SymbolGraphGen + if (SWIFTLIB_IS_STDLIB AND SWIFT_STDLIB_BUILD_SYMBOL_GRAPHS AND NOT ${name} STREQUAL "swiftDarwin" + AND NOT ${name} STREQUAL "swiftDifferentiationUnittest") list(APPEND SWIFTLIB_SINGLE_SWIFT_COMPILE_FLAGS "-Xfrontend;-emit-symbol-graph") list(APPEND SWIFTLIB_SINGLE_SWIFT_COMPILE_FLAGS "-Xfrontend;-emit-symbol-graph-dir;-Xfrontend;${out_lib_dir}/symbol-graph/${VARIANT_NAME}") + list(APPEND SWIFTLIB_SINGLE_SWIFT_COMPILE_FLAGS + "-Xfrontend;-symbol-graph-allow-availability-platforms;-Xfrontend;Swift") endif() if(MODULE) diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/AvailabilityFilter.swift b/test/SymbolGraph/Symbols/Mixins/Availability/AvailabilityFilter.swift new file mode 100644 index 0000000000000..d09bca0c8ff4d --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/AvailabilityFilter.swift @@ -0,0 +1,63 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend %s -module-name AvailabilityFilter -emit-module -emit-module-path %t/AvailabilityFilter.swiftmodule -emit-symbol-graph -emit-symbol-graph-dir %t/ +// RUN: %FileCheck %s --input-file %t/AvailabilityFilter.symbols.json --check-prefix DEFAULT + +// RUN: %target-swift-symbolgraph-extract -module-name AvailabilityFilter -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/AvailabilityFilter.symbols.json --check-prefix DEFAULT + +// Now checking the allowlist behavior... + +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend %s -module-name AvailabilityFilter -emit-module -emit-module-path %t/AvailabilityFilter.swiftmodule -emit-symbol-graph -emit-symbol-graph-dir %t/ -symbol-graph-allow-availability-platforms macOS,iOS +// RUN: %FileCheck %s --input-file %t/AvailabilityFilter.symbols.json --check-prefix ALLOWLIST + +// RUN: %target-swift-symbolgraph-extract -module-name AvailabilityFilter -I %t -pretty-print -output-dir %t -allow-availability-platforms macOS,iOS +// RUN: %FileCheck %s --input-file %t/AvailabilityFilter.symbols.json --check-prefix ALLOWLIST + +// Now checking the blocklist behavior... + +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend %s -module-name AvailabilityFilter -emit-module -emit-module-path %t/AvailabilityFilter.swiftmodule -emit-symbol-graph -emit-symbol-graph-dir %t/ -symbol-graph-block-availability-platforms macOS,iOS +// RUN: %FileCheck %s --input-file %t/AvailabilityFilter.symbols.json --check-prefix BLOCKLIST + +// RUN: %target-swift-symbolgraph-extract -module-name AvailabilityFilter -I %t -pretty-print -output-dir %t -block-availability-platforms macOS,iOS +// RUN: %FileCheck %s --input-file %t/AvailabilityFilter.symbols.json --check-prefix BLOCKLIST + +// Now test to ensure an empty allow list filters out all availability... + +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend %s -module-name AvailabilityFilter -emit-module -emit-module-path %t/AvailabilityFilter.swiftmodule -emit-symbol-graph -emit-symbol-graph-dir %t/ -symbol-graph-allow-availability-platforms "" +// RUN: %FileCheck %s --input-file %t/AvailabilityFilter.symbols.json --check-prefix EMPTY + +// RUN: %target-swift-symbolgraph-extract -module-name AvailabilityFilter -I %t -pretty-print -output-dir %t -allow-availability-platforms "" +// RUN: %FileCheck %s --input-file %t/AvailabilityFilter.symbols.json --check-prefix EMPTY + +// REQUIRES: OS=macosx + +@available(macOS 11.0, iOS 15.0, watchOS 15.0, *) +public struct S {} + +/// Ensure that regardless of platforms being removed, that universal availability info, +/// like unconditional deprecation, still lands in the symbol graph. +@available(*, deprecated) +public class C {} + +// DEFAULT-DAG: macOS +// DEFAULT-DAG: iOS +// DEFAULT-DAG: watchOS +// DEFAULT-DAG: "isUnconditionallyDeprecated":{{ ?}}true + +// ALLOWLIST-NOT: watchOS +// ALLOWLIST-DAG: macOS +// ALLOWLIST-DAG: iOS +// ALLOWLIST-DAG: "isUnconditionallyDeprecated":{{ ?}}true + +// BLOCKLIST-NOT: macOS +// BLOCKLIST-NOT: iOS +// BLOCKLIST-DAG: watchOS +// BLOCKLIST-DAG: "isUnconditionallyDeprecated":{{ ?}}true + +// EMPTY-NOT: macOS +// EMPTY-NOT: iOS +// EMPTY-NOT: watchOS +// EMPTY-DAG: "isUnconditionallyDeprecated":{{ ?}}true