Skip to content

Commit 043b79d

Browse files
committed
Add attributes, isLazy, and keyword to Property
1 parent 6f6c5dc commit 043b79d

File tree

3 files changed

+58
-11
lines changed

3 files changed

+58
-11
lines changed

Sources/MacroToolkit/DeclGroupProtocol.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ extension DeclGroupProtocol {
3838
public var properties: [Property] {
3939
members.compactMap(\.asVariable).flatMap { variable in
4040
var bindings = variable._syntax.bindings.flatMap { binding in
41-
Property.properties(from: binding)
41+
Property.properties(from: binding, in: variable)
4242
}
4343
// For the declaration `var a, b: Int` where `a` doesn't have an annotation,
4444
// `a` gets given the type of `b` (`Int`). To implement this, we 'drag' the

Sources/MacroToolkit/Property.swift

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
import SwiftSyntax
22

3+
// TODO: Handle all modifiers
4+
// TODO: Make keyword enum-typed instead of stringly-typed
5+
// TODO: Improve attribute API, perhaps with a way to ignore attributes in compiler
6+
// directive control flow blocks.
7+
// TODO: Implement a way for devs to easily verify the usage of their macros, e.g. not
8+
// attached to the same decl twice, only attached to static vars, etc.
39
/// A property of a declaration group such as a `struct`.
410
public struct Property {
511
public var _syntax: TokenSyntax
12+
public var attributes: [AttributeListElement]
13+
public var isLazy: Bool
14+
public var keyword: String
615
public var identifier: String
716
public var type: Type?
817
public var initialValue: Expr?
@@ -20,7 +29,9 @@ public struct Property {
2029
getter == nil
2130
}
2231

23-
static func properties(from binding: PatternBindingSyntax) -> [Property] {
32+
static func properties(from binding: PatternBindingSyntax, in decl: Variable)
33+
-> [Property]
34+
{
2435
let accessors: [AccessorDeclSyntax] = switch binding.accessorBlock?.accessors {
2536
case .accessors(let block):
2637
Array(block)
@@ -29,19 +40,30 @@ public struct Property {
2940
case .none:
3041
[]
3142
}
43+
let attributes: [AttributeListElement] = if decl.bindings.count == 1 {
44+
decl.attributes
45+
} else {
46+
[]
47+
}
3248
return properties(
3349
pattern: binding.pattern,
3450
initialValue: (binding.initializer?.value).map(Expr.init),
3551
type: (binding.typeAnnotation?.type).map(Type.init),
36-
accessors: accessors
52+
accessors: accessors,
53+
attributes: attributes,
54+
keyword: decl._syntax.bindingSpecifier.text,
55+
isLazy: decl._syntax.modifiers.contains { $0.name.text == "lazy" }
3756
)
3857
}
3958

4059
private static func properties(
4160
pattern: PatternSyntax,
4261
initialValue: Expr?,
4362
type: Type?,
44-
accessors: [AccessorDeclSyntax]
63+
accessors: [AccessorDeclSyntax],
64+
attributes: [AttributeListElement],
65+
keyword: String,
66+
isLazy: Bool
4567
) -> [Property] {
4668
switch pattern.asProtocol(PatternSyntaxProtocol.self) {
4769
case let pattern as IdentifierPatternSyntax:
@@ -67,6 +89,9 @@ public struct Property {
6789
return [
6890
Property(
6991
_syntax: pattern.identifier,
92+
attributes: attributes,
93+
isLazy: isLazy,
94+
keyword: keyword,
7095
identifier: pattern.identifier.text,
7196
type: type,
7297
initialValue: initialValue,
@@ -117,10 +142,10 @@ public struct Property {
117142
tupleType?.elements[index]
118143
}
119144

120-
// Tuple bindings can't have accessors
145+
// Tuple bindings can't have accessors or attributes (i.e. property wrappers or macros)
121146
return properties(
122147
pattern: element.pattern, initialValue: initialValue, type: type,
123-
accessors: []
148+
accessors: [], attributes: [], keyword: keyword, isLazy: isLazy
124149
)
125150
}
126151
case _ as WildcardPatternSyntax:

Tests/MacroToolkitTests/MacroToolkitTests.swift

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -446,13 +446,18 @@ final class MacroToolkitTests: XCTestCase {
446446
let decl: DeclSyntax = """
447447
struct MyStruct {
448448
var a: String, b: Int = 2
449+
@MyMacro
449450
var c, d: Int
450-
var e: Float
451+
var e: Float {
452+
1.0
453+
}
454+
@MyPropertyWrapper
451455
var f = 2
452-
var g = ""
456+
let g = ""
457+
@MyMacro
453458
var (h, i) = (1.0, [])
454459
var ((j, (k, l)), m): ((Int, (String, Float)), Int) = ((1, ("abc", 2)), 3)
455-
var n = [1, 2.5]
460+
lazy var n = [1, 2.5]
456461
}
457462
"""
458463

@@ -470,50 +475,66 @@ final class MacroToolkitTests: XCTestCase {
470475
XCTAssertEqual(a.identifier, "a")
471476
XCTAssertEqual(a.type?.description, "String")
472477
XCTAssertEqual(a.initialValue.debugDescription, "nil")
478+
XCTAssertEqual(a.isStored, true)
479+
XCTAssertEqual(a.isLazy, false)
480+
XCTAssertEqual(a.attributes.count, 0)
481+
XCTAssertEqual(a.keyword, "var")
473482
// Assignment in a multi-binding declaration.
474483
let b = declGroup.properties[1]
475484
XCTAssertEqual(b.identifier, "b")
476485
XCTAssertEqual(b.type?.description, "Int")
477486
XCTAssertEqual(b.initialValue?.asIntegerLiteral?.value, 2)
478487

479488
// The type of `c` should be inferred to be the same as `d` (`Int`), since `c`
480-
// doesn't have an annotation.
489+
// doesn't have an annotation. The attribute should be ignored as it isn't
490+
// attached to either of the properties, just the declaration of both together.
481491
let c = declGroup.properties[2]
482492
XCTAssertEqual(c.identifier, "c")
483493
XCTAssertEqual(c.type?.description, "Int")
484494
XCTAssertEqual(c.initialValue.debugDescription, "nil")
495+
XCTAssertEqual(c.attributes.count, 0)
485496
// Simple annotated binding in a multi-binding declaration.
486497
let d = declGroup.properties[3]
487498
XCTAssertEqual(d.identifier, "d")
488499
XCTAssertEqual(d.type?.description, "Int")
489500
XCTAssertEqual(d.initialValue.debugDescription, "nil")
501+
XCTAssertEqual(d.attributes.count, 0)
490502

491503
// Simple annotated binding in a single-binding declaration.
492504
let e = declGroup.properties[4]
493505
XCTAssertEqual(e.identifier, "e")
494506
XCTAssertEqual(e.type?.description, "Float")
495507
XCTAssertEqual(e.initialValue.debugDescription, "nil")
508+
XCTAssertEqual(e.isStored, false)
509+
XCTAssert(e.getter != nil)
510+
XCTAssert(e.setter == nil)
496511

497512
// Inferring a type from a literal.
498513
let f = declGroup.properties[5]
499514
XCTAssertEqual(f.identifier, "f")
500515
XCTAssertEqual(f.type?.description, "Int")
501516
XCTAssertEqual(f.initialValue?.asIntegerLiteral?.value, 2)
517+
XCTAssertEqual(f.attributes.count, 1)
502518
// Same as `f` but with a string literal.
503519
let g = declGroup.properties[6]
504520
XCTAssertEqual(g.identifier, "g")
505521
XCTAssertEqual(g.type?.description, "String")
506522
XCTAssertEqual(g.initialValue?.asStringLiteral?.value, "")
523+
XCTAssertEqual(g.keyword, "let")
507524

508-
// Tuple binding with types inferred from literals.
525+
// Tuple binding with types inferred from literals. The attribute should be
526+
// ignored as it isn't attached to either of the properties, just the declaration
527+
// of both together.
509528
let h = declGroup.properties[7]
510529
XCTAssertEqual(h.identifier, "h")
511530
XCTAssertEqual(h.type?.description, "Double")
512531
XCTAssertEqual(h.initialValue?.asFloatLiteral?.value, 1.0)
532+
XCTAssertEqual(h.attributes.count, 0)
513533
let i = declGroup.properties[8]
514534
XCTAssertEqual(i.identifier, "i")
515535
XCTAssertEqual(i.type?.description, "Array<Any>")
516536
XCTAssertEqual(i.initialValue?._syntax.description, "[]")
537+
XCTAssertEqual(i.attributes.count, 0)
517538

518539
// A horrible nested annotated tuple binding with a literal initial value.
519540
let j = declGroup.properties[9]
@@ -538,5 +559,6 @@ final class MacroToolkitTests: XCTestCase {
538559
XCTAssertEqual(n.identifier, "n")
539560
XCTAssertEqual(n.type?.description, "Array<Double>")
540561
XCTAssertEqual(n.initialValue?._syntax.description, "[1, 2.5]")
562+
XCTAssertEqual(n.isLazy, true)
541563
}
542564
}

0 commit comments

Comments
 (0)