Skip to content

[SE-0413] Add a throws clause to do..catch statements #2381

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CodeGeneration/Sources/SyntaxSupport/StmtNodes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,13 @@ public let STMT_NODES: [Node] = [
name: "doKeyword",
kind: .token(choices: [.keyword(.do)])
),
Child(
name: "throwsClause",
kind: .node(kind: .throwsClause),
experimentalFeature: .typedThrows,
documentation: "The clause specifying the type of errors thrown from the 'do' block.",
isOptional: true
),
Child(
name: "body",
kind: .node(kind: .codeBlock),
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftParser/Specifiers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,7 @@ extension TokenConsumer {

extension Parser {
/// Parse a throws clause after we've already parsed the 'throws' keyword to introduce it.
private mutating func parseThrowsClause(after throwsKeyword: RawTokenSyntax) -> RawThrowsClauseSyntax {
mutating func parseThrowsClause(after throwsKeyword: RawTokenSyntax) -> RawThrowsClauseSyntax {
guard self.at(.leftParen) && experimentalFeatures.contains(.typedThrows) else {
return RawThrowsClauseSyntax(
throwsSpecifier: throwsKeyword,
Expand Down
10 changes: 10 additions & 0 deletions Sources/SwiftParser/Statements.swift
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,15 @@ extension Parser {
/// Parse a do statement.
mutating func parseDoStatement(doHandle: RecoveryConsumptionHandle) -> RawDoStmtSyntax {
let (unexpectedBeforeDoKeyword, doKeyword) = self.eat(doHandle)

// Parse the optional throws clause.
let throwsClause: RawThrowsClauseSyntax?
if let throwsSpecifier = self.consume(if: .keyword(.throws)) {
throwsClause = parseThrowsClause(after: throwsSpecifier)
} else {
throwsClause = nil
}

let body = self.parseCodeBlock(introducer: doKeyword)

// If the next token is 'catch', this is a 'do'/'catch' statement.
Expand All @@ -402,6 +411,7 @@ extension Parser {
return RawDoStmtSyntax(
unexpectedBeforeDoKeyword,
doKeyword: doKeyword,
throwsClause: throwsClause,
body: body,
catchClauses: RawCatchClauseListSyntax(elements: elements, arena: self.arena),
arena: self.arena
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,13 +282,13 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
}
}

if let throwsSpecifier = node.throwsSpecifier {
if let throwsClause = node.throwsClause {
exchangeTokens(
unexpected: node.unexpectedAfterThrowsClause,
unexpectedTokenCondition: { AsyncEffectSpecifier(token: $0) != nil },
correctTokens: [node.asyncSpecifier],
message: { AsyncMustPrecedeThrows(asyncKeywords: $0, throwsKeyword: throwsSpecifier) },
moveFixIt: { MoveTokensInFrontOfFixIt(movedTokens: $0, inFrontOf: throwsSpecifier.tokenKind) },
message: { AsyncMustPrecedeThrows(asyncKeywords: $0, throwsKeyword: throwsClause.throwsSpecifier) },
moveFixIt: { MoveTokensInFrontOfFixIt(movedTokens: $0, inFrontOf: throwsClause.throwsSpecifier.tokenKind) },
removeRedundantFixIt: { RemoveRedundantFixIt(removeTokens: $0) }
)
}
Expand Down
8 changes: 6 additions & 2 deletions Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1029,8 +1029,12 @@ public func childName(_ keyPath: AnyKeyPath) -> String? {
return "unexpectedBeforeDoKeyword"
case \DoStmtSyntax.doKeyword:
return "doKeyword"
case \DoStmtSyntax.unexpectedBetweenDoKeywordAndBody:
return "unexpectedBetweenDoKeywordAndBody"
case \DoStmtSyntax.unexpectedBetweenDoKeywordAndThrowsClause:
return "unexpectedBetweenDoKeywordAndThrowsClause"
case \DoStmtSyntax.throwsClause:
return "throwsClause"
case \DoStmtSyntax.unexpectedBetweenThrowsClauseAndBody:
return "unexpectedBetweenThrowsClauseAndBody"
case \DoStmtSyntax.body:
return "body"
case \DoStmtSyntax.unexpectedBetweenBodyAndCatchClauses:
Expand Down
36 changes: 24 additions & 12 deletions Sources/SwiftSyntax/generated/raw/RawSyntaxNodesD.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2025,23 +2025,27 @@ public struct RawDoStmtSyntax: RawStmtSyntaxNodeProtocol {
public init(
_ unexpectedBeforeDoKeyword: RawUnexpectedNodesSyntax? = nil,
doKeyword: RawTokenSyntax,
_ unexpectedBetweenDoKeywordAndBody: RawUnexpectedNodesSyntax? = nil,
_ unexpectedBetweenDoKeywordAndThrowsClause: RawUnexpectedNodesSyntax? = nil,
throwsClause: RawThrowsClauseSyntax?,
_ unexpectedBetweenThrowsClauseAndBody: RawUnexpectedNodesSyntax? = nil,
body: RawCodeBlockSyntax,
_ unexpectedBetweenBodyAndCatchClauses: RawUnexpectedNodesSyntax? = nil,
catchClauses: RawCatchClauseListSyntax,
_ unexpectedAfterCatchClauses: RawUnexpectedNodesSyntax? = nil,
arena: __shared SyntaxArena
) {
let raw = RawSyntax.makeLayout(
kind: .doStmt, uninitializedCount: 7, arena: arena) { layout in
kind: .doStmt, uninitializedCount: 9, arena: arena) { layout in
layout.initialize(repeating: nil)
layout[0] = unexpectedBeforeDoKeyword?.raw
layout[1] = doKeyword.raw
layout[2] = unexpectedBetweenDoKeywordAndBody?.raw
layout[3] = body.raw
layout[4] = unexpectedBetweenBodyAndCatchClauses?.raw
layout[5] = catchClauses.raw
layout[6] = unexpectedAfterCatchClauses?.raw
layout[2] = unexpectedBetweenDoKeywordAndThrowsClause?.raw
layout[3] = throwsClause?.raw
layout[4] = unexpectedBetweenThrowsClauseAndBody?.raw
layout[5] = body.raw
layout[6] = unexpectedBetweenBodyAndCatchClauses?.raw
layout[7] = catchClauses.raw
layout[8] = unexpectedAfterCatchClauses?.raw
}
self.init(unchecked: raw)
}
Expand All @@ -2054,24 +2058,32 @@ public struct RawDoStmtSyntax: RawStmtSyntaxNodeProtocol {
layoutView.children[1].map(RawTokenSyntax.init(raw:))!
}

public var unexpectedBetweenDoKeywordAndBody: RawUnexpectedNodesSyntax? {
public var unexpectedBetweenDoKeywordAndThrowsClause: RawUnexpectedNodesSyntax? {
layoutView.children[2].map(RawUnexpectedNodesSyntax.init(raw:))
}

public var throwsClause: RawThrowsClauseSyntax? {
layoutView.children[3].map(RawThrowsClauseSyntax.init(raw:))
}

public var unexpectedBetweenThrowsClauseAndBody: RawUnexpectedNodesSyntax? {
layoutView.children[4].map(RawUnexpectedNodesSyntax.init(raw:))
}

public var body: RawCodeBlockSyntax {
layoutView.children[3].map(RawCodeBlockSyntax.init(raw:))!
layoutView.children[5].map(RawCodeBlockSyntax.init(raw:))!
}

public var unexpectedBetweenBodyAndCatchClauses: RawUnexpectedNodesSyntax? {
layoutView.children[4].map(RawUnexpectedNodesSyntax.init(raw:))
layoutView.children[6].map(RawUnexpectedNodesSyntax.init(raw:))
}

public var catchClauses: RawCatchClauseListSyntax {
layoutView.children[5].map(RawCatchClauseListSyntax.init(raw:))!
layoutView.children[7].map(RawCatchClauseListSyntax.init(raw:))!
}

public var unexpectedAfterCatchClauses: RawUnexpectedNodesSyntax? {
layoutView.children[6].map(RawUnexpectedNodesSyntax.init(raw:))
layoutView.children[8].map(RawUnexpectedNodesSyntax.init(raw:))
}
}

Expand Down
8 changes: 5 additions & 3 deletions Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1002,14 +1002,16 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) {
assertNoError(kind, 5, verify(layout[5], as: RawCatchClauseListSyntax.self))
assertNoError(kind, 6, verify(layout[6], as: RawUnexpectedNodesSyntax?.self))
case .doStmt:
assert(layout.count == 7)
assert(layout.count == 9)
assertNoError(kind, 0, verify(layout[0], as: RawUnexpectedNodesSyntax?.self))
assertNoError(kind, 1, verify(layout[1], as: RawTokenSyntax.self, tokenChoices: [.keyword("do")]))
assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self))
assertNoError(kind, 3, verify(layout[3], as: RawCodeBlockSyntax.self))
assertNoError(kind, 3, verify(layout[3], as: RawThrowsClauseSyntax?.self))
assertNoError(kind, 4, verify(layout[4], as: RawUnexpectedNodesSyntax?.self))
assertNoError(kind, 5, verify(layout[5], as: RawCatchClauseListSyntax.self))
assertNoError(kind, 5, verify(layout[5], as: RawCodeBlockSyntax.self))
assertNoError(kind, 6, verify(layout[6], as: RawUnexpectedNodesSyntax?.self))
assertNoError(kind, 7, verify(layout[7], as: RawCatchClauseListSyntax.self))
assertNoError(kind, 8, verify(layout[8], as: RawUnexpectedNodesSyntax?.self))
case .documentationAttributeArgumentList:
for (index, element) in layout.enumerated() {
assertNoError(kind, index, verify(element, as: RawDocumentationAttributeArgumentSyntax.self))
Expand Down
58 changes: 44 additions & 14 deletions Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesD.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3415,6 +3415,7 @@ public struct DoExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _LeafExprSyntaxN
/// ### Children
///
/// - `doKeyword`: `do`
/// - `throwsClause`: ``ThrowsClauseSyntax``?
/// - `body`: ``CodeBlockSyntax``
/// - `catchClauses`: ``CatchClauseListSyntax``
public struct DoStmtSyntax: StmtSyntaxProtocol, SyntaxHashable, _LeafStmtSyntaxNodeProtocol {
Expand All @@ -3429,12 +3430,15 @@ public struct DoStmtSyntax: StmtSyntaxProtocol, SyntaxHashable, _LeafStmtSyntaxN

/// - Parameters:
/// - leadingTrivia: Trivia to be prepended to the leading trivia of the node’s first token. If the node is empty, there is no token to attach the trivia to and the parameter is ignored.
/// - throwsClause: The clause specifying the type of errors thrown from the 'do' block.
/// - trailingTrivia: Trivia to be appended to the trailing trivia of the node’s last token. If the node is empty, there is no token to attach the trivia to and the parameter is ignored.
public init(
leadingTrivia: Trivia? = nil,
_ unexpectedBeforeDoKeyword: UnexpectedNodesSyntax? = nil,
doKeyword: TokenSyntax = .keyword(.do),
_ unexpectedBetweenDoKeywordAndBody: UnexpectedNodesSyntax? = nil,
_ unexpectedBetweenDoKeywordAndThrowsClause: UnexpectedNodesSyntax? = nil,
throwsClause: ThrowsClauseSyntax? = nil,
_ unexpectedBetweenThrowsClauseAndBody: UnexpectedNodesSyntax? = nil,
body: CodeBlockSyntax,
_ unexpectedBetweenBodyAndCatchClauses: UnexpectedNodesSyntax? = nil,
catchClauses: CatchClauseListSyntax = [],
Expand All @@ -3447,7 +3451,9 @@ public struct DoStmtSyntax: StmtSyntaxProtocol, SyntaxHashable, _LeafStmtSyntaxN
self = withExtendedLifetime((SyntaxArena(), (
unexpectedBeforeDoKeyword,
doKeyword,
unexpectedBetweenDoKeywordAndBody,
unexpectedBetweenDoKeywordAndThrowsClause,
throwsClause,
unexpectedBetweenThrowsClauseAndBody,
body,
unexpectedBetweenBodyAndCatchClauses,
catchClauses,
Expand All @@ -3456,7 +3462,9 @@ public struct DoStmtSyntax: StmtSyntaxProtocol, SyntaxHashable, _LeafStmtSyntaxN
let layout: [RawSyntax?] = [
unexpectedBeforeDoKeyword?.raw,
doKeyword.raw,
unexpectedBetweenDoKeywordAndBody?.raw,
unexpectedBetweenDoKeywordAndThrowsClause?.raw,
throwsClause?.raw,
unexpectedBetweenThrowsClauseAndBody?.raw,
body.raw,
unexpectedBetweenBodyAndCatchClauses?.raw,
catchClauses.raw,
Expand Down Expand Up @@ -3495,7 +3503,7 @@ public struct DoStmtSyntax: StmtSyntaxProtocol, SyntaxHashable, _LeafStmtSyntaxN
}
}

public var unexpectedBetweenDoKeywordAndBody: UnexpectedNodesSyntax? {
public var unexpectedBetweenDoKeywordAndThrowsClause: UnexpectedNodesSyntax? {
Comment on lines -3498 to +3506
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related to #2379 (comment), this needs a compatibility layer or a mention in the release notes.

get {
return Syntax(self).child(at: 2)?.cast(UnexpectedNodesSyntax.self)
}
Expand All @@ -3504,16 +3512,18 @@ public struct DoStmtSyntax: StmtSyntaxProtocol, SyntaxHashable, _LeafStmtSyntaxN
}
}

public var body: CodeBlockSyntax {
/// The clause specifying the type of errors thrown from the 'do' block.
@_spi(ExperimentalLanguageFeatures)
public var throwsClause: ThrowsClauseSyntax? {
get {
return Syntax(self).child(at: 3)!.cast(CodeBlockSyntax.self)
return Syntax(self).child(at: 3)?.cast(ThrowsClauseSyntax.self)
}
set(value) {
self = Syntax(self).replacingChild(at: 3, with: Syntax(value), arena: SyntaxArena()).cast(DoStmtSyntax.self)
}
}

public var unexpectedBetweenBodyAndCatchClauses: UnexpectedNodesSyntax? {
public var unexpectedBetweenThrowsClauseAndBody: UnexpectedNodesSyntax? {
get {
return Syntax(self).child(at: 4)?.cast(UnexpectedNodesSyntax.self)
}
Expand All @@ -3522,15 +3532,33 @@ public struct DoStmtSyntax: StmtSyntaxProtocol, SyntaxHashable, _LeafStmtSyntaxN
}
}

public var catchClauses: CatchClauseListSyntax {
public var body: CodeBlockSyntax {
get {
return Syntax(self).child(at: 5)!.cast(CatchClauseListSyntax.self)
return Syntax(self).child(at: 5)!.cast(CodeBlockSyntax.self)
}
set(value) {
self = Syntax(self).replacingChild(at: 5, with: Syntax(value), arena: SyntaxArena()).cast(DoStmtSyntax.self)
}
}

public var unexpectedBetweenBodyAndCatchClauses: UnexpectedNodesSyntax? {
get {
return Syntax(self).child(at: 6)?.cast(UnexpectedNodesSyntax.self)
}
set(value) {
self = Syntax(self).replacingChild(at: 6, with: Syntax(value), arena: SyntaxArena()).cast(DoStmtSyntax.self)
}
}

public var catchClauses: CatchClauseListSyntax {
get {
return Syntax(self).child(at: 7)!.cast(CatchClauseListSyntax.self)
}
set(value) {
self = Syntax(self).replacingChild(at: 7, with: Syntax(value), arena: SyntaxArena()).cast(DoStmtSyntax.self)
}
}

/// Adds the provided `element` to the node's `catchClauses`
/// collection.
///
Expand All @@ -3542,15 +3570,15 @@ public struct DoStmtSyntax: StmtSyntaxProtocol, SyntaxHashable, _LeafStmtSyntaxN
public func addCatchClause(_ element: CatchClauseSyntax) -> DoStmtSyntax {
var collection: RawSyntax
let arena = SyntaxArena()
if let col = raw.layoutView!.children[5] {
if let col = raw.layoutView!.children[7] {
collection = col.layoutView!.appending(element.raw, arena: arena)
} else {
collection = RawSyntax.makeLayout(kind: SyntaxKind.catchClauseList,
from: [element.raw], arena: arena)
}
return Syntax(self)
.replacingChild(
at: 5,
at: 7,
with: collection,
rawNodeArena: arena,
allocationArena: arena
Expand All @@ -3560,18 +3588,20 @@ public struct DoStmtSyntax: StmtSyntaxProtocol, SyntaxHashable, _LeafStmtSyntaxN

public var unexpectedAfterCatchClauses: UnexpectedNodesSyntax? {
get {
return Syntax(self).child(at: 6)?.cast(UnexpectedNodesSyntax.self)
return Syntax(self).child(at: 8)?.cast(UnexpectedNodesSyntax.self)
}
set(value) {
self = Syntax(self).replacingChild(at: 6, with: Syntax(value), arena: SyntaxArena()).cast(DoStmtSyntax.self)
self = Syntax(self).replacingChild(at: 8, with: Syntax(value), arena: SyntaxArena()).cast(DoStmtSyntax.self)
}
}

public static var structure: SyntaxNodeStructure {
return .layout([
\Self.unexpectedBeforeDoKeyword,
\Self.doKeyword,
\Self.unexpectedBetweenDoKeywordAndBody,
\Self.unexpectedBetweenDoKeywordAndThrowsClause,
\Self.throwsClause,
\Self.unexpectedBetweenThrowsClauseAndBody,
\Self.body,
\Self.unexpectedBetweenBodyAndCatchClauses,
\Self.catchClauses,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,7 @@ public struct ThrowStmtSyntax: StmtSyntaxProtocol, SyntaxHashable, _LeafStmtSynt
/// ### Contained in
///
/// - ``AccessorEffectSpecifiersSyntax``.``AccessorEffectSpecifiersSyntax/throwsClause``
/// - ``DoStmtSyntax``.``DoStmtSyntax/throwsClause``
/// - ``FunctionEffectSpecifiersSyntax``.``FunctionEffectSpecifiersSyntax/throwsClause``
/// - ``TypeEffectSpecifiersSyntax``.``TypeEffectSpecifiersSyntax/throwsClause``
public struct ThrowsClauseSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNodeProtocol {
Expand Down
8 changes: 6 additions & 2 deletions Sources/SwiftSyntaxBuilder/generated/BuildableNodes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,9 @@ extension DoStmtSyntax {
leadingTrivia: Trivia? = nil,
unexpectedBeforeDoKeyword: UnexpectedNodesSyntax? = nil,
doKeyword: TokenSyntax = .keyword(.do),
unexpectedBetweenDoKeywordAndBody: UnexpectedNodesSyntax? = nil,
unexpectedBetweenDoKeywordAndThrowsClause: UnexpectedNodesSyntax? = nil,
throwsClause: ThrowsClauseSyntax? = nil,
unexpectedBetweenThrowsClauseAndBody: UnexpectedNodesSyntax? = nil,
Comment on lines +384 to +386
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also related to #2379 (comment), this needs a compatibility layer or a mention in the release notes.

unexpectedBetweenBodyAndCatchClauses: UnexpectedNodesSyntax? = nil,
catchClauses: CatchClauseListSyntax = [],
unexpectedAfterCatchClauses: UnexpectedNodesSyntax? = nil,
Expand All @@ -392,7 +394,9 @@ extension DoStmtSyntax {
leadingTrivia: leadingTrivia,
unexpectedBeforeDoKeyword,
doKeyword: doKeyword,
unexpectedBetweenDoKeywordAndBody,
unexpectedBetweenDoKeywordAndThrowsClause,
throwsClause: throwsClause,
unexpectedBetweenThrowsClauseAndBody,
body: CodeBlockSyntax(statements: bodyBuilder()),
unexpectedBetweenBodyAndCatchClauses,
catchClauses: catchClauses,
Expand Down
Loading