From 77f4fb17dc622ae605062f752b92d8f3f6a5010a Mon Sep 17 00:00:00 2001 From: Alex Guretzki Date: Wed, 30 Apr 2025 16:36:11 +0200 Subject: [PATCH 1/7] Consolidating changes on function/enum parameters --- .../EnumCaseDeclSyntax+SwiftInterface.swift | 1 + .../FunctionDeclSyntax+SwiftInterface.swift | 2 +- ...InitializerDeclSyntax+SwiftInterface.swift | 2 +- .../SubscriptDeclSyntax+SwiftInterface.swift | 3 +- .../SwiftInterfaceElement+EnumCase.swift | 39 +----- .../SwiftInterfaceElement+Function.swift | 47 +------ .../SwiftInterfaceElement+Initializer.swift | 8 +- .../SwiftInterfaceElement+Subscript.swift | 42 +------ ...nterfaceElement+DiffHelper+Parameter.swift | 116 ++++++++++++++++++ .../SwiftInterfaceElement+DiffHelper.swift | 37 +++--- ...nterfaceElement+ParameterDescription.swift | 2 +- .../SwiftInterfaceElementParameter.swift | 50 ++++++++ ...ference-changes-swift-interface-private.md | 9 +- ...eference-changes-swift-interface-public.md | 3 +- 14 files changed, 211 insertions(+), 150 deletions(-) create mode 100644 Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElement+DiffHelper+Parameter.swift create mode 100644 Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElementParameter.swift diff --git a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/EnumCaseDeclSyntax+SwiftInterface.swift b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/EnumCaseDeclSyntax+SwiftInterface.swift index aff5b78..df57620 100644 --- a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/EnumCaseDeclSyntax+SwiftInterface.swift +++ b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/EnumCaseDeclSyntax+SwiftInterface.swift @@ -22,6 +22,7 @@ extension EnumCaseDeclSyntax { name: $0.name.trimmedDescription, parameters: $0.parameterClause?.parameters.map { .init( + attributes: [], firstName: $0.firstName?.trimmedDescription, secondName: $0.secondName?.trimmedDescription, type: $0.type.trimmedDescription, diff --git a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/FunctionDeclSyntax+SwiftInterface.swift b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/FunctionDeclSyntax+SwiftInterface.swift index a9f8da7..c5f200c 100644 --- a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/FunctionDeclSyntax+SwiftInterface.swift +++ b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/FunctionDeclSyntax+SwiftInterface.swift @@ -23,7 +23,7 @@ extension FunctionDeclSyntax { } } - let parameters: [SwiftInterfaceFunction.Parameter] = self.signature.parameterClause.parameters.map { + let parameters: [SwiftInterfaceElementParameter] = self.signature.parameterClause.parameters.map { .init( attributes: $0.attributes.sanitizedList, firstName: $0.firstName.trimmedDescription, diff --git a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/InitializerDeclSyntax+SwiftInterface.swift b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/InitializerDeclSyntax+SwiftInterface.swift index 47afcd2..1d5360b 100644 --- a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/InitializerDeclSyntax+SwiftInterface.swift +++ b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/InitializerDeclSyntax+SwiftInterface.swift @@ -23,7 +23,7 @@ extension InitializerDeclSyntax { } } - let parameters: [SwiftInterfaceFunction.Parameter] = self.signature.parameterClause.parameters.map { + let parameters: [SwiftInterfaceElementParameter] = self.signature.parameterClause.parameters.map { .init( attributes: $0.attributes.sanitizedList, firstName: $0.firstName.trimmedDescription, diff --git a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/SubscriptDeclSyntax+SwiftInterface.swift b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/SubscriptDeclSyntax+SwiftInterface.swift index 8c766f2..595ba95 100644 --- a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/SubscriptDeclSyntax+SwiftInterface.swift +++ b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/SubscriptDeclSyntax+SwiftInterface.swift @@ -12,8 +12,9 @@ extension SubscriptDeclSyntax { func toInterfaceElement() -> SwiftInterfaceSubscript { - let parameters: [SwiftInterfaceSubscript.Parameter] = self.parameterClause.parameters.map { + let parameters: [SwiftInterfaceElementParameter] = self.parameterClause.parameters.map { .init( + attributes: $0.attributes.sanitizedList, firstName: $0.firstName.trimmedDescription, secondName: $0.secondName?.trimmedDescription, type: $0.type.trimmedDescription, diff --git a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+EnumCase.swift b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+EnumCase.swift index e14a055..fe5cd40 100644 --- a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+EnumCase.swift +++ b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+EnumCase.swift @@ -6,39 +6,6 @@ import Foundation -extension SwiftInterfaceEnumCase { - - struct Parameter { - - let firstName: String? - - let secondName: String? - - let type: String - - let defaultValue: String? - - var description: String { - var description = [ - firstName, - secondName - ].compactMap { $0 }.joined(separator: " ") - - if description.isEmpty { - description += "\(type)" - } else { - description += ": \(type)" - } - - if let defaultValue { - description += " = \(defaultValue)" - } - - return description - } - } -} - class SwiftInterfaceEnumCase: SwiftInterfaceElement { /// e.g. @discardableResult, @MainActor, @objc, @_spi(...), ... @@ -49,7 +16,7 @@ class SwiftInterfaceEnumCase: SwiftInterfaceElement { let name: String - let parameters: [Parameter]? + let parameters: [SwiftInterfaceElementParameter]? let rawValue: String? @@ -72,7 +39,7 @@ class SwiftInterfaceEnumCase: SwiftInterfaceElement { attributes: [String], modifiers: [String], name: String, - parameters: [Parameter]?, + parameters: [SwiftInterfaceElementParameter]?, rawValue: String? ) { self.attributes = attributes @@ -90,7 +57,7 @@ extension SwiftInterfaceEnumCase { guard let other = otherElement as? Self else { return [] } changes += diffDescription(propertyType: "attribute", oldValues: other.attributes, newValues: attributes) changes += diffDescription(propertyType: "modifier", oldValues: other.modifiers, newValues: modifiers) - changes += diffDescription(propertyType: "parameter", oldValues: other.parameters?.map(\.description), newValues: parameters?.map(\.description)) + changes += diffDescription(oldParameters: other.parameters, newParameters: parameters) changes += diffDescription(propertyType: "raw value", oldValue: other.rawValue, newValue: rawValue) return changes.compactMap { $0 } } diff --git a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+Function.swift b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+Function.swift index 9d937d3..89f0aaa 100644 --- a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+Function.swift +++ b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+Function.swift @@ -6,45 +6,6 @@ import Foundation -extension SwiftInterfaceFunction { - - struct Parameter { - - /// e.g. @discardableResult, @MainActor, @objc, @_spi(...), ... - let attributes: [String] - - let firstName: String - - /// optional second "internal" name - can be ignored - let secondName: String? - - let type: String - - let defaultValue: String? - - var description: String { - let names = [ - firstName, - secondName - ].compactMap { $0 } - - var description = (attributes + names).joined(separator: " ") - - if description.isEmpty { - description += "\(type)" - } else { - description += ": \(type)" - } - - if let defaultValue { - description += " = \(defaultValue)" - } - - return description - } - } -} - class SwiftInterfaceFunction: SwiftInterfaceElement { /// e.g. @discardableResult, @MainActor, @objc, @_spi(...), ... @@ -55,7 +16,7 @@ class SwiftInterfaceFunction: SwiftInterfaceElement { /// e.g. let genericParameterDescription: String? - let parameters: [Parameter] + let parameters: [SwiftInterfaceElementParameter] /// e.g. async, throws, rethrows let effectSpecifiers: [String] @@ -76,7 +37,7 @@ class SwiftInterfaceFunction: SwiftInterfaceElement { var parent: (any SwiftInterfaceElement)? var diffableSignature: String { - "\(name)(\(parameters.map { "\($0.firstName):" }.joined()))" + "\(name)(\(parameters.map(\.valueForDiffableSignature).joined()))" } var consolidatableName: String { name } @@ -90,7 +51,7 @@ class SwiftInterfaceFunction: SwiftInterfaceElement { modifiers: [String], name: String, genericParameterDescription: String?, - parameters: [Parameter], + parameters: [SwiftInterfaceElementParameter], effectSpecifiers: [String], returnType: String?, genericWhereClauseDescription: String? @@ -114,7 +75,7 @@ extension SwiftInterfaceFunction { changes += diffDescription(propertyType: "attribute", oldValues: other.attributes, newValues: attributes) changes += diffDescription(propertyType: "modifier", oldValues: other.modifiers, newValues: modifiers) changes += diffDescription(propertyType: "generic parameter description", oldValue: other.genericParameterDescription, newValue: genericParameterDescription) - changes += diffDescription(propertyType: "parameter", oldValues: other.parameters.map(\.description), newValues: parameters.map(\.description)) // TODO: Maybe have a better way to show changes + changes += diffDescription(oldParameters: other.parameters, newParameters: parameters) changes += diffDescription(propertyType: "effect", oldValues: other.effectSpecifiers, newValues: effectSpecifiers) changes += diffDescription(propertyType: "return type", oldValue: other.returnType, newValue: returnType) changes += diffDescription(propertyType: "generic where clause", oldValue: other.genericWhereClauseDescription, newValue: genericWhereClauseDescription) diff --git a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+Initializer.swift b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+Initializer.swift index fafca00..6aa6de3 100644 --- a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+Initializer.swift +++ b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+Initializer.swift @@ -16,7 +16,7 @@ class SwiftInterfaceInitializer: SwiftInterfaceElement { /// e.g. let genericParameterDescription: String? - let parameters: [SwiftInterfaceFunction.Parameter] + let parameters: [SwiftInterfaceElementParameter] /// e.g. async, throws, rethrows let effectSpecifiers: [String] @@ -35,7 +35,7 @@ class SwiftInterfaceInitializer: SwiftInterfaceElement { var parent: (any SwiftInterfaceElement)? var diffableSignature: String { - "init(\(parameters.map { "\($0.firstName):" }.joined()))" + "init(\(parameters.map { $0.valueForDiffableSignature }.joined()))" } var consolidatableName: String { "init" } @@ -49,7 +49,7 @@ class SwiftInterfaceInitializer: SwiftInterfaceElement { modifiers: [String], optionalMark: String?, genericParameterDescription: String?, - parameters: [SwiftInterfaceFunction.Parameter], + parameters: [SwiftInterfaceElementParameter], effectSpecifiers: [String], genericWhereClauseDescription: String? ) { @@ -72,7 +72,7 @@ extension SwiftInterfaceInitializer { changes += diffDescription(propertyType: "modifier", oldValues: other.modifiers, newValues: modifiers) changes += diffDescription(propertyType: "optional mark", oldValue: other.optionalMark, newValue: optionalMark) changes += diffDescription(propertyType: "generic parameter description", oldValue: other.genericParameterDescription, newValue: genericParameterDescription) - changes += diffDescription(propertyType: "parameter", oldValues: other.parameters.map(\.description), newValues: parameters.map(\.description)) // TODO: Maybe have a better way to show changes + changes += diffDescription(oldParameters: other.parameters, newParameters: parameters) changes += diffDescription(propertyType: "effect", oldValues: other.effectSpecifiers, newValues: effectSpecifiers) changes += diffDescription(propertyType: "generic where clause", oldValue: other.genericWhereClauseDescription, newValue: genericWhereClauseDescription) return changes.compactMap { $0 } diff --git a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+Subscript.swift b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+Subscript.swift index 676aabe..7c295fd 100644 --- a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+Subscript.swift +++ b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+Subscript.swift @@ -6,40 +6,6 @@ import Foundation -extension SwiftInterfaceSubscript { - - struct Parameter { - - let firstName: String - - /// optional second "internal" name - can be ignored - let secondName: String? - - let type: String - - let defaultValue: String? - - var description: String { - var description = [ - firstName, - secondName - ].compactMap { $0 }.joined(separator: " ") - - if description.isEmpty { - description += "\(type)" - } else { - description += ": \(type)" - } - - if let defaultValue { - description += " = \(defaultValue)" - } - - return description - } - } -} - class SwiftInterfaceSubscript: SwiftInterfaceElement { let name: String = "subscript" @@ -53,7 +19,7 @@ class SwiftInterfaceSubscript: SwiftInterfaceElement { /// e.g. let genericParameterDescription: String? - let parameters: [Parameter] + let parameters: [SwiftInterfaceElementParameter] let returnType: String @@ -69,7 +35,7 @@ class SwiftInterfaceSubscript: SwiftInterfaceElement { var parent: (any SwiftInterfaceElement)? var diffableSignature: String { - "\(name)(\(parameters.map { "\($0.firstName):" }.joined()))" + "\(name)(\(parameters.map(\.valueForDiffableSignature).joined()))" } var consolidatableName: String { name } @@ -82,7 +48,7 @@ class SwiftInterfaceSubscript: SwiftInterfaceElement { attributes: [String], modifiers: [String], genericParameterDescription: String?, - parameters: [Parameter], + parameters: [SwiftInterfaceElementParameter], returnType: String, genericWhereClauseDescription: String?, accessors: String? @@ -105,7 +71,7 @@ extension SwiftInterfaceSubscript { changes += diffDescription(propertyType: "attribute", oldValues: other.attributes, newValues: attributes) changes += diffDescription(propertyType: "modifier", oldValues: other.modifiers, newValues: modifiers) changes += diffDescription(propertyType: "generic parameter description", oldValue: other.genericParameterDescription, newValue: genericParameterDescription) - changes += diffDescription(propertyType: "parameter", oldValues: other.parameters.map(\.description), newValues: parameters.map(\.description)) // TODO: Maybe have a better way to show changes + changes += diffDescription(oldParameters: other.parameters, newParameters: parameters) changes += diffDescription(propertyType: "return type", oldValue: other.returnType, newValue: returnType) changes += diffDescription(propertyType: "generic where clause", oldValue: other.genericWhereClauseDescription, newValue: genericWhereClauseDescription) changes += diffDescription(propertyType: "accessors", oldValue: other.accessors, newValue: accessors) diff --git a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElement+DiffHelper+Parameter.swift b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElement+DiffHelper+Parameter.swift new file mode 100644 index 0000000..f1f65a9 --- /dev/null +++ b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElement+DiffHelper+Parameter.swift @@ -0,0 +1,116 @@ +// +// SwiftInterfaceElement+DiffHelper+Parameter.swift +// public-api-diff +// +// Created by Alexander Guretzki on 30/04/2025. +// + +import Foundation +import PADCore + +extension SwiftInterfaceElement { + + /// Returns a list of change descriptions for changes between the old and new values + /// - Parameters: + /// - propertyType: The property type name (e.g. "accessor", "modifier", "generic where clause", ...) + /// for additional information + /// - oldParameters: The (optional) old parameters + /// - newParameters: The (optional) new parameters + /// - Returns: A list of change descriptions caused by a value change + func diffDescription( + oldParameters: [SwiftInterfaceElementParameter]?, + newParameters: [SwiftInterfaceElementParameter]? + ) -> [String] { + let propertyType = "parameter" + + guard let oldParameters else { + guard let newParameters else { return [] } + return newParameters.map { "Added \(propertyType) `\($0.description)`" } + } + + guard let newParameters else { + return oldParameters.map { "Removed \(propertyType) `\($0.description)`" } + } + + let oldParametersByName = oldParameters.indexedByFirstName + let newParametersByName = newParameters.indexedByFirstName + + var changes = [String]() + + // Check for removed parameters + oldParameters.enumerated().forEach { index, oldParameter in + let oldFirstName = oldParameter.firstName ?? "Parameter \(index)" + if newParametersByName[oldFirstName] == nil { + changes.append("Removed \(propertyType) `\(oldParameter.description)`") + } + } + + // Check for added and modified parameters + newParameters.enumerated().forEach { index, newParameter in + + let newFirstName = newParameter.firstName ?? "Parameter \(index)" + + guard let oldParameter = oldParametersByName[newFirstName] else { + // Parameter was added + changes.append("Added \(propertyType) `\(newParameter.description)`") + return + } + + let modificationDiffPrefix = modificationDiffDescriptionPrefix( + propertyType: propertyType, + firstName: oldParameter.firstName, + index: index + ) + + // Check if the type has changed + if oldParameter.type != newParameter.type { + changes.append("\(modificationDiffPrefix): Changed type from `\(oldParameter.type)` to `\(newParameter.type)`") + } + + // Check if the default value has changed + changes += diffDescription( + propertyType: "default value", + oldValue: oldParameter.defaultValue, + newValue: newParameter.defaultValue + ).map { "\(modificationDiffPrefix)`: \($0)" } + + // Check if the attributes did change + changes += diffDescription( + propertyType: "attribute", + oldValues: oldParameter.attributes, + newValues: newParameter.attributes + ).map { "\(modificationDiffPrefix)`: \($0)" } + } + + return changes + } +} + +fileprivate extension SwiftInterfaceElement { + + func modificationDiffDescriptionPrefix( + propertyType: String, + firstName: String?, + index: Int + ) -> String { + let ordinalFormatter = NumberFormatter() + ordinalFormatter.numberStyle = .ordinal + + if let firstName { + return "Modified \(propertyType) `\(firstName)`" + } else { + return "Modified \(ordinalFormatter.string(from: NSNumber(value: index+1)) ?? "\(index + 1)") \(propertyType)" + } + } +} + +private extension [SwiftInterfaceElementParameter] { + + var indexedByFirstName: [String: SwiftInterfaceElementParameter] { + Dictionary( + uniqueKeysWithValues: self.enumerated().map { + ($0.element.firstName ?? "Parameter \($0.offset)", $0.element) + } + ) + } +} diff --git a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElement+DiffHelper.swift b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElement+DiffHelper.swift index 88de350..71d2f8e 100644 --- a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElement+DiffHelper.swift +++ b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElement+DiffHelper.swift @@ -44,29 +44,32 @@ extension SwiftInterfaceElement { /// Returns a list of change descriptions for changes between the old and new values /// - Parameters: - /// - propertyType: The (optional) property type name (e.g. "accessor", "modifier", "generic where clause", ...) for additional information - /// - oldValue: The (optional) old values - /// - newValue: The (optional) new values + /// - propertyType: The property type name (e.g. "accessor", "modifier", "generic where clause", ...) + /// for additional information + /// - oldValues: The (optional) old values + /// - newValues: The (optional) new values /// - Returns: A list of change descriptions caused by a value change - func diffDescription(propertyType: String, oldValues: [String]?, newValues: [String]?) -> [String] { + func diffDescription( + propertyType: String, + oldValues: [String]?, + newValues: [String]? + ) -> [String] { - if let oldValues, let newValues { - let old = Set(oldValues) - let new = Set(newValues) - return old.symmetricDifference(new).map { - "\(new.contains($0) ? "Added" : "Removed") \(propertyType) `\($0)`" - } + guard let oldValues else { + guard let newValues else { return [] } + return newValues.map { "Added \(propertyType) `\($0.description)`" } } - if let oldValues { - return oldValues.map { "Removed \(propertyType) `\($0)`" } + guard let newValues else { + return oldValues.map { "Removed \(propertyType) `\($0.description)`" } } - - if let newValues { - return newValues.map { "Added \(propertyType) `\($0)`" } + + let old = Set(oldValues) + let new = Set(newValues) + + return old.symmetricDifference(new).map { + "\(new.contains($0) ? "Added" : "Removed") \(propertyType) `\($0)`" } - - return [] } } diff --git a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElement+ParameterDescription.swift b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElement+ParameterDescription.swift index 9875d9d..f99ace0 100644 --- a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElement+ParameterDescription.swift +++ b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElement+ParameterDescription.swift @@ -9,7 +9,7 @@ import Foundation extension SwiftInterfaceElement { func formattedParameterDescription(for parameterDescriptions: [String]) -> String { - // We're only doing multiline formatting if we have more than 1 character + // We're only doing multiline formatting if we have more than 1 parameter guard parameterDescriptions.count > 1 else { return parameterDescriptions.joined(separator: ", ") } let spacer = " " diff --git a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElementParameter.swift b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElementParameter.swift new file mode 100644 index 0000000..ba017f2 --- /dev/null +++ b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElementParameter.swift @@ -0,0 +1,50 @@ +// +// SwiftInterfaceElementParameter.swift +// public-api-diff +// +// Created by Alexander Guretzki on 30/04/2025. +// + + +struct SwiftInterfaceElementParameter { + + /// e.g. @discardableResult, @MainActor, @objc, @_spi(...), ... + let attributes: [String] + + let firstName: String? + + /// optional second "internal" name - can be ignored + let secondName: String? + + let type: String + + let defaultValue: String? + + var description: String { + let names = [ + firstName, + secondName + ].compactMap { $0 } + + var description = (attributes + names).joined(separator: " ") + + if description.isEmpty { + description += "\(type)" + } else { + description += ": \(type)" + } + + if let defaultValue { + description += " = \(defaultValue)" + } + + return description + } +} + +extension SwiftInterfaceElementParameter { + + var valueForDiffableSignature: String { + "\(firstName ?? "_"):" + } +} diff --git a/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-private.md b/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-private.md index 4bba9d0..80a5c4a 100644 --- a/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-private.md +++ b/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-private.md @@ -271,8 +271,7 @@ indirect case recursive(ReferencePackage.CustomEnum) /** Changes: -- Added parameter `ReferencePackage.CustomEnum` -- Removed parameter `ReferencePackage.CustomEnum` +- Modified 1st parameter: Changed type from `ReferencePackage.CustomEnum` to `ReferencePackage.CustomEnum` */ ``` #### ❌ Removed @@ -444,10 +443,8 @@ public init( /** Changes: -- Added parameter `getSetVar: T` -- Added parameter `getVar: T` -- Removed parameter `getSetVar: ReferencePackage.OpenSpiConformingClass.CustomAssociatedType` -- Removed parameter `getVar: ReferencePackage.OpenSpiConformingClass.CustomAssociatedType` +- Modified parameter `getSetVar`: Changed type from `ReferencePackage.OpenSpiConformingClass.CustomAssociatedType` to `T` +- Modified parameter `getVar`: Changed type from `ReferencePackage.OpenSpiConformingClass.CustomAssociatedType` to `T` */ ``` ```javascript diff --git a/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-public.md b/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-public.md index 3b453e3..901e6a1 100644 --- a/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-public.md +++ b/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-public.md @@ -256,8 +256,7 @@ indirect case recursive(ReferencePackage.CustomEnum) /** Changes: -- Added parameter `ReferencePackage.CustomEnum` -- Removed parameter `ReferencePackage.CustomEnum` +- Modified 1st parameter: Changed type from `ReferencePackage.CustomEnum` to `ReferencePackage.CustomEnum` */ ``` #### ❌ Removed From eabb166a145573cd4b1af66f7a5dfed9d0c2e0b8 Mon Sep 17 00:00:00 2001 From: Alex Guretzki Date: Tue, 17 Jun 2025 10:50:00 +0200 Subject: [PATCH 2/7] More descriptive removals --- .../ReferencePackage/ReferencePackage.swift | 17 +++++++++ .../SwiftInterfaceAnalyzer.swift | 2 +- ...ference-changes-swift-interface-private.md | 35 +++++++++++-------- ...eference-changes-swift-interface-public.md | 35 +++++++++++-------- 4 files changed, 60 insertions(+), 29 deletions(-) diff --git a/ReferencePackages/ReferencePackage/Sources/ReferencePackage/ReferencePackage.swift b/ReferencePackages/ReferencePackage/Sources/ReferencePackage/ReferencePackage.swift index 86f3687..7713fa4 100644 --- a/ReferencePackages/ReferencePackage/Sources/ReferencePackage/ReferencePackage.swift +++ b/ReferencePackages/ReferencePackage/Sources/ReferencePackage/ReferencePackage.swift @@ -140,3 +140,20 @@ public enum CustomEnum { indirect case recursive(CustomEnum) } + +public struct PublicStructThatIsOnlyAvailableInTheReferencePackage { + + public var foo: String + public func bar() -> Void { + print("Hello") + } +} + +public extension CustomEnum { + enum PublicEnumInExtensionOfCustomEnumThatIsOnlyAvailableInTheReferencePackage { + case alpha + case beta + } +} + +public extension CustomEnum {} diff --git a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceAnalyzer/SwiftInterfaceAnalyzer.swift b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceAnalyzer/SwiftInterfaceAnalyzer.swift index 8cf9481..762b0a7 100644 --- a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceAnalyzer/SwiftInterfaceAnalyzer.swift +++ b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceAnalyzer/SwiftInterfaceAnalyzer.swift @@ -81,7 +81,7 @@ struct SwiftInterfaceAnalyzer: SwiftInterfaceAnalyzing { // No matching element was found so either it was removed or added let changeType: IndependentSwiftInterfaceChange.ChangeType = oldFirst ? - .removal(lhsElement.description) : + .removal(lhsElement.recursiveDescription()) : .addition(lhsElement.recursiveDescription()) return [ diff --git a/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-private.md b/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-private.md index 80a5c4a..ac4f660 100644 --- a/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-private.md +++ b/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-private.md @@ -1,6 +1,6 @@ -# ⚠️ 58 public changes detected ⚠️ +# ⚠️ 57 public changes detected ⚠️ _Comparing `new_private` to `old_private`_ -
❇️34 Additions
🔀22 Modifications
2 Removals
+
❇️31 Additions
🔀22 Modifications
4 Removals
--- ## `ReferencePackage` @@ -33,12 +33,6 @@ public enum RawValueEnum: Swift.Equatable, Swift.Hashable, Swift.RawRepresentabl } ``` ```javascript -public protocol ParentProtocol { - associatedtype Iterator: Swift.Collection - associatedtype ParentType: Swift.Equatable where Self.ParentType == Self.Iterator.Element -} -``` -```javascript public protocol ParentProtocol { associatedtype Iterator: Swift.Collection associatedtype ParentType: Swift.Equatable where Self.ParentType == Self.Iterator.Element @@ -121,6 +115,13 @@ Changes: - Added generic where clause `where T : Swift.Strideable` */ ``` +#### ❌ Removed +```javascript +public struct PublicStructThatIsOnlyAvailableInTheReferencePackage { + public func bar() -> Swift.Void + public var foo: Swift.String +} +``` ### `Array` #### ❇️ Added ```javascript @@ -278,20 +279,26 @@ Changes: ```javascript case caseWithString(Swift.String) ``` -### `CustomProtocol` -#### ❇️ Added ```javascript -associatedtype AnotherAssociatedType: Swift.Strideable +public enum PublicEnumInExtensionOfCustomEnumThatIsOnlyAvailableInTheReferencePackage: Swift.Equatable, Swift.Hashable { + case alpha + case beta + public func hash(into hasher: inout Swift.Hasher) -> Swift.Void + public static func ==( + a: ReferencePackage.CustomEnum.PublicEnumInExtensionOfCustomEnumThatIsOnlyAvailableInTheReferencePackage, + b: ReferencePackage.CustomEnum.PublicEnumInExtensionOfCustomEnumThatIsOnlyAvailableInTheReferencePackage + ) -> Swift.Bool + public var hashValue: Swift.Int { get } +} ``` +### `CustomProtocol` +#### ❇️ Added ```javascript associatedtype AnotherAssociatedType: Swift.Strideable ``` ```javascript associatedtype CustomAssociatedType: Swift.Equatable ``` -```javascript -associatedtype CustomAssociatedType: Swift.Equatable -``` #### 🔀 Modified ```javascript // From diff --git a/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-public.md b/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-public.md index 901e6a1..ff72010 100644 --- a/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-public.md +++ b/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-public.md @@ -1,6 +1,6 @@ -# ⚠️ 49 public changes detected ⚠️ +# ⚠️ 48 public changes detected ⚠️ _Comparing `new_public` to `old_public`_ -
❇️31 Additions
🔀16 Modifications
2 Removals
+
❇️28 Additions
🔀16 Modifications
4 Removals
--- ## `ReferencePackage` @@ -33,12 +33,6 @@ public enum RawValueEnum: Swift.Equatable, Swift.Hashable, Swift.RawRepresentabl } ``` ```javascript -public protocol ParentProtocol { - associatedtype Iterator: Swift.Collection - associatedtype ParentType: Swift.Equatable where Self.ParentType == Self.Iterator.Element -} -``` -```javascript public protocol ParentProtocol { associatedtype Iterator: Swift.Collection associatedtype ParentType: Swift.Equatable where Self.ParentType == Self.Iterator.Element @@ -106,6 +100,13 @@ Changes: - Added generic where clause `where T : Swift.Strideable` */ ``` +#### ❌ Removed +```javascript +public struct PublicStructThatIsOnlyAvailableInTheReferencePackage { + public func bar() -> Swift.Void + public var foo: Swift.String +} +``` ### `Array` #### ❇️ Added ```javascript @@ -263,20 +264,26 @@ Changes: ```javascript case caseWithString(Swift.String) ``` -### `CustomProtocol` -#### ❇️ Added ```javascript -associatedtype AnotherAssociatedType: Swift.Strideable +public enum PublicEnumInExtensionOfCustomEnumThatIsOnlyAvailableInTheReferencePackage: Swift.Equatable, Swift.Hashable { + case alpha + case beta + public func hash(into hasher: inout Swift.Hasher) -> Swift.Void + public static func ==( + a: ReferencePackage.CustomEnum.PublicEnumInExtensionOfCustomEnumThatIsOnlyAvailableInTheReferencePackage, + b: ReferencePackage.CustomEnum.PublicEnumInExtensionOfCustomEnumThatIsOnlyAvailableInTheReferencePackage + ) -> Swift.Bool + public var hashValue: Swift.Int { get } +} ``` +### `CustomProtocol` +#### ❇️ Added ```javascript associatedtype AnotherAssociatedType: Swift.Strideable ``` ```javascript associatedtype CustomAssociatedType: Swift.Equatable ``` -```javascript -associatedtype CustomAssociatedType: Swift.Equatable -``` #### 🔀 Modified ```javascript // From From 3cc8a685922c3fb6f02d19df6e2e232056c71f3b Mon Sep 17 00:00:00 2001 From: Alex Guretzki Date: Tue, 17 Jun 2025 11:30:13 +0200 Subject: [PATCH 3/7] updating expected output --- Package.resolved | 12 +- ...ference-changes-swift-interface-private.md | 114 +++++++++--------- ...eference-changes-swift-interface-public.md | 96 +++++++-------- 3 files changed, 111 insertions(+), 111 deletions(-) diff --git a/Package.resolved b/Package.resolved index 28f7ba1..ab0f8b8 100644 --- a/Package.resolved +++ b/Package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-argument-parser", "state" : { - "revision" : "41982a3656a71c768319979febd796c6fd111d5c", - "version" : "1.5.0" + "revision" : "011f0c765fb46d9cac61bca19be0527e99c98c8b", + "version" : "1.5.1" } }, { @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-docc-plugin", "state" : { - "revision" : "85e4bb4e1cd62cec64a4b8e769dcefdf0c5b9d64", - "version" : "1.4.3" + "revision" : "d1691545d53581400b1de9b0472d45eb25c19fed", + "version" : "1.4.4" } }, { @@ -41,8 +41,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/nicklockwood/SwiftFormat", "state" : { - "revision" : "468a7d32dedc8d352c191594b3b45d9fd8ba291b", - "version" : "0.55.5" + "revision" : "6ebb96ce454ddb036320104a1160350ee9581767", + "version" : "0.56.4" } }, { diff --git a/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-private.md b/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-private.md index ac4f660..58a3a66 100644 --- a/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-private.md +++ b/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-private.md @@ -5,7 +5,7 @@ _Comparing `new_private` to `old_private`_ --- ## `ReferencePackage` #### ❇️ Added -```javascript +```swift @available(macOS 14, *) public enum NewEnumAvailableInVersion17: Swift.Codable, Swift.Equatable, Swift.Hashable, Swift.RawRepresentable, Swift.String { @available(macOS 14, *) @@ -17,13 +17,13 @@ public enum NewEnumAvailableInVersion17: Swift.Codable, Swift.Equatable, Swift.H public var rawValue: Swift.String { get } } ``` -```javascript +```swift @resultBuilder public struct SomeResultBuilder { public static func buildBlock(_ components: Swift.String) -> Swift.String } ``` -```javascript +```swift public enum RawValueEnum: Swift.Equatable, Swift.Hashable, Swift.RawRepresentable, Swift.String { case one case two @@ -32,22 +32,22 @@ public enum RawValueEnum: Swift.Equatable, Swift.Hashable, Swift.RawRepresentabl public var rawValue: Swift.String { get } } ``` -```javascript +```swift public protocol ParentProtocol { associatedtype Iterator: Swift.Collection associatedtype ParentType: Swift.Equatable where Self.ParentType == Self.Iterator.Element } ``` -```javascript +```swift public protocol ProtocolWithDefaultImplementation { func function() -> Swift.String } ``` -```javascript +```swift public protocol SimpleProtocol ``` #### 🔀 Modified -```javascript +```swift // From @_spi(SystemProgrammingInterface) open class OpenSpiConformingClass: ReferencePackage.CustomProtocol @@ -62,7 +62,7 @@ Changes: - Added generic where clause `where T : Swift.Strideable` */ ``` -```javascript +```swift // From public actor CustomActor @@ -74,7 +74,7 @@ Changes: - Added inheritance `ReferencePackage.SimpleProtocol` */ ``` -```javascript +```swift // From public enum CustomEnum @@ -87,7 +87,7 @@ Changes: - Added inheritance `ReferencePackage.SimpleProtocol` */ ``` -```javascript +```swift // From public protocol CustomProtocol @@ -102,7 +102,7 @@ Changes: - Added primary associated type `CustomAssociatedType` */ ``` -```javascript +```swift // From public struct CustomStruct: ReferencePackage.CustomProtocol @@ -116,7 +116,7 @@ Changes: */ ``` #### ❌ Removed -```javascript +```swift public struct PublicStructThatIsOnlyAvailableInTheReferencePackage { public func bar() -> Swift.Void public var foo: Swift.String @@ -124,33 +124,33 @@ public struct PublicStructThatIsOnlyAvailableInTheReferencePackage { ``` ### `Array` #### ❇️ Added -```javascript +```swift extension Swift.Array { public subscript(safe index: Swift.Int) -> Element? { get } } ``` ### `CustomClass` #### ❇️ Added -```javascript +```swift final public let a: Swift.Int { get } ``` -```javascript +```swift final public let b: Swift.Int { get } ``` -```javascript +```swift final public let c: Swift.Int { get } ``` -```javascript +```swift final public let d: Swift.Double { get } ``` -```javascript +```swift public subscript(index: Swift.Int) -> T? { get set } ``` -```javascript +```swift public var lazyVar: Swift.String { get set } ``` #### 🔀 Modified -```javascript +```swift // From @_Concurrency.MainActor public func asyncThrowingFunc() async throws -> Swift.Void @@ -166,7 +166,7 @@ Changes: - Added parameter `_ element: Element` */ ``` -```javascript +```swift // From convenience public init(value: T) @@ -178,7 +178,7 @@ Changes: - Added optional mark `!` */ ``` -```javascript +```swift // From public init( weakObject: ReferencePackage.CustomClass? = nil, @@ -197,7 +197,7 @@ Changes: - Added parameter `@ReferencePackage.SomeResultBuilder content: () -> Swift.String` */ ``` -```javascript +```swift // From public init() @@ -211,30 +211,30 @@ Changes: ``` ### `CustomEnum` #### ❇️ Added -```javascript +```swift case a ``` -```javascript +```swift case b ``` -```javascript +```swift case c ``` -```javascript +```swift case caseWithNamedString(title: T) ``` -```javascript +```swift case d ``` -```javascript +```swift case e(ReferencePackage.CustomEnum.NestedStructInExtension) ``` -```javascript +```swift extension ReferencePackage.CustomEnum where T == Swift.String { public var titleOfCaseWithNamedString: Swift.String? { get } } ``` -```javascript +```swift public struct NestedStructInExtension: Swift.CustomStringConvertible { public init(string: Swift.String = "Hello") public let string: Swift.String { get } @@ -242,7 +242,7 @@ public struct NestedStructInExtension: Swift.CustomStringConvertible { } ``` #### 🔀 Modified -```javascript +```swift // From case caseWithTuple( Swift.String, @@ -263,7 +263,7 @@ Changes: - Removed parameter `Swift.String` */ ``` -```javascript +```swift // From indirect case recursive(ReferencePackage.CustomEnum) @@ -276,10 +276,10 @@ Changes: */ ``` #### ❌ Removed -```javascript +```swift case caseWithString(Swift.String) ``` -```javascript +```swift public enum PublicEnumInExtensionOfCustomEnumThatIsOnlyAvailableInTheReferencePackage: Swift.Equatable, Swift.Hashable { case alpha case beta @@ -293,14 +293,14 @@ public enum PublicEnumInExtensionOfCustomEnumThatIsOnlyAvailableInTheReferencePa ``` ### `CustomProtocol` #### ❇️ Added -```javascript +```swift associatedtype AnotherAssociatedType: Swift.Strideable ``` -```javascript +```swift associatedtype CustomAssociatedType: Swift.Equatable ``` #### 🔀 Modified -```javascript +```swift // From func function() -> any Swift.Equatable @@ -312,7 +312,7 @@ Changes: - Modified return type from `any Swift.Equatable` to `Self.CustomAssociatedType` */ ``` -```javascript +```swift // From var getSetVar: any Swift.Equatable { get set } @@ -324,7 +324,7 @@ Changes: - Modified type from `any Swift.Equatable` to `Self.AnotherAssociatedType` */ ``` -```javascript +```swift // From var getVar: any Swift.Equatable { get } @@ -337,12 +337,12 @@ Changes: */ ``` #### ❌ Removed -```javascript +```swift typealias CustomAssociatedType = Swift.Equatable ``` ### `CustomStruct` #### ❇️ Added -```javascript +```swift @available(macOS, unavailable, message: "Unavailable on macOS") public struct NestedStruct { @available(*, deprecated, renamed: "nestedVar") @@ -351,20 +351,20 @@ public struct NestedStruct { public let nestedVar: Swift.String { get } } ``` -```javascript +```swift public typealias AnotherAssociatedType = Swift.Double ``` -```javascript +```swift public typealias CustomAssociatedType = Swift.Int ``` -```javascript +```swift public typealias Iterator = [ReferencePackage.CustomStruct.AnotherAssociatedType] ``` -```javascript +```swift public typealias ParentType = Swift.Double ``` #### 🔀 Modified -```javascript +```swift // From @discardableResult public func function() -> any Swift.Equatable @@ -378,7 +378,7 @@ Changes: - Modified return type from `any Swift.Equatable` to `Swift.Int` */ ``` -```javascript +```swift // From public var getSetVar: any Swift.Equatable @@ -390,7 +390,7 @@ Changes: - Modified type from `any Swift.Equatable` to `Swift.Double` */ ``` -```javascript +```swift // From public var getVar: any Swift.Equatable @@ -404,20 +404,20 @@ Changes: ``` ### `OpenSpiConformingClass` #### ❇️ Added -```javascript +```swift @_spi(SystemProgrammingInterface) public typealias AnotherAssociatedType = T ``` -```javascript +```swift @_spi(SystemProgrammingInterface) public typealias Iterator = [Swift.Double] ``` -```javascript +```swift @_spi(SystemProgrammingInterface) public typealias ParentType = Swift.Double ``` #### 🔀 Modified -```javascript +```swift // From @_spi(SystemProgrammingInterface) @inlinable @@ -433,7 +433,7 @@ Changes: - Modified return type from `ReferencePackage.OpenSpiConformingClass.CustomAssociatedType` to `T` */ ``` -```javascript +```swift // From @_spi(SystemProgrammingInterface) public init( @@ -454,7 +454,7 @@ Changes: - Modified parameter `getVar`: Changed type from `ReferencePackage.OpenSpiConformingClass.CustomAssociatedType` to `T` */ ``` -```javascript +```swift // From @_spi(SystemProgrammingInterface) public typealias CustomAssociatedType = any Swift.Equatable @@ -468,7 +468,7 @@ Changes: - Modified assignment from `any Swift.Equatable` to `T` */ ``` -```javascript +```swift // From @_spi(SystemProgrammingInterface) public var getSetVar: ReferencePackage.OpenSpiConformingClass.CustomAssociatedType @@ -482,7 +482,7 @@ Changes: - Modified type from `ReferencePackage.OpenSpiConformingClass.CustomAssociatedType` to `T` */ ``` -```javascript +```swift // From @_spi(SystemProgrammingInterface) public var getVar: ReferencePackage.OpenSpiConformingClass.CustomAssociatedType diff --git a/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-public.md b/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-public.md index ff72010..e7ed710 100644 --- a/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-public.md +++ b/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-public.md @@ -5,7 +5,7 @@ _Comparing `new_public` to `old_public`_ --- ## `ReferencePackage` #### ❇️ Added -```javascript +```swift @available(macOS 14, *) public enum NewEnumAvailableInVersion17: Swift.Codable, Swift.Equatable, Swift.Hashable, Swift.RawRepresentable, Swift.String { @available(macOS 14, *) @@ -17,13 +17,13 @@ public enum NewEnumAvailableInVersion17: Swift.Codable, Swift.Equatable, Swift.H public var rawValue: Swift.String { get } } ``` -```javascript +```swift @resultBuilder public struct SomeResultBuilder { public static func buildBlock(_ components: Swift.String) -> Swift.String } ``` -```javascript +```swift public enum RawValueEnum: Swift.Equatable, Swift.Hashable, Swift.RawRepresentable, Swift.String { case one case two @@ -32,22 +32,22 @@ public enum RawValueEnum: Swift.Equatable, Swift.Hashable, Swift.RawRepresentabl public var rawValue: Swift.String { get } } ``` -```javascript +```swift public protocol ParentProtocol { associatedtype Iterator: Swift.Collection associatedtype ParentType: Swift.Equatable where Self.ParentType == Self.Iterator.Element } ``` -```javascript +```swift public protocol ProtocolWithDefaultImplementation { func function() -> Swift.String } ``` -```javascript +```swift public protocol SimpleProtocol ``` #### 🔀 Modified -```javascript +```swift // From public actor CustomActor @@ -59,7 +59,7 @@ Changes: - Added inheritance `ReferencePackage.SimpleProtocol` */ ``` -```javascript +```swift // From public enum CustomEnum @@ -72,7 +72,7 @@ Changes: - Added inheritance `ReferencePackage.SimpleProtocol` */ ``` -```javascript +```swift // From public protocol CustomProtocol @@ -87,7 +87,7 @@ Changes: - Added primary associated type `CustomAssociatedType` */ ``` -```javascript +```swift // From public struct CustomStruct: ReferencePackage.CustomProtocol @@ -101,7 +101,7 @@ Changes: */ ``` #### ❌ Removed -```javascript +```swift public struct PublicStructThatIsOnlyAvailableInTheReferencePackage { public func bar() -> Swift.Void public var foo: Swift.String @@ -109,33 +109,33 @@ public struct PublicStructThatIsOnlyAvailableInTheReferencePackage { ``` ### `Array` #### ❇️ Added -```javascript +```swift extension Swift.Array { public subscript(safe index: Swift.Int) -> Element? { get } } ``` ### `CustomClass` #### ❇️ Added -```javascript +```swift final public let a: Swift.Int { get } ``` -```javascript +```swift final public let b: Swift.Int { get } ``` -```javascript +```swift final public let c: Swift.Int { get } ``` -```javascript +```swift final public let d: Swift.Double { get } ``` -```javascript +```swift public subscript(index: Swift.Int) -> T? { get set } ``` -```javascript +```swift public var lazyVar: Swift.String { get set } ``` #### 🔀 Modified -```javascript +```swift // From @_Concurrency.MainActor public func asyncThrowingFunc() async throws -> Swift.Void @@ -151,7 +151,7 @@ Changes: - Added parameter `_ element: Element` */ ``` -```javascript +```swift // From convenience public init(value: T) @@ -163,7 +163,7 @@ Changes: - Added optional mark `!` */ ``` -```javascript +```swift // From public init( weakObject: ReferencePackage.CustomClass? = nil, @@ -182,7 +182,7 @@ Changes: - Added parameter `@ReferencePackage.SomeResultBuilder content: () -> Swift.String` */ ``` -```javascript +```swift // From public init() @@ -196,30 +196,30 @@ Changes: ``` ### `CustomEnum` #### ❇️ Added -```javascript +```swift case a ``` -```javascript +```swift case b ``` -```javascript +```swift case c ``` -```javascript +```swift case caseWithNamedString(title: T) ``` -```javascript +```swift case d ``` -```javascript +```swift case e(ReferencePackage.CustomEnum.NestedStructInExtension) ``` -```javascript +```swift extension ReferencePackage.CustomEnum where T == Swift.String { public var titleOfCaseWithNamedString: Swift.String? { get } } ``` -```javascript +```swift public struct NestedStructInExtension: Swift.CustomStringConvertible { public init(string: Swift.String = "Hello") public let string: Swift.String { get } @@ -227,7 +227,7 @@ public struct NestedStructInExtension: Swift.CustomStringConvertible { } ``` #### 🔀 Modified -```javascript +```swift // From case caseWithTuple( Swift.String, @@ -248,7 +248,7 @@ Changes: - Removed parameter `Swift.String` */ ``` -```javascript +```swift // From indirect case recursive(ReferencePackage.CustomEnum) @@ -261,10 +261,10 @@ Changes: */ ``` #### ❌ Removed -```javascript +```swift case caseWithString(Swift.String) ``` -```javascript +```swift public enum PublicEnumInExtensionOfCustomEnumThatIsOnlyAvailableInTheReferencePackage: Swift.Equatable, Swift.Hashable { case alpha case beta @@ -278,14 +278,14 @@ public enum PublicEnumInExtensionOfCustomEnumThatIsOnlyAvailableInTheReferencePa ``` ### `CustomProtocol` #### ❇️ Added -```javascript +```swift associatedtype AnotherAssociatedType: Swift.Strideable ``` -```javascript +```swift associatedtype CustomAssociatedType: Swift.Equatable ``` #### 🔀 Modified -```javascript +```swift // From func function() -> any Swift.Equatable @@ -297,7 +297,7 @@ Changes: - Modified return type from `any Swift.Equatable` to `Self.CustomAssociatedType` */ ``` -```javascript +```swift // From var getSetVar: any Swift.Equatable { get set } @@ -309,7 +309,7 @@ Changes: - Modified type from `any Swift.Equatable` to `Self.AnotherAssociatedType` */ ``` -```javascript +```swift // From var getVar: any Swift.Equatable { get } @@ -322,12 +322,12 @@ Changes: */ ``` #### ❌ Removed -```javascript +```swift typealias CustomAssociatedType = Swift.Equatable ``` ### `CustomStruct` #### ❇️ Added -```javascript +```swift @available(macOS, unavailable, message: "Unavailable on macOS") public struct NestedStruct { @available(*, deprecated, renamed: "nestedVar") @@ -336,20 +336,20 @@ public struct NestedStruct { public let nestedVar: Swift.String { get } } ``` -```javascript +```swift public typealias AnotherAssociatedType = Swift.Double ``` -```javascript +```swift public typealias CustomAssociatedType = Swift.Int ``` -```javascript +```swift public typealias Iterator = [ReferencePackage.CustomStruct.AnotherAssociatedType] ``` -```javascript +```swift public typealias ParentType = Swift.Double ``` #### 🔀 Modified -```javascript +```swift // From @discardableResult public func function() -> any Swift.Equatable @@ -363,7 +363,7 @@ Changes: - Modified return type from `any Swift.Equatable` to `Swift.Int` */ ``` -```javascript +```swift // From public var getSetVar: any Swift.Equatable @@ -375,7 +375,7 @@ Changes: - Modified type from `any Swift.Equatable` to `Swift.Double` */ ``` -```javascript +```swift // From public var getVar: any Swift.Equatable From cfe51c10a636aad1ad3c8c142175f0ad2cfe562f Mon Sep 17 00:00:00 2001 From: Alex Guretzki Date: Tue, 17 Jun 2025 11:37:29 +0200 Subject: [PATCH 4/7] Better verification if an archive succeeded --- .../SwiftInterfaceProducer/XcodeTools.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/PublicModules/PADProjectBuilder/SwiftInterfaceProducer/XcodeTools.swift b/Sources/PublicModules/PADProjectBuilder/SwiftInterfaceProducer/XcodeTools.swift index e2405e3..177d617 100644 --- a/Sources/PublicModules/PADProjectBuilder/SwiftInterfaceProducer/XcodeTools.swift +++ b/Sources/PublicModules/PADProjectBuilder/SwiftInterfaceProducer/XcodeTools.swift @@ -23,6 +23,7 @@ struct XcodeTools { internal enum Constants { static let derivedDataPath: String = ".build" + static let buildDirPath: String = ".build/Build" static let simulatorSdkCommand = "xcrun --sdk iphonesimulator --show-sdk-path" } @@ -87,6 +88,7 @@ struct XcodeTools { let result = shell.execute(command) let derivedDataPath = "\(projectDirectoryPath)/\(Constants.derivedDataPath)" + let buildDirPath = "\(projectDirectoryPath)/\(Constants.buildDirPath)" logger?.debug(result, from: String(describing: Self.self)) @@ -94,9 +96,7 @@ struct XcodeTools { // so we have to check outside if they exist. // // Also see: https://github.com/swiftlang/swift/issues/56573 - guard fileHandler.fileExists(atPath: derivedDataPath) else { - print(result) - + guard fileHandler.fileExists(atPath: buildDirPath) else { throw XcodeToolsError( errorDescription: "💥 Building project failed", underlyingError: result From 18e09d0ed55c637e6b6e32c9a95e4728eb0f5fff Mon Sep 17 00:00:00 2001 From: Alex Guretzki Date: Tue, 17 Jun 2025 12:16:04 +0200 Subject: [PATCH 5/7] Adjusting tests --- Tests/UnitTests/XcodeToolsTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/UnitTests/XcodeToolsTests.swift b/Tests/UnitTests/XcodeToolsTests.swift index dd50017..0735915 100644 --- a/Tests/UnitTests/XcodeToolsTests.swift +++ b/Tests/UnitTests/XcodeToolsTests.swift @@ -103,7 +103,7 @@ private extension XcodeToolsTests { var expectedHandleDebugCalls: [(message: String, subsystem: String)] = [ (archiveResult, "XcodeTools") ] - var expectedHandleFileExistsCalls = ["PROJECT_DIRECTORY_PATH/.build"] + var expectedHandleFileExistsCalls = ["PROJECT_DIRECTORY_PATH/.build/Build"] var shell = MockShell() shell.handleExecute = { command in From 726f7e16ab927b32332d4d791b852b25cc22fe59 Mon Sep 17 00:00:00 2001 From: Alex Guretzki Date: Tue, 17 Jun 2025 12:53:41 +0200 Subject: [PATCH 6/7] Update run-tests.yml --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 2d0a3e4..bf2cacd 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -21,7 +21,7 @@ jobs: - name: Select latest Xcode uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '16.1' + xcode-version: '16.2' - name: 🛠️ Run All Tests run: | From d6de37ae9431a410f5e8cf3296f2a25bd94fdf28 Mon Sep 17 00:00:00 2001 From: Alex Guretzki Date: Tue, 17 Jun 2025 13:06:12 +0200 Subject: [PATCH 7/7] Fixing expected output for Xcode 16.2 --- .../IntegrationTests/ReferencePackageTests.swift | 4 ++-- ...-reference-changes-swift-interface-private.md | 16 ++++++++++++++-- ...d-reference-changes-swift-interface-public.md | 16 ++++++++++++++-- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/Tests/IntegrationTests/ReferencePackageTests.swift b/Tests/IntegrationTests/ReferencePackageTests.swift index 6287fff..08a825f 100644 --- a/Tests/IntegrationTests/ReferencePackageTests.swift +++ b/Tests/IntegrationTests/ReferencePackageTests.swift @@ -21,8 +21,8 @@ class ReferencePackageTests: XCTestCase { let newReferencePackageDirectory = referencePackagesRoot.appending(path: "UpdatedPackage") if - FileManager.default.fileExists(atPath: oldReferencePackageDirectory.appending(path: XcodeTools.Constants.derivedDataPath).path()), - FileManager.default.fileExists(atPath: newReferencePackageDirectory.appending(path: XcodeTools.Constants.derivedDataPath).path()) { + FileManager.default.fileExists(atPath: oldReferencePackageDirectory.appending(path: XcodeTools.Constants.buildDirPath).path()), + FileManager.default.fileExists(atPath: newReferencePackageDirectory.appending(path: XcodeTools.Constants.buildDirPath).path()) { return // Nothing to build } diff --git a/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-private.md b/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-private.md index 58a3a66..8a105df 100644 --- a/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-private.md +++ b/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-private.md @@ -1,6 +1,6 @@ -# ⚠️ 57 public changes detected ⚠️ +# ⚠️ 60 public changes detected ⚠️ _Comparing `new_private` to `old_private`_ -
❇️31 Additions
🔀22 Modifications
4 Removals
+
❇️34 Additions
🔀22 Modifications
4 Removals
--- ## `ReferencePackage` @@ -33,6 +33,12 @@ public enum RawValueEnum: Swift.Equatable, Swift.Hashable, Swift.RawRepresentabl } ``` ```swift +public protocol ParentProtocol { + associatedtype Iterator: Swift.Collection + associatedtype ParentType: Swift.Equatable where Self.ParentType == Self.Iterator.Element +} +``` +```swift public protocol ParentProtocol { associatedtype Iterator: Swift.Collection associatedtype ParentType: Swift.Equatable where Self.ParentType == Self.Iterator.Element @@ -297,6 +303,12 @@ public enum PublicEnumInExtensionOfCustomEnumThatIsOnlyAvailableInTheReferencePa associatedtype AnotherAssociatedType: Swift.Strideable ``` ```swift +associatedtype AnotherAssociatedType: Swift.Strideable +``` +```swift +associatedtype CustomAssociatedType: Swift.Equatable +``` +```swift associatedtype CustomAssociatedType: Swift.Equatable ``` #### 🔀 Modified diff --git a/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-public.md b/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-public.md index e7ed710..492b801 100644 --- a/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-public.md +++ b/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-public.md @@ -1,6 +1,6 @@ -# ⚠️ 48 public changes detected ⚠️ +# ⚠️ 51 public changes detected ⚠️ _Comparing `new_public` to `old_public`_ -
❇️28 Additions
🔀16 Modifications
4 Removals
+
❇️31 Additions
🔀16 Modifications
4 Removals
--- ## `ReferencePackage` @@ -33,6 +33,12 @@ public enum RawValueEnum: Swift.Equatable, Swift.Hashable, Swift.RawRepresentabl } ``` ```swift +public protocol ParentProtocol { + associatedtype Iterator: Swift.Collection + associatedtype ParentType: Swift.Equatable where Self.ParentType == Self.Iterator.Element +} +``` +```swift public protocol ParentProtocol { associatedtype Iterator: Swift.Collection associatedtype ParentType: Swift.Equatable where Self.ParentType == Self.Iterator.Element @@ -282,6 +288,12 @@ public enum PublicEnumInExtensionOfCustomEnumThatIsOnlyAvailableInTheReferencePa associatedtype AnotherAssociatedType: Swift.Strideable ``` ```swift +associatedtype AnotherAssociatedType: Swift.Strideable +``` +```swift +associatedtype CustomAssociatedType: Swift.Equatable +``` +```swift associatedtype CustomAssociatedType: Swift.Equatable ``` #### 🔀 Modified