Skip to content

Commit b7fbd13

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 27e8f91 commit b7fbd13

File tree

5 files changed

+93
-0
lines changed

5 files changed

+93
-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: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,13 @@ 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+
]
75+
),
6976
Trait(
7077
traitName: "EffectSpecifiers",
7178
children: [

CodeGeneration/Tests/ValidateSyntaxNodes/ValidateSyntaxNodes.swift

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,18 +534,56 @@ class ValidateSyntaxNodes: XCTestCase {
534534
failures,
535535
expectedFailures: [
536536
ValidationFailure(node: .accessorParameters, message: "could conform to trait 'NamedDecl' but does not"),
537+
ValidationFailure(node: .arrayElement, message: "could conform to trait 'EffectfulExpr' but does not"),
538+
ValidationFailure(node: .asExpr, message: "could conform to trait 'EffectfulExpr' but does not"),
537539
ValidationFailure(node: .availabilityCondition, message: "could conform to trait 'Parenthesized' but does not"),
540+
ValidationFailure(node: .borrowExpr, message: "could conform to trait 'EffectfulExpr' but does not"),
538541
ValidationFailure(node: ._canImportExpr, message: "could conform to trait 'Parenthesized' but does not"),
542+
ValidationFailure(node: .closureCapture, message: "could conform to trait 'EffectfulExpr' but does not"),
543+
ValidationFailure(node: .consumeExpr, message: "could conform to trait 'EffectfulExpr' but does not"),
544+
ValidationFailure(node: .copyExpr, message: "could conform to trait 'EffectfulExpr' but does not"),
539545
ValidationFailure(
540546
node: .differentiabilityArguments,
541547
message: "could conform to trait 'Parenthesized' but does not"
542548
),
549+
ValidationFailure(node: .discardStmt, message: "could conform to trait 'EffectfulExpr' but does not"),
543550
ValidationFailure(node: .editorPlaceholderDecl, message: "could conform to trait 'MissingNode' but does not"),
544551
ValidationFailure(node: .editorPlaceholderExpr, message: "could conform to trait 'MissingNode' but does not"),
545552
ValidationFailure(node: .enumCaseElement, message: "could conform to trait 'NamedDecl' but does not"),
553+
ValidationFailure(
554+
node: .expressionPattern,
555+
message: "could conform to trait 'EffectfulExpr' but does not"
556+
),
557+
ValidationFailure(node: .expressionStmt, message: "could conform to trait 'EffectfulExpr' but does not"),
558+
ValidationFailure(node: .forceUnwrapExpr, message: "could conform to trait 'EffectfulExpr' but does not"),
546559
ValidationFailure(node: .genericParameter, message: "could conform to trait 'NamedDecl' but does not"),
560+
ValidationFailure(
561+
node: .genericSpecializationExpr,
562+
message: "could conform to trait 'EffectfulExpr' but does not"
563+
),
564+
ValidationFailure(node: .inOutExpr, message: "could conform to trait 'EffectfulExpr' but does not"),
565+
ValidationFailure(node: .isExpr, message: "could conform to trait 'EffectfulExpr' but does not"),
566+
ValidationFailure(node: .labeledExpr, message: "could conform to trait 'EffectfulExpr' but does not"),
567+
ValidationFailure(
568+
node: .optionalChainingExpr,
569+
message: "could conform to trait 'EffectfulExpr' but does not"
570+
),
571+
ValidationFailure(
572+
node: .postfixOperatorExpr,
573+
message: "could conform to trait 'EffectfulExpr' but does not"
574+
),
575+
ValidationFailure(
576+
node: .prefixOperatorExpr,
577+
message: "could conform to trait 'EffectfulExpr' but does not"
578+
),
547579
ValidationFailure(node: .precedenceGroupName, message: "could conform to trait 'NamedDecl' but does not"),
548580
ValidationFailure(node: .primaryAssociatedType, message: "could conform to trait 'NamedDecl' but does not"),
581+
ValidationFailure(node: .thenStmt, message: "could conform to trait 'EffectfulExpr' but does not"),
582+
ValidationFailure(node: .throwStmt, message: "could conform to trait 'EffectfulExpr' but does not"),
583+
ValidationFailure(
584+
node: .yieldedExpression,
585+
message: "could conform to trait 'EffectfulExpr' but does not"
586+
),
549587
ValidationFailure(
550588
node: .yieldedExpressionsClause,
551589
message: "could conform to trait 'Parenthesized' but does not"

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: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,43 @@ 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+
145+
public extension EffectfulExprSyntax {
146+
/// Without this function, the `with` function defined on `SyntaxProtocol`
147+
/// does not work on existentials of this protocol type.
148+
@_disfavoredOverload
149+
func with<T>(_ keyPath: WritableKeyPath<EffectfulExprSyntax, T>, _ newChild: T) -> EffectfulExprSyntax {
150+
var copy: EffectfulExprSyntax = self
151+
copy[keyPath: keyPath] = newChild
152+
return copy
153+
}
154+
}
155+
156+
public extension SyntaxProtocol {
157+
/// Check whether the non-type erased version of this syntax node conforms to
158+
/// `EffectfulExprSyntax`.
159+
/// Note that this will incur an existential conversion.
160+
func isProtocol(_: EffectfulExprSyntax.Protocol) -> Bool {
161+
return self.asProtocol(EffectfulExprSyntax.self) != nil
162+
}
163+
164+
/// Return the non-type erased version of this syntax node if it conforms to
165+
/// `EffectfulExprSyntax`. Otherwise return `nil`.
166+
/// Note that this will incur an existential conversion.
167+
func asProtocol(_: EffectfulExprSyntax.Protocol) -> EffectfulExprSyntax? {
168+
return Syntax(self).asProtocol(SyntaxProtocol.self) as? EffectfulExprSyntax
169+
}
170+
}
171+
135172
// MARK: - EffectSpecifiersSyntax
136173

137174
public protocol EffectSpecifiersSyntax: SyntaxProtocol {
@@ -689,6 +726,8 @@ extension AttributedTypeSyntax: WithAttributesSyntax {}
689726

690727
extension AvailabilityArgumentSyntax: WithTrailingCommaSyntax {}
691728

729+
extension AwaitExprSyntax: EffectfulExprSyntax {}
730+
692731
extension CatchClauseSyntax: WithCodeBlockSyntax {}
693732

694733
extension CatchItemSyntax: WithTrailingCommaSyntax {}
@@ -831,6 +870,8 @@ extension SwitchCaseSyntax: WithStatementsSyntax {}
831870

832871
extension SwitchExprSyntax: BracedSyntax {}
833872

873+
extension TryExprSyntax: EffectfulExprSyntax {}
874+
834875
extension TupleExprSyntax: ParenthesizedSyntax {}
835876

836877
extension TuplePatternElementSyntax: WithTrailingCommaSyntax {}

0 commit comments

Comments
 (0)