@@ -14,7 +14,7 @@ import SwiftDiagnostics
14
14
import SwiftParser
15
15
import SwiftSyntax
16
16
import SwiftSyntaxBuilder
17
- import SwiftSyntaxMacros
17
+ @ _spi ( Testing ) import SwiftSyntaxMacros
18
18
import _SwiftSyntaxTestSupport
19
19
import XCTest
20
20
@@ -145,6 +145,125 @@ public struct FileIDMacro: ExpressionMacro {
145
145
}
146
146
}
147
147
148
+ extension PatternBindingSyntax {
149
+ /// When the variable is declaring a single binding, produce the name of
150
+ /// that binding.
151
+ fileprivate var singleBindingName : String ? {
152
+ if let identifierPattern = pattern. as ( IdentifierPatternSyntax . self) {
153
+ return identifierPattern. identifier. text
154
+ }
155
+
156
+ return nil
157
+ }
158
+ }
159
+
160
+ extension SyntaxProtocol {
161
+ /// Form a function name.
162
+ private func formFunctionName(
163
+ _ baseName: String , _ parameters: ParameterClauseSyntax ? ,
164
+ isSubscript: Bool = false
165
+ ) -> String {
166
+ let argumentNames : [ String ] = parameters? . parameterList. map { param in
167
+ let argumentLabelText = param. argumentName? . text ?? " _ "
168
+ return argumentLabelText + " : "
169
+ } ?? [ ]
170
+
171
+ return " \( baseName) ( \( argumentNames. joined ( separator: " " ) ) ) "
172
+ }
173
+
174
+ /// Form the #function name for the given node.
175
+ fileprivate func functionName< Context: MacroExpansionContext > (
176
+ in context: Context
177
+ ) -> String ? {
178
+ // Declarations with parameters.
179
+ // FIXME: Can we abstract over these?
180
+ if let function = self . as ( FunctionDeclSyntax . self) {
181
+ return formFunctionName (
182
+ function. identifier. text, function. signature. input
183
+ )
184
+ }
185
+
186
+ if let initializer = self . as ( InitializerDeclSyntax . self) {
187
+ return formFunctionName ( " init " , initializer. signature. input)
188
+ }
189
+
190
+ if let subscriptDecl = self . as ( SubscriptDeclSyntax . self) {
191
+ return formFunctionName (
192
+ " subscript " , subscriptDecl. indices, isSubscript: true
193
+ )
194
+ }
195
+
196
+ if let enumCase = self . as ( EnumCaseElementSyntax . self) {
197
+ guard let associatedValue = enumCase. associatedValue else {
198
+ return enumCase. identifier. text
199
+ }
200
+
201
+ let argumentNames = associatedValue. parameterList. map { param in
202
+ guard let firstName = param. firstName else {
203
+ return " _: "
204
+ }
205
+
206
+ return firstName. text + " : "
207
+ } . joined ( )
208
+
209
+ return " \( enumCase. identifier. text) ( \( argumentNames) ) "
210
+ }
211
+
212
+ // Accessors use their enclosing context, i.e., a subscript or pattern
213
+ // binding.
214
+ if self . is ( AccessorDeclSyntax . self) {
215
+ guard let parentContext = context. parentContext ( of: self ) else {
216
+ return nil
217
+ }
218
+
219
+ return parentContext. functionName ( in: context)
220
+ }
221
+
222
+ // All declarations with identifiers.
223
+ if let identified = self . asProtocol ( IdentifiedDeclSyntax . self) {
224
+ return identified. identifier. text
225
+ }
226
+
227
+ // Extensions
228
+ if let extensionDecl = self . as ( ExtensionDeclSyntax . self) {
229
+ // FIXME: It would be nice to be able to switch on type syntax...
230
+ let extendedType = extensionDecl. extendedType
231
+ if let simple = extendedType. as ( SimpleTypeIdentifierSyntax . self) {
232
+ return simple. name. text
233
+ }
234
+
235
+ if let member = extendedType. as ( MemberTypeIdentifierSyntax . self) {
236
+ return member. name. text
237
+ }
238
+ }
239
+
240
+ // Pattern bindings.
241
+ if let patternBinding = self . as ( PatternBindingSyntax . self) ,
242
+ let singleVarName = patternBinding. singleBindingName {
243
+ return singleVarName
244
+ }
245
+
246
+ return nil
247
+ }
248
+ }
249
+
250
+ public struct FunctionMacro : ExpressionMacro {
251
+ public static func expansion<
252
+ Node: FreestandingMacroExpansionSyntax ,
253
+ Context: MacroExpansionContext
254
+ > (
255
+ of node: Node ,
256
+ in context: Context
257
+ ) -> ExprSyntax {
258
+ guard let parentContext = context. parentContext ( of: node) ,
259
+ let name = parentContext. functionName ( in: context) else {
260
+ return #""<unknown>""#
261
+ }
262
+
263
+ return ExprSyntax ( " \( literal: name) " ) . with ( \. leadingTrivia, node. leadingTrivia)
264
+ }
265
+ }
266
+
148
267
/// Macro whose only purpose is to ensure that we cannot see "out" of the
149
268
/// macro expansion syntax node we were given.
150
269
struct CheckContextIndependenceMacro : ExpressionMacro {
@@ -707,6 +826,7 @@ public let testMacros: [String: Macro.Type] = [
707
826
" colorLiteral " : ColorLiteralMacro . self,
708
827
" column " : ColumnMacro . self,
709
828
" fileID " : FileIDMacro . self,
829
+ " function " : FunctionMacro . self,
710
830
" imageLiteral " : ImageLiteralMacro . self,
711
831
" stringify " : StringifyMacro . self,
712
832
" myError " : ErrorMacro . self,
@@ -769,6 +889,70 @@ final class MacroSystemTests: XCTestCase {
769
889
)
770
890
}
771
891
892
+ func testPoundFunction( ) {
893
+ assertMacroExpansion (
894
+ macros: testMacros,
895
+ """
896
+ func f(a: Int, _: Double, c: Int) {
897
+ print(#function)
898
+ }
899
+
900
+ struct X {
901
+ var computed: String {
902
+ get {
903
+ #function
904
+ }
905
+ }
906
+
907
+ init(from: String) {
908
+ #function
909
+ }
910
+
911
+ subscript(a: Int) -> String {
912
+ #function
913
+ }
914
+
915
+ subscript(a a: Int) -> String {
916
+ #function
917
+ }
918
+ }
919
+
920
+ extension A {
921
+ static var staticProp: String = #function
922
+ }
923
+ """ ,
924
+ """
925
+ func f(a: Int, _: Double, c: Int) {
926
+ print( " f(a:_:c:) " )
927
+ }
928
+
929
+ struct X {
930
+ var computed: String {
931
+ get {
932
+ " computed "
933
+ }
934
+ }
935
+
936
+ init(from: String) {
937
+ " init(from:) "
938
+ }
939
+
940
+ subscript(a: Int) -> String {
941
+ " subscript(_:) "
942
+ }
943
+
944
+ subscript(a a: Int) -> String {
945
+ " subscript(a:) "
946
+ }
947
+ }
948
+
949
+ extension A {
950
+ static var staticProp: String = " staticProp "
951
+ }
952
+ """
953
+ )
954
+ }
955
+
772
956
func testContextUniqueLocalNames( ) {
773
957
let context = BasicMacroExpansionContext ( )
774
958
0 commit comments