Skip to content

Commit 7bb5231

Browse files
authored
Merge pull request #2137 from ahoppen/ahoppen/509/fold-operators
[509] Fold operators using the standard operator table in `MacroSystem`
2 parents d918a1d + 39b5793 commit 7bb5231

File tree

4 files changed

+211
-11
lines changed

4 files changed

+211
-11
lines changed

Sources/SwiftCompilerPluginMessageHandling/PluginMacroExpansionContext.swift

+1-1
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

+15
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

+54-10
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),
@@ -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/MacroSystemTests.swift

+141
Original file line numberDiff line numberDiff line change
@@ -1947,4 +1947,145 @@ final class MacroSystemTests: XCTestCase {
19471947
indentationWidth: indentationWidth
19481948
)
19491949
}
1950+
1951+
func testFoldOperatorsOfFreestandingMacro() {
1952+
struct ForceSubtractMacro: ExpressionMacro {
1953+
static func expansion(
1954+
of node: some FreestandingMacroExpansionSyntax,
1955+
in context: some MacroExpansionContext
1956+
) throws -> ExprSyntax {
1957+
guard let argument = node.argumentList.first?.expression else {
1958+
fatalError("Must receive an argument")
1959+
}
1960+
guard var node = argument.as(InfixOperatorExprSyntax.self) else {
1961+
return argument
1962+
}
1963+
node.operator = ExprSyntax(BinaryOperatorExprSyntax(text: "-"))
1964+
return ExprSyntax(node)
1965+
}
1966+
}
1967+
assertMacroExpansion(
1968+
"#test(1 + 2)",
1969+
expandedSource: "1 - 2",
1970+
macros: ["test": ForceSubtractMacro.self]
1971+
)
1972+
}
1973+
1974+
func testDiagnosticFromFoldedOperatorsInFreestandingMacro() {
1975+
struct MyError: Error {}
1976+
1977+
struct DiagnoseFirstArgument: ExpressionMacro {
1978+
static func expansion(
1979+
of node: some FreestandingMacroExpansionSyntax,
1980+
in context: some MacroExpansionContext
1981+
) throws -> ExprSyntax {
1982+
guard let argument = node.argumentList.first?.expression else {
1983+
fatalError("Must receive an argument")
1984+
}
1985+
context.addDiagnostics(from: MyError(), node: argument)
1986+
return argument
1987+
}
1988+
}
1989+
1990+
assertMacroExpansion(
1991+
"""
1992+
/// Test
1993+
func test() {
1994+
#test(1 + 2)
1995+
}
1996+
""",
1997+
expandedSource: """
1998+
/// Test
1999+
func test() {
2000+
1 + 2
2001+
}
2002+
""",
2003+
diagnostics: [
2004+
DiagnosticSpec(message: "MyError()", line: 3, column: 9, severity: .error)
2005+
],
2006+
macros: ["test": DiagnoseFirstArgument.self]
2007+
)
2008+
}
2009+
2010+
func testFoldOperatorsInAttachedMacro() {
2011+
struct ForceSubtractMacro: MemberMacro {
2012+
static func expansion(
2013+
of node: AttributeSyntax,
2014+
providingMembersOf declaration: some DeclGroupSyntax,
2015+
in context: some MacroExpansionContext
2016+
) throws -> [DeclSyntax] {
2017+
guard case .argumentList(let arguments) = node.arguments, let argument = arguments.first?.expression else {
2018+
fatalError("Must receive an argument")
2019+
}
2020+
guard var node = argument.as(InfixOperatorExprSyntax.self) else {
2021+
return []
2022+
}
2023+
node.operator = ExprSyntax(BinaryOperatorExprSyntax(text: "- "))
2024+
return [
2025+
DeclSyntax(
2026+
"""
2027+
var x: Int { \(node.trimmed) }
2028+
"""
2029+
)
2030+
]
2031+
}
2032+
}
2033+
assertMacroExpansion(
2034+
"""
2035+
/// Test
2036+
/// And another line
2037+
@Test(1 + 2)
2038+
struct Foo {
2039+
}
2040+
""",
2041+
expandedSource: """
2042+
/// Test
2043+
/// And another line
2044+
struct Foo {
2045+
2046+
var x: Int {
2047+
1 - 2
2048+
}
2049+
}
2050+
""",
2051+
macros: ["Test": ForceSubtractMacro.self],
2052+
indentationWidth: indentationWidth
2053+
)
2054+
}
2055+
2056+
func testDiagnosticFromFoldedOperatorsInAttachedMacro() {
2057+
struct MyError: Error {}
2058+
2059+
struct DiagnoseFirstArgument: MemberMacro {
2060+
static func expansion(
2061+
of node: AttributeSyntax,
2062+
providingMembersOf declaration: some DeclGroupSyntax,
2063+
in context: some MacroExpansionContext
2064+
) throws -> [DeclSyntax] {
2065+
guard case .argumentList(let arguments) = node.arguments, let argument = arguments.first?.expression else {
2066+
fatalError("Must receive an argument")
2067+
}
2068+
context.addDiagnostics(from: MyError(), node: argument)
2069+
return []
2070+
}
2071+
}
2072+
2073+
assertMacroExpansion(
2074+
"""
2075+
/// Test
2076+
/// And another line
2077+
@Test(1 + 2)
2078+
struct Foo {}
2079+
""",
2080+
expandedSource: """
2081+
/// Test
2082+
/// And another line
2083+
struct Foo {}
2084+
""",
2085+
diagnostics: [
2086+
DiagnosticSpec(message: "MyError()", line: 3, column: 7, severity: .error)
2087+
],
2088+
macros: ["Test": DiagnoseFirstArgument.self]
2089+
)
2090+
}
19502091
}

0 commit comments

Comments
 (0)