[SymbolGraphGen] add flags to filter platforms out of availability metadata (#80778)

* add option to filter availability metadata in symbol graphs

* filter out platform-specific availability in the stdlib docs

rdar://144379124
This commit is contained in:
QuietMisdreavus
2025-04-14 16:20:22 -06:00
committed by GitHub
parent fed5b2628a
commit 41120da702
7 changed files with 155 additions and 3 deletions

View File

@@ -1764,6 +1764,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<"<level>">;
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<"<platforms>">;
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<"<platforms>">;
def pretty_print: Flag<["-"], "pretty-print">,
Flags<[SwiftSymbolGraphExtractOption]>,
HelpText<"Pretty-print the output JSON">;
@@ -1778,6 +1788,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<"<platforms>">;
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<"<platforms>">;
// swift-synthesize-interface-only options
def include_submodules : Flag<["-"], "include-submodules">,
Flags<[NoDriverOption, SwiftSynthesizeInterfaceOption]>,

View File

@@ -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<llvm::ArrayRef<StringRef>> AllowedReexportedModules = {};
/// If set, a list of availability platforms to restrict (or block) when
/// rendering symbol graphs.
std::optional<llvm::DenseSet<StringRef>> AvailabilityPlatforms = {};
/// Whether `AvailabilityPlatforms` is an allow list or a block list.
bool AvailabilityIsBlockList = false;
};
} // end namespace symbolgraphgen

View File

@@ -203,6 +203,25 @@ int swift_symbolgraph_extract_main(ArrayRef<const char *> Args,
.Default(AccessLevel::Public);
}
if (auto *A = ParsedArgs.getLastArg(OPT_allow_availability_platforms)) {
llvm::SmallVector<StringRef> AvailabilityPlatforms;
StringRef(A->getValue())
.split(AvailabilityPlatforms, ',', /*MaxSplits*/ -1,
/*KeepEmpty*/ false);
Options.AvailabilityPlatforms = llvm::DenseSet<StringRef>(
AvailabilityPlatforms.begin(), AvailabilityPlatforms.end());
Options.AvailabilityIsBlockList = false;
} else if (auto *A =
ParsedArgs.getLastArg(OPT_block_availability_platforms)) {
llvm::SmallVector<StringRef> AvailabilityPlatforms;
StringRef(A->getValue())
.split(AvailabilityPlatforms, ',', /*MaxSplits*/ -1,
/*KeepEmpty*/ false);
Options.AvailabilityPlatforms = llvm::DenseSet<StringRef>(
AvailabilityPlatforms.begin(), AvailabilityPlatforms.end());
Options.AvailabilityIsBlockList = true;
}
Invocation.getLangOptions().setCxxInteropFromArgs(ParsedArgs, Diags);
std::string InstanceSetupError;

View File

@@ -2215,6 +2215,25 @@ static void ParseSymbolGraphArgs(symbolgraphgen::SymbolGraphOptions &Opts,
Opts.MinimumAccessLevel = AccessLevel::Public;
}
if (auto *A = Args.getLastArg(OPT_symbol_graph_allow_availability_platforms)) {
llvm::SmallVector<StringRef> AvailabilityPlatforms;
StringRef(A->getValue())
.split(AvailabilityPlatforms, ',', /*MaxSplits*/ -1,
/*KeepEmpty*/ false);
Opts.AvailabilityPlatforms = llvm::DenseSet<StringRef>(
AvailabilityPlatforms.begin(), AvailabilityPlatforms.end());
Opts.AvailabilityIsBlockList = false;
} else if (auto *A = Args.getLastArg(
OPT_symbol_graph_block_availability_platforms)) {
llvm::SmallVector<StringRef> AvailabilityPlatforms;
StringRef(A->getValue())
.split(AvailabilityPlatforms, ',', /*MaxSplits*/ -1,
/*KeepEmpty*/ false);
Opts.AvailabilityPlatforms = llvm::DenseSet<StringRef>(
AvailabilityPlatforms.begin(), AvailabilityPlatforms.end());
Opts.AvailabilityIsBlockList = true;
}
// default values for generating symbol graphs during a build
Opts.PrettyPrint = false;
Opts.EmitSynthesizedMembers = true;

View File

@@ -697,6 +697,26 @@ void Symbol::serializeAvailabilityMixin(llvm::json::OStream &OS) const {
llvm::StringMap<Availability> 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;
}

View File

@@ -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)

View File

@@ -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