Skip to content

Commit f19e5dd

Browse files
committed
Add EffectfulExprSyntax syntax trait
Introduces the `EffectfulExprSyntax` protocol, designed to provide a common interface for effectful expressions such as `try` and `await`. Solves #2549
1 parent cfd0487 commit f19e5dd

File tree

6 files changed

+119
-0
lines changed

6 files changed

+119
-0
lines changed

CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ public let EXPR_NODES: [Node] = [
152152
kind: .awaitExpr,
153153
base: .expr,
154154
nameForDiagnostics: "'await' expression",
155+
traits: [
156+
"EffectfulExpr"
157+
],
155158
children: [
156159
Child(
157160
name: "awaitKeyword",
@@ -1914,6 +1917,9 @@ public let EXPR_NODES: [Node] = [
19141917
try! foo()
19151918
```
19161919
""",
1920+
traits: [
1921+
"EffectfulExpr"
1922+
],
19171923
children: [
19181924
Child(
19191925
name: "tryKeyword",

CodeGeneration/Sources/SyntaxSupport/Traits.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,18 @@ public let TRAITS: [Trait] = [
6666
Child(name: "memberBlock", kind: .node(kind: .memberBlock)),
6767
]
6868
),
69+
Trait(
70+
traitName: "EffectfulExpr",
71+
documentation: "Syntax trait for effectful expressions, such as `try` and `await`.",
72+
children: [
73+
Child(name: "expression", kind: .node(kind: .expr)),
74+
Child(
75+
name: "keyword",
76+
kind: .token(choices: [.keyword(.try), .keyword(.await)]),
77+
documentation: "The token that represents the specific keyword of the effectful expression."
78+
),
79+
]
80+
),
6981
Trait(
7082
traitName: "EffectSpecifiers",
7183
children: [

Release Notes/610.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Swift Syntax 610 Release Notes
2+
3+
## New APIs
4+
5+
- `EffectfulExprSyntax`
6+
- Description: A protocol for unified handling of `try` and `await` expressions, with a `keyword` property to identify the expression's specific keyword.
7+
- Issue: https://github.com/apple/swift-syntax/issues/2549
8+
- Pull Request: https://github.com/apple/swift-syntax/pull/2572
9+
10+
## API Behavior Changes
11+
12+
## Deprecations
13+
14+
## API-Incompatible Changes
15+
16+
## Template
17+
18+
- *Affected API or two word description*
19+
- Description: *A 1-2 sentence description of the new/modified API*
20+
- Issue: *If an issue exists for this change, a link to the issue*
21+
- Pull Request: *Link to the pull request(s) that introduces this change*
22+
- Migration steps: Steps that adopters of swift-syntax should take to move to the new API (required for deprecations and API-incompatible changes).
23+
- Notes: *In case of deprecations or API-incompatible changes, the reason why this change was made and the suggested alternative*
24+
25+
*Insert entries in chronological order, with newer entries at the bottom*

Sources/SwiftSyntax/CustomTraits.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,17 @@ extension ActorDeclSyntax {
2121
}
2222
}
2323

24+
extension AwaitExprSyntax {
25+
public var keyword: TokenSyntax {
26+
get {
27+
return awaitKeyword
28+
}
29+
set {
30+
awaitKeyword = newValue
31+
}
32+
}
33+
}
34+
2435
extension ClassDeclSyntax {
2536
public var introducer: TokenSyntax {
2637
get {
@@ -76,6 +87,17 @@ extension StructDeclSyntax {
7687
}
7788
}
7889

90+
extension TryExprSyntax {
91+
public var keyword: TokenSyntax {
92+
get {
93+
return tryKeyword
94+
}
95+
set {
96+
tryKeyword = newValue
97+
}
98+
}
99+
}
100+
79101
//==========================================================================//
80102
// IMPORTANT: If you are tempted to add an extension here, please insert //
81103
// it in alphabetical order above //

Sources/SwiftSyntax/Documentation.docc/generated/SwiftSyntax.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,7 @@ These articles are intended for developers wishing to contribute to SwiftSyntax
392392

393393
- <doc:SwiftSyntax/BracedSyntax>
394394
- <doc:SwiftSyntax/DeclGroupSyntax>
395+
- <doc:SwiftSyntax/EffectfulExprSyntax>
395396
- <doc:SwiftSyntax/EffectSpecifiersSyntax>
396397
- <doc:SwiftSyntax/FreestandingMacroExpansionSyntax>
397398
- <doc:SwiftSyntax/NamedDeclSyntax>

Sources/SwiftSyntax/generated/SyntaxTraits.swift

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,55 @@ public extension SyntaxProtocol {
132132
}
133133
}
134134

135+
// MARK: - EffectfulExprSyntax
136+
137+
/// Syntax trait for effectful expressions, such as `try` and `await`.
138+
public protocol EffectfulExprSyntax: SyntaxProtocol {
139+
var expression: ExprSyntax {
140+
get
141+
set
142+
}
143+
144+
/// The token that represents the specific keyword of the effectful expression.
145+
///
146+
/// ### Tokens
147+
///
148+
/// For syntax trees generated by the parser, this is guaranteed to be one of the following kinds:
149+
/// - `try`
150+
/// - `await`
151+
var keyword: TokenSyntax {
152+
get
153+
set
154+
}
155+
}
156+
157+
public extension EffectfulExprSyntax {
158+
/// Without this function, the `with` function defined on `SyntaxProtocol`
159+
/// does not work on existentials of this protocol type.
160+
@_disfavoredOverload
161+
func with<T>(_ keyPath: WritableKeyPath<EffectfulExprSyntax, T>, _ newChild: T) -> EffectfulExprSyntax {
162+
var copy: EffectfulExprSyntax = self
163+
copy[keyPath: keyPath] = newChild
164+
return copy
165+
}
166+
}
167+
168+
public extension SyntaxProtocol {
169+
/// Check whether the non-type erased version of this syntax node conforms to
170+
/// `EffectfulExprSyntax`.
171+
/// Note that this will incur an existential conversion.
172+
func isProtocol(_: EffectfulExprSyntax.Protocol) -> Bool {
173+
return self.asProtocol(EffectfulExprSyntax.self) != nil
174+
}
175+
176+
/// Return the non-type erased version of this syntax node if it conforms to
177+
/// `EffectfulExprSyntax`. Otherwise return `nil`.
178+
/// Note that this will incur an existential conversion.
179+
func asProtocol(_: EffectfulExprSyntax.Protocol) -> EffectfulExprSyntax? {
180+
return Syntax(self).asProtocol(SyntaxProtocol.self) as? EffectfulExprSyntax
181+
}
182+
}
183+
135184
// MARK: - EffectSpecifiersSyntax
136185

137186
public protocol EffectSpecifiersSyntax: SyntaxProtocol {
@@ -689,6 +738,8 @@ extension AttributedTypeSyntax: WithAttributesSyntax {}
689738

690739
extension AvailabilityArgumentSyntax: WithTrailingCommaSyntax {}
691740

741+
extension AwaitExprSyntax: EffectfulExprSyntax {}
742+
692743
extension CatchClauseSyntax: WithCodeBlockSyntax {}
693744

694745
extension CatchItemSyntax: WithTrailingCommaSyntax {}
@@ -831,6 +882,8 @@ extension SwitchCaseSyntax: WithStatementsSyntax {}
831882

832883
extension SwitchExprSyntax: BracedSyntax {}
833884

885+
extension TryExprSyntax: EffectfulExprSyntax {}
886+
834887
extension TupleExprSyntax: ParenthesizedSyntax {}
835888

836889
extension TuplePatternElementSyntax: WithTrailingCommaSyntax {}

0 commit comments

Comments
 (0)