Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,6 @@ jobs:
- name: Check Swift Syntax Compatibility
uses: davdroman/swift-syntax-compatibility-check@v1
with:
run-tests: false
run-tests: true
major-versions-only: true
disable-prebuilts: true
10 changes: 10 additions & 0 deletions .swiftformat
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,25 @@

--header ignore
--indent tab
--tabwidth 2
--maxwidth 120
--ifdef no-indent
--ranges preserve
--extensionacl on-declarations
--trailing-commas always
--nil-init insert
--import-grouping alpha
--wrap-arguments before-first
--wrap-conditions before-first
--wrap-parameters before-first
--wrap-return-type never
--guard-else next-line

# Opt-in rules
--enable blockComments
--enable wrapMultilineConditionalAssignment
--enable wrapMultilineFunctionChains

# Disabled rules
--disable blankLinesAroundMark
--disable blankLinesBetweenScopes
Expand Down
44 changes: 28 additions & 16 deletions Sources/AssociationMacro/AssociatedMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -221,13 +221,14 @@ extension AssociatedMacro {
policy: ExprSyntax,
defaultValue: ExprSyntax?,
) -> AccessorDeclSyntax {
let typeWithoutOptional = if let type = type.as(ImplicitlyUnwrappedOptionalTypeSyntax.self) {
type.wrappedType
} else if let type = type.as(OptionalTypeSyntax.self) {
type.wrappedType
} else {
type
}
let typeWithoutOptional =
if let type = type.as(ImplicitlyUnwrappedOptionalTypeSyntax.self) {
type.wrappedType
} else if let type = type.as(OptionalTypeSyntax.self) {
type.wrappedType
} else {
type
}

return AccessorDeclSyntax(
accessorSpecifier: .keyword(.get),
Expand Down Expand Up @@ -345,14 +346,31 @@ extension AssociatedMacro {
type: type,
accessor: didSet,
body: body,
).with(\.leadingTrivia, .newlines(2))
)
.with(\.leadingTrivia, .newlines(2))

Self.callDidSet()
}
},
)
}

private static var selfCapture: ClosureCaptureSyntax {
let selfReference = DeclReferenceExprSyntax(baseName: .keyword(.`self`))
#if canImport(SwiftSyntax601)
return ClosureCaptureSyntax(
name: .keyword(.`self`),
initializer: .init(value: selfReference),
)
#else
return ClosureCaptureSyntax(
name: .keyword(.`self`),
equal: .equalToken(),
expression: selfReference,
)
#endif
}

