Skip to content

Commit

Permalink
[core] Multiple bindings support.
Browse files Browse the repository at this point in the history
  • Loading branch information
VAndrJ committed Jul 9, 2024
1 parent f296063 commit 977e31c
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 41 deletions.
12 changes: 12 additions & 0 deletions Sources/VACopyWithClient/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,15 @@ let example1 = SomeClass1().mutating { $0.parameter1 = 42 }
assert(example1.parameter1 == 42)
example1.mutating { $0.parameter1 = 0 }
assert(example1.parameter1 == 0)


@CopyWith
struct SomeStruct5: Equatable {
let a, b: Int
let c: Bool
}

let value5 = SomeStruct5(a: 0, b: 1, c: false)
assert(value5.copyWith(a: 42) == SomeStruct5(a: 42, b: 1, c: false))
assert(value5.copyWith(b: 42) == SomeStruct5(a: 0, b: 42, c: false))
assert(value5.copyWith(c: true) == SomeStruct5(a: 0, b: 1, c: true))
44 changes: 25 additions & 19 deletions Sources/VACopyWithMacros/VACopyWithMacro+Support.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ public extension VariableDeclSyntax {
guard isInstance else {
return false
}
guard bindings.count == 1, let binding = bindings.first, !binding.pattern.is(TuplePatternSyntax.self) else {
throw VACopyWithMacroError.multipleBindings
guard let binding = bindings.first, !binding.pattern.is(TuplePatternSyntax.self) else {
throw VACopyWithMacroError.tupleBindings
}
guard isVar || isLet && binding.initializer == nil else {
return false
Expand All @@ -51,26 +51,32 @@ public extension VariableDeclSyntax {
}
}
}
var nameWithType: (name: String, type: TypeSyntax)? {
guard bindings.count == 1,
let binding = bindings.first,
let name = binding.pattern.as(IdentifierPatternSyntax.self)?.identifier.text else {
return nil
}

if let type = binding.typeAnnotation?.type {
return (name, type)
} else if let initializer = binding.initializer?.value {
if let type = initializer.literalOrExprType {
return (name, type)
} else if let member = initializer.as(ArrayExprSyntax.self)?.elements.first?.expression.literalOrExprType {
return (name, TypeSyntax("[\(raw: member.description)]"))
} else if let dict = initializer.as(DictionaryExprSyntax.self)?.content.as(DictionaryElementListSyntax.self)?.first, let key = dict.key.literalOrExprType, let value = dict.value.literalOrExprType {
return (name, TypeSyntax("[\(raw: key.description): \(raw: value.description)]"))
var nameWithType: [(name: String, type: TypeSyntax)] {
var names: [String] = []
var possibleType: TypeSyntax?

for binding in bindings {
if let name = binding.pattern.as(IdentifierPatternSyntax.self)?.identifier.text {
names.append(name)
if let type = binding.typeAnnotation?.type {
possibleType = type
} else if let initializer = binding.initializer?.value {
if let type = initializer.literalOrExprType {
possibleType = type
} else if let member = initializer.as(ArrayExprSyntax.self)?.elements.first?.expression.literalOrExprType {
possibleType = TypeSyntax("[\(raw: member.description)]")
} else if let dict = initializer.as(DictionaryExprSyntax.self)?.content.as(DictionaryElementListSyntax.self)?.first, let key = dict.key.literalOrExprType, let value = dict.value.literalOrExprType {
possibleType = TypeSyntax("[\(raw: key.description): \(raw: value.description)]")
}
}
}
}

return nil
if let possibleType {
return names.map { ($0, possibleType) }
} else {
return []
}
}
}

Expand Down
39 changes: 20 additions & 19 deletions Sources/VACopyWithMacros/VACopyWithMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public struct VACopyWithMacro: ExtensionMacro {
}

let storedProperties = try declaration.storedProperties()
let properties = storedProperties.compactMap(\.nameWithType)
let properties = storedProperties.flatMap(\.nameWithType)
guard !properties.isEmpty else {
return []
}
Expand Down Expand Up @@ -71,24 +71,25 @@ public struct VACopyWithMacro: ExtensionMacro {
}
"""
}
"""
\(raw: isContainsOptional ? "return " : "")\(FunctionCallExprSyntax(
calledExpression: DeclReferenceExprSyntax(baseName: .identifier(type.description)),
leftParen: .leftParenToken(),
arguments: LabeledExprListSyntax {
for (i, property) in properties.enumerated() {
LabeledExprSyntax(
leadingTrivia: .newline,
label: .identifier(property.name),
colon: .colonToken(),
expression: property.type.isOptional ? ExprSyntax("\(raw: property.name)") : ExprSyntax("\(raw: property.name) ?? self.\(raw: property.name)"),
trailingComma: i == properties.indices.last ? nil : .commaToken()
)
}
},
rightParen: .rightParenToken(leadingTrivia: .newline)
))
"""
ReturnStmtSyntax(
returnKeyword: .keyword(.return, presence: isContainsOptional ? .present : .missing),
expression: FunctionCallExprSyntax(
calledExpression: DeclReferenceExprSyntax(baseName: .identifier(type.description)),
leftParen: .leftParenToken(),
arguments: LabeledExprListSyntax {
for (i, property) in properties.enumerated() {
LabeledExprSyntax(
leadingTrivia: .newline,
label: .identifier(property.name),
colon: .colonToken(),
expression: property.type.isOptional ? ExprSyntax("\(raw: property.name)") : ExprSyntax("\(raw: property.name) ?? self.\(raw: property.name)"),
trailingComma: i == properties.indices.last ? nil : .commaToken()
)
}
},
rightParen: .rightParenToken(leadingTrivia: .newline)
)
)
}
)
},
Expand Down
4 changes: 2 additions & 2 deletions Sources/VACopyWithMacros/VACopyWithMacroError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

public enum VACopyWithMacroError: Error, CustomStringConvertible {
case notStruct
case multipleBindings
case tupleBindings
case notClassOrProtocol
case notStructOrProtocol
case notAnyObject
Expand All @@ -18,7 +18,7 @@ public enum VACopyWithMacroError: Error, CustomStringConvertible {
case .notStructOrProtocol: "Must be `struct` or `protocol` declaration"
case .notAnyObject: "Must inherit `AnyOject`"
case .notClassOrProtocol: "Must be `class` or `protocol` declaration"
case .multipleBindings: "Use single variable"
case .tupleBindings: "Use single variable"
}
}
}
33 changes: 32 additions & 1 deletion Tests/VACopyWithTests/VACopyWithTests+Properties.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ extension VACopyWithTests {
let (a, b): (Int, Int)
}
""",
diagnostics: [DiagnosticSpec(message: VACopyWithMacroError.multipleBindings.description, line: 1, column: 1)],
diagnostics: [DiagnosticSpec(message: VACopyWithMacroError.tupleBindings.description, line: 1, column: 1)],
macros: testMacros
)
}
Expand Down Expand Up @@ -355,5 +355,36 @@ extension VACopyWithTests {
macros: testMacros
)
}

func test_struct_propertes_stored_multiple_bindings() throws {
assertMacroExpansion(
"""
@CopyWith
struct SomeStruct {
let a, b, c: Int
}
""",
expandedSource: """
struct SomeStruct {
let a, b, c: Int
}
extension SomeStruct {
func copyWith(
a: Int? = nil,
b: Int? = nil,
c: Int? = nil
) -> SomeStruct {
SomeStruct(
a: a ?? self.a,
b: b ?? self.b,
c: c ?? self.c
)
}
}
""",
macros: testMacros
)
}
}
#endif

0 comments on commit 977e31c

Please sign in to comment.