Skip to content

Commit 0d1e0a2

Browse files
committed
[Macros] Add accessor declaration macros, to add accessors to a stored property
1 parent 18f57b6 commit 0d1e0a2

File tree

4 files changed

+131
-1
lines changed

4 files changed

+131
-1
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//
2+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
3+
// Licensed under Apache License v2.0 with Runtime Library Exception
4+
//
5+
// See https://swift.org/LICENSE.txt for license information
6+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
import SwiftSyntax
11+
12+
/// Describes a macro that adds accessors to a given declaration.
13+
public protocol AccessorDeclarationMacro: DeclarationMacro {
14+
/// Expand a macro that's expressed as a custom attribute attached to
15+
/// the given declaration. The result is a set of accessors for the
16+
/// declaration.
17+
static func expansion(
18+
of node: CustomAttributeSyntax,
19+
attachedTo declaration: DeclSyntax,
20+
in context: inout MacroExpansionContext
21+
) throws -> [AccessorDeclSyntax]
22+
}

Sources/_SwiftSyntaxMacros/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# See http://swift.org/CONTRIBUTORS.txt for Swift project authors
88

99
add_swift_host_library(_SwiftSyntaxMacros
10+
AccessorDeclarationMacro.swift
1011
DeclarationMacro.swift
1112
ExpressionMacro.swift
1213
FreestandingDeclarationMacro.swift

Sources/_SwiftSyntaxMacros/MacroSystem.swift

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,9 @@ class MacroApplication: SyntaxRewriter {
106106
return true
107107
}
108108

109-
return !(macro is PeerDeclarationMacro.Type || macro is MemberDeclarationMacro.Type)
109+
return !(macro is PeerDeclarationMacro.Type ||
110+
macro is MemberDeclarationMacro.Type ||
111+
macro is AccessorDeclarationMacro.Type)
110112
}
111113

112114
if newAttributes.isEmpty {
@@ -254,6 +256,52 @@ class MacroApplication: SyntaxRewriter {
254256
override func visit(_ node: ExtensionDeclSyntax) -> DeclSyntax {
255257
return visit(declGroup: node)
256258
}
259+
260+
// Properties
261+
override func visit(_ node: VariableDeclSyntax) -> DeclSyntax {
262+
let visitedNode = super.visit(node)
263+
guard let visitedVarDecl = visitedNode.as(VariableDeclSyntax.self) else {
264+
return visitedNode
265+
}
266+
267+
guard let binding = visitedVarDecl.bindings.first,
268+
visitedVarDecl.bindings.count == 1 else {
269+
return DeclSyntax(node)
270+
}
271+
272+
var accessors: [AccessorDeclSyntax] = []
273+
274+
let accessorMacroAttributes = getMacroAttributes(attachedTo: DeclSyntax(node), ofType: AccessorDeclarationMacro.Type.self)
275+
for (accessorAttr, accessorMacro) in accessorMacroAttributes {
276+
do {
277+
let newAccessors = try accessorMacro.expansion(
278+
of: accessorAttr,
279+
attachedTo: DeclSyntax(visitedNode),
280+
in: &context
281+
)
282+
283+
accessors.append(contentsOf: newAccessors)
284+
} catch {
285+
// FIXME: record the error
286+
}
287+
}
288+
289+
if accessors.isEmpty {
290+
return visitedNode
291+
}
292+
293+
return DeclSyntax(
294+
visitedVarDecl.withBindings(
295+
visitedVarDecl.bindings.replacing(childAt: 0, with: binding.withAccessor(.accessors(
296+
.init(
297+
leftBrace: .leftBraceToken(leadingTrivia: .space),
298+
accessors: .init(accessors),
299+
rightBrace: .rightBraceToken(leadingTrivia: .newline))
300+
))))
301+
)
302+
}
303+
304+
// Subscripts
257305
}
258306

259307
extension MacroApplication {

Tests/SwiftSyntaxMacrosTest/MacroSystemTests.swift

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,44 @@ struct DefineBitwidthNumberedStructsMacro: FreestandingDeclarationMacro {
208208
}
209209
}
210210

211+
public struct PropertyWrapper: AccessorDeclarationMacro {
212+
public static func expansion(
213+
of node: CustomAttributeSyntax,
214+
attachedTo declaration: DeclSyntax,
215+
in context: inout MacroExpansionContext
216+
) throws -> [AccessorDeclSyntax] {
217+
guard let varDecl = declaration.as(VariableDeclSyntax.self),
218+
let binding = varDecl.bindings.first,
219+
let identifier = binding.pattern.as(IdentifierPatternSyntax.self)?.identifier,
220+
let type = binding.typeAnnotation?.type,
221+
binding.accessor == nil else {
222+
return []
223+
}
224+
225+
guard let wrapperTypeNameExpr = node.argumentList?.first?.expression,
226+
let stringLiteral = wrapperTypeNameExpr.as(StringLiteralExprSyntax.self),
227+
stringLiteral.segments.count == 1,
228+
case let .stringSegment(wrapperTypeNameSegment)? = stringLiteral.segments.first else {
229+
return []
230+
}
231+
232+
return [
233+
"""
234+
235+
get {
236+
_\(identifier).wrappedValue
237+
}
238+
""",
239+
"""
240+
241+
set {
242+
_\(identifier).wrappedValue = newValue
243+
}
244+
"""
245+
]
246+
}
247+
}
248+
211249
public struct AddCompletionHandler: PeerDeclarationMacro {
212250
public static func expansion(
213251
of node: CustomAttributeSyntax,
@@ -395,6 +433,7 @@ public let testMacros: [String: Macro.Type] = [
395433
"stringify": StringifyMacro.self,
396434
"myError": ErrorMacro.self,
397435
"bitwidthNumberedStructs": DefineBitwidthNumberedStructsMacro.self,
436+
"wrapProperty" : PropertyWrapper.self,
398437
"addCompletionHandler": AddCompletionHandler.self,
399438
"addBackingStorage": AddBackingStorage.self,
400439
]
@@ -515,6 +554,26 @@ final class MacroSystemTests: XCTestCase {
515554
)
516555
}
517556

557+
func testPropertyWrapper() {
558+
AssertMacroExpansion(
559+
macros: testMacros,
560+
"""
561+
@wrapProperty("MyWrapperType")
562+
var x: Int
563+
""",
564+
"""
565+
566+
var x: Int {
567+
get {
568+
_x.wrappedValue
569+
}
570+
set {
571+
_x.wrappedValue = newValue
572+
}
573+
}
574+
""")
575+
}
576+
518577
func testAddCompletionHandler() {
519578
AssertMacroExpansion(
520579
macros: testMacros,

0 commit comments

Comments
 (0)