diff --git a/Sources/SwiftDocC/Infrastructure/Symbol Graph/SymbolGraphLoader.swift b/Sources/SwiftDocC/Infrastructure/Symbol Graph/SymbolGraphLoader.swift
index 6dc01370ab..a1f75e4ab6 100644
--- a/Sources/SwiftDocC/Infrastructure/Symbol Graph/SymbolGraphLoader.swift
+++ b/Sources/SwiftDocC/Infrastructure/Symbol Graph/SymbolGraphLoader.swift
@@ -47,6 +47,27 @@ struct SymbolGraphLoader {
/// The symbol graph decoding strategy to use.
private(set) var decodingStrategy: DecodingConcurrencyStrategy = .concurrentlyEachFileInBatches
+
+ /// The symbol default availability.
+ func symbolDefaultAvailability(_ moduleAvailability: [DefaultAvailability.ModuleAvailability]?) -> [DefaultAvailability.ModuleAvailability]? {
+ // Apply default availability options mutations.
+ guard let moduleAvailability else { return nil }
+ if let defaultAvailabilityOptions = bundle.info.defaultAvailabilityOptions {
+ // Remove the availability version if `inheritVersionNumber` is not part
+ // of the default availability options.
+ if !defaultAvailabilityOptions.shouldApplyOption(.inheritVersionNumber) {
+ return moduleAvailability.map { defaultAvailability in
+ var defaultAvailability = defaultAvailability
+ switch defaultAvailability.versionInformation {
+ case .available(_): defaultAvailability.versionInformation = .available(version: nil)
+ case .unavailable: ()
+ }
+ return defaultAvailability
+ }
+ }
+ }
+ return moduleAvailability
+ }
/// Loads all symbol graphs in the given bundle.
///
@@ -153,7 +174,7 @@ struct SymbolGraphLoader {
var defaultUnavailablePlatforms = [PlatformName]()
var defaultAvailableInformation = [DefaultAvailability.ModuleAvailability]()
- if let defaultAvailabilities = bundle.info.defaultAvailability?.modules[unifiedGraph.moduleName] {
+ if let defaultAvailabilities = symbolDefaultAvailability(bundle.info.defaultAvailability?.modules[unifiedGraph.moduleName]) {
let (unavailablePlatforms, availablePlatforms) = defaultAvailabilities.categorize(where: { $0.versionInformation == .unavailable })
defaultUnavailablePlatforms = unavailablePlatforms.map(\.platformName)
defaultAvailableInformation = availablePlatforms
@@ -282,7 +303,7 @@ struct SymbolGraphLoader {
private func addDefaultAvailability(to symbolGraph: inout SymbolGraph, moduleName: String) {
let selector = UnifiedSymbolGraph.Selector(forSymbolGraph: symbolGraph)
// Check if there are defined default availabilities for the current module
- if let defaultAvailabilities = bundle.info.defaultAvailability?.modules[moduleName],
+ if let defaultAvailabilities = symbolDefaultAvailability(bundle.info.defaultAvailability?.modules[moduleName]),
let platformName = symbolGraph.module.platform.name.map(PlatformName.init) {
// Prepare a default availability versions lookup for this module.
@@ -401,10 +422,12 @@ extension SymbolGraph.SemanticVersion {
extension SymbolGraph.Symbol.Availability.AvailabilityItem {
/// Create an availability item with a `domain` and an `introduced` version.
/// - parameter defaultAvailability: Default availability information for symbols that lack availability authored in code.
- /// - Note: If the `defaultAvailability` argument doesn't have a valid
- /// platform version that can be parsed as a `SemanticVersion`, returns `nil`.
+ /// - Note: If the `defaultAvailability` argument has a introduced version that can't
+ /// be parsed as a `SemanticVersion`, returns `nil`.
init?(_ defaultAvailability: DefaultAvailability.ModuleAvailability) {
- guard let introducedVersion = defaultAvailability.introducedVersion, let platformVersion = SymbolGraph.SemanticVersion(string: introducedVersion) else {
+ let introducedVersion = defaultAvailability.introducedVersion
+ let platformVersion = introducedVersion.map { SymbolGraph.SemanticVersion(string: $0) } ?? nil
+ if platformVersion == nil && introducedVersion != nil {
return nil
}
let domain = SymbolGraph.Symbol.Availability.Domain(rawValue: defaultAvailability.platformName.rawValue)
diff --git a/Sources/SwiftDocC/Infrastructure/Workspace/DefaultAvailability.swift b/Sources/SwiftDocC/Infrastructure/Workspace/DefaultAvailability.swift
index af56bd238b..9e944b56d4 100644
--- a/Sources/SwiftDocC/Infrastructure/Workspace/DefaultAvailability.swift
+++ b/Sources/SwiftDocC/Infrastructure/Workspace/DefaultAvailability.swift
@@ -51,7 +51,7 @@ public struct DefaultAvailability: Codable, Equatable {
/// Unavailable or Available with an introduced version.
enum VersionInformation: Hashable {
case unavailable
- case available(version: String)
+ case available(version: String?)
}
/// The name of the platform, e.g. "macOS".
@@ -71,7 +71,7 @@ public struct DefaultAvailability: Codable, Equatable {
public var introducedVersion: String? {
switch versionInformation {
case .available(let introduced):
- return introduced.description
+ return introduced?.description
case .unavailable:
return nil
}
diff --git a/Sources/SwiftDocC/Infrastructure/Workspace/DefaultAvailailabilityOptions.swift b/Sources/SwiftDocC/Infrastructure/Workspace/DefaultAvailailabilityOptions.swift
new file mode 100644
index 0000000000..781d829430
--- /dev/null
+++ b/Sources/SwiftDocC/Infrastructure/Workspace/DefaultAvailailabilityOptions.swift
@@ -0,0 +1,75 @@
+/*
+ This source file is part of the Swift.org open source project
+
+ Copyright (c) 2024 Apple Inc. and the Swift project authors
+ Licensed under Apache License v2.0 with Runtime Library Exception
+
+ See https://swift.org/LICENSE.txt for license information
+ See https://swift.org/CONTRIBUTORS.txt for Swift project authors
+*/
+
+import Foundation
+
+/// A collection of options that customaise the default availability behaviour.
+///
+/// Default availability options are applied to all the modules contained in the documentation bundle.
+///
+/// This information can be authored in the bundle's Info.plist file, as a dictionary of option name and boolean pairs.
+///
+/// ```
+/// CDDefaultAvailabilityOptions
+///
+/// OptionName
+///
+///
+/// ```
+public struct DefaultAvailabilityOptions: Codable, Equatable {
+
+ /// A set of non-standard behaviors that apply to this node.
+ fileprivate(set) var options: Options
+
+ /// Options that specify behaviors of the default availability logic.
+ struct Options: OptionSet {
+
+ let rawValue: Int
+
+ /// Enable or disable symbol availability version inference from the module default availability.
+ static let inheritVersionNumber = Options(rawValue: 1 << 0)
+ }
+
+ /// String representation of the default availability options.
+ private enum CodingKeys: String, CodingKey {
+ case inheritVersionNumber = "InheritVersionNumber"
+ }
+
+ public init() {
+ self.options = .inheritVersionNumber
+ }
+
+ public init(from decoder: any Decoder) throws {
+ self.init()
+ let values = try decoder.container(keyedBy: CodingKeys.self)
+ if try values.decodeIfPresent(Bool.self, forKey: .inheritVersionNumber) == false {
+ options.remove(.inheritVersionNumber)
+ }
+ }
+
+ public func encode(to encoder: any Encoder) throws {
+ var container = encoder.container(keyedBy: CodingKeys.self)
+ if !options.contains(.inheritVersionNumber) {
+ try container.encode(false, forKey: .inheritVersionNumber)
+ }
+ }
+
+ /// Convenient method to determine if an option has to be applied depending
+ /// on it's exsistence inside the options set.
+ func shouldApplyOption(_ option: Options) -> Bool {
+ switch option {
+ case .inheritVersionNumber:
+ return options.contains(.inheritVersionNumber)
+ default:
+ return false
+ }
+ }
+}
+
diff --git a/Sources/SwiftDocC/Infrastructure/Workspace/DocumentationBundle+Info.swift b/Sources/SwiftDocC/Infrastructure/Workspace/DocumentationBundle+Info.swift
index d68a209a07..7fd105f601 100644
--- a/Sources/SwiftDocC/Infrastructure/Workspace/DocumentationBundle+Info.swift
+++ b/Sources/SwiftDocC/Infrastructure/Workspace/DocumentationBundle+Info.swift
@@ -11,6 +11,16 @@
import Foundation
extension DocumentationBundle {
+
+ /// Options to define the inherit default availability behaviour.
+ public enum InheritDefaultAvailabilityOptions: String, Codable {
+ /// The platforms with the designated versions defined in the default availability will be used by the symbols as availability information.
+ /// This is the default behaviour.
+ case platformAndVersion
+ /// Only the platforms defined in the default availability will be passed to the symbols.
+ case platformOnly
+ }
+
/// Information about a documentation bundle that's unrelated to its documentation content.
///
/// This information is meant to be decoded from the bundle's Info.plist file.
@@ -39,6 +49,10 @@ extension DocumentationBundle {
/// The keys that must be present in an Info.plist file in order for doc compilation to proceed.
static let requiredKeys: Set = [.displayName, .identifier]
+ /// The default availability behaviour options.
+ public var defaultAvailabilityOptions: DefaultAvailabilityOptions?
+
+
enum CodingKeys: String, CodingKey, CaseIterable {
case displayName = "CFBundleDisplayName"
case identifier = "CFBundleIdentifier"
@@ -47,6 +61,7 @@ extension DocumentationBundle {
case defaultAvailability = "CDAppleDefaultAvailability"
case defaultModuleKind = "CDDefaultModuleKind"
case featureFlags = "CDExperimentalFeatureFlags"
+ case defaultAvailabilityOptions = "CDDefaultAvailabilityOptions"
var argumentName: String? {
switch self {
@@ -60,7 +75,7 @@ extension DocumentationBundle {
return "--default-code-listing-language"
case .defaultModuleKind:
return "--fallback-default-module-kind"
- case .defaultAvailability, .featureFlags:
+ case .defaultAvailability, .defaultAvailabilityOptions, .featureFlags:
return nil
}
}
@@ -91,13 +106,15 @@ extension DocumentationBundle {
/// - defaultCodeListingLanguage: The default language identifier for code listings in the bundle.
/// - defaultAvailability: The default availability for the various modules in the bundle.
/// - defaultModuleKind: The default kind for the various modules in the bundle.
+ /// - defaultAvailabilityOptions: The options to enable or disable symbol availability logic from the module default availability.
public init(
displayName: String,
identifier: String,
version: String?,
defaultCodeListingLanguage: String?,
defaultAvailability: DefaultAvailability?,
- defaultModuleKind: String?
+ defaultModuleKind: String?,
+ defaultAvailabilityOptions: DefaultAvailabilityOptions?
) {
self.displayName = displayName
self.identifier = identifier
@@ -105,6 +122,7 @@ extension DocumentationBundle {
self.defaultCodeListingLanguage = defaultCodeListingLanguage
self.defaultAvailability = defaultAvailability
self.defaultModuleKind = defaultModuleKind
+ self.defaultAvailabilityOptions = defaultAvailabilityOptions
}
/// Creates documentation bundle information from the given Info.plist data, falling back to the values
@@ -234,6 +252,8 @@ extension DocumentationBundle {
self.defaultCodeListingLanguage = try decodeOrFallbackIfPresent(String.self, with: .defaultCodeListingLanguage)
self.defaultModuleKind = try decodeOrFallbackIfPresent(String.self, with: .defaultModuleKind)
+ self.defaultAvailabilityOptions = try decodeOrFallbackIfPresent(DefaultAvailabilityOptions.self, with: .defaultAvailabilityOptions)
+ self.defaultAvailability = try decodeOrFallbackIfPresent(DefaultAvailability.self, with: .defaultAvailability)
self.defaultAvailability = try decodeOrFallbackIfPresent(DefaultAvailability.self, with: .defaultAvailability)
self.featureFlags = try decodeOrFallbackIfPresent(BundleFeatureFlags.self, with: .featureFlags)
}
@@ -245,7 +265,9 @@ extension DocumentationBundle {
defaultCodeListingLanguage: String? = nil,
defaultModuleKind: String? = nil,
defaultAvailability: DefaultAvailability? = nil,
- featureFlags: BundleFeatureFlags? = nil
+ featureFlags: BundleFeatureFlags? = nil,
+ inheritDefaultAvailability: InheritDefaultAvailabilityOptions? = nil,
+ defaultAvailabilityOptions: DefaultAvailabilityOptions? = nil
) {
self.displayName = displayName
self.identifier = identifier
@@ -254,6 +276,22 @@ extension DocumentationBundle {
self.defaultModuleKind = defaultModuleKind
self.defaultAvailability = defaultAvailability
self.featureFlags = featureFlags
+ self.defaultAvailabilityOptions = defaultAvailabilityOptions
+ }
+
+ public func encode(to encoder: any Encoder) throws {
+ var container: KeyedEncodingContainer = encoder.container(keyedBy: DocumentationBundle.Info.CodingKeys.self)
+
+ try container.encode(self.displayName, forKey: DocumentationBundle.Info.CodingKeys.displayName)
+ try container.encode(self.identifier, forKey: DocumentationBundle.Info.CodingKeys.identifier)
+ try container.encodeIfPresent(self.version, forKey: DocumentationBundle.Info.CodingKeys.version)
+ try container.encodeIfPresent(self.defaultCodeListingLanguage, forKey: DocumentationBundle.Info.CodingKeys.defaultCodeListingLanguage)
+ try container.encodeIfPresent(self.defaultAvailability, forKey: DocumentationBundle.Info.CodingKeys.defaultAvailability)
+ try container.encodeIfPresent(self.defaultModuleKind, forKey: DocumentationBundle.Info.CodingKeys.defaultModuleKind)
+ try container.encodeIfPresent(self.featureFlags, forKey: DocumentationBundle.Info.CodingKeys.featureFlags)
+ if defaultAvailabilityOptions != .init() {
+ try container.encodeIfPresent(self.defaultAvailabilityOptions, forKey: DocumentationBundle.Info.CodingKeys.defaultAvailabilityOptions)
+ }
}
}
}
@@ -272,6 +310,7 @@ extension BundleDiscoveryOptions {
/// - fallbackDefaultModuleKind: A fallback default module kind for the bundle.
/// - fallbackDefaultAvailability: A fallback default availability for the bundle.
/// - additionalSymbolGraphFiles: Additional symbol graph files to augment any discovered bundles.
+ /// - defaultAvailabilityOptions: Options to configure default availability behaviour.
public init(
fallbackDisplayName: String? = nil,
fallbackIdentifier: String? = nil,
@@ -279,7 +318,8 @@ extension BundleDiscoveryOptions {
fallbackDefaultCodeListingLanguage: String? = nil,
fallbackDefaultModuleKind: String? = nil,
fallbackDefaultAvailability: DefaultAvailability? = nil,
- additionalSymbolGraphFiles: [URL] = []
+ additionalSymbolGraphFiles: [URL] = [],
+ defaultAvailabilityOptions: DefaultAvailabilityOptions? = nil
) {
// Iterate over all possible coding keys with a switch
// to build up the dictionary of fallback options.
@@ -304,6 +344,8 @@ extension BundleDiscoveryOptions {
value = fallbackDefaultModuleKind
case .featureFlags:
value = nil
+ case .defaultAvailabilityOptions:
+ value = defaultAvailabilityOptions
}
guard let unwrappedValue = value else {
diff --git a/Sources/SwiftDocC/Model/Rendering/RenderNodeTranslator.swift b/Sources/SwiftDocC/Model/Rendering/RenderNodeTranslator.swift
index 9c19000123..3ddc28bb6c 100644
--- a/Sources/SwiftDocC/Model/Rendering/RenderNodeTranslator.swift
+++ b/Sources/SwiftDocC/Model/Rendering/RenderNodeTranslator.swift
@@ -1241,8 +1241,10 @@ public struct RenderNodeTranslator: SemanticVisitor {
node.metadata.platformsVariants = VariantCollection<[AvailabilityRenderItem]?>(from: symbol.availabilityVariants) { _, availability in
availability.availability
.compactMap { availability -> AvailabilityRenderItem? in
- // Filter items with insufficient availability data
- guard availability.introducedVersion != nil else {
+ // Filter items with insufficient availability data unless the default availability behaviour
+ // allows availability withound version information.
+ let omitDefaultAvailabilityVersionFromSymbols = bundle.info.defaultAvailabilityOptions?.shouldApplyOption(.inheritVersionNumber) == false
+ guard availability.introducedVersion != nil || omitDefaultAvailabilityVersionFromSymbols else {
return nil
}
guard let name = availability.domain.map({ PlatformName(operatingSystemName: $0.rawValue) }),
diff --git a/Tests/SwiftDocCTests/Rendering/DefaultAvailabilityTests.swift b/Tests/SwiftDocCTests/Rendering/DefaultAvailabilityTests.swift
index 6662c6a555..04248fa220 100644
--- a/Tests/SwiftDocCTests/Rendering/DefaultAvailabilityTests.swift
+++ b/Tests/SwiftDocCTests/Rendering/DefaultAvailabilityTests.swift
@@ -577,6 +577,234 @@ class DefaultAvailabilityTests: XCTestCase {
module.filter({ $0.platformName.displayName == "iPadOS" }).first?.versionInformation,
.available(version: "10.0")
)
+ }
+
+ func testInheritDefaultAvailabilityOptions() throws {
+ func makeInfoPlist(
+ inheritDefaultAvailability: String,
+ defaultAvailability: String? = nil
+ ) -> String {
+ let defaultAvailability = defaultAvailability ?? """
+
+ name
+ iOS
+ version
+ 8.0
+
+ """
+ return """
+
+
+ \(inheritDefaultAvailability)
+ CDAppleDefaultAvailability
+
+ MyModule
+
+ \(defaultAvailability)
+
+
+
+
+ """
+ }
+ func setupContext(
+ inheritDefaultAvailability: String = "",
+ defaultAvailability: String? = nil
+ ) throws -> (DocumentationBundle, DocumentationContext) {
+ // Create an empty bundle
+ let targetURL = try createTemporaryDirectory(named: "test.docc")
+ // Create symbol graph
+ let symbolGraphURL = targetURL.appendingPathComponent("MyModule.symbols.json")
+ try symbolGraphString.write(to: symbolGraphURL, atomically: true, encoding: .utf8)
+ // Create info plist
+ let infoPlistURL = targetURL.appendingPathComponent("Info.plist")
+ let infoPlist = makeInfoPlist(inheritDefaultAvailability: inheritDefaultAvailability, defaultAvailability: defaultAvailability)
+ try infoPlist.write(to: infoPlistURL, atomically: true, encoding: .utf8)
+ // Load the bundle & reference resolve symbol graph docs
+ let (_, bundle, context) = try loadBundle(from: targetURL)
+ return (bundle, context)
+ }
+
+ let symbols = """
+ {
+ "kind": {
+ "displayName" : "Instance Property",
+ "identifier" : "swift.property"
+ },
+ "identifier": {
+ "precise": "c:@F@SymbolWithAvailability",
+ "interfaceLanguage": "swift"
+ },
+ "pathComponents": [
+ "Foo"
+ ],
+ "names": {
+ "title": "Foo",
+ },
+ "accessLevel": "public",
+ "availability" : [
+ {
+ "domain" : "ios",
+ "introduced" : {
+ "major" : 10,
+ "minor" : 0
+ }
+ }
+ ]
+ },
+ {
+ "kind": {
+ "displayName" : "Instance Property",
+ "identifier" : "swift.property"
+ },
+ "identifier": {
+ "precise": "c:@F@SymbolWithoutAvailability",
+ "interfaceLanguage": "swift"
+ },
+ "pathComponents": [
+ "Foo"
+ ],
+ "names": {
+ "title": "Bar",
+ },
+ "accessLevel": "public"
+ }
+ """
+ let symbolGraphString = makeSymbolGraphString(
+ moduleName: "MyModule",
+ symbols: symbols,
+ platform: """
+ "operatingSystem" : {
+ "minimumVersion" : {
+ "major" : 10,
+ "minor" : 0
+ },
+ "name" : "ios"
+ }
+ """
+ )
+
+ // Don't use default availability version for symbols.
+
+ var (bundle, context) = try setupContext(
+ inheritDefaultAvailability: """
+ CDDefaultAvailabilityOptions
+
+ InheritVersionNumber
+
+
+ """,
+ defaultAvailability: """
+
+ name
+ iOS
+ version
+ 8.0
+
+ """
+ )
+ // Verify we add the version number into the symbols that have availability annotation.
+ guard let availability = (context.documentationCache["c:@F@SymbolWithAvailability"]?.semantic as? Symbol)?.availability?.availability else {
+ XCTFail("Did not find availability for symbol 'c:@F@SymbolWithAvailability'")
+ return
+ }
+ XCTAssertNotNil(availability.first(where: { $0.domain?.rawValue == "iOS" }))
+ XCTAssertEqual(availability.first(where: { $0.domain?.rawValue == "iOS" })?.introducedVersion, SymbolGraph.SemanticVersion(major: 10, minor: 0, patch: 0))
+ // Verify we don't add the version number into the symbols that don't have availability annotation.
+ guard let availability = (context.documentationCache["c:@F@SymbolWithoutAvailability"]?.semantic as? Symbol)?.availability?.availability else {
+ XCTFail("Did not find availability for symbol 'c:@F@SymbolWithoutAvailability'")
+ return
+ }
+ XCTAssertNotNil(availability.first(where: { $0.domain?.rawValue == "iOS" }))
+ XCTAssertEqual(availability.first(where: { $0.domain?.rawValue == "iOS" })?.introducedVersion, nil)
+ // Verify we keep the module availability information.
+ let identifier = ResolvedTopicReference(bundleIdentifier: "test", path: "/documentation/MyModule", fragment: nil, sourceLanguage: .swift)
+ let node = try context.entity(with: identifier)
+ var translator = RenderNodeTranslator(context: context, bundle: bundle, identifier: identifier)
+ let renderNode = translator.visit(node.semantic) as! RenderNode
+ XCTAssertEqual(renderNode.metadata.platforms?.count, 3)
+ XCTAssertEqual(renderNode.metadata.platforms?.first?.name, "iOS")
+ XCTAssertEqual(renderNode.metadata.platforms?.first?.introduced, "8.0")
+
+ // Add an extra default availability to test behaviour when mixin in source with default behaviour.
+ (_, context) = try setupContext(
+ inheritDefaultAvailability: """
+ CDDefaultAvailabilityOptions
+
+ InheritVersionNumber
+
+
+ """,
+ defaultAvailability: """
+
+ name
+ iOS
+ version
+ 8.0
+
+
+ name
+ watchOS
+ version
+ 5.0
+
+ """
+ )
+
+ // Verify we add the version number into the symbols that have availability annotation.
+ guard let availability = (context.documentationCache["c:@F@SymbolWithAvailability"]?.semantic as? Symbol)?.availability?.availability else {
+ XCTFail("Did not find availability for symbol 'c:@F@SymbolWithAvailability'")
+ return
+ }
+ XCTAssertNotNil(availability.first(where: { $0.domain?.rawValue == "iOS" }))
+ XCTAssertNotNil(availability.first(where: { $0.domain?.rawValue == "watchOS" }))
+ XCTAssertEqual(availability.first(where: { $0.domain?.rawValue == "iOS" })?.introducedVersion, SymbolGraph.SemanticVersion(major: 10, minor: 0, patch: 0))
+ XCTAssertEqual(availability.first(where: { $0.domain?.rawValue == "watchOS" })?.introducedVersion, nil)
+
+ // Use default availability version for symbols.
+
+ (_, context) = try setupContext(inheritDefaultAvailability: """
+ CDDefaultAvailabilityOptions
+
+ InheritVersionNumber
+
+
+ """)
+
+ // Verify we add the version number into the symbols that have availability annotation.
+ guard let availability = (context.documentationCache["c:@F@SymbolWithAvailability"]?.semantic as? Symbol)?.availability?.availability else {
+ XCTFail("Did not find availability for symbol 'c:@F@SymbolWithAvailability'")
+ return
+ }
+ XCTAssertNotNil(availability.first(where: { $0.domain?.rawValue == "iOS" }))
+ XCTAssertEqual(availability.first(where: { $0.domain?.rawValue == "iOS" })?.introducedVersion, SymbolGraph.SemanticVersion(major: 10, minor: 0, patch: 0))
+ // Verify we don't add the version number into the symbols that don't have availability annotation.
+ guard let availability = (context.documentationCache["c:@F@SymbolWithoutAvailability"]?.semantic as? Symbol)?.availability?.availability else {
+ XCTFail("Did not find availability for symbol 'c:@F@SymbolWithoutAvailability'")
+ return
+ }
+ XCTAssertNotNil(availability.first(where: { $0.domain?.rawValue == "iOS" }))
+ XCTAssertEqual(availability.first(where: { $0.domain?.rawValue == "iOS" })?.introducedVersion, SymbolGraph.SemanticVersion(major: 8, minor: 0, patch: 0))
+
+ // Don't specify availability inherit behaviour.
+
+ (_, context) = try setupContext()
+
+ // Verify we add the version number into the symbols that have availability annotation.
+ guard let availability = (context.documentationCache["c:@F@SymbolWithAvailability"]?.semantic as? Symbol)?.availability?.availability else {
+ XCTFail("Did not find availability for symbol 'c:@F@SymbolWithAvailability'")
+ return
+ }
+ XCTAssertNotNil(availability.first(where: { $0.domain?.rawValue == "iOS" }))
+ XCTAssertEqual(availability.first(where: { $0.domain?.rawValue == "iOS" })?.introducedVersion, SymbolGraph.SemanticVersion(major: 10, minor: 0, patch: 0))
+ // Verify we don't add the version number into the symbols that don't have availability annotation.
+ guard let availability = (context.documentationCache["c:@F@SymbolWithoutAvailability"]?.semantic as? Symbol)?.availability?.availability else {
+ XCTFail("Did not find availability for symbol 'c:@F@SymbolWithoutAvailability'")
+ return
+ }
+ XCTAssertNotNil(availability.first(where: { $0.domain?.rawValue == "iOS" }))
+ XCTAssertEqual(availability.first(where: { $0.domain?.rawValue == "iOS" })?.introducedVersion, SymbolGraph.SemanticVersion(major: 8, minor: 0, patch: 0))
}
+
}