Skip to content

Comments

Fix(MacroSystem): Validate MemberMacro application on non-group declarations (Issue #2206)#3257

Open
rickhohler wants to merge 1 commit intoswiftlang:mainfrom
rickhohler:fix/issue-2206-member-macro-validation
Open

Fix(MacroSystem): Validate MemberMacro application on non-group declarations (Issue #2206)#3257
rickhohler wants to merge 1 commit intoswiftlang:mainfrom
rickhohler:fix/issue-2206-member-macro-validation

Conversation

@rickhohler
Copy link

Fix(MacroSystem): Validate MemberMacro application on non-group declarations (Issue #2206)

Description

This PR fixes an issue where MemberMacro attributes applied to invalid declarations (such as var properties or functions) were silently ignored by the MacroApplication walker.

Before this change, the following code would compile successfully with 0 diagnostics, even though @Test is a MemberMacro:

@Test
var x: Int

After this change, it correctly produces the expected diagnostic:
error: macro 'Test' can only be applied to a struct, enum, class, extension, or actor

Fixes: #2206

Motivation

The visit(_ : MemberBlockSyntax) method handles member macros correctly for container types. However, when a MemberMacro is applied to a standalone declaration (like a VariableDecl), the walker visits it via visitAny or specific node visitors which previously lacked logic to check for member macro attributes. This resulted in silent failures where the user would expect either an expansion or an error.

Detailed Design

  • Added a new error case MacroApplicationError.memberMacroOnInvalidDecl(macroName: String).
  • Updated MacroApplication.visitAny(_:) to inspect DeclSyntax nodes.
  • If a node has attributes matching a registered MemberMacro but does not conform to DeclGroupSyntax (and is not an ExtensionDeclSyntax), a diagnostic is now added to the context.

Verification

  • Added Tests/SwiftSyntaxMacroExpansionTest/Issue2206Tests.swift.
  • Verified that the test case fails before the fix and passes after the fix.
  • Ran full test suite for SwiftSyntaxMacroExpansion.

Checklist

  • I have read the Contributing Guide.
  • I have processed the feedback of the Swift Syntax team (if applicable).
  • I have added a test case that demonstrates the issue and verified the fix.

Copy link
Contributor

@hamishknight hamishknight left a comment

Choose a reason for hiding this comment

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

Thanks! It's worth noting this isn't a problem specific to member macros, it seems to also apply to other attached macros e.g extension and accessor, if you're interested in fixing those too (either in this PR or a follow-up)

!attributedNode.attributes.isEmpty
{
// Check if there are any member macros generated on a non-group decl.
if !(node.isProtocol(DeclGroupSyntax.self) || node.is(ExtensionDeclSyntax.self)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

An ExtensionDeclSyntax is a DeclGroupSyntax so you should only need the former check I think

}
}

final class Issue2206Tests: XCTestCase {
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be folded into the existing tests we have for member macros in MemberMacroTests.swift


func testMemberMacroOnVariable() {
// Issue #2206: assertMacroExpansion should emit an error if member macro is applied to declaration that can’t have members
// Currently this is expected to FAIL because the diagnostic is swallowed.
Copy link
Contributor

Choose a reason for hiding this comment

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

Outdated comment?

case .peerMacroOnVariableWithMultipleBindings:
return "peer macro can only be applied to a single variable"
case .memberMacroOnInvalidDecl(let macroName):
return "macro '\(macroName)' can only be applied to a struct, enum, class, extension, or actor"
Copy link
Contributor

Choose a reason for hiding this comment

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

I would say member macro here for clarity, also I'm not sure it's worth listing every type decl kind (note you missed protocol), maybe it would be better just to say type declaration or extension instead?

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.

assertMacroExpansion should emit an error if member macro is applied to declaration that can’t have members

2 participants