Skip to content

Commit 12c0887

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 14a3d23 commit 12c0887

File tree

5 files changed

+211
-11
lines changed

5 files changed

+211
-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),
@@ -1011,4 +1012,47 @@ private extension SyntaxProtocol {
10111012

10121013
return self.detached
10131014
}
1015+
1016+
/// Fold operators in this node using the given operator table, detach the
1017+
/// node and inform the macro expansion context, if it needs to know.
1018+
func detach(
1019+
in context: MacroExpansionContext,
1020+
foldingWith operatorTable: OperatorTable?
1021+
) -> Syntax {
1022+
let folded: Syntax
1023+
if let operatorTable {
1024+
if let basicContext = context as? BasicMacroExpansionContext {
1025+
folded = basicContext.foldAllOperators(of: self, with: operatorTable)
1026+
} else {
1027+
folded = operatorTable.foldAll(self, errorHandler: { _ in /*ignore*/ })
1028+
}
1029+
} else {
1030+
folded = Syntax(self)
1031+
}
1032+
return folded.detach(in: context)
1033+
}
1034+
}
1035+
1036+
private extension FreestandingMacroExpansionSyntax {
1037+
/// Same as `SyntaxProtocol.detach(in:foldingWith:)` but returns a node of type
1038+
/// `Self` since we know that operator folding doesn't change the type of any
1039+
/// `FreestandingMacroExpansionSyntax`.
1040+
func detach(
1041+
in context: MacroExpansionContext,
1042+
foldingWith operatorTable: OperatorTable?
1043+
) -> Self {
1044+
return (detach(in: context, foldingWith: operatorTable) as Syntax).cast(Self.self)
1045+
}
1046+
}
1047+
1048+
private extension AttributeSyntax {
1049+
/// Same as `SyntaxProtocol.detach(in:foldingWith:)` but returns a node of type
1050+
/// `Self` since we know that operator folding doesn't change the type of any
1051+
/// `AttributeSyntax`.
1052+
func detach(
1053+
in context: MacroExpansionContext,
1054+
foldingWith operatorTable: OperatorTable?
1055+
) -> Self {
1056+
return (detach(in: context, foldingWith: operatorTable) as Syntax).cast(Self.self)
1057+
}
10141058
}

Tests/SwiftSyntaxMacroExpansionTest/ExpressionMacroTests.swift

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,4 +234,62 @@ final class ExpressionMacroTests: XCTestCase {
234234
)
235235
}
236236

237+
func testFoldOperators() {
238+
struct ForceSubtractMacro: ExpressionMacro {
239+
static func expansion(
240+
of node: some FreestandingMacroExpansionSyntax,
241+
in context: some MacroExpansionContext
242+
) throws -> ExprSyntax {
243+
guard let argument = node.argumentList.first?.expression else {
244+
fatalError("Must receive an argument")
245+
}
246+
guard var node = argument.as(InfixOperatorExprSyntax.self) else {
247+
return argument
248+
}
249+
node.operator = ExprSyntax(BinaryOperatorExprSyntax(text: "-"))
250+
return ExprSyntax(node)
251+
}
252+
}
253+
assertMacroExpansion(
254+
"#test(1 + 2)",
255+
expandedSource: "1 - 2",
256+
macros: ["test": ForceSubtractMacro.self]
257+
)
258+
}
259+
260+
func testDiagnosticFromFoldedOperators() {
261+
struct MyError: Error {}
262+
263+
struct DiagnoseFirstArgument: ExpressionMacro {
264+
static func expansion(
265+
of node: some FreestandingMacroExpansionSyntax,
266+
in context: some MacroExpansionContext
267+
) throws -> ExprSyntax {
268+
guard let argument = node.argumentList.first?.expression else {
269+
fatalError("Must receive an argument")
270+
}
271+
context.addDiagnostics(from: MyError(), node: argument)
272+
return argument
273+
}
274+
}
275+
276+
assertMacroExpansion(
277+
"""
278+
/// Test
279+
func test() {
280+
#test(1 + 2)
281+
}
282+
""",
283+
expandedSource: """
284+
/// Test
285+
func test() {
286+
1 + 2
287+
}
288+
""",
289+
diagnostics: [
290+
DiagnosticSpec(message: "MyError()", line: 3, column: 9, severity: .error)
291+
],
292+
macros: ["test": DiagnoseFirstArgument.self]
293+
)
294+
}
237295
}

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)