Skip to content

Commit 2381d39

Browse files
authored
[Observation] forward line numbers to didSet and other property observers from their spelled location to the code-generated duplicated storage location (#80661)
* [Observation] forward line numbers to didSet and other property observers from their spelled location to the code-generated duplicated storage location * Only emit source locations for the prolog of the body and emit an empty location for the epilog * Add missing attribute member * Use the fixed version of the locations since swift-syntax 6.0.1 reports sub-nodes correctly in all contexts
1 parent ad82261 commit 2381d39

File tree

1 file changed

+82
-12
lines changed

1 file changed

+82
-12
lines changed

lib/Macros/Sources/ObservationMacros/ObservableMacro.swift

Lines changed: 82 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,15 @@ public struct ObservableMacro {
109109
trailingTrivia: .space
110110
)
111111
}
112+
113+
static var trackedAttribute: AttributeSyntax {
114+
AttributeSyntax(
115+
leadingTrivia: .space,
116+
atSign: .atSignToken(),
117+
attributeName: IdentifierTypeSyntax(name: .identifier(trackedMacroName)),
118+
trailingTrivia: .space
119+
)
120+
}
112121
}
113122

114123
struct ObservationDiagnostic: DiagnosticMessage {
@@ -142,8 +151,13 @@ extension DiagnosticsError {
142151
}
143152
}
144153

