Skip to content

Commit e88e4e2

Browse files
authored
Merge pull request #1591 from bnbarham/format-macro-expansions
Automatically format expanded macros
2 parents 3d53f9d + e40c82c commit e88e4e2

File tree

7 files changed

+88
-13
lines changed

7 files changed

+88
-13
lines changed

Sources/SwiftCompilerPluginMessageHandling/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ add_swift_host_library(SwiftCompilerPluginMessageHandling
1616

1717
target_link_libraries(SwiftCompilerPluginMessageHandling PUBLIC
1818
SwiftSyntax
19+
SwiftBasicFormat
1920
SwiftDiagnostics
2021
SwiftParser
2122
SwiftSyntaxMacros

Sources/SwiftCompilerPluginMessageHandling/Macros.swift

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
import SwiftBasicFormat
1314
import SwiftDiagnostics
1415
import SwiftSyntax
1516
import SwiftSyntaxMacros
@@ -49,21 +50,21 @@ extension CompilerPluginMessageHandler {
4950
try exprMacroDef.expansion(of: node, in: context)
5051
}
5152
let rewritten = try _openExistential(macroSyntax, do: _expand)
52-
expandedSource = rewritten.description
53+
expandedSource = rewritten.formattedExpansion(macroDefinition.formatMode)
5354

5455
case let declMacroDef as DeclarationMacro.Type:
5556
func _expand<Node: FreestandingMacroExpansionSyntax>(node: Node) throws -> [DeclSyntax] {
5657
try declMacroDef.expansion(of: node, in: context)
5758
}
5859
let rewritten = try _openExistential(macroSyntax, do: _expand)
59-
expandedSource = CodeBlockItemListSyntax(rewritten.map { CodeBlockItemSyntax(item: .decl($0)) }).description
60+
expandedSource = CodeBlockItemListSyntax(rewritten.map { CodeBlockItemSyntax(item: .decl($0)) }).formattedExpansion(macroDefinition.formatMode)
6061

6162
case let codeItemMacroDef as CodeItemMacro.Type:
6263
func _expand<Node: FreestandingMacroExpansionSyntax>(node: Node) throws -> [CodeBlockItemSyntax] {
6364
try codeItemMacroDef.expansion(of: node, in: context)
6465
}
6566
let rewritten = try _openExistential(macroSyntax, do: _expand)
66-
expandedSource = CodeBlockItemListSyntax(rewritten).description
67+
expandedSource = CodeBlockItemListSyntax(rewritten).formattedExpansion(macroDefinition.formatMode)
6768

6869
default:
6970
throw MacroExpansionError.unmathedMacroRole
@@ -113,7 +114,7 @@ extension CompilerPluginMessageHandler {
113114
in: context
114115
)
115116
expandedSources = accessors.map {
116-
$0.trimmedDescription
117+
$0.formattedExpansion(macroDefinition.formatMode)
117118
}
118119

119120
case (let attachedMacro as MemberAttributeMacro.Type, .memberAttribute):
@@ -145,7 +146,7 @@ extension CompilerPluginMessageHandler {
145146

146147
// Form a buffer containing an attribute list to return to the caller.
147148
expandedSources = attributes.map {
148-
$0.trimmedDescription
149+
$0.formattedExpansion(macroDefinition.formatMode)
149150
}
150151

151152
case (let attachedMacro as MemberMacro.Type, .member):
@@ -170,7 +171,7 @@ extension CompilerPluginMessageHandler {
170171
let members = try _openExistential(declGroup, do: expandMemberMacro)
171172

172173
// Form a buffer of member declarations to return to the caller.
173-
expandedSources = members.map { $0.trimmedDescription }
174+
expandedSources = members.map { $0.formattedExpansion(macroDefinition.formatMode) }
174175

175176
case (let attachedMacro as PeerMacro.Type, .peer):
176177
let peers = try attachedMacro.expansion(
@@ -181,7 +182,7 @@ extension CompilerPluginMessageHandler {
181182

182183
// Form a buffer of peer declarations to return to the caller.
183184
expandedSources = peers.map {
184-
$0.trimmedDescription
185+
$0.formattedExpansion(macroDefinition.formatMode)
185186
}
186187

187188
case (let attachedMacro as ConformanceMacro.Type, .conformance):
@@ -234,3 +235,18 @@ extension CompilerPluginMessageHandler {
234235
)
235236
}
236237
}
238+
239+
fileprivate extension SyntaxProtocol {
240+
/// Perform a format if required and then trim any leading/trailing
241+
/// whitespace.
242+
func formattedExpansion(_ mode: FormatMode) -> String {
243+
let formatted: Syntax
244+
switch mode {
245+
case .auto:
246+
formatted = self.formatted()
247+
case .disabled:
248+
formatted = Syntax(self)
249+
}
250+
return formatted.trimmedDescription(matching: { $0.isWhitespace })
251+
}
252+
}

Sources/SwiftSyntax/Syntax.swift

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -541,13 +541,35 @@ public extension SyntaxProtocol {
541541
return self.with(\.leadingTrivia, []).with(\.trailingTrivia, [])
542542
}
543543

544-
/// The description of this node without the leading trivia of the first token
545-
/// in the node and the trailing trivia of the last token in the node.
544+
/// A copy of this node with pieces that match `matching` trimmed from the
545+
/// leading trivia of the first token and trailing trivia of the last token.
546+
func trimmed(matching filter: (TriviaPiece) -> Bool) -> Self {
547+
// TODO: Should only need one new node here
548+
return self.with(
549+
\.leadingTrivia,
550+
Trivia(pieces: leadingTrivia.pieces.drop(while: filter))
551+
).with(
552+
\.trailingTrivia,
553+
Trivia(pieces: trailingTrivia.pieces.reversed().drop(while: filter).reversed())
554+
)
555+
}
556+
557+
/// The description of this node with leading whitespace of the first token
558+
/// and trailing whitespace of the last token removed.
546559
var trimmedDescription: String {
547-
// TODO: We shouldn't need to create to copies just to get the description
548-
// without trivia.
560+
// TODO: We shouldn't need to create to copies just to get the trimmed
561+
// description.
549562
return self.trimmed.description
550563
}
564+
565+
/// The description of this node with pieces that match `matching` removed
566+
/// from the leading trivia of the first token and trailing trivia of the
567+
/// last token.
568+
func trimmedDescription(matching filter: (TriviaPiece) -> Bool) -> String {
569+
// TODO: We shouldn't need to create to copies just to get the trimmed
570+
// description.
571+
return self.trimmed(matching: filter).description
572+
}
551573
}
552574

553575
/// Provides debug descriptions for a node

Sources/SwiftSyntaxMacros/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ add_swift_host_library(SwiftSyntaxMacros
1515
MacroProtocols/ExpressionMacro.swift
1616
MacroProtocols/FreestandingMacro.swift
1717
MacroProtocols/Macro.swift
18+
MacroProtocols/Macro+Format.swift
1819
MacroProtocols/MemberAttributeMacro.swift
1920
MacroProtocols/MemberMacro.swift
2021
MacroProtocols/PeerMacro.swift
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
/// Describes the mode to use to format the result of an expansion.
14+
public enum FormatMode {
15+
/// Perform a basic format of the expansion. This is primarily for inserting
16+
/// whitespace as required (eg. between two keywords), but also adds simple
17+
/// newline and indentation.
18+
case auto
19+
20+
/// Disable automatically formatting the expanded macro. Trivia must be
21+
/// manually inserted where required (eg. adding spaces between keywords).
22+
case disabled
23+
}
24+
25+
public extension Macro {
26+
static var formatMode: FormatMode {
27+
return .auto
28+
}
29+
}

Sources/SwiftSyntaxMacros/MacroProtocols/Macro.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,8 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
/// Describes a macro.
14-
public protocol Macro {}
14+
public protocol Macro {
15+
/// How the resulting expansion should be formatted, `.auto` by default.
16+
/// Use `.disabled` for the expansion to be used as is.
17+
static var formatMode: FormatMode { get }
18+
}

lit_tests/compiler_plugin_basic.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ class MyClass {
2323
}
2424

2525
// For '@Metadata'
26-
// CHECK: static var __metadata__: [String: String] { ["name": "MyClass"] }
26+
// CHECK: {{^}}static var __metadata__: [String: String] {
27+
// CHECK-NEXT: {{^}} ["name": "MyClass"]
28+
// CHECK-NEXT: {{^}}}
2729

2830
// For '#echo(12)'
2931
// CHECK: /* echo */12

0 commit comments

Comments
 (0)