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
12 changes: 12 additions & 0 deletions Sources/StateStruct/Source.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ struct HashableState: Hashable {
var stored_1: Int = 18

var stored_2: Int

var stored_3: Int = 10 {
didSet {
print("stored_3 did set")
}
}

var computed_1: Int {
stored_1
Expand Down Expand Up @@ -108,6 +114,12 @@ struct HashableState: Hashable {
var stored_2: Int

var stored_3: Int?

var stored_4: Int = 10 {
didSet {
print("stored_4 did set")
}
}

var name = ""

Expand Down
93 changes: 88 additions & 5 deletions Sources/StateStructMacros/COWTrackingPropertyMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ extension COWTrackingPropertyMacro: PeerMacro {
return "_BackingStorage<\(type.trimmed)>"
})

// add init
_variableDecl = _variableDecl.with(
\.bindings,
.init(
Expand All @@ -68,9 +69,23 @@ extension COWTrackingPropertyMacro: PeerMacro {
return "_BackingStorage<\(type.trimmed)>"
})
.modifyingInit({ initializer in
return .init(value: "_BackingStorage.init(\(initializer.value))" as ExprSyntax)
return .init(value: "_BackingStorage.init(\(initializer.trimmed.value))" as ExprSyntax)
})
}

do {

// remove accessors
_variableDecl = _variableDecl.with(
\.bindings,
.init(
_variableDecl.bindings.map { binding in
binding.with(\.accessorBlock, nil)
}
)
)

}

newMembers.append(DeclSyntax(_variableDecl))

Expand All @@ -94,10 +109,11 @@ extension COWTrackingPropertyMacro: AccessorMacro {
else {
return []
}

let isConstant = variableDecl.bindingSpecifier.tokenKind == .keyword(.let)
let propertyName = identifierPattern.identifier.text
let backingName = "_backing_" + propertyName
let hasWillSet = variableDecl.willSetBlock != nil

let initAccessor = AccessorDeclSyntax(
"""
Expand All @@ -122,17 +138,23 @@ extension COWTrackingPropertyMacro: AccessorMacro {

let setAccessor = AccessorDeclSyntax(
"""
set {
set {

// willset
\(variableDecl.makeWillSetDoBlock())

(\(raw: backingName).value as? TrackingObject)?._tracking_context.path = _tracking_context.path?.pushed(.init("\(raw: propertyName)"))
_Tracking._tracking_modifyStorage {
$0.accessorSet(path: _tracking_context.path?.pushed(.init("\(raw: propertyName)")))
}
if !isKnownUniquelyReferenced(&\(raw: backingName)) {
\(raw: backingName) = .init(newValue)
} else {
\(raw: backingName).value = newValue
\(raw: backingName).value = newValue
}

// didSet
\(variableDecl.makeDidSetDoBlock())
}
"""
)
Expand All @@ -148,6 +170,9 @@ extension COWTrackingPropertyMacro: AccessorMacro {
\(raw: backingName) = .init(\(raw: backingName).value)
}
yield &\(raw: backingName).value

// didSet
\(variableDecl.makeDidSetDoBlock())
}
"""
)
Expand All @@ -162,7 +187,10 @@ extension COWTrackingPropertyMacro: AccessorMacro {

if !isConstant {
accessors.append(setAccessor)
accessors.append(modifyAccessor)

if hasWillSet == false {
accessors.append(modifyAccessor)
}
}

return accessors
Expand Down Expand Up @@ -244,3 +272,58 @@ extension VariableDeclSyntax {
}

}

extension VariableDeclSyntax {

func makeDidSetDoBlock() -> DoStmtSyntax {
guard let didSetBlock = self.didSetBlock else {
return .init(body: "{}")
}

return .init(body: didSetBlock)
}

func makeWillSetDoBlock() -> DoStmtSyntax {
guard let willSetBlock = self.willSetBlock else {
return .init(body: "{}")
}

return .init(body: willSetBlock)
}

var didSetBlock: CodeBlockSyntax? {
for binding in self.bindings {
if let accessorBlock = binding.accessorBlock {
switch accessorBlock.accessors {
case .accessors(let accessors):
for accessor in accessors {
if accessor.accessorSpecifier.tokenKind == .keyword(.didSet) {
return accessor.body
}
}
case .getter:
return nil
}
}
}
return nil
}

var willSetBlock: CodeBlockSyntax? {
for binding in self.bindings {
if let accessorBlock = binding.accessorBlock {
switch accessorBlock.accessors {
case .accessors(let accessors):
for accessor in accessors {
if accessor.accessorSpecifier.tokenKind == .keyword(.willSet) {
return accessor.body
}
}
case .getter:
return nil
}
}
}
return nil
}
}
18 changes: 16 additions & 2 deletions Sources/StateStructMacros/TrackingMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,22 @@ extension TrackingMacro: MemberAttributeMacro {

// to ignore computed properties
for binding in variableDecl.bindings {
if binding.accessorBlock != nil {
return []
if let accessorBlock = binding.accessorBlock {
// Check if this is a computed property (has a 'get' accessor)
// If it has only property observers like didSet/willSet, it's still a stored property

switch accessorBlock.accessors {
case .accessors(let accessors):
let hasGetter = accessors.contains { syntax in
syntax.accessorSpecifier.tokenKind == .keyword(.get)
}
if hasGetter {
return []
}
continue
case .getter:
return []
}
}
}

Expand Down
Loading
Loading