From af49fa199e662238ecd4985afb96113758cc58f3 Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Mon, 4 May 2026 14:52:17 -0700 Subject: [PATCH 1/3] AST: Avoid including unnecessary headers in ASTContext.h. Anything that can be forward declared in ASTContext.h should be because it is included by nearly every implementation file in the compiler. Avoiding these includes allows these various options types to be changed without a full rebuild. NFC. --- include/swift/AST/ASTContext.h | 16 +++++++++------- lib/AST/Module.cpp | 1 + lib/AST/PluginLoader.cpp | 2 ++ lib/ClangImporter/ClangIncludePaths.cpp | 1 + .../ClangModuleDependencyScanner.cpp | 5 +++-- lib/ClangImporter/ImportDecl.cpp | 1 + lib/IRGen/GenType.cpp | 1 + lib/Sema/ConstraintSystem.cpp | 1 + unittests/AST/TestContext.h | 4 ++++ unittests/ClangImporter/ClangImporterTests.cpp | 2 ++ unittests/Sema/SemaFixture.h | 4 ++++ 11 files changed, 29 insertions(+), 9 deletions(-) diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index b2e04578de7f8..22f7a49ccb347 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -23,18 +23,12 @@ #include "swift/AST/Identifier.h" #include "swift/AST/Import.h" #include "swift/AST/ProtocolConformanceOptions.h" -#include "swift/AST/SILOptions.h" -#include "swift/AST/SearchPathOptions.h" #include "swift/AST/Type.h" #include "swift/AST/TypeAlignments.h" #include "swift/AST/Types.h" #include "swift/Basic/BlockList.h" -#include "swift/Basic/CASOptions.h" -#include "swift/Basic/LangOptions.h" #include "swift/Basic/Located.h" #include "swift/Basic/Malloc.h" -#include "swift/Serialization/SerializationOptions.h" -#include "swift/SymbolGraphGen/SymbolGraphOptions.h" #include "clang/AST/DeclTemplate.h" #include "clang/Basic/DarwinSDKInfo.h" #include "llvm/ADT/ArrayRef.h" @@ -77,6 +71,7 @@ namespace swift { class AvailabilityRange; class BoundGenericType; class BuiltinTupleDecl; + class CASOptions; class ClangModuleLoader; class ClangNode; class ClangTypeConverter; @@ -100,6 +95,7 @@ namespace swift { class LazyContextData; class LazyIterableDeclContextData; class LazyMemberLoader; + class LangOptions; struct MacroDiscriminatorContext; class ModuleInterfaceChecker; class PatternBindingDecl; @@ -107,13 +103,14 @@ namespace swift { class PluginLoader; class SourceFile; class SourceLoc; + class SerializationOptions; + class SILOptions; struct TemplateInstantiationError; class Type; class TypeVariableType; class TupleType; class FunctionType; class ArchetypeType; - class Identifier; class InheritedNameSet; class ModuleDecl; class PackageUnit; @@ -142,6 +139,7 @@ namespace swift { class DiagnosticEngine; struct RawComment; class DocComment; + class SearchPathOptions; class SILBoxType; class SILTransform; class TypeAliasDecl; @@ -165,6 +163,10 @@ namespace ide { class TypeCheckCompletionCallback; } +namespace symbolgraphgen { + struct SymbolGraphOptions; +} + /// Lists the set of "known" Foundation entities that are used in the /// compiler. /// diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index adf39d5aec6b0..768e9c6e99fd0 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -40,6 +40,7 @@ #include "swift/AST/PrettyStackTrace.h" #include "swift/AST/PrintOptions.h" #include "swift/AST/ProtocolConformance.h" +#include "swift/AST/SearchPathOptions.h" #include "swift/AST/SourceFile.h" #include "swift/AST/SourceFileExtras.h" #include "swift/AST/SynthesizedFileUnit.h" diff --git a/lib/AST/PluginLoader.cpp b/lib/AST/PluginLoader.cpp index 571ab68df061a..46617ad685fa1 100644 --- a/lib/AST/PluginLoader.cpp +++ b/lib/AST/PluginLoader.cpp @@ -14,7 +14,9 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/DiagnosticsSema.h" +#include "swift/AST/SearchPathOptions.h" #include "swift/Basic/Assertions.h" +#include "swift/Basic/CASOptions.h" #include "swift/Basic/SourceManager.h" #include "swift/Parse/Lexer.h" #include "llvm/Config/config.h" diff --git a/lib/ClangImporter/ClangIncludePaths.cpp b/lib/ClangImporter/ClangIncludePaths.cpp index 832fb388b3d46..5bc725811f7c6 100644 --- a/lib/ClangImporter/ClangIncludePaths.cpp +++ b/lib/ClangImporter/ClangIncludePaths.cpp @@ -14,6 +14,7 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/DiagnosticsClangImporter.h" +#include "swift/AST/SearchPathOptions.h" #include "swift/Basic/Assertions.h" #include "swift/Basic/Platform.h" #include "swift/ClangImporter/ClangImporter.h" diff --git a/lib/ClangImporter/ClangModuleDependencyScanner.cpp b/lib/ClangImporter/ClangModuleDependencyScanner.cpp index e4258b31bed3c..b0a3ebd1a0af0 100644 --- a/lib/ClangImporter/ClangModuleDependencyScanner.cpp +++ b/lib/ClangImporter/ClangModuleDependencyScanner.cpp @@ -16,9 +16,10 @@ #include "ImporterImpl.h" #include "swift/AST/DiagnosticsSema.h" #include "swift/AST/ModuleDependencies.h" +#include "swift/Basic/Assertions.h" +#include "swift/Basic/CASOptions.h" #include "swift/Basic/SourceManager.h" #include "swift/ClangImporter/ClangImporter.h" -#include "swift/Basic/Assertions.h" #include "clang/Basic/Diagnostic.h" #include "clang/CAS/CASOptions.h" #include "clang/Frontend/CompilerInvocation.h" @@ -28,8 +29,8 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/FileSystem.h" -#include "llvm/Support/Signals.h" #include "llvm/Support/Path.h" +#include "llvm/Support/Signals.h" #include "llvm/Support/StringSaver.h" using namespace swift; diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 7ff5947226436..c1f607f587190 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -42,6 +42,7 @@ #include "swift/AST/Pattern.h" #include "swift/AST/PrettyStackTrace.h" #include "swift/AST/ProtocolConformance.h" +#include "swift/AST/SILOptions.h" #include "swift/AST/Stmt.h" #include "swift/AST/Type.h" #include "swift/AST/TypeCheckRequests.h" diff --git a/lib/IRGen/GenType.cpp b/lib/IRGen/GenType.cpp index f49329384add1..8b2acc1d3f6bc 100644 --- a/lib/IRGen/GenType.cpp +++ b/lib/IRGen/GenType.cpp @@ -21,6 +21,7 @@ #include "swift/AST/LazyResolver.h" #include "swift/AST/IRGenOptions.h" #include "swift/AST/PrettyStackTrace.h" +#include "swift/AST/SearchPathOptions.h" #include "swift/AST/Types.h" #include "swift/Basic/Assertions.h" #include "swift/Basic/Platform.h" diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 9d2ba68b10d2c..c98fcea21f36b 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -31,6 +31,7 @@ #include "swift/AST/MacroDefinition.h" #include "swift/AST/ParameterList.h" #include "swift/AST/ProtocolConformance.h" +#include "swift/AST/SILOptions.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/AST/TypeTransform.h" #include "swift/AST/Types.h" diff --git a/unittests/AST/TestContext.h b/unittests/AST/TestContext.h index f830b3e0d34d7..7ddcf3427fad1 100644 --- a/unittests/AST/TestContext.h +++ b/unittests/AST/TestContext.h @@ -13,9 +13,13 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/Module.h" +#include "swift/AST/SILOptions.h" +#include "swift/AST/SearchPathOptions.h" #include "swift/AST/SourceFile.h" +#include "swift/Basic/CASOptions.h" #include "swift/Basic/LangOptions.h" #include "swift/Basic/SourceManager.h" +#include "swift/Serialization/SerializationOptions.h" #include "swift/SymbolGraphGen/SymbolGraphOptions.h" #include "llvm/TargetParser/Host.h" diff --git a/unittests/ClangImporter/ClangImporterTests.cpp b/unittests/ClangImporter/ClangImporterTests.cpp index b643eb6b59b50..273fbb32420d5 100644 --- a/unittests/ClangImporter/ClangImporterTests.cpp +++ b/unittests/ClangImporter/ClangImporterTests.cpp @@ -1,5 +1,6 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/DiagnosticEngine.h" +#include "swift/AST/SILOptions.h" #include "swift/AST/SearchPathOptions.h" #include "swift/Basic/CASOptions.h" #include "swift/Basic/Defer.h" @@ -7,6 +8,7 @@ #include "swift/Basic/LangOptions.h" #include "swift/Basic/SourceManager.h" #include "swift/ClangImporter/ClangImporter.h" +#include "swift/Serialization/SerializationOptions.h" #include "swift/SymbolGraphGen/SymbolGraphOptions.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/Support/FileSystem.h" diff --git a/unittests/Sema/SemaFixture.h b/unittests/Sema/SemaFixture.h index bda2b433b4da2..f42993702a8aa 100644 --- a/unittests/Sema/SemaFixture.h +++ b/unittests/Sema/SemaFixture.h @@ -13,13 +13,17 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/Module.h" +#include "swift/AST/SILOptions.h" +#include "swift/AST/SearchPathOptions.h" #include "swift/AST/SourceFile.h" #include "swift/AST/Type.h" #include "swift/AST/Types.h" +#include "swift/Basic/CASOptions.h" #include "swift/Basic/LangOptions.h" #include "swift/Basic/Platform.h" #include "swift/Basic/SourceManager.h" #include "swift/Sema/ConstraintSystem.h" +#include "swift/Serialization/SerializationOptions.h" #include "swift/SymbolGraphGen/SymbolGraphOptions.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" From 4c2404b896e6aabd634c67b3028554fdd321ff62 Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Fri, 1 May 2026 07:51:34 -0700 Subject: [PATCH 2/3] SymbolGraphGen: Expand anyAppleOS availability in docs. In documentation, `@available(anyAppleOS 26, *)` should be expanded to platform-specific availability attributes, rather than being rendered as an attribute for the platform "Any Apple OS". Partially resolves rdar://174557919. --- lib/SymbolGraphGen/AvailabilityMixin.cpp | 17 ++- lib/SymbolGraphGen/AvailabilityMixin.h | 2 + lib/SymbolGraphGen/Symbol.cpp | 119 +++++++++++++----- .../Availability/AnyAppleOS/Expansion.swift | 93 ++++++++++++++ .../InnerOverridesOuterPlatform.swift | 50 ++++++++ .../InnerPlatformOverridesOuter.swift | 56 +++++++++ .../AnyAppleOS/PlatformSupersedes.swift | 53 ++++++++ .../Duplicated/MessageLastWins.swift | 8 +- 8 files changed, 354 insertions(+), 44 deletions(-) create mode 100644 test/SymbolGraph/Symbols/Mixins/Availability/AnyAppleOS/Expansion.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/Availability/AnyAppleOS/InnerOverridesOuterPlatform.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/Availability/AnyAppleOS/InnerPlatformOverridesOuter.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/Availability/AnyAppleOS/PlatformSupersedes.swift diff --git a/lib/SymbolGraphGen/AvailabilityMixin.cpp b/lib/SymbolGraphGen/AvailabilityMixin.cpp index 75a1c150e409b..ce8f6b8ec28a9 100644 --- a/lib/SymbolGraphGen/AvailabilityMixin.cpp +++ b/lib/SymbolGraphGen/AvailabilityMixin.cpp @@ -17,19 +17,18 @@ using namespace swift; using namespace symbolgraphgen; -namespace { -StringRef getDomain(const SemanticAvailableAttr &AvAttr) { +StringRef Availability::getDomainDescription(AvailabilityDomain Domain) { // FIXME: [avalailability] Move the definition of these strings into // AvailabilityDomain so that new domains are handled automatically. - if (AvAttr.getDomain().isPackageDescription()) + if (Domain.isPackageDescription()) return { "SwiftPM" }; - if (AvAttr.getDomain().isSwiftLanguageMode()) + if (Domain.isSwiftLanguageMode()) return { "Swift" }; // Platform-specific availability. - switch (AvAttr.getPlatform()) { + switch (Domain.getPlatformKind()) { case swift::PlatformKind::iOS: return { "iOS" }; case swift::PlatformKind::macCatalyst: @@ -73,12 +72,12 @@ StringRef getDomain(const SemanticAvailableAttr &AvAttr) { } llvm_unreachable("invalid platform kind"); } -} // end anonymous namespace Availability::Availability(const SemanticAvailableAttr &AvAttr) - : Domain(getDomain(AvAttr)), Introduced(AvAttr.getIntroduced()), - Deprecated(AvAttr.getDeprecated()), Obsoleted(AvAttr.getObsoleted()), - Message(AvAttr.getMessage()), Renamed(AvAttr.getRename()), + : Domain(getDomainDescription(AvAttr.getDomain())), + Introduced(AvAttr.getIntroduced()), Deprecated(AvAttr.getDeprecated()), + Obsoleted(AvAttr.getObsoleted()), Message(AvAttr.getMessage()), + Renamed(AvAttr.getRename()), IsUnconditionallyDeprecated(AvAttr.isUnconditionallyDeprecated()), IsUnconditionallyUnavailable(AvAttr.isUnconditionallyUnavailable()) { assert(!Domain.empty()); diff --git a/lib/SymbolGraphGen/AvailabilityMixin.h b/lib/SymbolGraphGen/AvailabilityMixin.h index 39fc82f007911..e66f8dea1da0c 100644 --- a/lib/SymbolGraphGen/AvailabilityMixin.h +++ b/lib/SymbolGraphGen/AvailabilityMixin.h @@ -49,6 +49,8 @@ struct Availability { /// If \c true, is unconditionally unavailable in this \c Domain. bool IsUnconditionallyUnavailable; + static StringRef getDomainDescription(AvailabilityDomain Domain); + Availability(const SemanticAvailableAttr &AvAttr); /// Update this availability from a duplicate @available diff --git a/lib/SymbolGraphGen/Symbol.cpp b/lib/SymbolGraphGen/Symbol.cpp index f5faae6d87801..e34ad4a669661 100644 --- a/lib/SymbolGraphGen/Symbol.cpp +++ b/lib/SymbolGraphGen/Symbol.cpp @@ -639,6 +639,75 @@ void Symbol::serializeLocationMixin(llvm::json::OStream &OS) const { } namespace { +/// Insert or merge a single Availability entry into a domain map, using +/// either parent-inheritance or duplicate-attribute rules. +void insertAvailability(Availability NewAvailability, + llvm::StringMap &Availabilities, + bool IsParent) { + if (NewAvailability.empty()) + return; + auto Existing = Availabilities.find(NewAvailability.Domain); + if (Existing != Availabilities.end()) { + // There are different rules for filling in missing components or replacing + // existing components from a parent's @available attribute compared to + // duplicate @available attributes on the same declaration. See the + // respective methods below for an explanation for the replacement/filling + // rules. + // FIXME: [availability] Use compiler's logic to synthesize availability. + // Some of this merge logic is out-of-sync with how the compiler determines + // availability when multiple attributes apply to the same platform. + if (IsParent) { + Existing->getValue().updateFromParent(NewAvailability); + } else { + Existing->getValue().updateFromDuplicate(NewAvailability); + } + } else { + // There are no availabilities for this domain yet, so either inherit the + // parent's in its entirety or set it from this declaration. + Availabilities.insert({NewAvailability.Domain, NewAvailability}); + } +} + +std::optional> +getPlatformsToExpand(SemanticAvailableAttr AvAttr) { + // For now, only expand platform availability for anyAppleOS attributes. + if (AvAttr.getDomain().contains( + AvailabilityDomain::forPlatform(PlatformKind::anyAppleOS))) { + static const PlatformKind ApplePlatforms[] = { + PlatformKind::macOS, + PlatformKind::iOS, + PlatformKind::watchOS, + PlatformKind::tvOS, + PlatformKind::visionOS, + }; + return ApplePlatforms; + } + + return {}; +} + +/// Expands a single availability attribute into one or more Availability +/// structs. +void expandInferredAvailabilityAttr(SemanticAvailableAttr AvAttr, + SmallVectorImpl &Expanded) { + if (auto Platforms = getPlatformsToExpand(AvAttr)) { + for (auto Platform : *Platforms) { + Availability InferredAvailability(AvAttr); + InferredAvailability.Domain = Availability::getDomainDescription( + AvailabilityDomain::forPlatform(Platform)); + // FIXME: [availability] Versions should be remapped, too. + // Version remapping is unnecessary at the moment because only anyAppleOS + // availability gets expanded and anyAppleOS doesn't require version + // remapping (yet). + Expanded.push_back(InferredAvailability); + } + return; + } + + // Just convert the attribute directly. + Expanded.push_back(Availability(AvAttr)); +} + /// Get the availabilities for each domain on a declaration without walking /// up the parent hierarchy. /// @@ -652,37 +721,17 @@ void getAvailabilities(const Decl *D, bool IsParent) { // DeclAttributes is a linked list in reverse order from where they // appeared in the source. Let's re-reverse them. - SmallVector AvAttrs; - for (auto Attr : D->getSemanticAvailableAttrs(/*includeInactive=*/true)) { + SmallVector AvAttrs; + for (auto Attr : D->getSemanticAvailableAttrs(/*includingInactive=*/true)) { AvAttrs.push_back(Attr); } - std::reverse(AvAttrs.begin(), AvAttrs.end()); // Now go through them in source order. - for (auto AvAttr : AvAttrs) { - Availability NewAvailability(AvAttr); - if (NewAvailability.empty()) { - continue; - } - auto ExistingAvailability = Availabilities.find(NewAvailability.Domain); - if (ExistingAvailability != Availabilities.end()) { - // There are different rules for filling in missing components - // or replacing existing components from a parent's @available - // attribute compared to duplicate @available attributes on the - // same declaration. - // See the respective methods below for an explanation for the - // replacement/filling rules. - if (IsParent) { - ExistingAvailability->getValue().updateFromParent(NewAvailability); - } else { - ExistingAvailability->getValue().updateFromDuplicate(NewAvailability); - } - } else { - // There are no availabilities for this domain yet, so either - // inherit the parent's in its entirety or set it from this declaration. - Availabilities.insert(std::make_pair(NewAvailability.Domain, - NewAvailability)); - } + for (auto AvAttr : llvm::reverse(AvAttrs)) { + SmallVector ExpandedAvailabilities; + expandInferredAvailabilityAttr(AvAttr, ExpandedAvailabilities); + for (auto &Avail : ExpandedAvailabilities) + insertAvailability(Avail, Availabilities, IsParent); } } @@ -734,10 +783,18 @@ void Symbol::serializeAvailabilityMixin(llvm::json::OStream &OS) const { return; } - OS.attributeArray("availability", [&]{ - for (const auto &Availability : Availabilities) { - Availability.getValue().serialize(OS); - } + OS.attributeArray("availability", [&] { + // Sort the availability entries to ensure that they are emitted in a stable + // order. + SmallVector SortedAvailabilities; + for (const auto &A : Availabilities) + SortedAvailabilities.push_back(A.getValue()); + llvm::sort(SortedAvailabilities, + [](const Availability &A, const Availability &B) { + return A.Domain < B.Domain; + }); + for (const auto &A : SortedAvailabilities) + A.serialize(OS); }); } diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/AnyAppleOS/Expansion.swift b/test/SymbolGraph/Symbols/Mixins/Availability/AnyAppleOS/Expansion.swift new file mode 100644 index 0000000000000..168f0ed035dac --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/AnyAppleOS/Expansion.swift @@ -0,0 +1,93 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name Expansion -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name Expansion -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/Expansion.symbols.json + +// CHECK-LABEL: "symbols": [ +// CHECK-LABEL: "precise": "s:9Expansion1SV", +// CHECK: "availability": [ +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "iOS", +// CHECK-NEXT: "introduced": { +// CHECK-NEXT: "major": 26 +// CHECK-NEXT: }, +// CHECK-NEXT: "deprecated": { +// CHECK-NEXT: "major": 26, +// CHECK-NEXT: "minor": 2 +// CHECK-NEXT: }, +// CHECK-NEXT: "obsoleted": { +// CHECK-NEXT: "major": 26, +// CHECK-NEXT: "minor": 4 +// CHECK-NEXT: }, +// CHECK-NEXT: "message": "Everyone makes mistakes", +// CHECK-NEXT: "renamed": "S2" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "macOS", +// CHECK-NEXT: "introduced": { +// CHECK-NEXT: "major": 26 +// CHECK-NEXT: }, +// CHECK-NEXT: "deprecated": { +// CHECK-NEXT: "major": 26, +// CHECK-NEXT: "minor": 2 +// CHECK-NEXT: }, +// CHECK-NEXT: "obsoleted": { +// CHECK-NEXT: "major": 26, +// CHECK-NEXT: "minor": 4 +// CHECK-NEXT: }, +// CHECK-NEXT: "message": "Everyone makes mistakes", +// CHECK-NEXT: "renamed": "S2" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "tvOS", +// CHECK-NEXT: "introduced": { +// CHECK-NEXT: "major": 26 +// CHECK-NEXT: }, +// CHECK-NEXT: "deprecated": { +// CHECK-NEXT: "major": 26, +// CHECK-NEXT: "minor": 2 +// CHECK-NEXT: }, +// CHECK-NEXT: "obsoleted": { +// CHECK-NEXT: "major": 26, +// CHECK-NEXT: "minor": 4 +// CHECK-NEXT: }, +// CHECK-NEXT: "message": "Everyone makes mistakes", +// CHECK-NEXT: "renamed": "S2" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "visionOS", +// CHECK-NEXT: "introduced": { +// CHECK-NEXT: "major": 26 +// CHECK-NEXT: }, +// CHECK-NEXT: "deprecated": { +// CHECK-NEXT: "major": 26, +// CHECK-NEXT: "minor": 2 +// CHECK-NEXT: }, +// CHECK-NEXT: "obsoleted": { +// CHECK-NEXT: "major": 26, +// CHECK-NEXT: "minor": 4 +// CHECK-NEXT: }, +// CHECK-NEXT: "message": "Everyone makes mistakes", +// CHECK-NEXT: "renamed": "S2" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "watchOS", +// CHECK-NEXT: "introduced": { +// CHECK-NEXT: "major": 26 +// CHECK-NEXT: }, +// CHECK-NEXT: "deprecated": { +// CHECK-NEXT: "major": 26, +// CHECK-NEXT: "minor": 2 +// CHECK-NEXT: }, +// CHECK-NEXT: "obsoleted": { +// CHECK-NEXT: "major": 26, +// CHECK-NEXT: "minor": 4 +// CHECK-NEXT: }, +// CHECK-NEXT: "message": "Everyone makes mistakes", +// CHECK-NEXT: "renamed": "S2" +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NOT: "domain": "Any Apple OS" + +@available(anyAppleOS, introduced: 26, deprecated: 26.2, obsoleted: 26.4, message: "Everyone makes mistakes", renamed: "S2") +public struct S {} diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/AnyAppleOS/InnerOverridesOuterPlatform.swift b/test/SymbolGraph/Symbols/Mixins/Availability/AnyAppleOS/InnerOverridesOuterPlatform.swift new file mode 100644 index 0000000000000..077c5520dbb4f --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/AnyAppleOS/InnerOverridesOuterPlatform.swift @@ -0,0 +1,50 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name InnerOverridesOuterPlatform -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name InnerOverridesOuterPlatform -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/InnerOverridesOuterPlatform.symbols.json + +// CHECK-LABEL: "symbols": [ +// CHECK-LABEL: "precise": "s:27InnerOverridesOuterPlatform0C0V0A0V", +// CHECK: "availability": [ +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "iOS", +// CHECK-NEXT: "introduced": { +// CHECK-NEXT: "major": 26 +// CHECK-NEXT: "minor": 4 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "macOS", +// CHECK-NEXT: "introduced": { +// CHECK-NEXT: "major": 26 +// CHECK-NEXT: "minor": 4 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "tvOS", +// CHECK-NEXT: "introduced": { +// CHECK-NEXT: "major": 26 +// CHECK-NEXT: "minor": 4 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "visionOS", +// CHECK-NEXT: "introduced": { +// CHECK-NEXT: "major": 26 +// CHECK-NEXT: "minor": 4 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "watchOS", +// CHECK-NEXT: "introduced": { +// CHECK-NEXT: "major": 26 +// CHECK-NEXT: "minor": 4 +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: ] + +@available(macOS 26, *) +public struct Outer { + @available(anyAppleOS 26.4, *) + public struct Inner {} +} diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/AnyAppleOS/InnerPlatformOverridesOuter.swift b/test/SymbolGraph/Symbols/Mixins/Availability/AnyAppleOS/InnerPlatformOverridesOuter.swift new file mode 100644 index 0000000000000..d01bc24b8fb24 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/AnyAppleOS/InnerPlatformOverridesOuter.swift @@ -0,0 +1,56 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name InnerPlatformOverridesOuter -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name InnerPlatformOverridesOuter -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/InnerPlatformOverridesOuter.symbols.json + +// CHECK-LABEL: "symbols": [ +// CHECK-LABEL: "precise": "s:27InnerPlatformOverridesOuter0D0V0A0V", +// CHECK: "availability": [ +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "iOS", +// CHECK-NEXT: "introduced": { +// CHECK-NEXT: "major": 26, +// CHECK-NEXT: "minor": 2 +// CHECK-NEXT: }, +// CHECK-NEXT: "obsoleted": { +// CHECK-NEXT: "major": 26, +// CHECK-NEXT: "minor": 4 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "macOS", +// CHECK-NEXT: "introduced": { +// CHECK-NEXT: "major": 26 +// CHECK-NEXT: "minor": 4 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "tvOS", +// CHECK-NEXT: "introduced": { +// CHECK-NEXT: "major": 26 +// CHECK-NEXT: }, +// CHECK-NEXT: "isUnconditionallyDeprecated": true +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "visionOS", +// CHECK-NEXT: "introduced": { +// CHECK-NEXT: "major": 26 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "watchOS", +// CHECK-NEXT: "introduced": { +// CHECK-NEXT: "major": 26, +// CHECK-NEXT: "minor": 2 +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: ] + +@available(anyAppleOS, introduced: 26) +public struct Outer { + @available(macOS, introduced: 26.4) + @available(iOS, introduced: 26.2, obsoleted: 26.4) + @available(watchOS 26.2, *) + @available(tvOS, deprecated) + public struct Inner {} +} diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/AnyAppleOS/PlatformSupersedes.swift b/test/SymbolGraph/Symbols/Mixins/Availability/AnyAppleOS/PlatformSupersedes.swift new file mode 100644 index 0000000000000..9e548765ccd90 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/AnyAppleOS/PlatformSupersedes.swift @@ -0,0 +1,53 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name PlatformSupersedes -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name PlatformSupersedes -I %t -pretty-print -output-dir %t +// RUN: %FileCheck %s --input-file %t/PlatformSupersedes.symbols.json + +// CHECK-LABEL: "precise": "s:18PlatformSupersedes1SV", +// CHECK: "availability": [ +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "iOS", +// CHECK-NEXT: "introduced": { +// CHECK-NEXT: "major": 26, +// CHECK-NEXT: "minor": 2 +// CHECK-NEXT: }, +// CHECK-NEXT: "obsoleted": { +// CHECK-NEXT: "major": 26, +// CHECK-NEXT: "minor": 4 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "macOS", +// CHECK-NEXT: "introduced": { +// CHECK-NEXT: "major": 26 +// CHECK-NEXT: "minor": 4 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "tvOS", +// CHECK-NEXT: "introduced": { +// CHECK-NEXT: "major": 26 +// CHECK-NEXT: }, +// CHECK-NEXT: "isUnconditionallyDeprecated": true +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "visionOS", +// CHECK-NEXT: "introduced": { +// CHECK-NEXT: "major": 26 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// FIXME: [availability] watchOS introduction should be 11 +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "watchOS", +// CHECK-NEXT: "introduced": { +// CHECK-NEXT: "major": 26 +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: ] + +@available(anyAppleOS, introduced: 26) +@available(macOS, introduced: 26.4) +@available(iOS, introduced: 26.2, obsoleted: 26.4) +@available(watchOS 11, *) +@available(tvOS, deprecated) +public struct S {} diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/MessageLastWins.swift b/test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/MessageLastWins.swift index 157ea906da57d..82705171f153a 100644 --- a/test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/MessageLastWins.swift +++ b/test/SymbolGraph/Symbols/Mixins/Availability/Duplicated/MessageLastWins.swift @@ -13,13 +13,13 @@ public func foo() {} // CHECK-LABEL: "precise": "s:15MessageLastWins3fooyyF", // CHECK: "availability": [ // CHECK-NEXT: { -// CHECK-NEXT: "domain": "macOS", -// CHECK-NEXT: "message": "second", +// CHECK-NEXT: "domain": "iOS", +// CHECK-NEXT: "message": "iOS", // CHECK-NEXT: "isUnconditionallyDeprecated": true // CHECK-NEXT: }, // CHECK-NEXT: { -// CHECK-NEXT: "domain": "iOS", -// CHECK-NEXT: "message": "iOS", +// CHECK-NEXT: "domain": "macOS", +// CHECK-NEXT: "message": "second", // CHECK-NEXT: "isUnconditionallyDeprecated": true // CHECK-NEXT: } // CHECK-NEXT: ] From dc3a5015f2a1a639bba821f3717849d9326c2cd2 Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Fri, 1 May 2026 07:52:29 -0700 Subject: [PATCH 3/3] SymbolGraphGen: Introduce -active-platform-availability-only flag. When -active-platform-availability-only is specified swift-symbolgraph-extract should only emit availability metadata for the active platform (inferred from the -target). Resolves rdar://174557919. --- include/swift/AST/PlatformKindUtils.h | 4 ++ include/swift/Option/Options.td | 6 +++ .../swift/SymbolGraphGen/SymbolGraphOptions.h | 4 ++ lib/AST/PlatformKindUtils.cpp | 2 +- .../swift_symbolgraph_extract_main.cpp | 7 +++ lib/SymbolGraphGen/Symbol.cpp | 52 ++++++++++++++----- .../Availability/AnyAppleOS/OnlyActive.swift | 20 +++++++ .../AnyAppleOS/PlatformSupersedes.swift | 1 + .../Mixins/Availability/OnlyActive.swift | 16 ++++++ 9 files changed, 99 insertions(+), 13 deletions(-) create mode 100644 test/SymbolGraph/Symbols/Mixins/Availability/AnyAppleOS/OnlyActive.swift create mode 100644 test/SymbolGraph/Symbols/Mixins/Availability/OnlyActive.swift diff --git a/include/swift/AST/PlatformKindUtils.h b/include/swift/AST/PlatformKindUtils.h index e0f3f240a0e9e..763e342c3988a 100644 --- a/include/swift/AST/PlatformKindUtils.h +++ b/include/swift/AST/PlatformKindUtils.h @@ -80,6 +80,10 @@ PlatformKind targetPlatform(const LangOptions &LangOpts); /// Returns the target variant platform for the given language options. PlatformKind targetVariantPlatform(const LangOptions &LangOpts); +/// Returns the target platform for the given triple and options. +PlatformKind platformForTriple(const llvm::Triple &triple, + bool enableAppExtensionRestrictions); + /// Returns true when availability attributes from the "parent" platform /// should also apply to the "child" platform for declarations without /// an explicit attribute for the child. diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index b9f1217230dc2..e330d2ff87fab 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -1939,6 +1939,12 @@ def block_availability_platforms: Separate<["-"], "block-availability-platforms" HelpText<"Remove the given platforms from symbol graph availability metadata, e.g. 'macOS,Swift'">, MetaVarName<"">; +def active_platform_availability_only + : Flag<["-"], "active-platform-availability-only">, + Flags<[SwiftSymbolGraphExtractOption]>, + HelpText<"Only emit availability metadata for platforms that are active " + "according to the target">; + // 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 3b43314dc9dba..82d859cb7f2b2 100644 --- a/include/swift/SymbolGraphGen/SymbolGraphOptions.h +++ b/include/swift/SymbolGraphGen/SymbolGraphOptions.h @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "swift/AST/PlatformKind.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseSet.h" #include "llvm/TargetParser/Triple.h" @@ -78,6 +79,9 @@ struct SymbolGraphOptions { /// Whether `AvailabilityPlatforms` is an allow list or a block list. bool AvailabilityIsBlockList = false; + /// If non-null, only emit availability metadata for the active platform. + std::optional ActivePlatform = std::nullopt; + /// Whether to use shortened, by using a hash of the module names, file names /// when writing symbol graph files to `OutputDir`. /// An additional JSON file is written at `OutputDir` that contains a mapping diff --git a/lib/AST/PlatformKindUtils.cpp b/lib/AST/PlatformKindUtils.cpp index 1b56a5968f1e3..f4a3454ed3b9e 100644 --- a/lib/AST/PlatformKindUtils.cpp +++ b/lib/AST/PlatformKindUtils.cpp @@ -194,7 +194,7 @@ bool swift::isPlatformActive(PlatformKind Platform, const LangOptions &LangOpts, ForRuntimeQuery); } -static PlatformKind platformForTriple(const llvm::Triple &triple, +PlatformKind swift::platformForTriple(const llvm::Triple &triple, bool enableAppExtensionRestrictions) { if (triple.isMacOSX()) { return (enableAppExtensionRestrictions diff --git a/lib/DriverTool/swift_symbolgraph_extract_main.cpp b/lib/DriverTool/swift_symbolgraph_extract_main.cpp index 764458c18fcac..245987b59e1a2 100644 --- a/lib/DriverTool/swift_symbolgraph_extract_main.cpp +++ b/lib/DriverTool/swift_symbolgraph_extract_main.cpp @@ -190,6 +190,13 @@ int swift_symbolgraph_extract_main(ArrayRef Args, ParsedArgs.hasFlag(OPT_emit_extension_block_symbols, OPT_omit_extension_block_symbols, /*default=*/false); Options.AllowedReexportedModules = AllowedRexports; + if (ParsedArgs.hasArg(OPT_active_platform_availability_only)) { + // The output should only include platform availability that applies to the + // platform inferred from the -target. Include app extension availability, + // too. + Options.ActivePlatform = + platformForTriple(Target, /*enableAppExtensionRestrictions=*/true); + } if (auto *A = ParsedArgs.getLastArg(OPT_minimum_access_level)) { Options.MinimumAccessLevel = diff --git a/lib/SymbolGraphGen/Symbol.cpp b/lib/SymbolGraphGen/Symbol.cpp index e34ad4a669661..6afb05f550af4 100644 --- a/lib/SymbolGraphGen/Symbol.cpp +++ b/lib/SymbolGraphGen/Symbol.cpp @@ -686,15 +686,34 @@ getPlatformsToExpand(SemanticAvailableAttr AvAttr) { return {}; } +static bool +isAvailabilityDomainActive(AvailabilityDomain Domain, + std::optional ActivePlatform) { + if (!ActivePlatform) + return true; + + if (!Domain.isPlatform()) + return true; + + auto Platform = Domain.getPlatformKind(); + return Platform == *ActivePlatform || + inheritsAvailabilityFromPlatform(*ActivePlatform, Platform); +} + /// Expands a single availability attribute into one or more Availability /// structs. -void expandInferredAvailabilityAttr(SemanticAvailableAttr AvAttr, - SmallVectorImpl &Expanded) { +void expandInferredAvailabilityAttr( + SemanticAvailableAttr AvAttr, SmallVectorImpl &Expanded, + std::optional ActivePlatform) { if (auto Platforms = getPlatformsToExpand(AvAttr)) { for (auto Platform : *Platforms) { + auto PlatformDomain = AvailabilityDomain::forPlatform(Platform); + if (!isAvailabilityDomainActive(PlatformDomain, ActivePlatform)) + continue; + Availability InferredAvailability(AvAttr); - InferredAvailability.Domain = Availability::getDomainDescription( - AvailabilityDomain::forPlatform(Platform)); + InferredAvailability.Domain = + Availability::getDomainDescription(PlatformDomain); // FIXME: [availability] Versions should be remapped, too. // Version remapping is unnecessary at the moment because only anyAppleOS // availability gets expanded and anyAppleOS doesn't require version @@ -718,28 +737,35 @@ void expandInferredAvailabilityAttr(SemanticAvailableAttr AvAttr, /// duplicate \c \@available attributes on the same declaration. void getAvailabilities(const Decl *D, llvm::StringMap &Availabilities, - bool IsParent) { + bool IsParent, + std::optional ActivePlatform) { // DeclAttributes is a linked list in reverse order from where they // appeared in the source. Let's re-reverse them. SmallVector AvAttrs; for (auto Attr : D->getSemanticAvailableAttrs(/*includingInactive=*/true)) { + if (!isAvailabilityDomainActive(Attr.getDomain(), ActivePlatform)) + continue; + AvAttrs.push_back(Attr); } // Now go through them in source order. for (auto AvAttr : llvm::reverse(AvAttrs)) { SmallVector ExpandedAvailabilities; - expandInferredAvailabilityAttr(AvAttr, ExpandedAvailabilities); + expandInferredAvailabilityAttr(AvAttr, ExpandedAvailabilities, + ActivePlatform); for (auto &Avail : ExpandedAvailabilities) insertAvailability(Avail, Availabilities, IsParent); } } -/// Get the availabilities of a declaration, considering all of its -/// parent context's except for the module. +/// Get the availabilities of a declaration, considering all of its parent +/// context's except for the module. If \p ActivePlatform is non-null, only +/// include availability relevant to the active platform. void getInheritedAvailabilities(const Decl *D, -llvm::StringMap &Availabilities) { - getAvailabilities(D, Availabilities, /*IsParent*/false); + llvm::StringMap &Availabilities, + std::optional ActivePlatform) { + getAvailabilities(D, Availabilities, /*IsParent*/ false, ActivePlatform); auto CurrentContext = D->getDeclContext(); while (CurrentContext) { @@ -747,7 +773,8 @@ llvm::StringMap &Availabilities) { if (isa(Parent)) { return; } - getAvailabilities(Parent, Availabilities, /*IsParent*/true); + getAvailabilities(Parent, Availabilities, /*IsParent*/ true, + ActivePlatform); } CurrentContext = CurrentContext->getParent(); } @@ -757,7 +784,8 @@ llvm::StringMap &Availabilities) { void Symbol::serializeAvailabilityMixin(llvm::json::OStream &OS) const { llvm::StringMap Availabilities; - getInheritedAvailabilities(D, Availabilities); + getInheritedAvailabilities(D, Availabilities, + Graph->Walker.Options.ActivePlatform); // If we were asked to filter the availability platforms for the output graph, // perform that filtering here. diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/AnyAppleOS/OnlyActive.swift b/test/SymbolGraph/Symbols/Mixins/Availability/AnyAppleOS/OnlyActive.swift new file mode 100644 index 0000000000000..f7471bba422dd --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/AnyAppleOS/OnlyActive.swift @@ -0,0 +1,20 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name OnlyActive -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name OnlyActive -I %t -pretty-print -active-platform-availability-only -output-dir %t +// RUN: %FileCheck %s --input-file %t/OnlyActive.symbols.json + +// REQUIRES: OS=macosx + +// CHECK-LABEL: "symbols": [ +// CHECK-LABEL: "precise": "s:10OnlyActive1SV", +// CHECK: "availability": [ +// CHECK-NEXT: { +// CHECK-NEXT: "domain": "macOS", +// CHECK-NEXT: "introduced": { +// CHECK-NEXT: "major": 26 +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: ] + +@available(anyAppleOS 26, *) +public struct S {} diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/AnyAppleOS/PlatformSupersedes.swift b/test/SymbolGraph/Symbols/Mixins/Availability/AnyAppleOS/PlatformSupersedes.swift index 9e548765ccd90..c6be6ced29d10 100644 --- a/test/SymbolGraph/Symbols/Mixins/Availability/AnyAppleOS/PlatformSupersedes.swift +++ b/test/SymbolGraph/Symbols/Mixins/Availability/AnyAppleOS/PlatformSupersedes.swift @@ -3,6 +3,7 @@ // RUN: %target-swift-symbolgraph-extract -module-name PlatformSupersedes -I %t -pretty-print -output-dir %t // RUN: %FileCheck %s --input-file %t/PlatformSupersedes.symbols.json +// CHECK-LABEL: "symbols": [ // CHECK-LABEL: "precise": "s:18PlatformSupersedes1SV", // CHECK: "availability": [ // CHECK-NEXT: { diff --git a/test/SymbolGraph/Symbols/Mixins/Availability/OnlyActive.swift b/test/SymbolGraph/Symbols/Mixins/Availability/OnlyActive.swift new file mode 100644 index 0000000000000..f078bb0ac0687 --- /dev/null +++ b/test/SymbolGraph/Symbols/Mixins/Availability/OnlyActive.swift @@ -0,0 +1,16 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -module-name OnlyActive -emit-module -emit-module-path %t/ +// RUN: %target-swift-symbolgraph-extract -module-name OnlyActive -I %t -pretty-print -active-platform-availability-only -output-dir %t +// RUN: %FileCheck %s --input-file %t/OnlyActive.symbols.json + +// REQUIRES: OS=macosx + +@available(macOS 10.9, iOS 8.0, watchOS 8.0, tvOS 8.0, visionOS 1.0, *) +public struct S {} + +// CHECK-NOT: iOS +// CHECK: "domain": "macOS" +// CHECK-NOT: tvOS +// CHECK-NOT: visionOS +// CHECK-NOT: watchOS +