@@ -14,7 +14,10 @@ import SwiftSyntax
14
14
import SwiftSyntaxMacros
15
15
import SwiftDiagnostics
16
16
17
- // TODO: docs
17
+ /// Macro implementing the TaskLocal functionality.
18
+ ///
19
+ /// It introduces a peer `static let $name: TaskLocal<Type>` as well as a getter
20
+ /// that assesses accesses the task local storage.
18
21
public enum TaskLocalMacro { }
19
22
20
23
extension TaskLocalMacro : PeerMacro {
@@ -26,25 +29,29 @@ extension TaskLocalMacro: PeerMacro {
26
29
guard let varDecl = try requireVar ( declaration, diagnose: false ) else {
27
30
return [ ]
28
31
}
29
- guard try requireModifier ( varDecl, . static , diagnose: false ) else {
32
+ guard try requireStaticContext ( varDecl, in : context , diagnose: false ) else {
30
33
return [ ]
31
34
}
32
35
33
36
guard let firstBinding = varDecl. bindings. first else {
34
- return [ ] // TODO: make error
37
+ throw DiagnosticsError (
38
+ syntax: declaration,
39
+ message: " '@TaskLocal' property must have declared binding " , id: . incompatibleDecl)
35
40
}
36
41
37
42
guard let name = firstBinding. pattern. as ( IdentifierPatternSyntax . self) ? . identifier else {
38
- return [ ] // TODO: make error
43
+ throw DiagnosticsError (
44
+ syntax: declaration,
45
+ message: " '@TaskLocal' property must have name " , id: . incompatibleDecl)
39
46
}
40
47
41
48
let type = firstBinding. typeAnnotation? . type
42
- let explicitType : String =
43
- if let type {
44
- " : TaskLocal< \( type. trimmed) > "
45
- } else {
46
- " "
47
- }
49
+ let explicitType : String
50
+ if let type {
51
+ explicitType = " : TaskLocal< \( type. trimmed) > "
52
+ } else {
53
+ explicitType = " "
54
+ }
48
55
49
56
let initialValue : Any
50
57
if let initializerValue = firstBinding. initializer? . value {
@@ -57,9 +64,18 @@ extension TaskLocalMacro: PeerMacro {
57
64
message: " '@TaskLocal' property must have default value, or be optional " , id: . mustBeVar)
58
65
}
59
66
67
+ // If the property is global, do not prefix the synthesised decl with 'static'
68
+ let isGlobal = context. lexicalContext. isEmpty
69
+ let staticKeyword : String
70
+ if isGlobal {
71
+ staticKeyword = " "
72
+ } else {
73
+ staticKeyword = " static "
74
+ }
75
+
60
76
return [
61
77
"""
62
- static let $ \( name) \( raw: explicitType) = TaskLocal(wrappedValue: \( raw: initialValue) )
78
+ \( raw : staticKeyword ) let $ \( name) \( raw: explicitType) = TaskLocal(wrappedValue: \( raw: initialValue) )
63
79
"""
64
80
]
65
81
}
@@ -77,7 +93,7 @@ extension TaskLocalMacro: AccessorMacro {
77
93
guard let varDecl = try requireVar ( declaration) else {
78
94
return [ ]
79
95
}
80
- try requireModifier ( varDecl, . static )
96
+ try requireStaticContext ( varDecl, in : context )
81
97
82
98
guard let firstBinding = varDecl. bindings. first else {
83
99
return [ ] // TODO: make error
@@ -107,24 +123,29 @@ private func requireVar(_ decl: some DeclSyntaxProtocol,
107
123
}
108
124
109
125
@discardableResult
110
- private func requireModifier ( _ decl: VariableDeclSyntax ,
111
- _ keyword : Keyword ,
112
- diagnose: Bool = true ) throws -> Bool {
126
+ private func requireStaticContext ( _ decl: VariableDeclSyntax ,
127
+ in context : some MacroExpansionContext ,
128
+ diagnose: Bool = true ) throws -> Bool {
113
129
let isStatic = decl. modifiers. contains { modifier in
114
- modifier. name. text == " \( keyword ) "
130
+ modifier. name. text == " \( Keyword . static ) "
115
131
}
116
132
117
- if !isStatic {
118
- if diagnose {
119
- throw DiagnosticsError (
120
- syntax: decl,
121
- message: " '@TaskLocal' can only be applied to 'static' property " , id: . mustBeStatic)
122
- } else {
123
- return false
124
- }
133
+ if isStatic {
134
+ return true
135
+ }
136
+
137
+ let isGlobal = context. lexicalContext. isEmpty
138
+ if isGlobal {
139
+ return true
125
140
}
126
141
127
- return true
142
+ if diagnose {
143
+ throw DiagnosticsError (
144
+ syntax: decl,
145
+ message: " '@TaskLocal' can only be applied to 'static' property " , id: . mustBeStatic)
146
+ }
147
+
148
+ return false
128
149
}
129
150
130
151
extension TypeSyntax {
@@ -141,8 +162,9 @@ extension TypeSyntax {
141
162
142
163
struct TaskLocalMacroDiagnostic : DiagnosticMessage {
143
164
enum ID : String {
144
- case mustBeStatic = " must be static "
145
165
case mustBeVar = " must be var "
166
+ case mustBeStatic = " must be static "
167
+ case incompatibleDecl = " incompatible declaration "
146
168
}
147
169
148
170
var message : String
0 commit comments