Skip to content

Commit 7c19b5b

Browse files
committed
adjust Indentation Of Freestanding Macro
1 parent f38bac3 commit 7c19b5b

File tree

2 files changed

+143
-17
lines changed

2 files changed

+143
-17
lines changed

Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,8 @@ private func expandFreestandingMemberDeclList(
7878
return nil
7979
}
8080

81-
let indentedSource =
82-
expanded
83-
.indented(by: node.indentationOfFirstLine)
84-
.wrappingInTrivia(from: node)
81+
let indentedSource = adjustIndentationOfFreestandingMacro(expandedCode: expanded, node: node)
82+
8583
return "\(raw: indentedSource)"
8684
}
8785

@@ -103,13 +101,8 @@ private func expandFreestandingCodeItemList(
103101
return nil
104102
}
105103

106-
// The macro expansion just provides an expansion for the content.
107-
// We need to make sure that we aren’t dropping the trivia before and after
108-
// the expansion.
109-
let indentedSource =
110-
expanded
111-
.indented(by: node.indentationOfFirstLine)
112-
.wrappingInTrivia(from: node)
104+
let indentedSource = adjustIndentationOfFreestandingMacro(expandedCode: expanded, node: node)
105+
113106
return "\(raw: indentedSource)"
114107
}
115108

@@ -131,13 +124,29 @@ private func expandFreestandingExpr(
131124
return nil
132125
}
133126

134-
let indentedSource =
135-
expanded
136-
.indented(by: node.indentationOfFirstLine)
137-
.wrappingInTrivia(from: node)
127+
let indentedSource = adjustIndentationOfFreestandingMacro(expandedCode: expanded, node: node)
128+
138129
return "\(raw: indentedSource)"
139130
}
140131

132+
/// Adds the appropriate indentation on expanded code even if it's multi line.
133+
/// Makes sure original macro expression's trivia is maintained by adding it to expanded code.
134+
private func adjustIndentationOfFreestandingMacro(expandedCode: String, node: some FreestandingMacroExpansionSyntax) -> String {
135+
let indentationOfFirstLine = node.indentationOfFirstLine
136+
137+
var indentedSource =
138+
expandedCode
139+
.indented(by: indentationOfFirstLine)
140+
.wrappingInTrivia(from: node)
141+
142+
// if the experssion started in middle of the line, then remove indentation of the first line
143+
if !node.leadingTrivia.contains(where: \.isNewline) {
144+
indentedSource.removeFirst(indentationOfFirstLine.sourceLength.utf8Length)
145+
}
146+
147+
return indentedSource
148+
}
149+
141150
private func expandMemberMacro(
142151
definition: MemberMacro.Type,
143152
attributeNode: AttributeSyntax,

Tests/SwiftSyntaxMacroExpansionTest/LexicalContextTests.swift

Lines changed: 119 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,24 @@ public struct FunctionMacro: ExpressionMacro {
173173
}
174174
}
175175

176+
public struct MultilineFunctionMacro: ExpressionMacro {
177+
public static func expansion<
178+
Node: FreestandingMacroExpansionSyntax,
179+
Context: MacroExpansionContext
180+
>(
181+
of node: Node,
182+
in context: Context
183+
) -> ExprSyntax {
184+
guard let lexicalContext = context.lexicalContext.first,
185+
let name = lexicalContext.functionName(in: context)
186+
else {
187+
return #""<unknown>""#
188+
}
189+
190+
return ExprSyntax("{\n\(literal: name)\n}")
191+
}
192+
}
193+
176194
public struct AllLexicalContextsMacro: DeclarationMacro {
177195
public static func expansion(
178196
of node: some FreestandingMacroExpansionSyntax,
@@ -194,7 +212,7 @@ final class LexicalContextTests: XCTestCase {
194212
""",
195213
expandedSource: """
196214
func f(a: Int, _: Double, c: Int) {
197-
print( "f(a:_:c:)")
215+
print("f(a:_:c:)")
198216
}
199217
""",
200218
macros: ["function": FunctionMacro.self],
@@ -263,14 +281,113 @@ final class LexicalContextTests: XCTestCase {
263281
""",
264282
expandedSource: """
265283
extension A {
266-
static var staticProp: String = "staticProp"
284+
static var staticProp: String = "staticProp"
267285
}
268286
""",
269287
macros: ["function": FunctionMacro.self],
270288
indentationWidth: indentationWidth
271289
)
272290
}
273291

292+
func testPoundMultilineFunction() {
293+
assertMacroExpansion(
294+
"""
295+
func f(a: Int, _: Double, c: Int) {
296+
print(#function)
297+
}
298+
""",
299+
expandedSource: """
300+
func f(a: Int, _: Double, c: Int) {
301+
print({
302+
"f(a:_:c:)"
303+
})
304+
}
305+
""",
306+
macros: ["function": MultilineFunctionMacro.self],
307+
indentationWidth: indentationWidth
308+
)
309+
310+
311+
assertMacroExpansion(
312+
"""
313+
struct X {
314+
init(from: String) {
315+
#function
316+
}
317+
318+
subscript(a: Int) -> String {
319+
#function
320+
}
321+
322+
subscript(a a: Int) -> String {
323+
#function
324+
}
325+
}
326+
""",
327+
expandedSource: """
328+
struct X {
329+
init(from: String) {
330+
{
331+
"init(from:)"
332+
}
333+
}
334+
335+
subscript(a: Int) -> String {
336+
{
337+
"subscript(_:)"
338+
}
339+
}
340+
341+
subscript(a a: Int) -> String {
342+
{
343+
"subscript(a:)"
344+
}
345+
}
346+
}
347+
""",
348+
macros: ["function": MultilineFunctionMacro.self],
349+
indentationWidth: indentationWidth
350+
)
351+
352+
assertMacroExpansion(
353+
"""
354+
var computed: String {
355+
get {
356+
#function
357+
}
358+
}
359+
""",
360+
expandedSource: """
361+
var computed: String {
362+
get {
363+
{
364+
"computed"
365+
}
366+
}
367+
}
368+
""",
369+
macros: ["function": MultilineFunctionMacro.self],
370+
indentationWidth: indentationWidth
371+
)
372+
373+
assertMacroExpansion(
374+
"""
375+
extension A {
376+
static var staticProp: String = #function
377+
}
378+
""",
379+
expandedSource: """
380+
extension A {
381+
static var staticProp: String = {
382+
"staticProp"
383+
}
384+
}
385+
""",
386+
macros: ["function": MultilineFunctionMacro.self],
387+
indentationWidth: indentationWidth
388+
)
389+
}
390+
274391
func testAllLexicalContexts() {
275392
assertMacroExpansion(
276393
"""

0 commit comments

Comments
 (0)