Skip to content

Add support for body macros on variables and accessors#87869

Open
calda wants to merge 7 commits intoswiftlang:mainfrom
calda:cal--body-macro-computed-properties
Open

Add support for body macros on variables and accessors#87869
calda wants to merge 7 commits intoswiftlang:mainfrom
calda:cal--body-macro-computed-properties

Conversation

@calda
Copy link
Copy Markdown
Contributor

@calda calda commented Mar 14, 2026

Corresponding Swift Syntax PR: swiftlang/swift-syntax#3298


According to SE-0415, body macros should be supported on variables and accessors:

Function body macros can be applied to accessors as well, in which case they go on the accessor itself, e.g.,

var area: Double {
  @Logged get {
    return length * width
  }
}

When using the shorthand syntax for get-only properties, a function body macro can be applied to the property itself:

@Logged var area: Double {
  return length * width
}

However, both forms are currently unsupported with errors like error: 'body' macro cannot be attached to var ('area') or error: Declaration is not a type with an optional code block.

This PR adds implements supports for both of these use cases.

  • To support body macros on accessors, we only have to fix an issue where the SwiftSyntax plugin did not properly parse the AccessorDeclSyntax received via a plugin message.
  • To support body macros on computed property declarations, we have to update the compiler to pass through the VariableDeclSyntax, and then within Swift Syntax synthesize an implicit AccessorDeclSyntax to use in the expansion(of:providingBodyFor:in:) call (which needs a WithOptionalCodeBlockSyntax).

At Airbnb, we'd like to use this functionality to enable adding instrumentation to SwiftUI view bodies, like:

struct MyView: View {
  @Instrumented // Enabled by this fix :)
  var body: some View {
    Text("Hello world!")
  }
}

Fixes #75715.

Please review: @DougGregor @ahoppen

@calda calda force-pushed the cal--body-macro-computed-properties branch from d69e125 to dede638 Compare March 14, 2026 23:25
@xedin
Copy link
Copy Markdown
Contributor

xedin commented Mar 17, 2026

@swift-ci please test

@calda
Copy link
Copy Markdown
Contributor Author

calda commented Mar 17, 2026

Thanks for triggering CI @xedin, although I think you will also need to include the SwiftSyntax change when running the tests: swiftlang/swift-syntax#3298

@xedin
Copy link
Copy Markdown
Contributor

xedin commented Mar 17, 2026

swiftlang/swift-syntax#3298
@swift-ci please test

@calda
Copy link
Copy Markdown
Contributor Author

calda commented Mar 17, 2026

Ah there was a SwiftSyntax test failure that I missed, fixed over in swiftlang/swift-syntax#3298

@calda
Copy link
Copy Markdown
Contributor Author

calda commented Mar 17, 2026

The updated macro_expand_body.swift tests passed on macOS, so not sure what the issue is on Linux and Windows. Both failures were issues building the macro plugin used by the tests, but I don't see any specific error. Just in case it was related, I removed the unnecessary -enable-experimental-feature args from the macro definition build call.

@rintaro
Copy link
Copy Markdown
Member

rintaro commented Mar 20, 2026

swiftlang/swift-syntax#3298
@swift-ci please test

@calda
Copy link
Copy Markdown
Contributor Author

calda commented Mar 20, 2026

swiftlang/swift-syntax#3298 was merged 😄

@rintaro
Copy link
Copy Markdown
Member

rintaro commented Mar 20, 2026

@swift-ci please test

}
}

fn.forEachAttachedMacro(MacroRole::Body, expandMacro);
Copy link
Copy Markdown
Member

@rintaro rintaro Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interested in this case.

@BodyMacro var value: Int {
  @AnotherBodyMacro get { ... }
  set { ... }
}

I think @AnotherBodyMacro should take the precedence for get, but this is trying the @BodyMacro first and exit? And @BodyMacro should still be applied to set?

Could you remind me how it should behave for multiple body macros in general?:

@BodyMacro @AnotherBodyMacro func foo() { ... }

If both applies to the func, should get above take those two body macros as well?

Copy link
Copy Markdown
Contributor Author

@calda calda Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interested in this case.

@BodyMacro var value: Int {
  @AnotherBodyMacro get { ... }
  set { ... }
}

Good catch. Updated the PR to disallow a macro to the decl itself if it has an explicit get { ... } accessor. This was not specifically called out as supported in SE-0415, and disallowing it prevents this ambiguous case where a body macro could be applied to both the decl and accessor.

Could you remind me how it should behave for multiple body macros in general?

SE-0415 says "At most one body macro can be applied to a given function.". However, when I test applying multiple body macros to a function declaration in a sample project, there are no errors or diagnostics (one of them wins silently). Seems not-ideal, but also seems like it can be out of scope for this change given this is the existing behavior for function decls.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Body macro can’t be attached to variables

3 participants