From 75815312e4c22ff702e32c2bfcc428e368a6c88c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 26 Feb 2025 10:48:33 -0800 Subject: [PATCH 1/4] [SE-0458] The unsafe expression and for..in effect is no longer experimental Keep the experimental feature around for just a moment so we don't have to pair commits across the swift-syntax and swift repositories. --- CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift | 1 - CodeGeneration/Sources/SyntaxSupport/StmtNodes.swift | 1 - 2 files changed, 2 deletions(-) diff --git a/CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift b/CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift index 7aad34d15ec..18cef4ca1dd 100644 --- a/CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift +++ b/CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift @@ -183,7 +183,6 @@ public let EXPR_NODES: [Node] = [ Node( kind: .unsafeExpr, base: .expr, - experimentalFeature: .unsafeExpression, nameForDiagnostics: "'unsafe' expression", children: [ Child( diff --git a/CodeGeneration/Sources/SyntaxSupport/StmtNodes.swift b/CodeGeneration/Sources/SyntaxSupport/StmtNodes.swift index 654adbe3cec..3ac5bab1109 100644 --- a/CodeGeneration/Sources/SyntaxSupport/StmtNodes.swift +++ b/CodeGeneration/Sources/SyntaxSupport/StmtNodes.swift @@ -287,7 +287,6 @@ public let STMT_NODES: [Node] = [ Child( name: "unsafeKeyword", kind: .token(choices: [.keyword(.unsafe)]), - experimentalFeature: .unsafeExpression, isOptional: true ), Child( From 062938f5edd98cf1aaa21398ca70f92476d0a7be Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 26 Feb 2025 10:50:39 -0800 Subject: [PATCH 2/4] Regenerate sources now that SE-0458 has been accepted --- .../SwiftSyntax/Documentation.docc/generated/SwiftSyntax.md | 1 + Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift | 2 -- Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift | 1 + Sources/SwiftSyntax/generated/SyntaxEnum.swift | 2 -- Sources/SwiftSyntax/generated/SyntaxKind.swift | 1 - Sources/SwiftSyntax/generated/SyntaxRewriter.swift | 3 +-- Sources/SwiftSyntax/generated/SyntaxVisitor.swift | 6 ++---- .../SwiftSyntax/generated/raw/RawSyntaxNodesTUVWXYZ.swift | 1 - .../SwiftSyntax/generated/syntaxNodes/SyntaxNodesEF.swift | 3 --- .../generated/syntaxNodes/SyntaxNodesTUVWXYZ.swift | 3 --- 10 files changed, 5 insertions(+), 18 deletions(-) diff --git a/Sources/SwiftSyntax/Documentation.docc/generated/SwiftSyntax.md b/Sources/SwiftSyntax/Documentation.docc/generated/SwiftSyntax.md index 7e45d4c8dd1..88ad12bc5ed 100644 --- a/Sources/SwiftSyntax/Documentation.docc/generated/SwiftSyntax.md +++ b/Sources/SwiftSyntax/Documentation.docc/generated/SwiftSyntax.md @@ -135,6 +135,7 @@ allows Swift tools to parse, inspect, generate, and transform Swift source code. - - - +- ### Patterns diff --git a/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift b/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift index f39da01d4cc..6de3d50f4aa 100644 --- a/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift +++ b/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift @@ -2294,12 +2294,10 @@ open class SyntaxAnyVisitor: SyntaxVisitor { visitAnyPost(node._syntaxNode) } - @_spi(ExperimentalLanguageFeatures) override open func visit(_ node: UnsafeExprSyntax) -> SyntaxVisitorContinueKind { return visitAny(node._syntaxNode) } - @_spi(ExperimentalLanguageFeatures) override open func visitPost(_ node: UnsafeExprSyntax) { visitAnyPost(node._syntaxNode) } diff --git a/Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift b/Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift index 6acd121b3dd..8b9cccfe59e 100644 --- a/Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift +++ b/Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift @@ -506,6 +506,7 @@ extension Syntax { /// - ``UnresolvedAsExprSyntax`` /// - ``UnresolvedIsExprSyntax`` /// - ``UnresolvedTernaryExprSyntax`` +/// - ``UnsafeExprSyntax`` public struct ExprSyntax: ExprSyntaxProtocol, SyntaxHashable { public let _syntaxNode: Syntax diff --git a/Sources/SwiftSyntax/generated/SyntaxEnum.swift b/Sources/SwiftSyntax/generated/SyntaxEnum.swift index a8e19c6be78..1d55a7cb1c5 100644 --- a/Sources/SwiftSyntax/generated/SyntaxEnum.swift +++ b/Sources/SwiftSyntax/generated/SyntaxEnum.swift @@ -303,7 +303,6 @@ public enum SyntaxEnum: Sendable { case unresolvedAsExpr(UnresolvedAsExprSyntax) case unresolvedIsExpr(UnresolvedIsExprSyntax) case unresolvedTernaryExpr(UnresolvedTernaryExprSyntax) - @_spi(ExperimentalLanguageFeatures) case unsafeExpr(UnsafeExprSyntax) case valueBindingPattern(ValueBindingPatternSyntax) case variableDecl(VariableDeclSyntax) @@ -1051,7 +1050,6 @@ public enum ExprSyntaxEnum { case unresolvedAsExpr(UnresolvedAsExprSyntax) case unresolvedIsExpr(UnresolvedIsExprSyntax) case unresolvedTernaryExpr(UnresolvedTernaryExprSyntax) - @_spi(ExperimentalLanguageFeatures) case unsafeExpr(UnsafeExprSyntax) } diff --git a/Sources/SwiftSyntax/generated/SyntaxKind.swift b/Sources/SwiftSyntax/generated/SyntaxKind.swift index 1c2f7340cea..887cf329302 100644 --- a/Sources/SwiftSyntax/generated/SyntaxKind.swift +++ b/Sources/SwiftSyntax/generated/SyntaxKind.swift @@ -303,7 +303,6 @@ public enum SyntaxKind: Sendable { case unresolvedAsExpr case unresolvedIsExpr case unresolvedTernaryExpr - @_spi(ExperimentalLanguageFeatures) case unsafeExpr case valueBindingPattern case variableDecl diff --git a/Sources/SwiftSyntax/generated/SyntaxRewriter.swift b/Sources/SwiftSyntax/generated/SyntaxRewriter.swift index f67d2be5d0e..c297d8f31ce 100644 --- a/Sources/SwiftSyntax/generated/SyntaxRewriter.swift +++ b/Sources/SwiftSyntax/generated/SyntaxRewriter.swift @@ -2048,10 +2048,9 @@ open class SyntaxRewriter { return ExprSyntax(UnresolvedTernaryExprSyntax(unsafeCasting: visitChildren(node._syntaxNode))) } - /// Visit a `UnsafeExprSyntax`. + /// Visit a ``UnsafeExprSyntax``. /// - Parameter node: the node that is being visited /// - Returns: the rewritten node - @_spi(ExperimentalLanguageFeatures) open func visit(_ node: UnsafeExprSyntax) -> ExprSyntax { return ExprSyntax(UnsafeExprSyntax(unsafeCasting: visitChildren(node._syntaxNode))) } diff --git a/Sources/SwiftSyntax/generated/SyntaxVisitor.swift b/Sources/SwiftSyntax/generated/SyntaxVisitor.swift index 89640deef16..519c8a64ec9 100644 --- a/Sources/SwiftSyntax/generated/SyntaxVisitor.swift +++ b/Sources/SwiftSyntax/generated/SyntaxVisitor.swift @@ -3380,17 +3380,15 @@ open class SyntaxVisitor { open func visitPost(_ node: UnresolvedTernaryExprSyntax) { } - /// Visiting `UnsafeExprSyntax` specifically. + /// Visiting ``UnsafeExprSyntax`` specifically. /// - Parameter node: the node we are visiting. /// - Returns: how should we continue visiting. - @_spi(ExperimentalLanguageFeatures) open func visit(_ node: UnsafeExprSyntax) -> SyntaxVisitorContinueKind { return .visitChildren } - /// The function called after visiting `UnsafeExprSyntax` and its descendants. + /// The function called after visiting ``UnsafeExprSyntax`` and its descendants. /// - node: the node we just finished visiting. - @_spi(ExperimentalLanguageFeatures) open func visitPost(_ node: UnsafeExprSyntax) { } diff --git a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesTUVWXYZ.swift b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesTUVWXYZ.swift index 6c7d2dffc5d..39f3bde6d01 100644 --- a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesTUVWXYZ.swift +++ b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesTUVWXYZ.swift @@ -1951,7 +1951,6 @@ public struct RawUnresolvedTernaryExprSyntax: RawExprSyntaxNodeProtocol { } } -@_spi(ExperimentalLanguageFeatures) @_spi(RawSyntax) public struct RawUnsafeExprSyntax: RawExprSyntaxNodeProtocol { @_spi(RawSyntax) diff --git a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesEF.swift b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesEF.swift index 588cb283ecd..e098442d675 100644 --- a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesEF.swift +++ b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesEF.swift @@ -2841,7 +2841,6 @@ public struct ForStmtSyntax: StmtSyntaxProtocol, SyntaxHashable, _LeafStmtSyntax } } - @_spi(ExperimentalLanguageFeatures) public var unexpectedBetweenAwaitKeywordAndUnsafeKeyword: UnexpectedNodesSyntax? { get { return Syntax(self).child(at: 6)?.cast(UnexpectedNodesSyntax.self) @@ -2854,7 +2853,6 @@ public struct ForStmtSyntax: StmtSyntaxProtocol, SyntaxHashable, _LeafStmtSyntax /// ### Tokens /// /// For syntax trees generated by the parser, this is guaranteed to be `unsafe`. - @_spi(ExperimentalLanguageFeatures) public var unsafeKeyword: TokenSyntax? { get { return Syntax(self).child(at: 7)?.cast(TokenSyntax.self) @@ -2864,7 +2862,6 @@ public struct ForStmtSyntax: StmtSyntaxProtocol, SyntaxHashable, _LeafStmtSyntax } } - @_spi(ExperimentalLanguageFeatures) public var unexpectedBetweenUnsafeKeywordAndCaseKeyword: UnexpectedNodesSyntax? { get { return Syntax(self).child(at: 8)?.cast(UnexpectedNodesSyntax.self) diff --git a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesTUVWXYZ.swift b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesTUVWXYZ.swift index 86dde405b86..22a1546cb57 100644 --- a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesTUVWXYZ.swift +++ b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesTUVWXYZ.swift @@ -3272,13 +3272,10 @@ public struct UnresolvedTernaryExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _ // MARK: - UnsafeExprSyntax -/// - Note: Requires experimental feature `unsafeExpression`. -/// /// ### Children /// /// - `unsafeKeyword`: `unsafe` /// - `expression`: ``ExprSyntax`` -@_spi(ExperimentalLanguageFeatures) public struct UnsafeExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _LeafExprSyntaxNodeProtocol { public let _syntaxNode: Syntax From 14ea0a4b77f7bf3594dd0d7c2e7b04e195e4409d Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 26 Feb 2025 10:52:41 -0800 Subject: [PATCH 3/4] [SE-0458] Always parse "unsafe" expressions and for..in effects --- Sources/SwiftParser/TokenSpecSet.swift | 2 +- Tests/SwiftParserTest/ExpressionTests.swift | 3 +-- Tests/SwiftParserTest/StatementTests.swift | 12 ++++-------- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/Sources/SwiftParser/TokenSpecSet.swift b/Sources/SwiftParser/TokenSpecSet.swift index 2806c15440d..2519b689246 100644 --- a/Sources/SwiftParser/TokenSpecSet.swift +++ b/Sources/SwiftParser/TokenSpecSet.swift @@ -714,7 +714,7 @@ enum ExpressionModifierKeyword: TokenSpecSet { case TokenSpec(.repeat): self = .repeat case TokenSpec(.each): self = .each case TokenSpec(.any): self = .any - case TokenSpec(.unsafe) where experimentalFeatures.contains(.unsafeExpression): self = .unsafe + case TokenSpec(.unsafe): self = .unsafe default: return nil } } diff --git a/Tests/SwiftParserTest/ExpressionTests.swift b/Tests/SwiftParserTest/ExpressionTests.swift index ef1b83f699f..02114ed5083 100644 --- a/Tests/SwiftParserTest/ExpressionTests.swift +++ b/Tests/SwiftParserTest/ExpressionTests.swift @@ -2194,8 +2194,7 @@ final class StatementExpressionTests: ParserTestCase { func f() { let x = unsafe y } - """, - experimentalFeatures: .unsafeExpression + """ ) assertParse( diff --git a/Tests/SwiftParserTest/StatementTests.swift b/Tests/SwiftParserTest/StatementTests.swift index 8c7080de2c9..42e12f6785c 100644 --- a/Tests/SwiftParserTest/StatementTests.swift +++ b/Tests/SwiftParserTest/StatementTests.swift @@ -960,23 +960,19 @@ final class StatementTests: ParserTestCase { func testForUnsafeStatement() { assertParse( - "for try await unsafe x in e { }", - experimentalFeatures: [.unsafeExpression] + "for try await unsafe x in e { }" ) assertParse( - "for try await unsafe in e { }", - experimentalFeatures: [.unsafeExpression] + "for try await unsafe in e { }" ) assertParse( - "for unsafe in e { }", - experimentalFeatures: [.unsafeExpression] + "for unsafe in e { }" ) assertParse( - "for unsafe: Int in e { }", - experimentalFeatures: [.unsafeExpression] + "for unsafe: Int in e { }" ) } } From 7262c1a3b1c150f4a3c919ae4e0b33566dbf0802 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 26 Feb 2025 11:08:59 -0800 Subject: [PATCH 4/4] [SE-0458] Disambiguate existing uses of `unsafe` better Only treat `unsafe` as starting an unsafe expression if the following token is on the same line and isn't a `)`. The former is in line with how we handle other modifier expressions like this, and the latter is because of the archaic `@MainActor(unsafe)`. --- Sources/SwiftParser/Expressions.swift | 4 ++++ Tests/SwiftParserTest/ExpressionTests.swift | 19 +++---------------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/Sources/SwiftParser/Expressions.swift b/Sources/SwiftParser/Expressions.swift index bf1c9fba52d..7c9fd73fd1d 100644 --- a/Sources/SwiftParser/Expressions.swift +++ b/Sources/SwiftParser/Expressions.swift @@ -445,6 +445,10 @@ extension Parser { ) ) case (.unsafe, let handle)?: + if self.peek().isAtStartOfLine || self.peek(isAt: .rightParen) { + break EXPR_PREFIX + } + let unsafeTok = self.eat(handle) let sub = self.parseSequenceExpressionElement( flavor: flavor, diff --git a/Tests/SwiftParserTest/ExpressionTests.swift b/Tests/SwiftParserTest/ExpressionTests.swift index 02114ed5083..c50af6fc2da 100644 --- a/Tests/SwiftParserTest/ExpressionTests.swift +++ b/Tests/SwiftParserTest/ExpressionTests.swift @@ -2200,24 +2200,11 @@ final class StatementExpressionTests: ParserTestCase { assertParse( """ func f() { - let x = unsafe1️⃣ y + let x = unsafe + y } """, - diagnostics: [ - DiagnosticSpec( - message: "consecutive statements on a line must be separated by newline or ';'", - fixIts: [ - "insert newline", - "insert ';'", - ] - ) - ], - fixedSource: """ - func f() { - let x = unsafe - y - } - """ + substructure: DeclReferenceExprSyntax(baseName: .identifier("unsafe")) ) }