Skip to content

Commit 6f7ffe4

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 shoud to match the compiler behavior. rdar://114786803 Fixes #2128
1 parent acf23c8 commit 6f7ffe4

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
import SwiftSyntax
1516
import SwiftSyntaxBuilder
1617
@_spi(MacroExpansion) import SwiftParser
@@ -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),
@@ -979,4 +980,47 @@ private extension SyntaxProtocol {
979980

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

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
@@ -132,4 +132,87 @@ final class MemberMacroTests: XCTestCase {
132132
indentationWidth: indentationWidth
133133
)
134134
}
135+
136+
func testFoldOperators() {
137+
struct ForceSubtractMacro: MemberMacro {
138+
static func expansion(
139+
of node: AttributeSyntax,
140+
providingMembersOf declaration: some DeclGroupSyntax,
141+
in context: some MacroExpansionContext
142+
) throws -> [DeclSyntax] {
143+
guard case .argumentList(let arguments) = node.arguments, let argument = arguments.first?.expression else {
144+
fatalError("Must receive an argument")
145+
}
146+
guard var node = argument.as(InfixOperatorExprSyntax.self) else {
147+
return []
148+
}
149+
node.operator = ExprSyntax(BinaryOperatorExprSyntax(text: "- "))
150+
return [
151+
DeclSyntax(
152+
"""
153+
var x: Int { \(node.trimmed) }
154+
"""
155+
)
156+
]
157+
}
158+
}
159+
assertMacroExpansion(
160+
"""
161+
/// Test
162+
/// And another line
163+
@Test(1 + 2)
164+
struct Foo {
165+
}
166+
""",
167+
expandedSource: """
168+
/// Test
169+
/// And another line
170+
struct Foo {
171+
172+
var x: Int {
173+
1 - 2
174+
}
175+
}
176+
""",
177+
macros: ["Test": ForceSubtractMacro.self],
178+
indentationWidth: indentationWidth
179+
)
180+
}
181+
182+
func testDiagnosticFromFoldedOperators() {
183+
struct MyError: Error {}
184+
185+
struct DiagnoseFirstArgument: MemberMacro {
186+
static func expansion(
187+
of node: AttributeSyntax,
188+
providingMembersOf declaration: some DeclGroupSyntax,
189+
in context: some MacroExpansionContext
190+
) throws -> [DeclSyntax] {
191+
guard case .argumentList(let arguments) = node.arguments, let argument = arguments.first?.expression else {
192+
fatalError("Must receive an argument")
193+
}
194+
context.addDiagnostics(from: MyError(), node: argument)
195+
return []
196+
}
197+
}
198+
199+
assertMacroExpansion(
200+
"""
201+
/// Test
202+
/// And another line
203+
@Test(1 + 2)
204+
struct Foo {}
205+
""",
206+
expandedSource: """
207+
/// Test
208+
/// And another line
209+
struct Foo {}
210+
""",
211+
diagnostics: [
212+
DiagnosticSpec(message: "MyError()", line: 3, column: 7, severity: .error)
213+
],
214+
macros: ["Test": DiagnoseFirstArgument.self]
215+
)
216+
}
217+
135218
}

0 commit comments

Comments
 (0)