swiftlang/swift-syntax

Source location retrieving doesn't work in body macro expansion assertion

mateusrodriguesxyz opened this issue · 1 comments

Description

The location method of BasicMacroExpansionContext returns nil when a body macro is expanded using assertMacroExpansion. This makes it difficult to write tests for macros that depend on non-nil location value.

Steps to Reproduce

Consider the following macro:

struct SourceLocationMacro: BodyMacro {
  public static var formatMode: FormatMode { .disabled }

  public static func expansion(
    of node: AttributeSyntax,
    providingBodyFor declaration: some DeclSyntaxProtocol & WithOptionalCodeBlockSyntax,
    in context: some MacroExpansionContext
  ) throws -> [CodeBlockItemSyntax] {
    guard let statements = declaration.body?.statements else {
      return []
    }
    let body =
      if let location = context.location(of: statements, at: .afterLeadingTrivia, filePathMode: .filePath) {
        CodeBlockItemListSyntax {
          "#sourceLocation(file: \(location.file), line: \(location.line))"
          statements
          "#sourceLocation()"
        }
      } else {
        statements
      }
    return body.map(\.self)
  }
}

Assertion of it produces an unexpected expanded source code:

assertMacroExpansion(
  """
  @SourceLocationMacro
  func f() {
    let x: Int = 1
  }
  """,
  expandedSource:
    """
    func f() {
      #sourceLocation(file: "test.swift", line: 3)
      let x: Int = 1
      #sourceLocation()
    }
    """,
  macros: ["SourceLocationMacro": SourceLocationMacro.self],
  indentationWidth: indentationWidth
)

 failed - Macro expansion did not produce the expected expanded source

Actual expanded source:

func f() {
  let x: Int = 1
}

Synced to Apple’s issue tracker as rdar://133489460