Skip to content

FunctionCallExprSyntax.addArgument does not add separating comma between arguments #1937

Closed
@bnickel

Description

@bnickel

Description

I have the following two functions that should produce identical results:

  • asSyntaxSimple creates two different function expressions based on whether text has a value.
  • asSyntaxAdvanced creates a function expression uses addArgument to add an argument if needed.
struct Node {
    var name: String?
    var text: String?
    
    func asSyntaxSimple() -> ExprSyntax {
        
        if let text = text {
            return ".init(\(literal: name), text: \(literal: text))"
        } else {
            return ".init(\(literal: name))"
        }
    }
    
    func asSyntaxAdvanced() -> ExprSyntax {
        
        var syntax = (".init(\(literal: name))" as ExprSyntax).cast(FunctionCallExprSyntax.self)
        
        if let text = text {
            syntax = syntax.addArgument(.init(label: "text", expression: StringLiteralExprSyntax(content: text)))
        }
        
        return syntax.cast(ExprSyntax.self)
    }
}

When testing with assertMacroExpansion, the function with addArgument fails to insert the comma between the existing argument and the new one. It similarly fails when calling addArgument multiple times.

Steps to Reproduce

Test the following code:

import XCTest
import SwiftSyntaxMacrosTestSupport
import SwiftSyntax
import SwiftSyntaxMacros

struct Node {
    var name: String?
    var text: String?
    
    func asSyntaxSimple() -> ExprSyntax {
        
        if let text = text {
            return ".init(\(literal: name), text: \(literal: text))"
        } else {
            return ".init(\(literal: name))"
        }
    }
    
    func asSyntaxAdvanced() -> ExprSyntax {
        
        var syntax = (".init(\(literal: name))" as ExprSyntax).cast(FunctionCallExprSyntax.self)
        
        if let text = text {
            syntax = syntax.addArgument(.init(label: "text", expression: StringLiteralExprSyntax(content: text)))
        }
        
        return syntax.cast(ExprSyntax.self)
    }
}

public struct NodeSimple: ExpressionMacro {
  public static func expansion(
    of node: some FreestandingMacroExpansionSyntax,
    in context: some MacroExpansionContext
  ) -> ExprSyntax {
      return Node(name: "NAME", text: "TEXT").asSyntaxSimple()
  }
}

public struct NodeAdvanced: ExpressionMacro {
  public static func expansion(
    of node: some FreestandingMacroExpansionSyntax,
    in context: some MacroExpansionContext
  ) -> ExprSyntax {
      return Node(name: "NAME", text: "TEXT").asSyntaxAdvanced()
  }
}

final class AddArgumentsTests: XCTestCase {
    
    func testSimple() throws {
        assertMacroExpansion(
            "#NodeSimple",
            expandedSource: #".init("NAME", text: "TEXT")"#,
            macros: [
                "NodeSimple": NodeSimple.self,
            ])
    }
    
    func testAdvanced() throws {
        assertMacroExpansion(
            "#NodeAdvanced",
            expandedSource: #".init("NAME", text: "TEXT")"#,
            macros: [
                "NodeAdvanced": NodeAdvanced.self,
            ])
    }
}

testAdvanced fails with the following error:

failed - Actual output (+) differed from expected output (-):
–.init("NAME", text: "TEXT")
+.init("NAME" text: "TEXT")

Actual expanded source:
.init("NAME" text: "TEXT")

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions