Skip to content

Commit 948fcd9

Browse files
committed
[Parser] Replace #function with a macro.
Stop creating `PoundFunctionSyntaxExpr` nodes in the parser. Implement a macro version instead.
1 parent 646870d commit 948fcd9

File tree

7 files changed

+204
-13
lines changed

7 files changed

+204
-13
lines changed

Sources/SwiftParser/Expressions.swift

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,12 +1035,6 @@ extension Parser {
10351035
poundFilePath: tok,
10361036
arena: self.arena
10371037
))
1038-
case (.poundFunctionKeyword, let handle)?:
1039-
let tok = self.eat(handle)
1040-
return RawExprSyntax(RawPoundFunctionExprSyntax(
1041-
poundFunction: tok,
1042-
arena: self.arena
1043-
))
10441038
case (.__function__Keyword, let handle)?:
10451039
let tok = self.eat(handle)
10461040
return RawExprSyntax(RawPoundFunctionExprSyntax(

Sources/SwiftParser/Lexer.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1543,7 +1543,6 @@ extension Lexer.Cursor {
15431543
case "file": kind = .poundFileKeyword
15441544
case "fileID": kind = .poundFileIDKeyword
15451545
case "filePath": kind = .poundFilePathKeyword
1546-
case "function": kind = .poundFunctionKeyword
15471546
case "assert": kind = .poundAssertKeyword
15481547
case "sourceLocation": kind = .poundSourceLocationKeyword
15491548
case "warning": kind = .poundWarningKeyword

Sources/SwiftParser/RawTokenKindSubset.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -743,7 +743,6 @@ enum PrimaryExpressionStart: RawTokenKindSubset {
743743
case poundFileKeyword
744744
case poundFileLiteralKeyword
745745
case poundFilePathKeyword
746-
case poundFunctionKeyword
747746
case poundImageLiteralKeyword
748747
case poundKeyPathKeyword
749748
case poundSelectorKeyword
@@ -780,7 +779,6 @@ enum PrimaryExpressionStart: RawTokenKindSubset {
780779
case .poundFileKeyword: self = .poundFileKeyword
781780
case .poundFileLiteralKeyword: self = .poundFileLiteralKeyword
782781
case .poundFilePathKeyword: self = .poundFilePathKeyword
783-
case .poundFunctionKeyword: self = .poundFunctionKeyword
784782
case .poundImageLiteralKeyword: self = .poundImageLiteralKeyword
785783
case .poundKeyPathKeyword: self = .poundKeyPathKeyword
786784
case .poundSelectorKeyword: self = .poundSelectorKeyword
@@ -820,7 +818,6 @@ enum PrimaryExpressionStart: RawTokenKindSubset {
820818
case .poundFileKeyword: return .poundFileKeyword
821819
case .poundFileLiteralKeyword: return .poundFileLiteralKeyword
822820
case .poundFilePathKeyword: return .poundFilePathKeyword
823-
case .poundFunctionKeyword: return .poundFunctionKeyword
824821
case .poundImageLiteralKeyword: return .poundImageLiteralKeyword
825822
case .poundKeyPathKeyword: return .poundKeyPathKeyword
826823
case .poundSelectorKeyword: return .poundSelectorKeyword

Sources/SwiftSyntaxMacros/MacroEvaluationContext.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,18 @@ import SwiftSyntax
33
/// System-supplied structure that provides information about the context in
44
/// which a given macro is being expanded.
55
public struct MacroEvaluationContext {
6+
/// The name of the module in which the macro is being evaluated.
7+
public let moduleName: String
8+
69
/// Used to map the provided syntax nodes into source locations.
710
public let sourceLocationConverter: SourceLocationConverter
811

912
@_spi(Testing)
10-
public init(sourceLocationConverter: SourceLocationConverter) {
13+
public init(
14+
moduleName: String,
15+
sourceLocationConverter: SourceLocationConverter
16+
) {
17+
self.moduleName = moduleName
1118
self.sourceLocationConverter = sourceLocationConverter
1219
}
1320
}

Sources/SwiftSyntaxMacros/MacroSystem+Examples.swift

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,123 @@ struct LineMacro: ExpressionMacro {
2828
}
2929
}
3030

31+
extension PatternBindingSyntax {
32+
/// When the variable is declaring a single binding, produce the name of
33+
/// that binding.
34+
fileprivate var singleBindingName: String? {
35+
if let identifierPattern = pattern.as(IdentifierPatternSyntax.self) {
36+
return identifierPattern.identifier.text
37+
}
38+
39+
return nil
40+
}
41+
}
42+
43+
struct FunctionMacro: ExpressionMacro {
44+
static var name: String { "function" }
45+
46+
/// Form a function name.
47+
private static func formFunctionName(
48+
_ baseName: String, _ parameters: ParameterClauseSyntax?,
49+
isSubscript: Bool = false
50+
) -> String {
51+
let argumentNames: [String] = parameters?.parameterList.map { param in
52+
if let argumentName = param.firstName?.text,
53+
!isSubscript || param.secondName != nil {
54+
return "\(argumentName):"
55+
}
56+
57+
return "_:"
58+
} ?? []
59+
60+
return "\(baseName)(\(argumentNames.joined(separator: "")))"
61+
}
62+
63+
private static func findEnclosingName(
64+
_ macro: MacroExpansionExprSyntax
65+
) -> String? {
66+
var currentNode = Syntax(macro)
67+
while let parent = currentNode.parent {
68+
switch parent.as(SyntaxEnum.self) {
69+
case .accessorDecl(let accessor):
70+
if let accessorList = accessor.parent?.as(AccessorListSyntax.self),
71+
let accessorBlock = accessorList.parent?.as(AccessorBlockSyntax.self),
72+
let binding = accessorBlock.parent?.as(PatternBindingSyntax.self),
73+
let varName = binding.singleBindingName {
74+
return varName
75+
}
76+
77+
break
78+
79+
case .functionDecl(let function):
80+
return formFunctionName(
81+
function.identifier.text, function.signature.input
82+
)
83+
84+
case .initializerDecl(let initializer):
85+
return formFunctionName("init", initializer.signature.input)
86+
87+
case .subscriptDecl(let subscriptDecl):
88+
return formFunctionName(
89+
"subscript", subscriptDecl.indices, isSubscript: true
90+
)
91+
92+
case .enumCaseElement(let enumCase):
93+
return formFunctionName(
94+
enumCase.identifier.text, enumCase.associatedValue
95+
)
96+
97+
case .structDecl(let structDecl):
98+
return structDecl.identifier.text
99+
100+
case .enumDecl(let enumDecl):
101+
return enumDecl.identifier.text
102+
103+
case .classDecl(let classDecl):
104+
return classDecl.identifier.text
105+
106+
case .actorDecl(let actorDecl):
107+
return actorDecl.identifier.text
108+
109+
case .protocolDecl(let protocolDecl):
110+
return protocolDecl.identifier.text
111+
112+
case .extensionDecl(let extensionDecl):
113+
// FIXME: It would be nice to be able to switch on type syntax...
114+
let extendedType = extensionDecl.extendedType
115+
if let simple = extendedType.as(SimpleTypeIdentifierSyntax.self) {
116+
return simple.name.text
117+
}
118+
119+
if let member = extendedType.as(MemberTypeIdentifierSyntax.self) {
120+
return member.name.text
121+
}
122+
123+
return nil
124+
125+
default:
126+
break
127+
}
128+
129+
currentNode = parent
130+
}
131+
132+
return nil
133+
}
134+
135+
static func apply(
136+
_ macro: MacroExpansionExprSyntax, in context: MacroEvaluationContext
137+
) -> MacroResult<ExprSyntax> {
138+
let name = findEnclosingName(macro) ?? context.moduleName
139+
let literal: ExprSyntax = #""\#(name)""#
140+
if let leadingTrivia = macro.leadingTrivia {
141+
return .init(literal.withLeadingTrivia(leadingTrivia))
142+
}
143+
144+
return .init(literal)
145+
}
146+
}
147+
31148
struct Stringify: ExpressionMacro {
32149
static var name: String { "stringify" }
33150

@@ -61,6 +178,7 @@ extension MacroSystem {
61178
public static var exampleSystem: MacroSystem {
62179
var macroSystem = MacroSystem()
63180
try! macroSystem.add(ColumnMacro.self)
181+
try! macroSystem.add(FunctionMacro.self)
64182
try! macroSystem.add(LineMacro.self)
65183
try! macroSystem.add(Stringify.self)
66184
try! macroSystem.add(ColorLiteral.self)

Sources/swift-parser-cli/swift-parser-cli.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,9 @@ class ExpandMacros: ParsableCommand {
227227
}
228228

229229
let converter = SourceLocationConverter(file: self.sourceFile, tree: resultTree)
230-
let context = MacroEvaluationContext(sourceLocationConverter: converter)
230+
let context = MacroEvaluationContext(
231+
moduleName: "MyModule", sourceLocationConverter: converter
232+
)
231233
var diags = ParseDiagnosticsGenerator.diagnostics(for: tree)
232234
let transformedTree = MacroSystem.exampleSystem.evaluateMacros(
233235
node: resultTree, in: context

Tests/SwiftSyntaxMacrosTest/MacroSystemTests.swift

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ final class MacroSystemTests: XCTestCase {
1515
let c = #column
1616
"""
1717
let converter = SourceLocationConverter(file: "test.swift", tree: sf)
18-
let context = MacroEvaluationContext(sourceLocationConverter: converter)
18+
let context = MacroEvaluationContext(
19+
moduleName: "MyModule", sourceLocationConverter: converter
20+
)
1921
let transformedSF = MacroSystem.exampleSystem.evaluateMacros(
2022
node: sf, in: context, errorHandler: { error in }
2123
)
@@ -30,4 +32,76 @@ final class MacroSystemTests: XCTestCase {
3032
"""
3133
)
3234
}
35+
36+
func testPoundFunctionExpansion() {
37+
let sf: SourceFileSyntax =
38+
"""
39+
func f(a: Int, _: Double, c: Int) {
40+
print(#function)
41+
}
42+
43+
struct X {
44+
var computed: String {
45+
get {
46+
(#function)
47+
}
48+
}
49+
50+
init(from: String) {
51+
(#function)
52+
}
53+
54+
subscript(a: Int) -> String {
55+
(#function)
56+
}
57+
58+
subscript(a a: Int) -> String {
59+
(#function)
60+
}
61+
}
62+
63+
extension A {
64+
static var staticProp: String = #function
65+
}
66+
"""
67+
let converter = SourceLocationConverter(file: "test.swift", tree: sf)
68+
let context = MacroEvaluationContext(
69+
moduleName: "MyModule", sourceLocationConverter: converter
70+
)
71+
let transformedSF = MacroSystem.exampleSystem.evaluateMacros(
72+
node: sf, in: context, errorHandler: { error in }
73+
)
74+
AssertStringsEqualWithDiff(
75+
transformedSF.description,
76+
"""
77+
func f(a: Int, _: Double, c: Int) {
78+
print("f(a:_:c:)")
79+
}
80+
81+
struct X {
82+
var computed: String {
83+
get {
84+
("computed")
85+
}
86+
}
87+
88+
init(from: String) {
89+
("init(from:)")
90+
}
91+
92+
subscript(a: Int) -> String {
93+
("subscript(_:)")
94+
}
95+
96+
subscript(a a: Int) -> String {
97+
("subscript(a:)")
98+
}
99+
}
100+
101+
extension A {
102+
static var staticProp: String = "A"
103+
}
104+
"""
105+
)
106+
}
33107
}

0 commit comments

Comments
 (0)