Skip to content

Commit 0af1cb8

Browse files
committed
Fold operators using the standard operator table in MacroSystem
The compiler folds operators in attributes and freestanding macro nodes but `MacroSystem` wasn’t doing that. But it should to match the compiler behavior. rdar://114786803 Fixes #2128
1 parent f4fa6e7 commit 0af1cb8

File tree

5 files changed

+212
-11
lines changed

5 files changed

+212
-11
lines changed

Sources/SwiftCompilerPluginMessageHandling/PluginMacroExpansionContext.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ class SourceManager {
8585
case .attribute:
8686
node = Syntax(AttributeSyntax.parse(from: &parser))
8787
}
88-
if let operatorTable = operatorTable {
88+
if let operatorTable {
8989
node = operatorTable.foldAll(node, errorHandler: { _ in /*ignore*/ })
9090
}
9191

Sources/SwiftSyntaxMacroExpansion/BasicMacroExpansionContext.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
import SwiftDiagnostics
14+
import SwiftOperators
1415
import SwiftSyntax
1516
import SwiftSyntaxMacros
1617

@@ -77,6 +78,20 @@ extension BasicMacroExpansionContext {
7778
detachedNodes[Syntax(detached)] = Syntax(node)
7879
return detached
7980
}
81+
82+
/// Fold all operators in `node` and associated the ``KnownSourceFile``
83+
/// information of `node` with the original new, folded tree.
84+
func foldAllOperators(of node: some SyntaxProtocol, with operatorTable: OperatorTable) -> Syntax {
85+
let folded = operatorTable.foldAll(node, errorHandler: { _ in /*ignore*/ })
86+
if let originalSourceFile = node.root.as(SourceFileSyntax.self),
87+
let newSourceFile = folded.root.as(SourceFileSyntax.self)
88+
{
89+
// Folding operators doesn't change the source file and its associated locations
90+
// Record the `KnownSourceFile` information for the folded tree.
91+
sourceFiles[newSourceFile] = sourceFiles[originalSourceFile]
92+
}
93+
return folded
94+
}
8095
}
8196

