Skip to content

Expand macros on members guarded by an #if - #3396

Open
maxches99 wants to merge 1 commit into
swiftlang:mainfrom
maxches99:fix-3106-member-attribute-ifconfig
Open

Expand macros on members guarded by an #if#3396
maxches99 wants to merge 1 commit into
swiftlang:mainfrom
maxches99:fix-3106-member-attribute-ifconfig

Conversation

@maxches99

Copy link
Copy Markdown

MacroApplication expands the members of a declaration in visit(_: MemberBlockSyntax), but the members guarded by an #if are not stored in a MemberBlockSyntax — they live directly in the IfConfigClauseSyntax as a MemberBlockItemListSyntax, for which there is no override. Those members are therefore never routed through the member expansion, and macros attached to them are silently dropped.

This affects more than member attribute macros:

@wrapAllProperties struct S {
  #if true
  var value = 1     // never gets @Wrapper
  #endif
}

struct S {
  #if true
  @AddPeer
  var value = 1     // attribute is removed, peer is never added
  #endif
}

Freestanding declaration macros inside such an #if are likewise left unexpanded. (Top-level #if already works, because there the clause holds a CodeBlockItemListSyntax, which does have an override.)

The member handling is extracted from visit(_: MemberBlockSyntax) into expandMemberBlockItems(_:parentDeclGroup:isInsideIfConfig:), which is then also applied to the members of every clause of an #if, recursively.

Two roles are deliberately not expanded inside an #if, because their output does not stay where the member is:

  • Extension macros. Extensions are hoisted to the top level, where the #if no longer applies, so expanding them would emit an extension of a type that may not be compiled at all. They keep being skipped, and there is now a test that pins this down.
  • Member macros of the enclosing declaration. The members they add belong to the declaration, not to a nested #if, so they are still added once, to the outer member block.

Macros are expanded in every clause rather than only in the active one. MacroApplication does not resolve #if itself and keeps all branches in its output, so restricting expansion to one clause would leave the others looking unexpanded; expanding per clause is also correct whichever branch ends up being compiled, since these macros are applied per member. If you would rather gate this on the BuildConfiguration that assertMacroExpansion already accepts (as suggested in the issue), I am happy to follow up — that would also let the inactive clauses be left entirely untouched.

Fixes #3106

MacroApplication expands the members of a declaration in
visit(_: MemberBlockSyntax), but members guarded by an #if are stored
directly in the IfConfigClauseSyntax as a MemberBlockItemListSyntax, for
which there is no override. Those members were never routed through the
member expansion, so member attribute macros, peer macros and
freestanding declaration macros attached to them were silently dropped.

Extract the member handling into expandMemberBlockItems and apply it to
the members of every clause of an #if as well.

Extension macros and the member macros of the enclosing declaration are
not expanded inside an #if, since their expansion is not inserted in
place of the member: an extension would be hoisted to the top level,
where the #if no longer applies.

Fixes swiftlang#3106
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.

MemberAttributeMacro do not visit members that are guarded with IfConfigDecl

1 participant