154+
155+
struct LocalMacroExpansionContext<Context: MacroExpansionContext> {
156+
var context: Context
157+
}
158+
145159
extension DeclModifierListSyntax {
146-
func privatePrefixed(_ prefix: String) -> DeclModifierListSyntax {
160+
func privatePrefixed(_ prefix: String, in context: LocalMacroExpansionContext<some MacroExpansionContext>) -> DeclModifierListSyntax {
147161
let modifier: DeclModifierSyntax = DeclModifierSyntax(name: "private", trailingTrivia: .space)
148162
return [modifier] + filter {
149163
switch $0.name.tokenKind {
@@ -170,7 +184,7 @@ extension DeclModifierListSyntax {
170184
}
171185

172186
extension TokenSyntax {
173-
func privatePrefixed(_ prefix: String) -> TokenSyntax {
187+
func privatePrefixed(_ prefix: String, in context: LocalMacroExpansionContext<some MacroExpansionContext>) -> TokenSyntax {
174188
switch tokenKind {
175189
case .identifier(let identifier):
176190
return TokenSyntax(.identifier(prefix + identifier), leadingTrivia: leadingTrivia, trailingTrivia: trailingTrivia, presence: presence)
@@ -180,8 +194,58 @@ extension TokenSyntax {
180194
}
181195
}
182196

197+
extension CodeBlockSyntax {
198+
func locationAnnotated(in context: LocalMacroExpansionContext<some MacroExpansionContext>) -> CodeBlockSyntax {
199+
guard let firstStatement = statements.first, let loc = context.context.location(of: firstStatement) else {
200+
return self
201+
}
202+
203+
return CodeBlockSyntax(
204+
leadingTrivia: leadingTrivia,
205+
leftBrace: leftBrace,
206+
statements: CodeBlockItemListSyntax {
207+
"#sourceLocation(file: \(loc.file), line: \(loc.line))"
208+
statements
209+
"#sourceLocation()"
210+
},
211+
rightBrace: rightBrace,
212+
trailingTrivia: trailingTrivia
213+
)
214+
}
215+
}
216+
217+
218+
extension AccessorDeclSyntax {
219+
func locationAnnotated(in context: LocalMacroExpansionContext<some MacroExpansionContext>) -> AccessorDeclSyntax {
220+
return AccessorDeclSyntax(
221+
leadingTrivia: leadingTrivia,
222+
attributes: attributes,
223+
modifier: modifier,
224+
accessorSpecifier: accessorSpecifier,
225+
parameters: parameters,
226+
effectSpecifiers: effectSpecifiers,
227+
body: body?.locationAnnotated(in: context),
228+
trailingTrivia: trailingTrivia
229+
)
230+
}
231+
}
232+
233+
extension AccessorBlockSyntax {
234+
func locationAnnotated(in context: LocalMacroExpansionContext<some MacroExpansionContext>) -> AccessorBlockSyntax {
235+
switch accessors {
236+
case .accessors(let accessorList):
237+
let remapped = AccessorDeclListSyntax {
238+
accessorList.map { $0.locationAnnotated(in: context) }
239+
}
240+
return AccessorBlockSyntax(accessors: .accessors(remapped))
241+
case .getter(let codeBlockList):
242+
return AccessorBlockSyntax(accessors: .getter(codeBlockList))
243+
}
244+
}
245+
}
246+
183247
extension PatternBindingListSyntax {
184-
func privatePrefixed(_ prefix: String) -> PatternBindingListSyntax {
248+
func privatePrefixed(_ prefix: String, in context: LocalMacroExpansionContext<some MacroExpansionContext>) -> PatternBindingListSyntax {
185249
var bindings = self.map { $0 }
186250
for index in 0..<bindings.count {
187251
let binding = bindings[index]
@@ -190,12 +254,12 @@ extension PatternBindingListSyntax {
190254
leadingTrivia: binding.leadingTrivia,
191255
pattern: IdentifierPatternSyntax(
192256
leadingTrivia: identifier.leadingTrivia,
193-
identifier: identifier.identifier.privatePrefixed(prefix),
257+
identifier: identifier.identifier.privatePrefixed(prefix, in: context),
194258
trailingTrivia: identifier.trailingTrivia
195259
),
196260
typeAnnotation: binding.typeAnnotation,
197261
initializer: binding.initializer,
198-
accessorBlock: binding.accessorBlock,
262+
accessorBlock: binding.accessorBlock?.locationAnnotated(in: context),
199263
trailingComma: binding.trailingComma,
200264
trailingTrivia: binding.trailingTrivia)
201265

@@ -207,14 +271,20 @@ extension PatternBindingListSyntax {
207271
}
208272

209273
extension VariableDeclSyntax {
210-
func privatePrefixed(_ prefix: String, addingAttribute attribute: AttributeSyntax) -> VariableDeclSyntax {
211-
let newAttributes = attributes + [.attribute(attribute)]
274+
func privatePrefixed(_ prefix: String, addingAttribute attribute: AttributeSyntax, removingAttribute toRemove: AttributeSyntax, in context: LocalMacroExpansionContext<some MacroExpansionContext>) -> VariableDeclSyntax {
275+
let newAttributes = attributes.filter { attribute in
276+
switch attribute {
277+
case .attribute(let attr):
278+
attr.attributeName.identifier != toRemove.attributeName.identifier
279+
default: true
280+
}
281+
} + [.attribute(attribute)]
212282
return VariableDeclSyntax(
213283
leadingTrivia: leadingTrivia,
214284
attributes: newAttributes,
215-
modifiers: modifiers.privatePrefixed(prefix),
285+
modifiers: modifiers.privatePrefixed(prefix, in: context),
216286
bindingSpecifier: TokenSyntax(bindingSpecifier.tokenKind, leadingTrivia: .space, trailingTrivia: .space, presence: .present),
217-
bindings: bindings.privatePrefixed(prefix),
287+
bindings: bindings.privatePrefixed(prefix, in: context),
218288
trailingTrivia: trailingTrivia
219289
)
220290
}
@@ -417,12 +487,12 @@ extension ObservationTrackedMacro: PeerMacro {
417487
return []
418488
}
419489

420-
if property.hasMacroApplication(ObservableMacro.ignoredMacroName) ||
421-
property.hasMacroApplication(ObservableMacro.trackedMacroName) {
490+
if property.hasMacroApplication(ObservableMacro.ignoredMacroName) {
422491
return []
423492
}
424493

425-
let storage = DeclSyntax(property.privatePrefixed("_", addingAttribute: ObservableMacro.ignoredAttribute))
494+
let localContext = LocalMacroExpansionContext(context: context)
495+
let storage = DeclSyntax(property.privatePrefixed("_", addingAttribute: ObservableMacro.ignoredAttribute, removingAttribute: ObservableMacro.trackedAttribute, in: localContext))
426496
return [storage]
427497
}
428498
}

0 commit comments

Comments
 (0)