/// `willSet` closure
///
/// Convert a willSet accessor to a closure variable in the following format.
Expand Down Expand Up @@ -393,10 +411,7 @@ extension AssociatedMacro {
value: ClosureExprSyntax(
signature: .init(
capture: .init {
ClosureCaptureSyntax(
name: .keyword(.`self`),
expression: DeclReferenceExprSyntax(baseName: .keyword(.`self`)),
)
Self.selfCapture
},
parameterClause: .init(ClosureShorthandParameterListSyntax {
ClosureShorthandParameterSyntax(name: newValue)
Expand Down Expand Up @@ -450,10 +465,7 @@ extension AssociatedMacro {
value: ClosureExprSyntax(
signature: .init(
capture: .init {
ClosureCaptureSyntax(
name: .keyword(.`self`),
expression: DeclReferenceExprSyntax(baseName: .keyword(.`self`)),
)
Self.selfCapture
},
parameterClause: .init(ClosureShorthandParameterListSyntax {
ClosureShorthandParameterSyntax(name: oldValue)
Expand Down
4 changes: 3 additions & 1 deletion Sources/Swizzling/AnyHook.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ public class AnyHook {

/// Validate that the selector exists on the active class.
@discardableResult func validate(expectedState: State = .prepared) throws -> Method {
guard let method = class_getInstanceMethod(`class`, selector) else { throw SwizzlingError.methodNotFound(`class`, selector) }
guard let method = class_getInstanceMethod(`class`, selector) else {
throw SwizzlingError.methodNotFound(`class`, selector)
}
guard state == expectedState else { throw SwizzlingError.invalidState(expectedState: expectedState) }
return method
}
Expand Down
7 changes: 5 additions & 2 deletions Sources/Swizzling/SwizzlingHook.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ final class SwizzlingHook<MethodSignature, HookSignature>: TypedHook<MethodSigna
init(
`class`: AnyClass,
selector: Selector,
implementation: (SwizzlingHook<MethodSignature, HookSignature>) -> HookSignature?, // this must be optional or swift runtime will crash. Or swiftc may segfault. Compiler bug?
// `HookSignature?` must be optional or the Swift runtime will crash. `swiftc` may also segfault. Compiler bug?
implementation: (SwizzlingHook<MethodSignature, HookSignature>) -> HookSignature?,
) throws {
try super.init(class: `class`, selector: selector)
replacementIMP = imp_implementationWithBlock(implementation(self) as Any)
Expand All @@ -45,7 +46,9 @@ final class SwizzlingHook<MethodSignature, HookSignature>: TypedHook<MethodSigna
let method = try validate(expectedState: .swizzled)
precondition(origIMP != nil)
let previousIMP = class_replaceMethod(`class`, selector, origIMP!, method_getTypeEncoding(method))
guard previousIMP == replacementIMP else { throw SwizzlingError.unexpectedImplementation(`class`, selector, previousIMP) }
guard previousIMP == replacementIMP else {
throw SwizzlingError.unexpectedImplementation(`class`, selector, previousIMP)
}
Swizzling.log("Restored -[\(`class`).\(selector)] IMP: \(origIMP!)")
}

Expand Down
17 changes: 11 additions & 6 deletions Sources/SwizzlingMacro/SwizzleMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,17 @@ struct SwizzleMacro: ExpressionMacro {

let selector = firstArgument.with(\.trailingComma, nil).trimmedDescription.replacingOccurrences(of: #"\"#, with: "")

let params = node.arguments.dropFirst().prefix(while: { $0.label?.text != "returning" && $0.label?.text != "implementation" })
let paramTypes = if !params.isEmpty {
", " + params.map { $0.expression.trimmedDescription.replacingOccurrences(of: ".self", with: "") }.joined(separator: ", ")
} else {
""
}
let params = node.arguments
.dropFirst()
.prefix(while: { $0.label?.text != "returning" && $0.label?.text != "implementation" })
let paramTypes =
if !params.isEmpty {
", " + params
.map { $0.expression.trimmedDescription.replacingOccurrences(of: ".self", with: "") }
.joined(separator: ", ")
} else {
""
}

let returning = node.arguments.first(where: { $0.label?.text == "returning" })
let returnType = returning?.expression.trimmedDescription.replacingOccurrences(of: ".self", with: "") ?? "Void"
Expand Down
43 changes: 32 additions & 11 deletions Tests/AssociationTests/PatternBindingSyntax+Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ import Testing
struct PatternBindingSyntaxTests {
@Test
func `reads setter`() {
let setter = AccessorDeclSyntax(accessorSpecifier: .keyword(.set), body: .init(statements: CodeBlockItemListSyntax {}))
let setter = AccessorDeclSyntax(
accessorSpecifier: .keyword(.set),
body: .init(statements: CodeBlockItemListSyntax {}),
)

let binding: PatternBindingSyntax = .init(
pattern: IdentifierPatternSyntax(identifier: .identifier("value")),
Expand All @@ -46,7 +49,10 @@ struct PatternBindingSyntaxTests {

@Test
func `reads getter`() throws {
let getter = AccessorDeclSyntax(accessorSpecifier: .keyword(.get), body: .init(statements: CodeBlockItemListSyntax {}))
let getter = AccessorDeclSyntax(
accessorSpecifier: .keyword(.get),
body: .init(statements: CodeBlockItemListSyntax {}),
)

var binding: PatternBindingSyntax = .init(
pattern: IdentifierPatternSyntax(identifier: .identifier("value")),
Expand All @@ -59,7 +65,7 @@ struct PatternBindingSyntaxTests {

#expect(getter.description == binding.getter?.description)

/* getter only */
// getter only
let body = try #require(getter.body, "body must not be nil")

binding = .init(
Expand All @@ -74,7 +80,10 @@ struct PatternBindingSyntaxTests {

@Test
func `sets setter`() {
let setter = AccessorDeclSyntax(accessorSpecifier: .keyword(.set), body: .init(statements: CodeBlockItemListSyntax {}))
let setter = AccessorDeclSyntax(
accessorSpecifier: .keyword(.set),
body: .init(statements: CodeBlockItemListSyntax {}),
)
var binding: PatternBindingSyntax = .init(
pattern: IdentifierPatternSyntax(identifier: .identifier("value")),
accessorBlock: .init(
Expand All @@ -93,7 +102,7 @@ struct PatternBindingSyntaxTests {
binding.setter = newSetter
#expect(newSetter.description == binding.setter?.description)

/* getter only */
// getter only
binding = .init(
pattern: IdentifierPatternSyntax(identifier: .identifier("value")),
accessorBlock: .init(
Expand All @@ -109,7 +118,10 @@ struct PatternBindingSyntaxTests {

@Test
func `sets getter`() {
let getter = AccessorDeclSyntax(accessorSpecifier: .keyword(.get), body: .init(statements: CodeBlockItemListSyntax {}))
let getter = AccessorDeclSyntax(
accessorSpecifier: .keyword(.get),
body: .init(statements: CodeBlockItemListSyntax {}),
)
var binding: PatternBindingSyntax = .init(
pattern: IdentifierPatternSyntax(identifier: .identifier("value")),
accessorBlock: .init(
Expand All @@ -129,7 +141,7 @@ struct PatternBindingSyntaxTests {
binding.getter = newGetter
#expect(newGetter.description == binding.getter?.description)

/* getter only */
// getter only
binding = .init(
pattern: IdentifierPatternSyntax(identifier: .identifier("value")),
accessorBlock: .init(
Expand All @@ -142,7 +154,7 @@ struct PatternBindingSyntaxTests {
binding.getter = newGetter
#expect(newGetter.description == binding.getter?.description)

/* setter only */
// setter only
binding = .init(
pattern: IdentifierPatternSyntax(identifier: .identifier("value")),
accessorBlock: .init(
Expand All @@ -158,7 +170,10 @@ struct PatternBindingSyntaxTests {

@Test
func `willSet`() {
let `willSet` = AccessorDeclSyntax(accessorSpecifier: .keyword(.willSet), body: .init(statements: CodeBlockItemListSyntax {}))
let `willSet` = AccessorDeclSyntax(
accessorSpecifier: .keyword(.willSet),
body: .init(statements: CodeBlockItemListSyntax {}),
)

let binding: PatternBindingSyntax = .init(
pattern: IdentifierPatternSyntax(identifier: .identifier("value")),
Expand All @@ -174,7 +189,10 @@ struct PatternBindingSyntaxTests {

@Test
func `didSet`() {
let `didSet` = AccessorDeclSyntax(accessorSpecifier: .keyword(.didSet), body: .init(statements: CodeBlockItemListSyntax {}))
let `didSet` = AccessorDeclSyntax(
accessorSpecifier: .keyword(.didSet),
body: .init(statements: CodeBlockItemListSyntax {}),
)

let binding: PatternBindingSyntax = .init(
pattern: IdentifierPatternSyntax(identifier: .identifier("value")),
Expand All @@ -190,7 +208,10 @@ struct PatternBindingSyntaxTests {

@Test
func `sets willSet`() {
let `willSet` = AccessorDeclSyntax(accessorSpecifier: .keyword(.willSet), body: .init(statements: CodeBlockItemListSyntax {}))
let `willSet` = AccessorDeclSyntax(
accessorSpecifier: .keyword(.willSet),
body: .init(statements: CodeBlockItemListSyntax {}),
)

var binding: PatternBindingSyntax = .init(
pattern: IdentifierPatternSyntax(identifier: .identifier("value")),
Expand Down
3 changes: 3 additions & 0 deletions mise.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
min_version = "2026.8.6"

[settings]
not_found_system_fallback = false

[tool_config]
locked = true

Expand Down
Loading