From ffeceb2b3002eb1cfb81e0a51985c95d27bd72d5 Mon Sep 17 00:00:00 2001 From: Franklin Schrans Date: Thu, 31 Mar 2022 17:52:49 +0200 Subject: [PATCH] Accept more Objective-C symbol graph identifiers (#110) Accept "objective-c" and "c" as interface language identifiers in symbol graph files emitted by clang. rdar://90361614 --- Sources/SwiftDocC/Model/SourceLanguage.swift | 31 ++++++++++---- ...tationDataVariants+SymbolGraphSymbol.swift | 6 +-- .../Symbol/DocumentationDataVariants.swift | 13 +++++- .../SwiftDocC/Semantics/Symbol/Symbol.swift | 2 +- .../SemaToRenderNodeMultiLanguageTests.swift | 41 ++++++++++++++++--- .../Model/SourceLanguageTests.swift | 19 +++++++++ .../DocumentationDataVariantsTests.swift | 13 ++++++ 7 files changed, 107 insertions(+), 18 deletions(-) create mode 100644 Tests/SwiftDocCTests/Model/SourceLanguageTests.swift diff --git a/Sources/SwiftDocC/Model/SourceLanguage.swift b/Sources/SwiftDocC/Model/SourceLanguage.swift index 3b54a7e3f4..5ae5aa1e6f 100644 --- a/Sources/SwiftDocC/Model/SourceLanguage.swift +++ b/Sources/SwiftDocC/Model/SourceLanguage.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift.org open source project - Copyright (c) 2021 Apple Inc. and the Swift project authors + Copyright (c) 2021-2022 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 @@ -14,14 +14,18 @@ public struct SourceLanguage: Hashable, Codable { public var name: String /// A globally unique identifier for the language. public var id: String + /// Aliases for the language's identifier. + public var idAliases: [String] = [] /// Creates a new language with a given name and identifier. /// - Parameters: /// - name: The display name of the programming language. /// - id: A globally unique identifier for the language. - public init(name: String, id: String) { + /// - idAliases: Aliases for the language's identifier. + public init(name: String, id: String, idAliases: [String] = []) { self.name = name self.id = id + self.idAliases = idAliases } /// Finds the programming language that matches a given query identifier. @@ -34,7 +38,7 @@ public struct SourceLanguage: Hashable, Codable { switch queryID { case "swift": self = .swift - case "occ": + case "occ", "objective-c", "c": self = .objectiveC case "javascript": self = .javaScript @@ -52,7 +56,7 @@ public struct SourceLanguage: Hashable, Codable { public init(id: String) { switch id { case "swift": self = .swift - case "occ": self = .objectiveC + case "occ", "objective-c", "c": self = .objectiveC case "javascript": self = .javaScript case "data": self = .data case "metal": self = .metal @@ -101,17 +105,30 @@ public struct SourceLanguage: Hashable, Codable { } private static func firstKnownLanguage(withName name: String) -> SourceLanguage? { - return SourceLanguage.knownLanguages.first { $0.name.lowercased() == name.lowercased() } + SourceLanguage.knownLanguages.first { $0.name.lowercased() == name.lowercased() } } private static func firstKnownLanguage(withIdentifier id: String) -> SourceLanguage? { - return SourceLanguage.knownLanguages.first { $0.id.lowercased() == id.lowercased() } + SourceLanguage.knownLanguages.first { knownLanguage in + ([knownLanguage.id] + knownLanguage.idAliases) + .map { $0.lowercased() } + .contains(id) + } } /// The Swift programming language. public static let swift = SourceLanguage(name: "Swift", id: "swift") + /// The Objective-C programming language. - public static let objectiveC = SourceLanguage(name: "Objective-C", id: "occ") + public static let objectiveC = SourceLanguage( + name: "Objective-C", + id: "occ", + idAliases: [ + "objective-c", + "c", // FIXME: DocC should display C as its own language (SR-16050). + ] + ) + /// The JavaScript programming language or another language that conforms to the ECMAScript specification. public static let javaScript = SourceLanguage(name: "JavaScript", id: "javascript") /// Miscellaneous data, that's not a programming language. diff --git a/Sources/SwiftDocC/Semantics/Symbol/DocumentationDataVariants+SymbolGraphSymbol.swift b/Sources/SwiftDocC/Semantics/Symbol/DocumentationDataVariants+SymbolGraphSymbol.swift index 33a5f874b2..c91df44ccd 100644 --- a/Sources/SwiftDocC/Semantics/Symbol/DocumentationDataVariants+SymbolGraphSymbol.swift +++ b/Sources/SwiftDocC/Semantics/Symbol/DocumentationDataVariants+SymbolGraphSymbol.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift.org open source project - Copyright (c) 2021 Apple Inc. and the Swift project authors + Copyright (c) 2021-2022 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 @@ -23,7 +23,7 @@ extension DocumentationDataVariants { guard selector.platform == platformName else { return nil } return ( - DocumentationDataVariantsTrait(interfaceLanguage: selector.interfaceLanguage), + DocumentationDataVariantsTrait(for: selector), transform(value) ) } @@ -44,7 +44,7 @@ extension DocumentationDataVariants { else { return nil } return ( - DocumentationDataVariantsTrait(interfaceLanguage: selector.interfaceLanguage), + DocumentationDataVariantsTrait(for: selector), value ) } diff --git a/Sources/SwiftDocC/Semantics/Symbol/DocumentationDataVariants.swift b/Sources/SwiftDocC/Semantics/Symbol/DocumentationDataVariants.swift index debaf39231..81656529ba 100644 --- a/Sources/SwiftDocC/Semantics/Symbol/DocumentationDataVariants.swift +++ b/Sources/SwiftDocC/Semantics/Symbol/DocumentationDataVariants.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift.org open source project - Copyright (c) 2021 Apple Inc. and the Swift project authors + Copyright (c) 2021-2022 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 @@ -9,6 +9,7 @@ */ import Foundation +import SymbolKit /// A model type that encapsulates variants of documentation node data. /// @@ -141,4 +142,14 @@ public struct DocumentationDataVariantsTrait: Hashable { public init(interfaceLanguage: String? = nil) { self.interfaceLanguage = interfaceLanguage } + + /// Creates a new trait given a symbol graph selector. + /// + /// - Parameter selector: The symbol graph selector to use when creating the trait. + public init(for selector: UnifiedSymbolGraph.Selector) { + self.init( + interfaceLanguage: SourceLanguage(knownLanguageIdentifier: selector.interfaceLanguage)?.id + ?? selector.interfaceLanguage + ) + } } diff --git a/Sources/SwiftDocC/Semantics/Symbol/Symbol.swift b/Sources/SwiftDocC/Semantics/Symbol/Symbol.swift index 35fa19f903..9ee2b78735 100644 --- a/Sources/SwiftDocC/Semantics/Symbol/Symbol.swift +++ b/Sources/SwiftDocC/Semantics/Symbol/Symbol.swift @@ -303,7 +303,7 @@ extension Symbol { /// depending on variances in their implementation across platforms (e.g. use `NSPoint` vs `CGPoint` parameter in a method). /// This method finds matching symbols between graphs and merges their declarations in case there are differences. func mergeDeclaration(mergingDeclaration: SymbolGraph.Symbol.DeclarationFragments, identifier: String, symbolAvailability: SymbolGraph.Symbol.Availability?, selector: UnifiedSymbolGraph.Selector) throws { - let trait = DocumentationDataVariantsTrait(interfaceLanguage: selector.interfaceLanguage) + let trait = DocumentationDataVariantsTrait(for: selector) let platformName = selector.platform if let platformName = platformName, diff --git a/Tests/SwiftDocCTests/Model/SemaToRenderNodeMultiLanguageTests.swift b/Tests/SwiftDocCTests/Model/SemaToRenderNodeMultiLanguageTests.swift index f0490140d6..0e15479dc1 100644 --- a/Tests/SwiftDocCTests/Model/SemaToRenderNodeMultiLanguageTests.swift +++ b/Tests/SwiftDocCTests/Model/SemaToRenderNodeMultiLanguageTests.swift @@ -1,7 +1,7 @@ /* This source file is part of the Swift.org open source project - Copyright (c) 2021 Apple Inc. and the Swift project authors + Copyright (c) 2021-2022 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 @@ -76,9 +76,26 @@ class SemaToRenderNodeMixedLanguageTests: XCTestCase { ) } } - - func testOutputsMultiLanguageRenderNodes() throws { - let outputConsumer = try mixedLanguageFrameworkConsumer() + + func assertOutputsMultiLanguageRenderNodes(variantInterfaceLanguage: String) throws { + let outputConsumer = try mixedLanguageFrameworkConsumer { bundleURL in + // Update the clang symbol graph with the Objective-C identifier given in variantInterfaceLanguage. + + let clangSymbolGraphLocation = bundleURL + .appendingPathComponent("symbol-graphs") + .appendingPathComponent("clang") + .appendingPathComponent("MixedLanguageFramework.symbols.json") + + var clangSymbolGraph = try JSONDecoder().decode(SymbolGraph.self, from: Data(contentsOf: clangSymbolGraphLocation)) + + clangSymbolGraph.symbols = clangSymbolGraph.symbols.mapValues { symbol in + var symbol = symbol + symbol.identifier.interfaceLanguage = variantInterfaceLanguage + return symbol + } + + try JSONEncoder().encode(clangSymbolGraph).write(to: clangSymbolGraphLocation) + } XCTAssertEqual( Set( @@ -136,7 +153,7 @@ class SemaToRenderNodeMixedLanguageTests: XCTestCase { "c:@M@TestFramework@objc(pl)MixedLanguageProtocol(im)mixedLanguageMethod", "c:@M@TestFramework@objc(cs)MixedLanguageClassConformingToProtocol(im)init", "c:@CM@TestFramework@objc(cs)MixedLanguageClassConformingToProtocol(im)mixedLanguageMethod", - + "MixedLanguageProtocol Implementations", "Article", "APICollection", @@ -146,7 +163,19 @@ class SemaToRenderNodeMixedLanguageTests: XCTestCase { ] ) } - + + func testOutputsMultiLanguageRenderNodesWithOccIdentifier() throws { + try assertOutputsMultiLanguageRenderNodes(variantInterfaceLanguage: "occ") + } + + func testOutputsMultiLanguageRenderNodesWithObjectiveCIdentifier() throws { + try assertOutputsMultiLanguageRenderNodes(variantInterfaceLanguage: "objective-c") + } + + func testOutputsMultiLanguageRenderNodesWithCIdentifier() throws { + try assertOutputsMultiLanguageRenderNodes(variantInterfaceLanguage: "c") + } + func testFrameworkRenderNodeHasExpectedContentAcrossLanguages() throws { let outputConsumer = try mixedLanguageFrameworkConsumer() let mixedLanguageFrameworkRenderNode = try outputConsumer.renderNode( diff --git a/Tests/SwiftDocCTests/Model/SourceLanguageTests.swift b/Tests/SwiftDocCTests/Model/SourceLanguageTests.swift new file mode 100644 index 0000000000..2affb15751 --- /dev/null +++ b/Tests/SwiftDocCTests/Model/SourceLanguageTests.swift @@ -0,0 +1,19 @@ +/* + This source file is part of the Swift.org open source project + + Copyright (c) 2022 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 +*/ + +@testable import SwiftDocC +import XCTest + +class SourceLanguageTests: XCTestCase { + func testUsesIDAliasesWhenQueryingFirstKnownLanguage() { + XCTAssertEqual(SourceLanguage(knownLanguageIdentifier: "objective-c"), .objectiveC) + XCTAssertEqual(SourceLanguage(knownLanguageIdentifier: "c"), .objectiveC) + } +} diff --git a/Tests/SwiftDocCTests/Semantics/DocumentationDataVariantsTests.swift b/Tests/SwiftDocCTests/Semantics/DocumentationDataVariantsTests.swift index 738f8472e6..1b40fbdbb6 100644 --- a/Tests/SwiftDocCTests/Semantics/DocumentationDataVariantsTests.swift +++ b/Tests/SwiftDocCTests/Semantics/DocumentationDataVariantsTests.swift @@ -10,6 +10,7 @@ import Foundation import XCTest +import SymbolKit @testable import SwiftDocC class DocumentationDataVariantsTests: XCTestCase { @@ -84,4 +85,16 @@ class DocumentationDataVariantsTests: XCTestCase { variants[objectiveCTrait] = "Objective-C" XCTAssertEqual(variants.firstValue, "Swift") // Swift is still treated as the default value. } + + func testInitializesUsingSelectorLanguage() { + XCTAssertEqual( + DocumentationDataVariantsTrait( + for: UnifiedSymbolGraph.Selector( + interfaceLanguage: "MyLanguage", + platform: nil + ) + ).interfaceLanguage, + "MyLanguage" + ) + } }