Skip to content

Commit 2712b63

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 23be62c commit 2712b63

File tree

4 files changed

+212
-11
lines changed

4 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),
@@ -1007,4 +1008,47 @@ private extension SyntaxProtocol {
10071008

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

Tests/SwiftSyntaxMacroExpansionTest/MacroSystemTests.swift

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1894,4 +1894,146 @@ final class MacroSystemTests: XCTestCase {
18941894
macros: ["decl": DeclMacro.self, "Peer": MyPeerMacro.self]
18951895
)
18961896
}
1897+
1898+
func testFoldOperatorsOfFreestandingMacro() {
1899+
struct ForceSubtractMacro: ExpressionMacro {
1900+
static func expansion(
1901+
of node: some FreestandingMacroExpansionSyntax,
1902+
in context: some MacroExpansionContext
1903+
) throws -> ExprSyntax {
1904+
guard let argument = node.argumentList.first?.expression else {
1905+
fatalError("Must receive an argument")
1906+
}
1907+
guard var node = argument.as(InfixOperatorExprSyntax.self) else {
1908+
return argument
1909+
}
1910+
node.operator = ExprSyntax(BinaryOperatorExprSyntax(text: "-"))
1911+
return ExprSyntax(node)
1912+
}
1913+
}
1914+
assertMacroExpansion(
1915+
"#test(1 + 2)",
1916+
expandedSource: "1 - 2",
1917+
macros: ["test": ForceSubtractMacro.self]
1918+
)
1919+
}
1920+
1921+
func testDiagnosticFromFoldedOperatorsInFreestandingMacro() {
1922+
struct MyError: Error {}
1923+
1924+
struct DiagnoseFirstArgument: ExpressionMacro {
1925+
static func expansion(
1926+
of node: some FreestandingMacroExpansionSyntax,
1927+
in context: some MacroExpansionContext
1928+
) throws -> ExprSyntax {
1929+
guard let argument = node.argumentList.first?.expression else {
1930+
fatalError("Must receive an argument")
1931+
}
1932+
context.addDiagnostics(from: MyError(), node: argument)
1933+
return argument
1934+
}
1935+
}
1936+
1937+
assertMacroExpansion(
1938+
"""
1939+
/// Test
1940+
func test() {
1941+
#test(1 + 2)
1942+
}
1943+
""",
1944+
expandedSource: """
1945+
/// Test
1946+
func test() {
1947+
1 + 2
1948+
}
1949+
""",
1950+
diagnostics: [
1951+
DiagnosticSpec(message: "MyError()", line: 3, column: 9, severity: .error)
1952+
],
1953+
macros: ["test": DiagnoseFirstArgument.self]
1954+
)
1955+
}
1956+
1957+
func testFoldOperatorsInAttachedMacro() {
1958+
struct ForceSubtractMacro: MemberMacro {
1959+
static func expansion(
1960+
of node: AttributeSyntax,
1961+
providingMembersOf declaration: some DeclGroupSyntax,
1962+
in context: some MacroExpansionContext
1963+
) throws -> [DeclSyntax] {
1964+
guard case .argumentList(let arguments) = node.arguments, let argument = arguments.first?.expression else {
1965+
fatalError("Must receive an argument")
1966+
}
1967+
guard var node = argument.as(InfixOperatorExprSyntax.self) else {
1968+
return []
1969+
}
1970+
node.operator = ExprSyntax(BinaryOperatorExprSyntax(text: "- "))
1971+
return [
1972+
DeclSyntax(
1973+
"""
1974+
var x: Int { \(node.trimmed) }
1975+
"""
1976+
)
1977+
]
1978+
}
1979+
}
1980+
assertMacroExpansion(
1981+
"""
1982+
/// Test
1983+
/// And another line
1984+
@Test(1 + 2)
1985+
struct Foo {
1986+
}
1987+
""",
1988+
expandedSource: """
1989+
/// Test
1990+
/// And another line
1991+
struct Foo {
1992+
1993+
var x: Int {
1994+
1 - 2
1995+
}
1996+
}
1997+
""",
1998+
macros: ["Test": ForceSubtractMacro.self],
1999+
indentationWidth: indentationWidth
2000+
)
2001+
}
2002+
2003+
func testDiagnosticFromFoldedOperatorsInAttachedMacro() {
2004+
struct MyError: Error {}
2005+
2006+
struct DiagnoseFirstArgument: MemberMacro {
2007+
static func expansion(
2008+
of node: AttributeSyntax,
2009+
providingMembersOf declaration: some DeclGroupSyntax,
2010+
in context: some MacroExpansionContext
2011+
) throws -> [DeclSyntax] {
2012+
guard case .argumentList(let arguments) = node.arguments, let argument = arguments.first?.expression else {
2013+
fatalError("Must receive an argument")
2014+
}
2015+
context.addDiagnostics(from: MyError(), node: argument)
2016+
return []
2017+
}
2018+
}
2019+
2020+
assertMacroExpansion(
2021+
"""
2022+
/// Test
2023+
/// And another line
2024+
@Test(1 + 2)
2025+
struct Foo {}
2026+
""",
2027+
expandedSource: """
2028+
/// Test
2029+
/// And another line
2030+
struct Foo {}
2031+
""",
2032+
diagnostics: [
2033+
DiagnosticSpec(message: "MyError()", line: 3, column: 7, severity: .error)
2034+
],
2035+
macros: ["Test": DiagnoseFirstArgument.self]
2036+
)
2037+
}
2038+
18972039
}

0 commit comments

Comments
 (0)