Skip to content

Commit c978386

Browse files
authored
Merge pull request #84833 from jamieQ/obs-macro-ii
[Observation]: fix @observable macro producing invalid code with certain comments
2 parents 87af553 + 21b57f7 commit c978386

File tree

2 files changed

+87
-3
lines changed

2 files changed

+87
-3
lines changed

lib/Macros/Sources/ObservationMacros/ObservableMacro.swift

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -272,18 +272,33 @@ extension PatternBindingListSyntax {
272272

273273
extension VariableDeclSyntax {
274274
func privatePrefixed(_ prefix: String, addingAttribute attribute: AttributeSyntax, removingAttribute toRemove: AttributeSyntax, in context: LocalMacroExpansionContext<some MacroExpansionContext>) -> VariableDeclSyntax {
275+
var newAttribute = attribute
276+
newAttribute.leadingTrivia = .newline
277+
275278
let newAttributes = attributes.filter { attribute in
276279
switch attribute {
277280
case .attribute(let attr):
278281
attr.attributeName.identifier != toRemove.attributeName.identifier
279282
default: true
280283
}
281-
} + [.attribute(attribute)]
284+
} + [.attribute(newAttribute)]
285+
286+
var newModifiers = modifiers.privatePrefixed(prefix, in: context)
287+
let hasModifiers = !newModifiers.isEmpty
288+
if hasModifiers {
289+
newModifiers.leadingTrivia += .newline
290+
}
291+
282292
return VariableDeclSyntax(
283293
leadingTrivia: leadingTrivia,
284294
attributes: newAttributes,
285-
modifiers: modifiers.privatePrefixed(prefix, in: context),
286-
bindingSpecifier: TokenSyntax(bindingSpecifier.tokenKind, leadingTrivia: .space, trailingTrivia: .space, presence: .present),
295+
modifiers: newModifiers,
296+
bindingSpecifier: TokenSyntax(
297+
bindingSpecifier.tokenKind,
298+
leadingTrivia: hasModifiers ? .space : .newline,
299+
trailingTrivia: .space,
300+
presence: .present
301+
),
287302
bindings: bindings.privatePrefixed(prefix, in: context),
288303
trailingTrivia: trailingTrivia
289304
)
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// REQUIRES: swift_swift_parser, asserts
2+
//
3+
// UNSUPPORTED: back_deploy_concurrency
4+
// REQUIRES: concurrency
5+
// REQUIRES: observation
6+
//
7+
// RUN: %empty-directory(%t)
8+
// RUN: %empty-directory(%t-scratch)
9+
10+
// RUN: %target-swift-frontend -swift-version 5 -typecheck -plugin-path %swift-plugin-dir -I %t -dump-macro-expansions %s 2>&1 | %FileCheck %s --color
11+
12+
import Observation
13+
14+
// Test cases for comment handling with Observable macro
15+
_ = 0 // absorbs trivia so file check expectations don't leak into macro expansions
16+
17+
@available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *)
18+
@Observable
19+
final class CommentAfterGlobalActorAnnotation {
20+
@MainActor // Innocent comment
21+
internal var it = 0
22+
}
23+
24+
// CHECK-LABEL: @__swiftmacro{{.*}}CommentAfterGlobalActorAnnotation{{.*}}ObservationTracked{{.*}}.swift
25+
// CHECK: @MainActor // Innocent comment
26+
// CHECK-NEXT: @ObservationIgnored
27+
// CHECK-NEXT: private var _it = 0
28+
_ = 0
29+
30+
@available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *)
31+
@Observable
32+
final class CommentAfterAvailabilityAnnotation {
33+
@available(*, deprecated) // Innocent comment
34+
internal var it = 0
35+
}
36+
37+
// CHECK-LABEL: @__swiftmacro{{.*}}CommentAfterAvailabilityAnnotation{{.*}}ObservationTracked{{.*}}.swift
38+
// CHECK-NEXT: {{-+}}
39+
// CHECK-NEXT: @available(*, deprecated) // Innocent comment
40+
// CHECK-NEXT: @ObservationIgnored
41+
// CHECK-NEXT: private var _it = 0
42+
_ = 0
43+
44+
@available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *)
45+
@Observable
46+
final class CommentOnSameLineAsOtherAnnotation {
47+
@MainActor /* Innocent comment */ public var it = 0
48+
}
49+
50+
// CHECK-LABEL: @__swiftmacro{{.*}}CommentOnSameLineAsOtherAnnotation{{.*}}ObservationTracked{{.*}}.swift
51+
// CHECK: @MainActor /* Innocent comment */
52+
// CHECK-NEXT: @ObservationIgnored
53+
// CHECK-NEXT: private var _it = 0
54+
_ = 0
55+
56+
@available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *)
57+
@Observable
58+
final class CommentOnSameLineNoAnnotation {
59+
/* Innocent comment */ public /*1*/ final /*2*/ var /*3*/ it /*4*/ = 0
60+
}
61+
62+
// Note: seems there's some weirdness with the existing macro eating/duplicating trivia in some
63+
// cases but we'll just test for the current behavior since it doesn't seem to be covered elsewhere:
64+
65+
// CHECK-LABEL: @__swiftmacro{{.*}}CommentOnSameLineNoAnnotation{{.*}}ObservationTracked{{.*}}.swift
66+
// CHECK: /* Innocent comment */
67+
// CHECK-NEXT: @ObservationIgnored
68+
// CHECK-NEXT: private final /*2*/ var _it /*4*/ /*4*/ = 0
69+
_ = 0

0 commit comments

Comments
 (0)