Skip to content

Commit 616978f

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 62e240a commit 616978f

File tree

3 files changed

+147
-20
lines changed

3 files changed

+147
-20
lines changed

Release Notes/601.md

Lines changed: 5 additions & 0 deletions
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

Lines changed: 0 additions & 20 deletions
This file was deleted.

Sources/SwiftSyntax/SymbolName.swift

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
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.trimmedDescription,
21+
andParameters: 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+
// FIXME: If the syntax tree is not generated by parser can this be other token than init?
34+
fromBaseName: "init",
35+
andParameters: signature.parameterClause
36+
)
37+
}
38+
}
39+
40+
extension SubscriptDeclSyntax {
41+
/// The symbol name of the subscript declaration.
42+
///
43+
/// The symbol name is a string representation of the subscript along with its parameter labels.
44+
/// For example, a subscript `subscript(index: Int)` will have the symbol name `subscript(_:)`.
45+
public var symbolName: String {
46+
SwiftSyntax.symbolName(
47+
fromBaseName: "subscript",
48+
andParameters: parameterClause
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 associatedValue = parameterClause else {
62+
return caseName
63+
}
64+
65+
let argumentNames = associatedValue.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)(\(argumentNames))"
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.trimmedDescription,
87+
andParameters: signature.parameterClause
88+
)
89+
}
90+
}
91+
92+
/// Generates the symbol name by combining the base name and parameter labels.
93+
///
94+
/// - Parameters:
95+
/// - baseName: The base name of the symbol (e.g., function name, initializer, subscript).
96+
/// - parameters: The function parameter clause containing the parameter labels.
97+
/// - Returns: The symbol name with the base name and parameter labels combined.
98+
private func symbolName(
99+
fromBaseName baseName: String,
100+
andParameters parameters: FunctionParameterClauseSyntax
101+
) -> String {
102+
let argumentNames = parameters.parameters
103+
.map { parameter in
104+
let argumentLabelText = parameter.argumentName?.text ?? "_"
105+
return argumentLabelText + ":"
106+
}
107+
.joined()
108+
109+
return "\(baseName)(\(argumentNames))"
110+
}
111+
112+
extension FunctionParameterSyntax {
113+
/// The argument name (label) of the function parameter.
114+
///
115+
/// If the parameter has two names (e.g., `external internal: Int`), the first name is considered the argument label.
116+
/// If the parameter has only one name and it's not a subscript parameter, it is considered the argument label.
117+
fileprivate var argumentName: TokenSyntax? {
118+
// If we have two names, the first one is the argument label
119+
if secondName != nil {
120+
return firstName.asIdentifierToken
121+
}
122+
123+
// If we have only one name, it might be an argument label.
124+
if let superParent = parent?.parent?.parent, superParent.is(SubscriptDeclSyntax.self) {
125+
return nil
126+
}
127+
128+
return firstName.asIdentifierToken
129+
}
130+
}
131+
132+
extension TokenSyntax {
133+
/// Converts the token to an identifier token if it represents an identifier.
134+
///
135+
/// - Returns: The trimmed token if it is an identifier or dollar identifier, otherwise `nil`.
136+
fileprivate var asIdentifierToken: TokenSyntax? {
137+
switch tokenKind {
138+
case .identifier, .dollarIdentifier: return self.trimmed
139+
default: return nil
140+
}
141+
}
142+
}

0 commit comments

Comments
 (0)