Repository: swiftlang/swift-syntax (swift)

Issue:
Diagnostic triggered for every multi-binding variable declaration
### Description

The `MacroApplicationError.accessorMacroOnVariableWithMultipleBindings` diagnostic error is leaking into all nested variable declarations that have multiple bindings.

For quick reference, here's the invalid usage that merits the error:

```
@constantOneGetter
var x: Int, y: Int
┬─────────────────
╰─ 🛑 swift-syntax applies macros syntactically and there is no way to represent a variable
      declaration with multiple bindings that have accessors syntactically. While the compiler 
      allows this expansion, swift-syntax cannot represent it and thus disallows it.
```

Here are two cases where the error is erroneously triggered:

```
@Test
struct S {
  var x: Int, y: Int
  ┬─────────────────
  ╰─ 🛑 swift-syntax … no way to represent a variable declaration with multiple bindings…
}

@Test
struct Q {
  struct R {
    var i: Int, j: Int
    ┬─────────────────
    ╰─ 🛑 swift-syntax … no way to represent a variable declaration with multiple bindings…
  }
}
```

### Steps to Reproduce

Add these two two failing tests to `MemberMacroTests` (failing on `main`, I tested 71b7378): 

```swift
// ...

  func testStructVariableDeclWithMultipleBindings() {
    struct TestMacro: MemberMacro {
      static func expansion(
        of node: AttributeSyntax,
        providingMembersOf declaration: some DeclGroupSyntax,
        in context: some MacroExpansionContext
      ) throws -> [DeclSyntax] {
        return []
      }
    }

    assertMacroExpansion(
      """
      @Test
      struct S {
        var x: Int, y: Int
      }
      """,
      expandedSource: """
      struct S {
        var x: Int, y: Int
      }
      """,
      macros: ["Test": TestMacro.self],
      indentationWidth: indentationWidth
    )
  }

  func testNestedStructVariableDeclWithMultipleBindings() {
    struct TestMacro: MemberMacro {
      static func expansion(
        of node: AttributeSyntax,
        providingMembersOf declaration: some DeclGroupSyntax,
        in context: some MacroExpansionContext
      ) throws -> [DeclSyntax] {
        return []
      }
    }

    assertMacroExpansion(
      """
      @Test
      struct Q {
        struct R {
          var i: Int, j: Int
        }
      }
      """,
      expandedSource: """
      struct Q {
        struct R {
          var i: Int, j: Int
        }
      }
      """,
      macros: ["Test": TestMacro.self],
      indentationWidth: indentationWidth
    )
  }

// ...
```

Pull request description:
When there are no macro attributes attached to the visited `VariableDeclSyntax` node, proceed without performing any additional checks or transformations.

- Addressed the erroneous triggering of `MacroApplicationError.accessorMacroOnVariableWithMultipleBindings` diagnostic for nested multi-binding variable declarations.
- Added corresponding tests to validate the fix.
- Introduce fileprivate `NoOpMemberMacro`, extracted from `testCommentAroundeAttachedMacro` and used in 2 new tests.

Fixes #2133
rdar://114836887

Patch:
diff --git a/Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift b/Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift
index 6e657e064c0..33658be811e 100644
--- a/Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift
+++ b/Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift
@@ -606,6 +606,10 @@ private class MacroApplication<Context: MacroExpansionContext>: SyntaxRewriter {
   }
 
   override func visit(_ node: VariableDeclSyntax) -> DeclSyntax {
+    guard !macroAttributes(attachedTo: DeclSyntax(node), ofType: AccessorMacro.Type.self).isEmpty else {
+      return super.visit(node).cast(DeclSyntax.self)
+    }
+
     var node = super.visit(node).cast(VariableDeclSyntax.self)
     guard node.bindings.count == 1, let binding = node.bindings.first else {
       context.addDiagnostics(from: MacroApplicationError.accessorMacroOnVariableWithMultipleBindings, node: node)


Test patch:
diff --git a/Tests/SwiftSyntaxMacroExpansionTest/MemberMacroTests.swift b/Tests/SwiftSyntaxMacroExpansionTest/MemberMacroTests.swift
index a713662b323..2045a376e03 100644
--- a/Tests/SwiftSyntaxMacroExpansionTest/MemberMacroTests.swift
+++ b/Tests/SwiftSyntaxMacroExpansionTest/MemberMacroTests.swift
@@ -24,6 +24,16 @@ import SwiftSyntaxMacroExpansion
 import SwiftSyntaxMacrosTestSupport
 import XCTest
 
+fileprivate struct NoOpMemberMacro: MemberMacro {
+  static func expansion(
+    of node: AttributeSyntax,
+    providingMembersOf declaration: some DeclGroupSyntax,
+    in context: some MacroExpansionContext
+  ) throws -> [DeclSyntax] {
+    return []
+  }
+}
+
 final class MemberMacroTests: XCTestCase {
   private let indentationWidth: Trivia = .spaces(2)
 
@@ -103,16 +113,6 @@ final class MemberMacroTests: XCTestCase {
   }
 
   func testCommentAroundeAttachedMacro() {
-    struct TestMacro: MemberMacro {
-      static func expansion(
-        of node: AttributeSyntax,
-        providingMembersOf declaration: some DeclGroupSyntax,
-        in context: some MacroExpansionContext
-      ) throws -> [DeclSyntax] {
-        return []
-      }
-    }
-
     assertMacroExpansion(
       """
       /// Some doc comment
@@ -128,7 +128,47 @@ final class MemberMacroTests: XCTestCase {
           var value: Int
         }
         """,
-      macros: ["Test": TestMacro.self],
+      macros: ["Test": NoOpMemberMacro.self],
+      indentationWidth: indentationWidth
+    )
+  }
+
+  func testStructVariableDeclWithMultipleBindings() {
+    assertMacroExpansion(
+      """
+      @Test
+      struct S {
+        var x: Int, y: Int
+      }
+      """,
+      expandedSource: """
+        struct S {
+          var x: Int, y: Int
+        }
+        """,
+      macros: ["Test": NoOpMemberMacro.self],
+      indentationWidth: indentationWidth
+    )
+  }
+
+  func testNestedStructVariableDeclWithMultipleBindings() {
+    assertMacroExpansion(
+      """
+      @Test
+      struct Q {
+        struct R {
+          var i: Int, j: Int
+        }
+      }
+      """,
+      expandedSource: """
+        struct Q {
+          struct R {
+            var i: Int, j: Int
+          }
+        }
+        """,
+      macros: ["Test": NoOpMemberMacro.self],
       indentationWidth: indentationWidth
     )
   }