Skip to content

Commit 9a4f045

Browse files
committed
Add symbolName property for some syntax nodes
`FunctionDecl`, `InitializerDecl`, `SubscriptDecl`, `EnumCaseElement`, and `MacroDecl` gained a new computed property: `symbolName` The `symbolName` property provides a string representation of the declaration's name along with its parameter labels. For example, a function `func greet(name: String)` will have the symbol name `greet(name:)`.
1 parent 8775262 commit 9a4f045

File tree

3 files changed

+144
-20
lines changed

3 files changed

+144
-20
lines changed

Release Notes/601.md

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
## New APIs
44

5+
- `FunctionDecl`, `InitializerDecl`, `SubscriptDecl`, `EnumCaseElement`, and `MacroDecl` gained a new computed property: `symbolName`
6+
- Description: The `symbolName` property provides a string representation of the declaration's name along with its parameter labels. For example, a function `func greet(name: String)` will have the symbol name `greet(name:)`.
7+
- Issue: https://github.com/apple/swift-syntax/issues/2488
8+
- Pull Request: https://github.com/apple/swift-syntax/pull/2583
9+
510
## API Behavior Changes
611

712
## Deprecations

Release Notes/610.md

-20
This file was deleted.

Sources/SwiftSyntax/SymbolName.swift

+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
extension FunctionDeclSyntax {
14+
/// The symbol name of the function declaration.
15+
///
16+
/// The symbol name is a string representation of the function name along with its parameter labels.
17+
/// For example, a function `func greet(name: String)` will have the symbol name `greet(name:)`.
18+
public var symbolName: String {
19+
SwiftSyntax.symbolName(
20+
fromBaseName: name.text,
21+
parameters: signature.parameterClause
22+
)
23+
}
24+
}
25+
26+
extension InitializerDeclSyntax {
27+
/// The symbol name of the initializer declaration.
28+
///
29+
/// The symbol name is a string representation of the initializer along with its parameter labels.
30+
/// For example, an initializer `init(name: String)` will have the symbol name `init(name:)`.
31+
public var symbolName: String {
32+
SwiftSyntax.symbolName(
33+
fromBaseName: initKeyword.text,
34+
parameters: signature.parameterClause
35+
)
36+
}
37+
}
38+
39+
extension SubscriptDeclSyntax {
40+
/// The symbol name of the subscript declaration.
41+
///
42+
/// The symbol name is a string representation of the subscript along with its parameter labels.
43+
/// For example, a subscript `subscript(index: Int)` will have the symbol name `subscript(_:)`.
44+
public var symbolName: String {
45+
SwiftSyntax.symbolName(
46+
fromBaseName: subscriptKeyword.text,
47+
parameters: parameterClause,
48+
isForSubscript: true
49+
)
50+
}
51+
}
52+
53+
extension EnumCaseElementSyntax {
54+
/// The symbol name of the enum case element.
55+
///
56+
/// The symbol name is a string representation of the enum case name along with its associated value labels (if any).
57+
/// For example, an enum case `case foo(bar: Int)` will have the symbol name `foo(bar:)`.
58+
public var symbolName: String {
59+
let caseName = name.text
60+
61+
guard let associatedValues = parameterClause else {
62+
return caseName
63+
}
64+
65+
let argumentLabels = associatedValues.parameters
66+
.map { parameter in
67+
guard let firstName = parameter.firstName else {
68+
return "_:"
69+
}
70+
71+
return firstName.text + ":"
72+
}
73+
.joined()
74+
75+
return "\(caseName)(\(argumentLabels))"
76+
}
77+
}
78+
79+
extension MacroDeclSyntax {
80+
/// The symbol name of the macro declaration.
81+
///
82+
/// The symbol name is a string representation of the macro name along with its parameter labels.
83+
/// For example, a macro `macro greet(name: String)` will have the symbol name `greet(name:)`.
84+
public var symbolName: String {
85+
SwiftSyntax.symbolName(
86+
fromBaseName: name.text,
87+
parameters: signature.parameterClause
88+
)
89+
}
90+
}
91+
92+
/// Generates the symbol name by combining the base name and parameter labels.
93+
///
94+
/// If the parameter has two names (e.g., `external internal: Int`), the first name is considered the argument label.
95+
/// If the parameter has only one name and it's not a subscript parameter, it is considered the argument label.
96+
///
97+
/// - Parameters:
98+
/// - baseName: The base name of the symbol (e.g., function name, initializer, subscript).
99+
/// - parameters: The function parameter clause containing the parameter labels.
100+
/// - Returns: The symbol name with the base name and parameter labels combined.
101+
private func symbolName(
102+
fromBaseName baseName: String,
103+
parameters: FunctionParameterClauseSyntax,
104+
isForSubscript: Bool = false
105+
) -> String {
106+
let argumentLabels = parameters.parameters
107+
.map { parameter in
108+
let argumentLabelText = parameter.argumentName(isForSubscript: isForSubscript) ?? "_"
109+
return argumentLabelText + ":"
110+
}
111+
.joined()
112+
113+
return "\(baseName)(\(argumentLabels))"
114+
}
115+
116+
extension FunctionParameterSyntax {
117+
/// The argument name (label) of the function parameter.
118+
fileprivate func argumentName(isForSubscript: Bool = false) -> String? {
119+
// If we have two names, the first one is the argument label
120+
if secondName != nil {
121+
return firstName.asIdentifierToken
122+
}
123+
124+
if isForSubscript {
125+
return nil
126+
}
127+
128+
return firstName.asIdentifierToken
129+
}
130+
}
131+
132+
extension TokenSyntax {
133+
fileprivate var asIdentifierToken: String? {
134+
switch tokenKind {
135+
case .identifier: return self.text
136+
default: return nil
137+
}
138+
}
139+
}

0 commit comments

Comments
 (0)