Skip to content

Commit

Permalink
Update
Browse files Browse the repository at this point in the history
  • Loading branch information
muukii committed Feb 4, 2025
1 parent 614eabe commit 9fcce3b
Show file tree
Hide file tree
Showing 7 changed files with 270 additions and 79 deletions.
34 changes: 34 additions & 0 deletions Sources/StructTransaction/CopyOnWrite.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,37 @@ final class ReferenceEdgeStorage<Value>: @unchecked Sendable {
}

}

#if DEBUG
private struct Before {

var value: Int

}

private struct After {

var value: Int {
get {
_cow_value.value
}
set {
if !isKnownUniquelyReferenced(&_cow_value) {
_cow_value = .init(_cow_value.value)
} else {
_cow_value.value = newValue
}
}
_modify {
if !isKnownUniquelyReferenced(&_cow_value) {
_cow_value = .init(_cow_value.value)
}
yield &_cow_value.value
}
}

private var _cow_value: ReferenceEdgeStorage<Int>

}

#endif
8 changes: 7 additions & 1 deletion Sources/StructTransaction/Source.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
)
public macro Tracking() = #externalMacro(module: "StructTransactionMacros", type: "TrackingMacro")

@attached(
accessor,
names: named(willSet)
)
public macro TrackingIgnored() = #externalMacro(module: "StructTransactionMacros", type: "TrackingIgnoredMacro")