8297
extension String {

Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift

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

1313
import SwiftDiagnostics
14+
import SwiftOperators
1415
@_spi(MacroExpansion) import SwiftParser
1516
import SwiftSyntax
1617
import SwiftSyntaxBuilder
@@ -55,7 +56,7 @@ private func expandFreestandingMemberDeclList(
5556
let expanded = try expandFreestandingMacro(
5657
definition: definition,
5758
macroRole: inferFreestandingMacroRole(definition: definition),
58-
node: node.detach(in: context),
59+
node: node.detach(in: context, foldingWith: .standardOperators),
5960
in: context,
6061
indentationWidth: indentationWidth
6162
)
@@ -80,7 +81,7 @@ private func expandFreestandingCodeItemList(
8081
let expanded = try expandFreestandingMacro(
8182
definition: definition,
8283
macroRole: inferFreestandingMacroRole(definition: definition),
83-
node: node.detach(in: context),
84+
node: node.detach(in: context, foldingWith: .standardOperators),
8485
in: context,
8586
indentationWidth: indentationWidth
8687
)
@@ -108,7 +109,7 @@ private func expandFreestandingExpr(
108109
let expanded = expandFreestandingMacro(
109110
definition: definition,
110111
macroRole: .expression,
111-
node: node.detach(in: context),
112+
node: node.detach(in: context, foldingWith: .standardOperators),
112113
in: context,
113114
indentationWidth: indentationWidth
114115
)
@@ -134,7 +135,7 @@ private func expandMemberMacro(
134135
let expanded = expandAttachedMacro(
135136
definition: definition,
136137
macroRole: .member,
137-
attributeNode: attributeNode.detach(in: context),
138+
attributeNode: attributeNode.detach(in: context, foldingWith: .standardOperators),
138139
declarationNode: attachedTo.detach(in: context),
139140
parentDeclNode: nil,
140141
extendedType: nil,
@@ -163,7 +164,7 @@ private func expandMemberAttributeMacro(
163164
let expanded = expandAttachedMacro(
164165
definition: definition,
165166
macroRole: .memberAttribute,
166-
attributeNode: attributeNode.detach(in: context),
167+
attributeNode: attributeNode.detach(in: context, foldingWith: .standardOperators),
167168
declarationNode: member.detach(in: context),
168169
parentDeclNode: declaration.detach(in: context),
169170
extendedType: nil,
@@ -191,7 +192,7 @@ private func expandPeerMacroMember(
191192
let expanded = expandAttachedMacro(
192193
definition: definition,
193194
macroRole: .peer,
194-
attributeNode: attributeNode.detach(in: context),
195+
attributeNode: attributeNode.detach(in: context, foldingWith: .standardOperators),
195196
declarationNode: attachedTo.detach(in: context),
196197
parentDeclNode: nil,
197198
extendedType: nil,
@@ -219,7 +220,7 @@ private func expandPeerMacroCodeItem(
219220
let expanded = expandAttachedMacro(
220221
definition: definition,
221222
macroRole: .peer,
222-
attributeNode: attributeNode.detach(in: context),
223+
attributeNode: attributeNode.detach(in: context, foldingWith: .standardOperators),
223224
declarationNode: attachedTo.detach(in: context),
224225
parentDeclNode: nil,
225226
extendedType: nil,
@@ -251,7 +252,7 @@ private func expandAccessorMacroWithoutExistingAccessors(
251252
let expanded = expandAttachedMacro(
252253
definition: definition,
253254
macroRole: .accessor,
254-
attributeNode: attributeNode.detach(in: context),
255+
attributeNode: attributeNode.detach(in: context, foldingWith: .standardOperators),
255256
declarationNode: attachedTo.detach(in: context),
256257
parentDeclNode: nil,
257258
extendedType: nil,
@@ -285,7 +286,7 @@ private func expandAccessorMacroWithExistingAccessors(
285286
let expanded = expandAttachedMacro(
286287
definition: definition,
287288
macroRole: .accessor,
288-
attributeNode: attributeNode.detach(in: context),
289+
attributeNode: attributeNode.detach(in: context, foldingWith: .standardOperators),
289290
declarationNode: attachedTo.detach(in: context),
290291
parentDeclNode: nil,
291292
extendedType: nil,
@@ -322,7 +323,7 @@ private func expandExtensionMacro(
322323
let expanded = expandAttachedMacro(
323324
definition: definition,
324325
macroRole: .extension,
325-
attributeNode: attributeNode.detach(in: context),
326+
attributeNode: attributeNode.detach(in: context, foldingWith: .standardOperators),
326327
declarationNode: attachedTo.detach(in: context),
327328
parentDeclNode: nil,
328329
extendedType: extendedType.detach(in: context),
@@ -983,4 +984,47 @@ private extension SyntaxProtocol {
983984

984985
return self.detached
985986
}
987+
988+
/// Fold operators in this node using the given operator table, detach the
989+
/// node and inform the macro expansion context, if it needs to know.
990+
func detach(
991+
in context: MacroExpansionContext,
992+
foldingWith operatorTable: OperatorTable?
993+
) -> Syntax {
994+
let folded: Syntax
995+
if let operatorTable {
996+
if let basicContext = context as? BasicMacroExpansionContext {
997+
folded = basicContext.foldAllOperators(of: self, with: operatorTable)
998+
} else {
999+
folded = operatorTable.foldAll(self, errorHandler: { _ in /*ignore*/ })
1000+
}
1001+
} else {
1002+
folded = Syntax(self)
1003+
}
1004+
return folded.detach(in: context)
1005+
}
1006+
}
1007+
1008+
private extension FreestandingMacroExpansionSyntax {
1009+
/// Same as `SyntaxProtocol.detach(in:foldingWith:)` but returns a node of type
1010+
/// `Self` since we know that operator folding doesn't change the type of any
1011+
/// `FreestandingMacroExpansionSyntax`.
1012+
func detach(
1013+
in context: MacroExpansionContext,
1014+
foldingWith operatorTable: OperatorTable?
1015+
) -> Self {
1016+
return (detach(in: context, foldingWith: operatorTable) as Syntax).cast(Self.self)
1017+
}
1018+
}
1019+
1020+
private extension AttributeSyntax {
1021+
/// Same as `SyntaxProtocol.detach(in:foldingWith:)` but returns a node of type
1022+
/// `Self` since we know that operator folding doesn't change the type of any
1023+
/// `AttributeSyntax`.
1024+
func detach(
1025+
in context: MacroExpansionContext,
1026+
foldingWith operatorTable: OperatorTable?
1027+
) -> Self {
1028+
return (detach(in: context, foldingWith: operatorTable) as Syntax).cast(Self.self)
1029+
}
9861030
}

Tests/SwiftSyntaxMacroExpansionTest/ExpressionMacroTests.swift

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,4 +200,63 @@ final class ExpressionMacroTests: XCTestCase {
200200
indentationWidth: indentationWidth
201201
)
202202
}
203+
204+
func testFoldOperators() {
205+
struct ForceSubtractMacro: ExpressionMacro {
206+
static func expansion(
207+
of node: some FreestandingMacroExpansionSyntax,
208+
in context: some MacroExpansionContext
209+
) throws -> ExprSyntax {
210+
guard let argument = node.argumentList.first?.expression else {
211+
fatalError("Must receive an argument")
212+
}
213+
guard var node = argument.as(InfixOperatorExprSyntax.self) else {
214+
return argument
215+
}
216+
node.operator = ExprSyntax(BinaryOperatorExprSyntax(text: "-"))
217+
return ExprSyntax(node)
218+
}
219+
}
220+
assertMacroExpansion(
221+
"#test(1 + 2)",
222+
expandedSource: "1 - 2",
223+
macros: ["test": ForceSubtractMacro.self]
224+
)
225+
}
226+
227+
func testDiagnosticFromFoldedOperators() {
228+
struct MyError: Error {}
229+
230+
struct DiagnoseFirstArgument: ExpressionMacro {
231+
static func expansion(
232+
of node: some FreestandingMacroExpansionSyntax,
233+
in context: some MacroExpansionContext
234+
) throws -> ExprSyntax {
235+
guard let argument = node.argumentList.first?.expression else {
236+
fatalError("Must receive an argument")
237+
}
238+
context.addDiagnostics(from: MyError(), node: argument)
239+
return argument
240+
}
241+
}
242+
243+
assertMacroExpansion(
244+
"""
245+
/// Test
246+
func test() {
247+
#test(1 + 2)
248+
}
249+
""",
250+
expandedSource: """
251+
/// Test
252+
func test() {
253+
1 + 2
254+
}
255+
""",
256+
diagnostics: [
257+
DiagnosticSpec(message: "MyError()", line: 3, column: 9, severity: .error)
258+
],
259+
macros: ["test": DiagnoseFirstArgument.self]
260+
)
261+
}
203262
}

Tests/SwiftSyntaxMacroExpansionTest/MemberMacroTests.swift

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,4 +172,87 @@ final class MemberMacroTests: XCTestCase {
172172
indentationWidth: indentationWidth
173173
)
174174
}
175+
176+
func testFoldOperators() {
177+
struct ForceSubtractMacro: MemberMacro {
178+
static func expansion(
179+
of node: AttributeSyntax,
180+
providingMembersOf declaration: some DeclGroupSyntax,
181+
in context: some MacroExpansionContext
182+
) throws -> [DeclSyntax] {
183+
guard case .argumentList(let arguments) = node.arguments, let argument = arguments.first?.expression else {
184+
fatalError("Must receive an argument")
185+
}
186+
guard var node = argument.as(InfixOperatorExprSyntax.self) else {
187+
return []
188+
}
189+
node.operator = ExprSyntax(BinaryOperatorExprSyntax(text: "- "))
190+
return [
191+
DeclSyntax(
192+
"""
193+
var x: Int { \(node.trimmed) }
194+
"""
195+
)
196+
]
197+
}
198+
}
199+
assertMacroExpansion(
200+
"""
201+
/// Test
202+
/// And another line
203+
@Test(1 + 2)
204+
struct Foo {
205+
}
206+
""",
207+
expandedSource: """
208+
/// Test
209+
/// And another line
210+
struct Foo {
211+
212+
var x: Int {
213+
1 - 2
214+
}
215+
}
216+
""",
217+
macros: ["Test": ForceSubtractMacro.self],
218+
indentationWidth: indentationWidth
219+
)
220+
}
221+
222+
func testDiagnosticFromFoldedOperators() {
223+
struct MyError: Error {}
224+
225+
struct DiagnoseFirstArgument: MemberMacro {
226+
static func expansion(
227+
of node: AttributeSyntax,
228+
providingMembersOf declaration: some DeclGroupSyntax,
229+
in context: some MacroExpansionContext
230+
) throws -> [DeclSyntax] {
231+
guard case .argumentList(let arguments) = node.arguments, let argument = arguments.first?.expression else {
232+
fatalError("Must receive an argument")
233+
}
234+
context.addDiagnostics(from: MyError(), node: argument)
235+
return []
236+
}
237+
}
238+
239+
assertMacroExpansion(
240+
"""
241+
/// Test
242+
/// And another line
243+
@Test(1 + 2)
244+
struct Foo {}
245+
""",
246+
expandedSource: """
247+
/// Test
248+
/// And another line
249+
struct Foo {}
250+
""",
251+
diagnostics: [
252+
DiagnosticSpec(message: "MyError()", line: 3, column: 7, severity: .error)
253+
],
254+
macros: ["Test": DiagnoseFirstArgument.self]
255+
)
256+
}
257+
175258
}

0 commit comments

Comments
 (0)