From 6eb9aeaec8653bcd520254d20bd90be9aa912682 Mon Sep 17 00:00:00 2001
From: "Patrick Stephen Jr."
Date: Mon, 27 Oct 2025 13:37:13 -0500
Subject: [PATCH] Update Swift Syntax to 602.0.0
---
Package.swift | 6 +-
.../GenericMethodsWithInlineArrayTypes.swift | 30 ++++
.../MockedMethodMacro+TypeErasure.swift | 156 ++++++++++++++++--
.../MockMethodNameComponents.swift | 154 ++++++++++++++++-
4 files changed, 322 insertions(+), 24 deletions(-)
create mode 100644 Sources/MockingClient/GenericMethodsWithInlineArrayTypes.swift
diff --git a/Package.swift b/Package.swift
index 70d91f0..1ae3a0f 100644
--- a/Package.swift
+++ b/Package.swift
@@ -25,15 +25,15 @@ let package = Package(
dependencies: [
.package(
url: "https://github.com/fetch-rewards/swift-locking.git",
- exact: "0.2.0"
+ exact: "0.2.1"
),
.package(
url: "https://github.com/swiftlang/swift-syntax.git",
- exact: "600.0.0" // Must match SwiftSyntaxSugar's swift-syntax version
+ from: "602.0.0" // Must match SwiftSyntaxSugar's swift-syntax version
),
.package(
url: "https://github.com/fetch-rewards/SwiftSyntaxSugar.git",
- exact: "0.1.1" // Must match swift-locking's SwiftSyntaxSugar version
+ from: "0.1.2" // Must match swift-locking's SwiftSyntaxSugar version
),
],
targets: [
diff --git a/Sources/MockingClient/GenericMethodsWithInlineArrayTypes.swift b/Sources/MockingClient/GenericMethodsWithInlineArrayTypes.swift
new file mode 100644
index 0000000..3cc56ac
--- /dev/null
+++ b/Sources/MockingClient/GenericMethodsWithInlineArrayTypes.swift
@@ -0,0 +1,30 @@
+//
+// GenericMethodsWithInlineArrayTypes.swift
+//
+// Copyright © 2025 Fetch.
+//
+
+import Foundation
+public import Mocking
+
+/// A protocol for verifying Mocked's handling of generic methods that leverage
+/// inline array syntax and value-generic arguments.
+///
+/// - Important: Please only use this protocol for permanent verification of
+/// Mocked's handling of inline array syntax. For temporary testing of Mocked's
+/// expansion, use the `Playground` protocol in `main.swift`.
+@available(macOS 26.0, *)
+@Mocked
+public protocol GenericMethodsWithInlineArrayTypes {
+ func genericMethodWithInlineArrayParameter(
+ parameter: [3 of Element]
+ ) -> [3 of Element]
+
+ func genericMethodReturningInlineArray(
+ parameter: InlineArray<3, Element>
+ ) -> InlineArray<3, Element>
+
+ func genericMethodWithInlineArraySameTypeRequirement(
+ parameter: InlineArray<3, Element>
+ ) -> Element where Element: Sendable, InlineArray<3, Element> == InlineArray<3, Element>
+}
diff --git a/Sources/MockingMacros/Macros/MockedMethodMacro/MockedMethodMacro+TypeErasure.swift b/Sources/MockingMacros/Macros/MockedMethodMacro/MockedMethodMacro+TypeErasure.swift
index 8f002ab..130ba05 100644
--- a/Sources/MockingMacros/Macros/MockedMethodMacro/MockedMethodMacro+TypeErasure.swift
+++ b/Sources/MockingMacros/Macros/MockedMethodMacro/MockedMethodMacro+TypeErasure.swift
@@ -247,6 +247,15 @@ extension MockedMethodMacro {
ifTypeIsContainedIn: genericParameters,
typeConstrainedBy: genericWhereClause
)
+ case var .inlineArrayType(type):
+ let (element, didTypeEraseElement) = self.syntax(
+ type.element,
+ ifTypeIsContainedIn: genericParameters,
+ typeConstrainedBy: genericWhereClause
+ )
+
+ type = type.with(\.element, element)
+ result = (newType: type, didTypeErase: didTypeEraseElement)
case let .tupleType(type):
result = self.syntax(
type,
@@ -456,9 +465,7 @@ extension MockedMethodMacro {
):
let (newGenericArgumentClause, didTypeErase) = self.syntax(
genericArgumentClause,
- withElementsInCollectionAt: \.arguments,
- typeErasedAt: \.argument,
- ifTypeIsContainedIn: genericParameters,
+ typeErasedIfElementsIn: genericParameters,
typeConstrainedBy: genericWhereClause
)
let newType = type.with(\.genericArgumentClause, newGenericArgumentClause)
@@ -470,15 +477,25 @@ extension MockedMethodMacro {
):
let (newGenericArgumentClause, didTypeErase) = self.syntax(
genericArgumentClause,
- withElementsInCollectionAt: \.arguments,
- typeErasedAt: \.argument,
- ifTypeIsContainedIn: genericParameters,
+ typeErasedIfElementsIn: genericParameters,
typeConstrainedBy: genericWhereClause
) { index in
index == .zero ? AnyHashable.self : Any.self
}
let newType = type.with(\.genericArgumentClause, newGenericArgumentClause)
+ return (newType, didTypeErase)
+ case let .identifierType(type) where self.isIdentifierType(
+ type,
+ named: "InlineArray"
+ ):
+ let (newGenericArgumentClause, didTypeErase) = self.inlineArrayGenericArgumentClause(
+ genericArgumentClause,
+ typeErasingElementsIn: genericParameters,
+ typeConstrainedBy: genericWhereClause
+ )
+ let newType = type.with(\.genericArgumentClause, newGenericArgumentClause)
+
return (newType, didTypeErase)
case let .memberType(type) where self.isMemberType(
type,
@@ -487,9 +504,7 @@ extension MockedMethodMacro {
):
let (newGenericArgumentClause, didTypeErase) = self.syntax(
genericArgumentClause,
- withElementsInCollectionAt: \.arguments,
- typeErasedAt: \.argument,
- ifTypeIsContainedIn: genericParameters,
+ typeErasedIfElementsIn: genericParameters,
typeConstrainedBy: genericWhereClause
)
let newType = type.with(\.genericArgumentClause, newGenericArgumentClause)
@@ -502,22 +517,31 @@ extension MockedMethodMacro {
):
let (newGenericArgumentClause, didTypeErase) = self.syntax(
genericArgumentClause,
- withElementsInCollectionAt: \.arguments,
- typeErasedAt: \.argument,
- ifTypeIsContainedIn: genericParameters,
+ typeErasedIfElementsIn: genericParameters,
typeConstrainedBy: genericWhereClause
) { index in
index == .zero ? AnyHashable.self : Any.self
}
let newType = type.with(\.genericArgumentClause, newGenericArgumentClause)
+ return (newType, didTypeErase)
+ case let .memberType(type) where self.isMemberType(
+ type,
+ named: "InlineArray",
+ withBaseTypeNamed: "Swift"
+ ):
+ let (newGenericArgumentClause, didTypeErase) = self.inlineArrayGenericArgumentClause(
+ genericArgumentClause,
+ typeErasingElementsIn: genericParameters,
+ typeConstrainedBy: genericWhereClause
+ )
+ let newType = type.with(\.genericArgumentClause, newGenericArgumentClause)
+
return (newType, didTypeErase)
default:
let (_, didTypeErase) = self.syntax(
genericArgumentClause,
- withElementsInCollectionAt: \.arguments,
- typeErasedAt: \.argument,
- ifTypeIsContainedIn: genericParameters,
+ typeErasedIfElementsIn: genericParameters,
typeConstrainedBy: genericWhereClause
)
@@ -531,6 +555,108 @@ extension MockedMethodMacro {
}
}
+ private static func syntax(
+ _ argument: GenericArgumentSyntax,
+ ifTypeIsContainedIn genericParameters: GenericParameterListSyntax?,
+ typeConstrainedBy genericWhereClause: GenericWhereClauseSyntax?,
+ typeErasedType: (some Any).Type = Any.self
+ ) -> (GenericArgumentSyntax, Bool) {
+ guard case let .type(type) = argument.argument else {
+ return (argument, false)
+ }
+
+ let (erasedType, didTypeErase) = self.type(
+ type,
+ typeErasedIfNecessaryUsing: genericParameters,
+ typeConstrainedBy: genericWhereClause,
+ typeErasedType: typeErasedType
+ )
+
+ let newArgument = argument.with(
+ \.argument,
+ .type(TypeSyntax(erasedType))
+ )
+
+ return (newArgument, didTypeErase)
+ }
+
+ private static func syntax(
+ _ clause: GenericArgumentClauseSyntax,
+ typeErasedIfElementsIn genericParameters: GenericParameterListSyntax?,
+ typeConstrainedBy genericWhereClause: GenericWhereClauseSyntax?,
+ typeErasedType: (Int) -> Any.Type
+ ) -> (GenericArgumentClauseSyntax, Bool) {
+ var didTypeErase = false
+ var newArguments: [GenericArgumentSyntax] = []
+
+ for (index, argument) in clause.arguments.enumerated() {
+ let (newArgument, didTypeEraseArgument) = self.syntax(
+ argument,
+ ifTypeIsContainedIn: genericParameters,
+ typeConstrainedBy: genericWhereClause,
+ typeErasedType: typeErasedType(index)
+ )
+
+ newArguments.append(newArgument)
+ didTypeErase = didTypeErase || didTypeEraseArgument
+ }
+
+ let newClause = clause.with(
+ \.arguments,
+ GenericArgumentListSyntax(newArguments)
+ )
+
+ return (newClause, didTypeErase)
+ }
+
+ private static func syntax(
+ _ clause: GenericArgumentClauseSyntax,
+ typeErasedIfElementsIn genericParameters: GenericParameterListSyntax?,
+ typeConstrainedBy genericWhereClause: GenericWhereClauseSyntax?
+ ) -> (GenericArgumentClauseSyntax, Bool) {
+ self.syntax(
+ clause,
+ typeErasedIfElementsIn: genericParameters,
+ typeConstrainedBy: genericWhereClause
+ ) { _ in Any.self }
+ }
+
+ private static func inlineArrayGenericArgumentClause(
+ _ clause: GenericArgumentClauseSyntax,
+ typeErasingElementsIn genericParameters: GenericParameterListSyntax?,
+ typeConstrainedBy genericWhereClause: GenericWhereClauseSyntax?
+ ) -> (GenericArgumentClauseSyntax, Bool) {
+ guard !clause.arguments.isEmpty else {
+ return (clause, false)
+ }
+
+ var didTypeErase = false
+ var newArguments: [GenericArgumentSyntax] = []
+
+ for (index, argument) in clause.arguments.enumerated() {
+ if index == .zero {
+ newArguments.append(argument)
+ continue
+ }
+
+ let (newArgument, didTypeEraseArgument) = self.syntax(
+ argument,
+ ifTypeIsContainedIn: genericParameters,
+ typeConstrainedBy: genericWhereClause
+ )
+
+ newArguments.append(newArgument)
+ didTypeErase = didTypeErase || didTypeEraseArgument
+ }
+
+ let newClause = clause.with(
+ \.arguments,
+ GenericArgumentListSyntax(newArguments)
+ )
+
+ return (newClause, didTypeErase)
+ }
+
/// Returns a Boolean value indicating whether the provided identifier
/// type's name matches any of the provided `names`.
///
diff --git a/Sources/MockingMacros/Models/MockMethodNameComponents/MockMethodNameComponents.swift b/Sources/MockingMacros/Models/MockMethodNameComponents/MockMethodNameComponents.swift
index dcb4a59..d633c93 100644
--- a/Sources/MockingMacros/Models/MockMethodNameComponents/MockMethodNameComponents.swift
+++ b/Sources/MockingMacros/Models/MockMethodNameComponents/MockMethodNameComponents.swift
@@ -228,6 +228,8 @@ extension MockMethodNameComponents {
self.capitalizedDescription(of: type)
case let .attributedType(type):
self.capitalizedDescription(of: type)
+ case let .inlineArrayType(type):
+ self.capitalizedDescription(of: type)
case let .classRestrictionType(type):
self.capitalizedDescription(of: type.classKeyword)
case let .compositionType(type):
@@ -296,6 +298,8 @@ extension MockMethodNameComponents {
result += specifier.arguments.reduce("") { result, argument in
result + self.capitalizedDescription(of: argument.parameter)
}
+ case let .nonisolatedTypeSpecifier(specifier):
+ result += self.capitalizedDescription(of: specifier)
}
}
let baseTypeDescription = self.capitalizedDescription(of: type.baseType)
@@ -440,9 +444,19 @@ extension MockMethodNameComponents {
return nameDescription + "Of" + genericArgumentsDescription
}
- return self.capitalizedDescription(
- of: DictionaryTypeSyntax(key: key, value: value)
- )
+ if
+ let keyType = Self.type(from: key),
+ let valueType = Self.type(from: value)
+ {
+ return self.capitalizedDescription(
+ of: DictionaryTypeSyntax(key: keyType, value: valueType)
+ )
+ }
+
+ let keyDescription = Self.capitalizedDescription(of: key)
+ let valueDescription = Self.capitalizedDescription(of: value)
+
+ return "DictionaryOf" + keyDescription + "To" + valueDescription
}
/// Returns a capitalized description of the provided `type`.
@@ -503,9 +517,20 @@ extension MockMethodNameComponents {
return baseTypeDescription + nameDescription + "Of" + genericArgumentsDescription
}
- let dictionaryDescription = self.capitalizedDescription(
- of: DictionaryTypeSyntax(key: key, value: value)
- )
+ let dictionaryDescription: String
+
+ if
+ let keyType = Self.type(from: key),
+ let valueType = Self.type(from: value)
+ {
+ dictionaryDescription = self.capitalizedDescription(
+ of: DictionaryTypeSyntax(key: keyType, value: valueType)
+ )
+ } else {
+ let keyDescription = Self.capitalizedDescription(of: key)
+ let valueDescription = Self.capitalizedDescription(of: value)
+ dictionaryDescription = "DictionaryOf" + keyDescription + "To" + valueDescription
+ }
return baseTypeDescription + dictionaryDescription
}
@@ -693,4 +718,121 @@ extension MockMethodNameComponents {
return description
}
+
+ /// Returns a capitalized description of the provided `type`.
+ ///
+ /// - Parameter type: The inline array type syntax to describe.
+ ///
+ /// - Returns: A capitalized description of the provided `type`.
+ private static func capitalizedDescription(
+ of type: InlineArrayTypeSyntax
+ ) -> String {
+ let countDescription = self.capitalizedDescription(of: type.count.argument)
+ let elementDescription = self.capitalizedDescription(of: type.element.argument)
+
+ return "InlineArrayOf" + countDescription + "Of" + elementDescription
+ }
+
+ /// Returns a capitalized description of the provided `argument`.
+ ///
+ /// - Parameter argument: The generic argument syntax to describe.
+ ///
+ /// - Returns: A capitalized description of the provided `argument`.
+ private static func capitalizedDescription(
+ of argument: GenericArgumentSyntax.Argument
+ ) -> String {
+ switch argument {
+ case let .type(type):
+ self.capitalizedDescription(of: type)
+ case let .expr(expr):
+ self.capitalizedDescription(of: expr)
+ }
+ }
+
+ /// Returns a capitalized description of the provided `expression`.
+ ///
+ /// - Parameter expression: The expression syntax to describe.
+ ///
+ /// - Returns: A capitalized description of the provided `expression`.
+ private static func capitalizedDescription(
+ of expression: ExprSyntax
+ ) -> String {
+ expression.trimmedDescription.withFirstCharacterCapitalized()
+ }
+
+ /// Returns a capitalized description of the provided `specifier`.
+ ///
+ /// - Parameter specifier: The nonisolated type specifier syntax to describe.
+ ///
+ /// - Returns: A capitalized description of the provided `specifier`.
+ private static func capitalizedDescription(
+ of specifier: NonisolatedTypeSpecifierSyntax
+ ) -> String {
+ var description = self.capitalizedDescription(of: specifier.nonisolatedKeyword)
+
+ if let argument = specifier.argument {
+ description += Self.capitalizedDescription(of: argument)
+ }
+
+ return description
+ }
+
+ /// Returns a capitalized description of the provided `argument`.
+ ///
+ /// - Parameter argument: The nonisolated specifier argument syntax to describe.
+ ///
+ /// - Returns: A capitalized description of the provided `argument`.
+ private static func capitalizedDescription(
+ of argument: NonisolatedSpecifierArgumentSyntax
+ ) -> String {
+ argument.trimmedDescription.withFirstCharacterCapitalized()
+ }
+
+ /// Returns a capitalized description of the provided `type`.
+ ///
+ /// - Parameter type: The same-type requirement's left type to describe.
+ ///
+ /// - Returns: A capitalized description of the provided `type`.
+ private static func capitalizedDescription(
+ of type: SameTypeRequirementSyntax.LeftType
+ ) -> String {
+ switch type {
+ case let .type(type):
+ self.capitalizedDescription(of: type)
+ case let .expr(expr):
+ self.capitalizedDescription(of: expr)
+ }
+ }
+
+ /// Returns a capitalized description of the provided `type`.
+ ///
+ /// - Parameter type: The same-type requirement's right type to describe.
+ ///
+ /// - Returns: A capitalized description of the provided `type`.
+ private static func capitalizedDescription(
+ of type: SameTypeRequirementSyntax.RightType
+ ) -> String {
+ switch type {
+ case let .type(type):
+ self.capitalizedDescription(of: type)
+ case let .expr(expr):
+ self.capitalizedDescription(of: expr)
+ }
+ }
+
+ /// Returns a `TypeSyntax` when the provided `argument` is a type argument.
+ ///
+ /// - Parameter argument: The generic argument from which to extract a type.
+ ///
+ /// - Returns: A `TypeSyntax` if the argument represents a type; otherwise, `nil`.
+ private static func type(
+ from argument: GenericArgumentSyntax.Argument
+ ) -> TypeSyntax? {
+ switch argument {
+ case let .type(type):
+ type
+ case .expr:
+ nil
+ }
+ }
}