@attached(
accessor,
names: named(init), named(get), named(set), named(_modify)
Expand Down Expand Up @@ -71,7 +77,7 @@ import Observation
@available(macOS 14.0, iOS 17.0, tvOS 15.0, watchOS 8.0, *)
@Observable
class Hoge {

let stored: Int

var stored_2: Int
Expand Down
1 change: 1 addition & 0 deletions Sources/StructTransactionMacros/Plugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ struct Plugin: CompilerPlugin {
let providingMacros: [Macro.Type] = [
TrackingMacro.self,
TrackingPropertyMacro.self,
TrackingIgnoredMacro.self,
]
}
15 changes: 15 additions & 0 deletions Sources/StructTransactionMacros/TrackingIgnoredMacro.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import SwiftCompilerPlugin
import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros

public struct TrackingIgnoredMacro: Macro {

}

extension TrackingIgnoredMacro: AccessorMacro {
public static func expansion(of node: AttributeSyntax, providingAccessorsOf declaration: some DeclSyntaxProtocol, in context: some MacroExpansionContext) throws -> [AccessorDeclSyntax] {
return []
}
}

128 changes: 69 additions & 59 deletions Sources/StructTransactionMacros/TrackingPropertyMacro.swift
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@

import SwiftCompilerPlugin
import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros

public struct TrackingPropertyMacro {

public enum Error: Swift.Error {

}
}

Expand All @@ -17,30 +16,43 @@ extension TrackingPropertyMacro: PeerMacro {
providingPeersOf declaration: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {



guard let variableDecl = declaration.as(VariableDeclSyntax.self) else {
return []
}

let renameRewriter = RenameIdentifierRewriter()
let modifierRewriter = MakePrivateRewriter()


var newMembers: [DeclSyntax] = []


let ignoreMacroAttached = variableDecl.attributes.contains {
switch $0 {
case .attribute(let attribute):
return attribute.attributeName.description == "TrackingIgnored"
case .ifConfigDecl:
return false
}
}

guard !ignoreMacroAttached else {
return []
}

for binding in variableDecl.bindings {
if binding.accessorBlock != nil {
// skip computed properties
continue
}

let backingStorageDecl = modifierRewriter.visit(
renameRewriter.visit(variableDecl.trimmed)
)

newMembers.append(backingStorageDecl)
}


var _variableDecl = variableDecl
_variableDecl.attributes = [.init(.init(stringLiteral: "@TrackingIgnored"))]

_variableDecl = _variableDecl
.renamingIdentifier(with: "_backing_")
.withPrivateModifier()


newMembers.append(_variableDecl.as(DeclSyntax.self)!)

return newMembers
}
}
Expand All @@ -51,20 +63,20 @@ extension TrackingPropertyMacro: AccessorMacro {
providingAccessorsOf declaration: some SwiftSyntax.DeclSyntaxProtocol,
in context: some SwiftSyntaxMacros.MacroExpansionContext
) throws -> [SwiftSyntax.AccessorDeclSyntax] {

guard let variableDecl = declaration.as(VariableDeclSyntax.self) else {
return []
}

// `@TrackingProperty` は単一のプロパティにのみ適用可能

guard let binding = variableDecl.bindings.first,
let identifierPattern = binding.pattern.as(IdentifierPatternSyntax.self) else {
let identifierPattern = binding.pattern.as(IdentifierPatternSyntax.self)
else {
return []
}

let propertyName = identifierPattern.identifier.text
let backingName = "_backing_" + propertyName

let initAccessor = AccessorDeclSyntax(
"""
@storageRestrictions(initializes: \(raw: backingName))
Expand All @@ -73,78 +85,76 @@ extension TrackingPropertyMacro: AccessorMacro {
}
"""
)

let getAccessor = AccessorDeclSyntax(
"""
get { \(raw: backingName) }
"""
)

let setAccessor = AccessorDeclSyntax(
"""
set { \(raw: backingName) = newValue }
"""
)

let modifyAccessor = AccessorDeclSyntax(
"""
_modify {
yield &\(raw: backingName)
}
"""
)
if binding.initializer == nil {

if binding.initializer == nil {
return [
initAccessor,
getAccessor,
setAccessor,
modifyAccessor
modifyAccessor,
]
} else {
return [
getAccessor,
setAccessor,
modifyAccessor
modifyAccessor,
]
}

}


}

extension VariableDeclSyntax {
func renamingIdentifier(with newName: String) -> VariableDeclSyntax {
let newBindings = self.bindings.map { binding -> PatternBindingSyntax in

if let identifierPattern = binding.pattern.as(IdentifierPatternSyntax.self) {

let propertyName = identifierPattern.identifier.text

let newIdentifierPattern = identifierPattern.with(
\.identifier, "\(raw: newName)\(raw: propertyName)")
return binding.with(\.pattern, .init(newIdentifierPattern))
}
return binding
}

final class RenameIdentifierRewriter: SyntaxRewriter {

init() {}

override func visit(_ node: IdentifierPatternSyntax) -> PatternSyntax {

let propertyName = node.identifier.text
let newIdentifier = IdentifierPatternSyntax.init(identifier: "_backing_\(raw: propertyName)")

return super.visit(newIdentifier)

return self.with(\.bindings, .init(newBindings))
}
}

final class MakePrivateRewriter: SyntaxRewriter {

init() {}

// override func visit(_ node: VariableDeclSyntax) -> DeclSyntax {
// return super.visit(node.trimmed(matching: { $0.isNewline }))
// }

override func visit(_ node: DeclModifierListSyntax) -> DeclModifierListSyntax {
if node.contains(where: { $0.name.tokenKind == .keyword(.private) }) {
return super.visit(node)
extension VariableDeclSyntax {
func withPrivateModifier() -> VariableDeclSyntax {

let privateModifier = DeclModifierSyntax.init(
name: .keyword(.private), trailingTrivia: .spaces(1))

var modifiers = self.modifiers
if modifiers.contains(where: { $0.name.tokenKind == .keyword(.private) }) {
return self
}

var modified = node
modified.append(.init(name: .keyword(.private), trailingTrivia: .spaces(1)))

return super.visit(modified)
modifiers.append(privateModifier)
return self.with(\.modifiers, modifiers)
}
}
27 changes: 8 additions & 19 deletions Tests/StructTransactionMacroTests/TrackingMacroTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,42 +42,31 @@ final class TrackingMacroTests: XCTestCase {
}
"""
} expansion: {

"""
struct MyState {
@TrackingProperty
var name: String
private var stored_0: Int = 18
@TrackingProperty
var stored_1: String
@TrackingProperty
let stored_2: Int = 0
var age: Int { 0 }
var age2: Int {
get { 0 }
set { }
}
@Clamp
@TrackingProperty
var height: Int
func compute() {
}
}
"""
}

Expand Down
Loading

0 comments on commit 9fcce3b

Please sign in to comment.