From 848e5fed804299dac55e37a4283f04f87e83bb29 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Fri, 28 Mar 2025 14:50:36 -0700 Subject: [PATCH 01/21] Fix build warning `name` should have been in a string interpolation here. This makes swift-syntax build without warnings --- Sources/SwiftIfConfig/BuildConfiguration.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SwiftIfConfig/BuildConfiguration.swift b/Sources/SwiftIfConfig/BuildConfiguration.swift index 1621db389f1..407aa1a13d6 100644 --- a/Sources/SwiftIfConfig/BuildConfiguration.swift +++ b/Sources/SwiftIfConfig/BuildConfiguration.swift @@ -43,7 +43,7 @@ enum BuildConfigurationError: Error, CustomStringConvertible { var description: String { switch self { case .experimentalFeature(let name): - return "'name' is an experimental feature" + return "'\(name)' is an experimental feature" } } } From c8f189e510e7990f002975a7e58aca5441a599a4 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Tue, 1 Apr 2025 10:09:51 -0700 Subject: [PATCH 02/21] [bazel] Add new C files --- BUILD.bazel | 1 + 1 file changed, 1 insertion(+) diff --git a/BUILD.bazel b/BUILD.bazel index 1360cff9043..288ea92e065 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -376,6 +376,7 @@ cc_library( cc_library( name = "_SwiftSyntaxCShims", + srcs = glob(["Sources/_SwiftSyntaxCShims/*.c"]), hdrs = glob(["Sources/_SwiftSyntaxCShims/include/*.h"]), includes = ["Sources/_SwiftSyntaxCShims/include"], tags = ["swift_module=_SwiftSyntaxCShims"], From 52c4bc2fa925fea2e5c51249ebe9a9934cb91c22 Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Wed, 10 Jul 2024 12:45:38 -0500 Subject: [PATCH 03/21] Include try and await expressions which are parents of a freestanding expression macro in lexicalContext Resolves rdar://109470248 --- .../SwiftSyntaxMacros/Syntax+LexicalContext.swift | 14 ++++++++++++++ .../LexicalContextTests.swift | 8 +++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Sources/SwiftSyntaxMacros/Syntax+LexicalContext.swift b/Sources/SwiftSyntaxMacros/Syntax+LexicalContext.swift index 5f17e391db4..96110ce5803 100644 --- a/Sources/SwiftSyntaxMacros/Syntax+LexicalContext.swift +++ b/Sources/SwiftSyntaxMacros/Syntax+LexicalContext.swift @@ -67,6 +67,20 @@ extension SyntaxProtocol { case let freestandingMacro as FreestandingMacroExpansionSyntax: return Syntax(freestandingMacro.detached) as Syntax + // Try and await are preserved: A freestanding expression macro preceded + // by try or await may need to know whether those keywords are present so it + // can propagate them to any expressions in its expansion which were passed + // as arguments to the macro. The expression of the try or await is replaced + // with a trivial placeholder, though. + case var tryExpr as TryExprSyntax: + tryExpr = tryExpr.detached + tryExpr.expression = ExprSyntax(TypeExprSyntax(type: IdentifierTypeSyntax(name: .wildcardToken()))) + return Syntax(tryExpr) + case var awaitExpr as AwaitExprSyntax: + awaitExpr = awaitExpr.detached + awaitExpr.expression = ExprSyntax(TypeExprSyntax(type: IdentifierTypeSyntax(name: .wildcardToken()))) + return Syntax(awaitExpr) + default: return nil } diff --git a/Tests/SwiftSyntaxMacroExpansionTest/LexicalContextTests.swift b/Tests/SwiftSyntaxMacroExpansionTest/LexicalContextTests.swift index 533a2a4d19f..6b426aa7431 100644 --- a/Tests/SwiftSyntaxMacroExpansionTest/LexicalContextTests.swift +++ b/Tests/SwiftSyntaxMacroExpansionTest/LexicalContextTests.swift @@ -531,7 +531,7 @@ final class LexicalContextTests: XCTestCase { struct S { let arg: C var contextDescription: String { - #lexicalContextDescription + try await #lexicalContextDescription } } return S(arg: c) @@ -542,7 +542,9 @@ final class LexicalContextTests: XCTestCase { struct S { let arg: C var contextDescription: String { - """ + try await """ + await _ + try _ contextDescription: String struct S {} { c in @@ -551,7 +553,7 @@ final class LexicalContextTests: XCTestCase { struct S { let arg: C var contextDescription: String { - #lexicalContextDescription + try await #lexicalContextDescription } } return S(arg: c) From 2b9a763eab621d8874122f670e0fd2367754053a Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Tue, 1 Apr 2025 21:02:09 +0100 Subject: [PATCH 04/21] Handle `try` and `await` in unfolded sequences We can say that any `try`/`await` element also covers all elements to the right of it in an unfolded sequence. Cases where this isn't true will be rejected by the compiler, e.g: ``` 0 * try foo() + bar() _ = try foo() ~~~ bar() // Assuming `~~~` has lower precedence than `=` ``` rdar://109470248 --- .../OperatorTable+Folding.swift | 2 + .../Syntax+LexicalContext.swift | 33 ++++ .../LexicalContextTests.swift | 161 ++++++++++++++++++ 3 files changed, 196 insertions(+) diff --git a/Sources/SwiftOperators/OperatorTable+Folding.swift b/Sources/SwiftOperators/OperatorTable+Folding.swift index 06371c9f50a..c1c4f7f6025 100644 --- a/Sources/SwiftOperators/OperatorTable+Folding.swift +++ b/Sources/SwiftOperators/OperatorTable+Folding.swift @@ -155,6 +155,8 @@ extension OperatorTable { ) ) } + // NOTE: If you add a new try/await/unsafe-like hoisting case here, make + // sure to also update `allMacroLexicalContexts` to handle it. // The form of the binary operation depends on the operator itself, // which will be one of the unresolved infix operators. diff --git a/Sources/SwiftSyntaxMacros/Syntax+LexicalContext.swift b/Sources/SwiftSyntaxMacros/Syntax+LexicalContext.swift index 96110ce5803..6de517bee35 100644 --- a/Sources/SwiftSyntaxMacros/Syntax+LexicalContext.swift +++ b/Sources/SwiftSyntaxMacros/Syntax+LexicalContext.swift @@ -106,6 +106,39 @@ extension SyntaxProtocol { if let parentContext = parentNode.asMacroLexicalContext() { parentContexts.append(parentContext) } + if let sequence = parentNode.as(SequenceExprSyntax.self) { + var sequenceExprContexts: [Syntax] = [] + for elt in sequence.elements { + if elt.range.contains(self.position) { + // `sequenceExprContexts` is built from the top-down, but we + // build the rest of the contexts bottom-up. Reverse for + // consistency. + parentContexts += sequenceExprContexts.reversed() + break + } + var elt = elt + while true { + if let tryElt = elt.as(TryExprSyntax.self) { + sequenceExprContexts.append(tryElt.asMacroLexicalContext()!) + elt = tryElt.expression + continue + } + if let awaitElt = elt.as(AwaitExprSyntax.self) { + sequenceExprContexts.append(awaitElt.asMacroLexicalContext()!) + elt = awaitElt.expression + continue + } + if let unsafeElt = elt.as(UnsafeExprSyntax.self) { + // No scope for this currently, but we need to look through it + // since it's similar to 'try' in that it's hoisted above a + // binary operator when appearing on the LHS. + elt = unsafeElt.expression + continue + } + break + } + } + } currentNode = parentNode } diff --git a/Tests/SwiftSyntaxMacroExpansionTest/LexicalContextTests.swift b/Tests/SwiftSyntaxMacroExpansionTest/LexicalContextTests.swift index 6b426aa7431..2e1b5c8399c 100644 --- a/Tests/SwiftSyntaxMacroExpansionTest/LexicalContextTests.swift +++ b/Tests/SwiftSyntaxMacroExpansionTest/LexicalContextTests.swift @@ -567,4 +567,165 @@ final class LexicalContextTests: XCTestCase { macros: ["lexicalContextDescription": LexicalContextDescriptionMacro.self] ) } + + func testTryAwaitInSequenceLexicalContext() { + // Valid cases. + assertMacroExpansion( + "try await #lexicalContextDescription + #lexicalContextDescription", + expandedSource: #""" + try await """ + await _ + try _ + """ + """ + await _ + try _ + """ + """#, + macros: ["lexicalContextDescription": LexicalContextDescriptionMacro.self] + ) + assertMacroExpansion( + "try await 0 + 1 + foo(#lexicalContextDescription) + 2", + expandedSource: #""" + try await 0 + 1 + foo(""" + await _ + try _ + """) + 2 + """#, + macros: ["lexicalContextDescription": LexicalContextDescriptionMacro.self] + ) + assertMacroExpansion( + "x = try await 0 + 1 + foo(#lexicalContextDescription) + 2", + expandedSource: #""" + x = try await 0 + 1 + foo(""" + await _ + try _ + """) + 2 + """#, + macros: ["lexicalContextDescription": LexicalContextDescriptionMacro.self] + ) + // `try await` in the 'then' branch doesn't cover condition or else. + assertMacroExpansion( + "#lexicalContextDescription ? try await #lexicalContextDescription : #lexicalContextDescription", + expandedSource: #""" + """ + """ ? try await """ + await _ + try _ + """ : """ + """ + """#, + macros: ["lexicalContextDescription": LexicalContextDescriptionMacro.self] + ) + // Same for else. + assertMacroExpansion( + "#lexicalContextDescription ? #lexicalContextDescription : try await #lexicalContextDescription", + expandedSource: #""" + """ + """ ? """ + """ : try await """ + await _ + try _ + """ + """#, + macros: ["lexicalContextDescription": LexicalContextDescriptionMacro.self] + ) + // 'try await' in the condition here covers the entire expression + assertMacroExpansion( + "try await #lexicalContextDescription ? #lexicalContextDescription : #lexicalContextDescription ~~ #lexicalContextDescription", + expandedSource: #""" + try await """ + await _ + try _ + """ ? """ + await _ + try _ + """ : """ + await _ + try _ + """ ~~ """ + await _ + try _ + """ + """#, + macros: ["lexicalContextDescription": LexicalContextDescriptionMacro.self] + ) + assertMacroExpansion( + "x = unsafe try try! await 0 + #lexicalContextDescription", + expandedSource: #""" + x = unsafe try try! await 0 + """ + await _ + try! _ + try _ + """ + """#, + macros: ["lexicalContextDescription": LexicalContextDescriptionMacro.self] + ) + + // Invalid cases + assertMacroExpansion( + "0 + try await #lexicalContextDescription", + expandedSource: #""" + 0 + try await """ + await _ + try _ + """ + """#, + macros: ["lexicalContextDescription": LexicalContextDescriptionMacro.self] + ) + // The 'try await' may not actually cover 'lexicalContextDescription' here, + // but this will be rejected by the compiler. + assertMacroExpansion( + "0 + try await 1 ^ #lexicalContextDescription", + expandedSource: #""" + 0 + try await 1 ^ """ + await _ + try _ + """ + """#, + macros: ["lexicalContextDescription": LexicalContextDescriptionMacro.self] + ) + // Invalid if '^' has a lower precedence than '='. + assertMacroExpansion( + "x = try await 0 ^ #lexicalContextDescription", + expandedSource: #""" + x = try await 0 ^ """ + await _ + try _ + """ + """#, + macros: ["lexicalContextDescription": LexicalContextDescriptionMacro.self] + ) + // Unassignable + assertMacroExpansion( + "#lexicalContextDescription = try await 0 + 1", + expandedSource: #""" + """ + """ = try await 0 + 1 + """#, + macros: ["lexicalContextDescription": LexicalContextDescriptionMacro.self] + ) + assertMacroExpansion( + "try await #lexicalContextDescription = 0 + #lexicalContextDescription", + expandedSource: #""" + try await """ + await _ + try _ + """ = 0 + """ + await _ + try _ + """ + """#, + macros: ["lexicalContextDescription": LexicalContextDescriptionMacro.self] + ) + assertMacroExpansion( + "try await foo() ? 0 : 1 = #lexicalContextDescription", + expandedSource: #""" + try await foo() ? 0 : 1 = """ + await _ + try _ + """ + """#, + macros: ["lexicalContextDescription": LexicalContextDescriptionMacro.self] + ) + } } From 2c16aebd00ebe17b1f99b0cb6655b914249fed2f Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Thu, 3 Apr 2025 20:39:03 +0100 Subject: [PATCH 05/21] Add `unsafe` as a lexical context too Match the behavior of `try` and `await`. --- .../Syntax+LexicalContext.swift | 24 ++++-- .../LexicalContextTests.swift | 82 +++++++++++-------- 2 files changed, 66 insertions(+), 40 deletions(-) diff --git a/Sources/SwiftSyntaxMacros/Syntax+LexicalContext.swift b/Sources/SwiftSyntaxMacros/Syntax+LexicalContext.swift index 6de517bee35..f9132040dff 100644 --- a/Sources/SwiftSyntaxMacros/Syntax+LexicalContext.swift +++ b/Sources/SwiftSyntaxMacros/Syntax+LexicalContext.swift @@ -67,11 +67,11 @@ extension SyntaxProtocol { case let freestandingMacro as FreestandingMacroExpansionSyntax: return Syntax(freestandingMacro.detached) as Syntax - // Try and await are preserved: A freestanding expression macro preceded - // by try or await may need to know whether those keywords are present so it - // can propagate them to any expressions in its expansion which were passed - // as arguments to the macro. The expression of the try or await is replaced - // with a trivial placeholder, though. + // `try`, `await`, and `unsafe` are preserved: A freestanding expression + // macro may need to know whether those keywords are present so it can + // propagate them to any expressions in its expansion which were passed as + // arguments to the macro. The sub-expression is replaced with a trivial + // placeholder, though. case var tryExpr as TryExprSyntax: tryExpr = tryExpr.detached tryExpr.expression = ExprSyntax(TypeExprSyntax(type: IdentifierTypeSyntax(name: .wildcardToken()))) @@ -80,6 +80,10 @@ extension SyntaxProtocol { awaitExpr = awaitExpr.detached awaitExpr.expression = ExprSyntax(TypeExprSyntax(type: IdentifierTypeSyntax(name: .wildcardToken()))) return Syntax(awaitExpr) + case var unsafeExpr as UnsafeExprSyntax: + unsafeExpr = unsafeExpr.detached + unsafeExpr.expression = ExprSyntax(TypeExprSyntax(type: IdentifierTypeSyntax(name: .wildcardToken()))) + return Syntax(unsafeExpr) default: return nil @@ -106,6 +110,12 @@ extension SyntaxProtocol { if let parentContext = parentNode.asMacroLexicalContext() { parentContexts.append(parentContext) } + // Unfolded sequence expressions require special handling - effect marker + // nodes like `try`, `await`, and `unsafe` are treated as lexical contexts + // for all the nodes on their right. Cases where they don't end up + // covering nodes to their right in the folded tree are invalid and will + // be diagnosed by the compiler. This matches the compiler's ASTScope + // handling logic. if let sequence = parentNode.as(SequenceExprSyntax.self) { var sequenceExprContexts: [Syntax] = [] for elt in sequence.elements { @@ -129,9 +139,7 @@ extension SyntaxProtocol { continue } if let unsafeElt = elt.as(UnsafeExprSyntax.self) { - // No scope for this currently, but we need to look through it - // since it's similar to 'try' in that it's hoisted above a - // binary operator when appearing on the LHS. + sequenceExprContexts.append(unsafeElt.asMacroLexicalContext()!) elt = unsafeElt.expression continue } diff --git a/Tests/SwiftSyntaxMacroExpansionTest/LexicalContextTests.swift b/Tests/SwiftSyntaxMacroExpansionTest/LexicalContextTests.swift index 2e1b5c8399c..e0cbe3ef9e2 100644 --- a/Tests/SwiftSyntaxMacroExpansionTest/LexicalContextTests.swift +++ b/Tests/SwiftSyntaxMacroExpansionTest/LexicalContextTests.swift @@ -531,7 +531,7 @@ final class LexicalContextTests: XCTestCase { struct S { let arg: C var contextDescription: String { - try await #lexicalContextDescription + unsafe try await #lexicalContextDescription } } return S(arg: c) @@ -542,9 +542,10 @@ final class LexicalContextTests: XCTestCase { struct S { let arg: C var contextDescription: String { - try await """ + unsafe try await """ await _ try _ + unsafe _ contextDescription: String struct S {} { c in @@ -553,7 +554,7 @@ final class LexicalContextTests: XCTestCase { struct S { let arg: C var contextDescription: String { - try await #lexicalContextDescription + unsafe try await #lexicalContextDescription } } return S(arg: c) @@ -568,49 +569,54 @@ final class LexicalContextTests: XCTestCase { ) } - func testTryAwaitInSequenceLexicalContext() { + func testEffectMarkersInSequenceLexicalContext() { // Valid cases. assertMacroExpansion( - "try await #lexicalContextDescription + #lexicalContextDescription", + "unsafe try await #lexicalContextDescription + #lexicalContextDescription", expandedSource: #""" - try await """ + unsafe try await """ await _ try _ + unsafe _ """ + """ await _ try _ + unsafe _ """ """#, macros: ["lexicalContextDescription": LexicalContextDescriptionMacro.self] ) assertMacroExpansion( - "try await 0 + 1 + foo(#lexicalContextDescription) + 2", + "try unsafe await 0 + 1 + foo(#lexicalContextDescription) + 2", expandedSource: #""" - try await 0 + 1 + foo(""" + try unsafe await 0 + 1 + foo(""" await _ + unsafe _ try _ """) + 2 """#, macros: ["lexicalContextDescription": LexicalContextDescriptionMacro.self] ) assertMacroExpansion( - "x = try await 0 + 1 + foo(#lexicalContextDescription) + 2", + "x = try await unsafe 0 + 1 + foo(#lexicalContextDescription) + 2", expandedSource: #""" - x = try await 0 + 1 + foo(""" + x = try await unsafe 0 + 1 + foo(""" + unsafe _ await _ try _ """) + 2 """#, macros: ["lexicalContextDescription": LexicalContextDescriptionMacro.self] ) - // `try await` in the 'then' branch doesn't cover condition or else. + // `unsafe try await` in the 'then' branch doesn't cover condition or else. assertMacroExpansion( - "#lexicalContextDescription ? try await #lexicalContextDescription : #lexicalContextDescription", + "#lexicalContextDescription ? unsafe try await #lexicalContextDescription : #lexicalContextDescription", expandedSource: #""" """ - """ ? try await """ + """ ? unsafe try await """ await _ try _ + unsafe _ """ : """ """ """#, @@ -618,33 +624,38 @@ final class LexicalContextTests: XCTestCase { ) // Same for else. assertMacroExpansion( - "#lexicalContextDescription ? #lexicalContextDescription : try await #lexicalContextDescription", + "#lexicalContextDescription ? #lexicalContextDescription : unsafe try await #lexicalContextDescription", expandedSource: #""" """ """ ? """ - """ : try await """ + """ : unsafe try await """ await _ try _ + unsafe _ """ """#, macros: ["lexicalContextDescription": LexicalContextDescriptionMacro.self] ) - // 'try await' in the condition here covers the entire expression + // 'unsafe try await' in the condition here covers the entire expression assertMacroExpansion( - "try await #lexicalContextDescription ? #lexicalContextDescription : #lexicalContextDescription ~~ #lexicalContextDescription", + "unsafe try await #lexicalContextDescription ? #lexicalContextDescription : #lexicalContextDescription ~~ #lexicalContextDescription", expandedSource: #""" - try await """ + unsafe try await """ await _ try _ + unsafe _ """ ? """ await _ try _ + unsafe _ """ : """ await _ try _ + unsafe _ """ ~~ """ await _ try _ + unsafe _ """ """#, macros: ["lexicalContextDescription": LexicalContextDescriptionMacro.self] @@ -656,6 +667,7 @@ final class LexicalContextTests: XCTestCase { await _ try! _ try _ + unsafe _ """ """#, macros: ["lexicalContextDescription": LexicalContextDescriptionMacro.self] @@ -663,66 +675,72 @@ final class LexicalContextTests: XCTestCase { // Invalid cases assertMacroExpansion( - "0 + try await #lexicalContextDescription", + "0 + unsafe try await #lexicalContextDescription", expandedSource: #""" - 0 + try await """ + 0 + unsafe try await """ await _ try _ + unsafe _ """ """#, macros: ["lexicalContextDescription": LexicalContextDescriptionMacro.self] ) - // The 'try await' may not actually cover 'lexicalContextDescription' here, - // but this will be rejected by the compiler. + // The `unsafe try await` may not actually cover `lexicalContextDescription` + // here, but this will be rejected by the compiler. assertMacroExpansion( - "0 + try await 1 ^ #lexicalContextDescription", + "0 + unsafe try await 1 ^ #lexicalContextDescription", expandedSource: #""" - 0 + try await 1 ^ """ + 0 + unsafe try await 1 ^ """ await _ try _ + unsafe _ """ """#, macros: ["lexicalContextDescription": LexicalContextDescriptionMacro.self] ) // Invalid if '^' has a lower precedence than '='. assertMacroExpansion( - "x = try await 0 ^ #lexicalContextDescription", + "x = unsafe try await 0 ^ #lexicalContextDescription", expandedSource: #""" - x = try await 0 ^ """ + x = unsafe try await 0 ^ """ await _ try _ + unsafe _ """ """#, macros: ["lexicalContextDescription": LexicalContextDescriptionMacro.self] ) // Unassignable assertMacroExpansion( - "#lexicalContextDescription = try await 0 + 1", + "#lexicalContextDescription = unsafe try await 0 + 1", expandedSource: #""" """ - """ = try await 0 + 1 + """ = unsafe try await 0 + 1 """#, macros: ["lexicalContextDescription": LexicalContextDescriptionMacro.self] ) assertMacroExpansion( - "try await #lexicalContextDescription = 0 + #lexicalContextDescription", + "unsafe try await #lexicalContextDescription = 0 + #lexicalContextDescription", expandedSource: #""" - try await """ + unsafe try await """ await _ try _ + unsafe _ """ = 0 + """ await _ try _ + unsafe _ """ """#, macros: ["lexicalContextDescription": LexicalContextDescriptionMacro.self] ) assertMacroExpansion( - "try await foo() ? 0 : 1 = #lexicalContextDescription", + "unsafe try await foo() ? 0 : 1 = #lexicalContextDescription", expandedSource: #""" - try await foo() ? 0 : 1 = """ + unsafe try await foo() ? 0 : 1 = """ await _ try _ + unsafe _ """ """#, macros: ["lexicalContextDescription": LexicalContextDescriptionMacro.self] From 660b9a1fa789440aa1cb94caa0e58e3416d80fcb Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Thu, 3 Apr 2025 16:28:47 +0100 Subject: [PATCH 06/21] [Windows] Remove extra newlines from diagnostic output. We are outputting lots of extra newlines on Windows, which makes it hard to read the diagnostics. rdar://148520063 --- .../DiagnosticsFormatter.swift | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/Sources/SwiftDiagnostics/DiagnosticsFormatter.swift b/Sources/SwiftDiagnostics/DiagnosticsFormatter.swift index 28f4d18bf4e..9717ecb4ac7 100644 --- a/Sources/SwiftDiagnostics/DiagnosticsFormatter.swift +++ b/Sources/SwiftDiagnostics/DiagnosticsFormatter.swift @@ -293,10 +293,13 @@ public struct DiagnosticsFormatter { ) ) - // If the line did not end with \n (e.g. the last line), append it manually - if annotatedSource.last != "\n" { - annotatedSource.append("\n") + // Remove any trailing newline and replace it; this may seem + // counterintuitive, but if we're running within CMake and we let a + // '\r\n' through, CMake will turn that into *two* newlines. + if let last = annotatedSource.last, last.isNewline { + annotatedSource.removeLast() } + annotatedSource.append("\n") let columnsWithDiagnostics = Set( annotatedLine.diagnostics.map { @@ -331,8 +334,13 @@ public struct DiagnosticsFormatter { } // Add suffix text. - annotatedSource.append(annotatedLine.suffixText) - if annotatedSource.last != "\n" { + if !annotatedLine.suffixText.isEmpty { + annotatedSource.append(annotatedLine.suffixText) + + // See above for an explanation of why we do this + if let last = annotatedSource.last, last.isNewline { + annotatedSource.removeLast() + } annotatedSource.append("\n") } } From 820501e3cf5894ad1deb8de95649b938a8bbe9f4 Mon Sep 17 00:00:00 2001 From: Brandon Williams <135203+mbrandonw@users.noreply.github.com> Date: Fri, 4 Apr 2025 13:23:21 -0700 Subject: [PATCH 07/21] Add dependencies to SwiftSyntax60[12] (#3034) Adds missing dependencies to the version marker modules from SwiftSyntax so that they can be found from a `canImport`. --- BUILD.bazel | 9 +++++++++ Package.swift | 4 +++- Sources/VersionMarkerModules/CMakeLists.txt | 4 ++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/BUILD.bazel b/BUILD.bazel index 1360cff9043..0ac2123ce63 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -219,6 +219,8 @@ swift_syntax_library( ":SwiftSyntax509", ":SwiftSyntax510", ":SwiftSyntax600", + ":SwiftSyntax601", + ":SwiftSyntax602", ":_SwiftSyntaxCShims", ], ) @@ -251,6 +253,13 @@ swift_syntax_library( ], ) +swift_syntax_library( + name = "SwiftSyntax602", + srcs = glob(["Sources/VersionMarkerModules/SwiftSyntax602/**/*.swift"]), + deps = [ + ], +) + swift_syntax_library( name = "SwiftSyntaxBuilder", deps = [ diff --git a/Package.swift b/Package.swift index 43df607866b..5c20ee04b86 100644 --- a/Package.swift +++ b/Package.swift @@ -210,7 +210,9 @@ let package = Package( .target( name: "SwiftSyntax", - dependencies: ["_SwiftSyntaxCShims", "SwiftSyntax509", "SwiftSyntax510", "SwiftSyntax600"], + dependencies: [ + "_SwiftSyntaxCShims", "SwiftSyntax509", "SwiftSyntax510", "SwiftSyntax600", "SwiftSyntax601", "SwiftSyntax602", + ], exclude: ["CMakeLists.txt"], swiftSettings: swiftSyntaxSwiftSettings ), diff --git a/Sources/VersionMarkerModules/CMakeLists.txt b/Sources/VersionMarkerModules/CMakeLists.txt index 52457d79b36..467d4aa2202 100644 --- a/Sources/VersionMarkerModules/CMakeLists.txt +++ b/Sources/VersionMarkerModules/CMakeLists.txt @@ -14,3 +14,7 @@ add_library(${SWIFTSYNTAX_TARGET_NAMESPACE}SwiftSyntax510 STATIC SwiftSyntax509/Empty.swift) add_library(${SWIFTSYNTAX_TARGET_NAMESPACE}SwiftSyntax600 STATIC SwiftSyntax509/Empty.swift) +add_library(${SWIFTSYNTAX_TARGET_NAMESPACE}SwiftSyntax601 STATIC + SwiftSyntax509/Empty.swift) +add_library(${SWIFTSYNTAX_TARGET_NAMESPACE}SwiftSyntax602 STATIC + SwiftSyntax509/Empty.swift) \ No newline at end of file From 2fcf9c4481a0362dd948eb42d7c78ba3b1066cfd Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Mon, 7 Apr 2025 09:54:13 -0700 Subject: [PATCH 08/21] =?UTF-8?q?Don=E2=80=99t=20exclude=205.8=20in=20PR?= =?UTF-8?q?=20testing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Swift 5.8 was removed from the underlying workflow in https://github.com/swiftlang/github-workflows/pull/107 --- .github/workflows/pull_request.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 01cce58414d..19031c98946 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -8,8 +8,6 @@ jobs: tests: name: Test uses: swiftlang/github-workflows/.github/workflows/swift_package_test.yml@main - with: - linux_exclude_swift_versions: "[{\"swift_version\": \"5.8\"}]" soundness: name: Soundness uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main From 491c5c9837a32e2aff973b7fe8196d2023e70354 Mon Sep 17 00:00:00 2001 From: TTOzzi Date: Tue, 8 Apr 2025 04:02:50 +0900 Subject: [PATCH 09/21] Add concurrency control to cancel in-progress PR workflows (#3042) --- .github/workflows/pull_request.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 01cce58414d..62d15071b42 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -4,6 +4,10 @@ on: pull_request: types: [opened, reopened, synchronize] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: tests: name: Test From 163756675ef150e9c3a54646b95349b942e24e25 Mon Sep 17 00:00:00 2001 From: ttozzi Date: Sat, 5 Apr 2025 20:04:40 +0900 Subject: [PATCH 10/21] Removes duplicate trivia from UnsafeExprSyntax --- Sources/SwiftOperators/OperatorTable+Folding.swift | 4 +--- Tests/SwiftOperatorsTest/OperatorTableTests.swift | 6 ++++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Sources/SwiftOperators/OperatorTable+Folding.swift b/Sources/SwiftOperators/OperatorTable+Folding.swift index c1c4f7f6025..3c2c63d3e97 100644 --- a/Sources/SwiftOperators/OperatorTable+Folding.swift +++ b/Sources/SwiftOperators/OperatorTable+Folding.swift @@ -141,7 +141,6 @@ extension OperatorTable { if let unsafeExpr = lhs.as(UnsafeExprSyntax.self) { return ExprSyntax( UnsafeExprSyntax( - leadingTrivia: unsafeExpr.leadingTrivia, unsafeExpr.unexpectedBeforeUnsafeKeyword, unsafeKeyword: unsafeExpr.unsafeKeyword, unsafeExpr.unexpectedBetweenUnsafeKeywordAndExpression, @@ -150,8 +149,7 @@ extension OperatorTable { op: op, rhs: rhs ), - unsafeExpr.unexpectedAfterExpression, - trailingTrivia: unsafeExpr.trailingTrivia + unsafeExpr.unexpectedAfterExpression ) ) } diff --git a/Tests/SwiftOperatorsTest/OperatorTableTests.swift b/Tests/SwiftOperatorsTest/OperatorTableTests.swift index e4c81c4605b..47e5bd2ad2f 100644 --- a/Tests/SwiftOperatorsTest/OperatorTableTests.swift +++ b/Tests/SwiftOperatorsTest/OperatorTableTests.swift @@ -435,4 +435,10 @@ class OperatorPrecedenceTests: XCTestCase { } } + + func testTriviaAroundUnsafeExpr() throws { + let original = ExprSyntax("/*leading*/ unsafe a /*trailing*/ + b") + let folded = try OperatorTable.standardOperators.foldAll(original) + XCTAssertEqual(original.description, folded.description) + } } From 35a9cde9560eafb447bd97b58f52822560d1f948 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 4 Apr 2025 16:32:29 -0700 Subject: [PATCH 11/21] [SwiftParser] Allow nonisolated to accept `nonsending` modifier --- .../Sources/SyntaxSupport/KeywordSpec.swift | 3 ++ Sources/SwiftParser/Modifiers.swift | 6 +++- Sources/SwiftParser/TokenPrecedence.swift | 1 + Sources/SwiftSyntax/generated/Keyword.swift | 4 +++ Tests/SwiftParserTest/DeclarationTests.swift | 35 +++++++++++++++++-- 5 files changed, 45 insertions(+), 4 deletions(-) diff --git a/CodeGeneration/Sources/SyntaxSupport/KeywordSpec.swift b/CodeGeneration/Sources/SyntaxSupport/KeywordSpec.swift index 2cf01f217b0..7c4b72eeafb 100644 --- a/CodeGeneration/Sources/SyntaxSupport/KeywordSpec.swift +++ b/CodeGeneration/Sources/SyntaxSupport/KeywordSpec.swift @@ -210,6 +210,7 @@ public enum Keyword: CaseIterable { case none case nonisolated case nonmutating + case nonsending case objc case obsoleted case of @@ -551,6 +552,8 @@ public enum Keyword: CaseIterable { return KeywordSpec("nonisolated") case .nonmutating: return KeywordSpec("nonmutating") + case .nonsending: + return KeywordSpec("nonsending") case .objc: return KeywordSpec("objc") case .obsoleted: diff --git a/Sources/SwiftParser/Modifiers.swift b/Sources/SwiftParser/Modifiers.swift index 73a6066a9cf..57ef2b302ae 100644 --- a/Sources/SwiftParser/Modifiers.swift +++ b/Sources/SwiftParser/Modifiers.swift @@ -236,7 +236,11 @@ extension Parser { let detail: RawDeclModifierDetailSyntax? if self.at(.leftParen) { let (unexpectedBeforeLeftParen, leftParen) = self.expect(.leftParen) - let (unexpectedBeforeDetailToken, detailToken) = self.expect(TokenSpec(.unsafe, remapping: .identifier)) + let (unexpectedBeforeDetailToken, detailToken) = self.expect( + TokenSpec(.unsafe, remapping: .identifier), + TokenSpec(.nonsending, remapping: .identifier), + default: TokenSpec(.identifier) + ) let (unexpectedBeforeRightParen, rightParen) = self.expect(.rightParen) detail = RawDeclModifierDetailSyntax( unexpectedBeforeLeftParen, diff --git a/Sources/SwiftParser/TokenPrecedence.swift b/Sources/SwiftParser/TokenPrecedence.swift index 39784f5d2aa..01fdbc06f78 100644 --- a/Sources/SwiftParser/TokenPrecedence.swift +++ b/Sources/SwiftParser/TokenPrecedence.swift @@ -326,6 +326,7 @@ enum TokenPrecedence: Comparable { .module, .noasync, .none, + .nonsending, .obsoleted, .of, .Protocol, diff --git a/Sources/SwiftSyntax/generated/Keyword.swift b/Sources/SwiftSyntax/generated/Keyword.swift index 2abff52c6f4..20b36ff1455 100644 --- a/Sources/SwiftSyntax/generated/Keyword.swift +++ b/Sources/SwiftSyntax/generated/Keyword.swift @@ -155,6 +155,7 @@ public enum Keyword: UInt8, Hashable, Sendable { case none case nonisolated case nonmutating + case nonsending case objc case obsoleted case of @@ -584,6 +585,8 @@ public enum Keyword: UInt8, Hashable, Sendable { self = .higherThan case "introduced": self = .introduced + case "nonsending": + self = .nonsending case "visibility": self = .visibility default: @@ -893,6 +896,7 @@ public enum Keyword: UInt8, Hashable, Sendable { "none", "nonisolated", "nonmutating", + "nonsending", "objc", "obsoleted", "of", diff --git a/Tests/SwiftParserTest/DeclarationTests.swift b/Tests/SwiftParserTest/DeclarationTests.swift index 261edaf59e6..7ec097ec5bd 100644 --- a/Tests/SwiftParserTest/DeclarationTests.swift +++ b/Tests/SwiftParserTest/DeclarationTests.swift @@ -261,8 +261,8 @@ final class DeclarationTests: ParserTestCase { """, diagnostics: [ DiagnosticSpec( - message: "expected 'unsafe' in modifier", - fixIts: ["replace 'safe' with 'unsafe'"] + message: "expected identifier in modifier", + fixIts: ["replace 'safe' with identifier"] ) ], fixedSource: """ @@ -271,7 +271,36 @@ final class DeclarationTests: ParserTestCase { struct A { nonisolated(unsafe) let b = 0 nonisolated(unsafe) var c: Int { 0 } - nonisolated(unsafe) let d = 0 + nonisolated(<#identifier#>) let d = 0 + } + """ + ) + } + + func testNonisolatedNonSendingParsing() { + assertParse( + """ + nonisolated(nonsending) let a = 0 + + struct A { + nonisolated(nonsending) let b = 0 + nonisolated(nonsending) var c: Int { 0 } + nonisolated(1️⃣sending) let d = 0 + } + """, + diagnostics: [ + DiagnosticSpec( + message: "expected identifier in modifier", + fixIts: ["replace 'sending' with identifier"] + ) + ], + fixedSource: """ + nonisolated(nonsending) let a = 0 + + struct A { + nonisolated(nonsending) let b = 0 + nonisolated(nonsending) var c: Int { 0 } + nonisolated(<#identifier#>) let d = 0 } """ ) From a7f65cd80ec35a587a7e57c35f662f1b6dffc981 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 7 Apr 2025 14:39:45 -0700 Subject: [PATCH 12/21] [SwiftParser] Allow `nonisolated` modifier to have a single `nonsending` argument This is part of SE-0461 proposal where `nonisolated` has to be marked as `nonsending` in type context to indicate that the function type it's attached to is caller isolated. --- .../SyntaxSupport/SyntaxNodeKind.swift | 3 + .../Sources/SyntaxSupport/TypeNodes.swift | 49 ++- Sources/SwiftParser/Types.swift | 36 +++ .../generated/Parser+TokenSpecSet.swift | 9 - .../SyntaxExtensions.swift | 1 + .../SyntaxKindNameForDiagnostics.swift | 2 + .../generated/SwiftSyntax.md | 2 + .../generated/ChildNameForKeyPath.swift | 24 ++ .../generated/SyntaxAnyVisitor.swift | 16 + .../generated/SyntaxBaseNodes.swift | 2 + .../generated/SyntaxCollections.swift | 35 ++- .../SwiftSyntax/generated/SyntaxEnum.swift | 6 + .../SwiftSyntax/generated/SyntaxKind.swift | 6 + .../generated/SyntaxRewriter.swift | 32 ++ .../SwiftSyntax/generated/SyntaxTraits.swift | 2 + .../SwiftSyntax/generated/SyntaxVisitor.swift | 48 +++ .../generated/raw/RawSyntaxNodesJKLMN.swift | 152 ++++++++++ .../generated/raw/RawSyntaxNodesTUVWXYZ.swift | 7 +- .../generated/raw/RawSyntaxValidation.swift | 26 +- .../syntaxNodes/SyntaxNodesJKLMN.swift | 280 ++++++++++++++++++ .../syntaxNodes/SyntaxNodesQRS.swift | 3 +- .../generated/ResultBuilders.swift | 4 + Tests/SwiftParserTest/TypeTests.swift | 32 ++ 23 files changed, 759 insertions(+), 18 deletions(-) diff --git a/CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift b/CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift index ab07a05f470..6e50fe39500 100644 --- a/CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift +++ b/CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift @@ -210,6 +210,9 @@ public enum SyntaxNodeKind: String, CaseIterable, IdentifierConvertible, TypeCon case multipleTrailingClosureElementList case namedOpaqueReturnType case nilLiteralExpr + case nonisolatedSpecifierArgument + case nonisolatedSpecifierArgumentList + case nonisolatedTypeSpecifier case objCSelectorPiece case objCSelectorPieceList case operatorDecl diff --git a/CodeGeneration/Sources/SyntaxSupport/TypeNodes.swift b/CodeGeneration/Sources/SyntaxSupport/TypeNodes.swift index 9b9aa3c6e1e..3d65755fa45 100644 --- a/CodeGeneration/Sources/SyntaxSupport/TypeNodes.swift +++ b/CodeGeneration/Sources/SyntaxSupport/TypeNodes.swift @@ -676,6 +676,52 @@ public let TYPE_NODES: [Node] = [ ] ), + Node( + kind: .nonisolatedSpecifierArgument, + base: .syntax, + nameForDiagnostics: nil, + documentation: """ + A single argument that can be added to a nonisolated specifier: 'nonsending'. + + ### Example + `data` in `func foo(data: nonisolated(nonsending) () async -> Void) -> X` + """, + traits: [ + "Parenthesized" + ], + children: [ + Child( + name: "leftParen", + kind: .token(choices: [.token(.leftParen)]) + ), + Child( + name: "nonsendingKeyword", + kind: .token(choices: [.keyword(.nonsending)]) + ), + Child( + name: "rightParen", + kind: .token(choices: [.token(.rightParen)]) + ), + ] + ), + + Node( + kind: .nonisolatedTypeSpecifier, + base: .syntax, + nameForDiagnostics: "'nonisolated' specifier", + children: [ + Child( + name: "nonisolatedKeyword", + kind: .token(choices: [.keyword(.nonisolated)]) + ), + Child( + name: "argument", + kind: .node(kind: .nonisolatedSpecifierArgument), + isOptional: true + ), + ] + ), + Node( kind: .simpleTypeSpecifier, base: .syntax, @@ -689,7 +735,6 @@ public let TYPE_NODES: [Node] = [ .keyword(.__shared), .keyword(.__owned), .keyword(.isolated), - .keyword(.nonisolated), .keyword(._const), .keyword(.borrowing), .keyword(.consuming), @@ -704,6 +749,6 @@ public let TYPE_NODES: [Node] = [ kind: .typeSpecifierList, base: .syntaxCollection, nameForDiagnostics: nil, - elementChoices: [.simpleTypeSpecifier, .lifetimeTypeSpecifier] + elementChoices: [.simpleTypeSpecifier, .lifetimeTypeSpecifier, .nonisolatedTypeSpecifier] ), ] diff --git a/Sources/SwiftParser/Types.swift b/Sources/SwiftParser/Types.swift index 373788f7d73..11f7de1fc5a 100644 --- a/Sources/SwiftParser/Types.swift +++ b/Sources/SwiftParser/Types.swift @@ -1056,6 +1056,40 @@ extension Parser { return .lifetimeTypeSpecifier(lifetimeSpecifier) } + private mutating func parseNonisolatedTypeSpecifier() -> RawTypeSpecifierListSyntax.Element { + let (unexpectedBeforeNonisolatedKeyword, nonisolatedKeyword) = self.expect(.keyword(.nonisolated)) + + guard let leftParen = self.consume(if: .leftParen) else { + let nonisolatedSpecifier = RawNonisolatedTypeSpecifierSyntax( + unexpectedBeforeNonisolatedKeyword, + nonisolatedKeyword: nonisolatedKeyword, + argument: nil, + arena: self.arena + ) + return .nonisolatedTypeSpecifier(nonisolatedSpecifier) + } + + let (unexpectedBeforeModifier, modifier) = self.expect(.keyword(.nonsending)) + let (unexpectedBeforeRightParen, rightParen) = self.expect(.rightParen) + + let argument = RawNonisolatedSpecifierArgumentSyntax( + leftParen: leftParen, + unexpectedBeforeModifier, + nonsendingKeyword: modifier, + unexpectedBeforeRightParen, + rightParen: rightParen, + arena: self.arena + ) + + let nonisolatedSpecifier = RawNonisolatedTypeSpecifierSyntax( + unexpectedBeforeNonisolatedKeyword, + nonisolatedKeyword: nonisolatedKeyword, + argument: argument, + arena: self.arena + ) + return .nonisolatedTypeSpecifier(nonisolatedSpecifier) + } + private mutating func parseSimpleTypeSpecifier( specifierHandle: TokenConsumptionHandle ) -> RawTypeSpecifierListSyntax.Element { @@ -1079,6 +1113,8 @@ extension Parser { } else { break SPECIFIER_PARSING } + } else if self.at(.keyword(.nonisolated)) { + specifiers.append(parseNonisolatedTypeSpecifier()) } else { break SPECIFIER_PARSING } diff --git a/Sources/SwiftParser/generated/Parser+TokenSpecSet.swift b/Sources/SwiftParser/generated/Parser+TokenSpecSet.swift index 1aaa0d9c734..bcdb11f0b95 100644 --- a/Sources/SwiftParser/generated/Parser+TokenSpecSet.swift +++ b/Sources/SwiftParser/generated/Parser+TokenSpecSet.swift @@ -3503,7 +3503,6 @@ extension SimpleTypeSpecifierSyntax { case __shared case __owned case isolated - case nonisolated case _const case borrowing case consuming @@ -3519,8 +3518,6 @@ extension SimpleTypeSpecifierSyntax { self = .__owned case TokenSpec(.isolated): self = .isolated - case TokenSpec(.nonisolated): - self = .nonisolated case TokenSpec(._const): self = ._const case TokenSpec(.borrowing): @@ -3544,8 +3541,6 @@ extension SimpleTypeSpecifierSyntax { self = .__owned case TokenSpec(.isolated): self = .isolated - case TokenSpec(.nonisolated): - self = .nonisolated case TokenSpec(._const): self = ._const case TokenSpec(.borrowing): @@ -3569,8 +3564,6 @@ extension SimpleTypeSpecifierSyntax { return .keyword(.__owned) case .isolated: return .keyword(.isolated) - case .nonisolated: - return .keyword(.nonisolated) case ._const: return .keyword(._const) case .borrowing: @@ -3596,8 +3589,6 @@ extension SimpleTypeSpecifierSyntax { return .keyword(.__owned) case .isolated: return .keyword(.isolated) - case .nonisolated: - return .keyword(.nonisolated) case ._const: return .keyword(._const) case .borrowing: diff --git a/Sources/SwiftParserDiagnostics/SyntaxExtensions.swift b/Sources/SwiftParserDiagnostics/SyntaxExtensions.swift index 07fb77669b2..3dc0aa8e82b 100644 --- a/Sources/SwiftParserDiagnostics/SyntaxExtensions.swift +++ b/Sources/SwiftParserDiagnostics/SyntaxExtensions.swift @@ -212,6 +212,7 @@ extension TypeSpecifierListSyntax { switch specifier { case .simpleTypeSpecifier(let specifier): return specifier.specifier case .lifetimeTypeSpecifier: return nil + case .nonisolatedTypeSpecifier: return nil #if RESILIENT_LIBRARIES @unknown default: fatalError() diff --git a/Sources/SwiftParserDiagnostics/generated/SyntaxKindNameForDiagnostics.swift b/Sources/SwiftParserDiagnostics/generated/SyntaxKindNameForDiagnostics.swift index a5afdfbf444..9a9d00393e7 100644 --- a/Sources/SwiftParserDiagnostics/generated/SyntaxKindNameForDiagnostics.swift +++ b/Sources/SwiftParserDiagnostics/generated/SyntaxKindNameForDiagnostics.swift @@ -294,6 +294,8 @@ extension SyntaxKind { return "trailing closure" case .namedOpaqueReturnType: return "named opaque return type" + case .nonisolatedTypeSpecifier: + return "'nonisolated' specifier" case .objCSelectorPieceList: return "Objective-C selector" case .objCSelectorPiece: diff --git a/Sources/SwiftSyntax/Documentation.docc/generated/SwiftSyntax.md b/Sources/SwiftSyntax/Documentation.docc/generated/SwiftSyntax.md index 6d74ef33dcb..4ec126dbe4f 100644 --- a/Sources/SwiftSyntax/Documentation.docc/generated/SwiftSyntax.md +++ b/Sources/SwiftSyntax/Documentation.docc/generated/SwiftSyntax.md @@ -294,6 +294,7 @@ allows Swift tools to parse, inspect, generate, and transform Swift source code. - - - +- - - - @@ -357,6 +358,7 @@ allows Swift tools to parse, inspect, generate, and transform Swift source code. - - - +- - - - diff --git a/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift b/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift index 4f0645c8267..802b6ae8003 100644 --- a/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift +++ b/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift @@ -2370,6 +2370,30 @@ public func childName(_ keyPath: AnyKeyPath) -> String? { return "nilKeyword" case \NilLiteralExprSyntax.unexpectedAfterNilKeyword: return "unexpectedAfterNilKeyword" + case \NonisolatedSpecifierArgumentSyntax.unexpectedBeforeLeftParen: + return "unexpectedBeforeLeftParen" + case \NonisolatedSpecifierArgumentSyntax.leftParen: + return "leftParen" + case \NonisolatedSpecifierArgumentSyntax.unexpectedBetweenLeftParenAndNonsendingKeyword: + return "unexpectedBetweenLeftParenAndNonsendingKeyword" + case \NonisolatedSpecifierArgumentSyntax.nonsendingKeyword: + return "nonsendingKeyword" + case \NonisolatedSpecifierArgumentSyntax.unexpectedBetweenNonsendingKeywordAndRightParen: + return "unexpectedBetweenNonsendingKeywordAndRightParen" + case \NonisolatedSpecifierArgumentSyntax.rightParen: + return "rightParen" + case \NonisolatedSpecifierArgumentSyntax.unexpectedAfterRightParen: + return "unexpectedAfterRightParen" + case \NonisolatedTypeSpecifierSyntax.unexpectedBeforeNonisolatedKeyword: + return "unexpectedBeforeNonisolatedKeyword" + case \NonisolatedTypeSpecifierSyntax.nonisolatedKeyword: + return "nonisolatedKeyword" + case \NonisolatedTypeSpecifierSyntax.unexpectedBetweenNonisolatedKeywordAndArgument: + return "unexpectedBetweenNonisolatedKeywordAndArgument" + case \NonisolatedTypeSpecifierSyntax.argument: + return "argument" + case \NonisolatedTypeSpecifierSyntax.unexpectedAfterArgument: + return "unexpectedAfterArgument" case \ObjCSelectorPieceSyntax.unexpectedBeforeName: return "unexpectedBeforeName" case \ObjCSelectorPieceSyntax.name: diff --git a/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift b/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift index a48aab9ea71..124561440e0 100644 --- a/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift +++ b/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift @@ -1585,6 +1585,22 @@ open class SyntaxAnyVisitor: SyntaxVisitor { visitAnyPost(node._syntaxNode) } + override open func visit(_ node: NonisolatedSpecifierArgumentSyntax) -> SyntaxVisitorContinueKind { + return visitAny(node._syntaxNode) + } + + override open func visitPost(_ node: NonisolatedSpecifierArgumentSyntax) { + visitAnyPost(node._syntaxNode) + } + + override open func visit(_ node: NonisolatedTypeSpecifierSyntax) -> SyntaxVisitorContinueKind { + return visitAny(node._syntaxNode) + } + + override open func visitPost(_ node: NonisolatedTypeSpecifierSyntax) { + visitAnyPost(node._syntaxNode) + } + override open func visit(_ node: ObjCSelectorPieceListSyntax) -> SyntaxVisitorContinueKind { return visitAny(node._syntaxNode) } diff --git a/Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift b/Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift index 89d739db3ed..8c1a8444f88 100644 --- a/Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift +++ b/Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift @@ -1699,6 +1699,8 @@ extension Syntax { .node(MultipleTrailingClosureElementSyntax.self), .node(NamedOpaqueReturnTypeSyntax.self), .node(NilLiteralExprSyntax.self), + .node(NonisolatedSpecifierArgumentSyntax.self), + .node(NonisolatedTypeSpecifierSyntax.self), .node(ObjCSelectorPieceListSyntax.self), .node(ObjCSelectorPieceSyntax.self), .node(OperatorDeclSyntax.self), diff --git a/Sources/SwiftSyntax/generated/SyntaxCollections.swift b/Sources/SwiftSyntax/generated/SyntaxCollections.swift index 05305c97829..a34717855b7 100644 --- a/Sources/SwiftSyntax/generated/SyntaxCollections.swift +++ b/Sources/SwiftSyntax/generated/SyntaxCollections.swift @@ -1913,7 +1913,7 @@ public struct TupleTypeElementListSyntax: SyntaxCollection, SyntaxHashable { /// ### Children /// -/// (``SimpleTypeSpecifierSyntax`` | `LifetimeTypeSpecifierSyntax`) `*` +/// (``SimpleTypeSpecifierSyntax`` | `LifetimeTypeSpecifierSyntax` | ``NonisolatedTypeSpecifierSyntax``) `*` /// /// ### Contained in /// @@ -1926,6 +1926,7 @@ public struct TypeSpecifierListSyntax: SyntaxCollection, SyntaxHashable { /// - Note: Requires experimental feature `nonescapableTypes`. @_spi(ExperimentalLanguageFeatures) case lifetimeTypeSpecifier(LifetimeTypeSpecifierSyntax) + case nonisolatedTypeSpecifier(NonisolatedTypeSpecifierSyntax) public var _syntaxNode: Syntax { switch self { @@ -1933,6 +1934,8 @@ public struct TypeSpecifierListSyntax: SyntaxCollection, SyntaxHashable { return node._syntaxNode case .lifetimeTypeSpecifier(let node): return node._syntaxNode + case .nonisolatedTypeSpecifier(let node): + return node._syntaxNode } } @@ -1946,18 +1949,24 @@ public struct TypeSpecifierListSyntax: SyntaxCollection, SyntaxHashable { self = .lifetimeTypeSpecifier(node) } + public init(_ node: NonisolatedTypeSpecifierSyntax) { + self = .nonisolatedTypeSpecifier(node) + } + public init?(_ node: __shared some SyntaxProtocol) { if let node = node.as(SimpleTypeSpecifierSyntax.self) { self = .simpleTypeSpecifier(node) } else if let node = node.as(LifetimeTypeSpecifierSyntax.self) { self = .lifetimeTypeSpecifier(node) + } else if let node = node.as(NonisolatedTypeSpecifierSyntax.self) { + self = .nonisolatedTypeSpecifier(node) } else { return nil } } public static var structure: SyntaxNodeStructure { - return .choices([.node(SimpleTypeSpecifierSyntax.self), .node(LifetimeTypeSpecifierSyntax.self)]) + return .choices([.node(SimpleTypeSpecifierSyntax.self), .node(LifetimeTypeSpecifierSyntax.self), .node(NonisolatedTypeSpecifierSyntax.self)]) } /// Checks if the current syntax node can be cast to ``SimpleTypeSpecifierSyntax``. @@ -2009,6 +2018,28 @@ public struct TypeSpecifierListSyntax: SyntaxCollection, SyntaxHashable { public func cast(_ syntaxType: LifetimeTypeSpecifierSyntax.Type) -> LifetimeTypeSpecifierSyntax { return self.as(LifetimeTypeSpecifierSyntax.self)! } + + /// Checks if the current syntax node can be cast to ``NonisolatedTypeSpecifierSyntax``. + /// + /// - Returns: `true` if the node can be cast, `false` otherwise. + public func `is`(_ syntaxType: NonisolatedTypeSpecifierSyntax.Type) -> Bool { + return self.as(syntaxType) != nil + } + + /// Attempts to cast the current syntax node to ``NonisolatedTypeSpecifierSyntax``. + /// + /// - Returns: An instance of ``NonisolatedTypeSpecifierSyntax``, or `nil` if the cast fails. + public func `as`(_ syntaxType: NonisolatedTypeSpecifierSyntax.Type) -> NonisolatedTypeSpecifierSyntax? { + return NonisolatedTypeSpecifierSyntax.init(self) + } + + /// Force-casts the current syntax node to ``NonisolatedTypeSpecifierSyntax``. + /// + /// - Returns: An instance of ``NonisolatedTypeSpecifierSyntax``. + /// - Warning: This function will crash if the cast is not possible. Use `as` to safely attempt a cast. + public func cast(_ syntaxType: NonisolatedTypeSpecifierSyntax.Type) -> NonisolatedTypeSpecifierSyntax { + return self.as(NonisolatedTypeSpecifierSyntax.self)! + } } public let _syntaxNode: Syntax diff --git a/Sources/SwiftSyntax/generated/SyntaxEnum.swift b/Sources/SwiftSyntax/generated/SyntaxEnum.swift index dacab406c29..283c1d82d03 100644 --- a/Sources/SwiftSyntax/generated/SyntaxEnum.swift +++ b/Sources/SwiftSyntax/generated/SyntaxEnum.swift @@ -216,6 +216,8 @@ public enum SyntaxEnum: Sendable { case multipleTrailingClosureElement(MultipleTrailingClosureElementSyntax) case namedOpaqueReturnType(NamedOpaqueReturnTypeSyntax) case nilLiteralExpr(NilLiteralExprSyntax) + case nonisolatedSpecifierArgument(NonisolatedSpecifierArgumentSyntax) + case nonisolatedTypeSpecifier(NonisolatedTypeSpecifierSyntax) case objCSelectorPieceList(ObjCSelectorPieceListSyntax) case objCSelectorPiece(ObjCSelectorPieceSyntax) case operatorDecl(OperatorDeclSyntax) @@ -699,6 +701,10 @@ extension Syntax { return .namedOpaqueReturnType(NamedOpaqueReturnTypeSyntax(self)!) case .nilLiteralExpr: return .nilLiteralExpr(NilLiteralExprSyntax(self)!) + case .nonisolatedSpecifierArgument: + return .nonisolatedSpecifierArgument(NonisolatedSpecifierArgumentSyntax(self)!) + case .nonisolatedTypeSpecifier: + return .nonisolatedTypeSpecifier(NonisolatedTypeSpecifierSyntax(self)!) case .objCSelectorPieceList: return .objCSelectorPieceList(ObjCSelectorPieceListSyntax(self)!) case .objCSelectorPiece: diff --git a/Sources/SwiftSyntax/generated/SyntaxKind.swift b/Sources/SwiftSyntax/generated/SyntaxKind.swift index f1476b00ef4..62883dd7ac3 100644 --- a/Sources/SwiftSyntax/generated/SyntaxKind.swift +++ b/Sources/SwiftSyntax/generated/SyntaxKind.swift @@ -216,6 +216,8 @@ public enum SyntaxKind: Sendable { case multipleTrailingClosureElement case namedOpaqueReturnType case nilLiteralExpr + case nonisolatedSpecifierArgument + case nonisolatedTypeSpecifier case objCSelectorPieceList case objCSelectorPiece case operatorDecl @@ -824,6 +826,10 @@ public enum SyntaxKind: Sendable { return NamedOpaqueReturnTypeSyntax.self case .nilLiteralExpr: return NilLiteralExprSyntax.self + case .nonisolatedSpecifierArgument: + return NonisolatedSpecifierArgumentSyntax.self + case .nonisolatedTypeSpecifier: + return NonisolatedTypeSpecifierSyntax.self case .objCSelectorPieceList: return ObjCSelectorPieceListSyntax.self case .objCSelectorPiece: diff --git a/Sources/SwiftSyntax/generated/SyntaxRewriter.swift b/Sources/SwiftSyntax/generated/SyntaxRewriter.swift index fe361ee516f..77eb14701ba 100644 --- a/Sources/SwiftSyntax/generated/SyntaxRewriter.swift +++ b/Sources/SwiftSyntax/generated/SyntaxRewriter.swift @@ -1427,6 +1427,20 @@ open class SyntaxRewriter { return ExprSyntax(NilLiteralExprSyntax(unsafeCasting: visitChildren(node._syntaxNode))) } + /// Visit a ``NonisolatedSpecifierArgumentSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: NonisolatedSpecifierArgumentSyntax) -> NonisolatedSpecifierArgumentSyntax { + return NonisolatedSpecifierArgumentSyntax(unsafeCasting: visitChildren(node._syntaxNode)) + } + + /// Visit a ``NonisolatedTypeSpecifierSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: NonisolatedTypeSpecifierSyntax) -> NonisolatedTypeSpecifierSyntax { + return NonisolatedTypeSpecifierSyntax(unsafeCasting: visitChildren(node._syntaxNode)) + } + /// Visit a ``ObjCSelectorPieceListSyntax``. /// - Parameter node: the node that is being visited /// - Returns: the rewritten node @@ -3094,6 +3108,16 @@ open class SyntaxRewriter { Syntax(visit(NilLiteralExprSyntax(unsafeCasting: node))) } + @inline(never) + private func visitNonisolatedSpecifierArgumentSyntaxImpl(_ node: Syntax) -> Syntax { + Syntax(visit(NonisolatedSpecifierArgumentSyntax(unsafeCasting: node))) + } + + @inline(never) + private func visitNonisolatedTypeSpecifierSyntaxImpl(_ node: Syntax) -> Syntax { + Syntax(visit(NonisolatedTypeSpecifierSyntax(unsafeCasting: node))) + } + @inline(never) private func visitObjCSelectorPieceListSyntaxImpl(_ node: Syntax) -> Syntax { Syntax(visit(ObjCSelectorPieceListSyntax(unsafeCasting: node))) @@ -3988,6 +4012,10 @@ open class SyntaxRewriter { return self.visitNamedOpaqueReturnTypeSyntaxImpl(_:) case .nilLiteralExpr: return self.visitNilLiteralExprSyntaxImpl(_:) + case .nonisolatedSpecifierArgument: + return self.visitNonisolatedSpecifierArgumentSyntaxImpl(_:) + case .nonisolatedTypeSpecifier: + return self.visitNonisolatedTypeSpecifierSyntaxImpl(_:) case .objCSelectorPieceList: return self.visitObjCSelectorPieceListSyntaxImpl(_:) case .objCSelectorPiece: @@ -4570,6 +4598,10 @@ open class SyntaxRewriter { return visitNamedOpaqueReturnTypeSyntaxImpl(node) case .nilLiteralExpr: return visitNilLiteralExprSyntaxImpl(node) + case .nonisolatedSpecifierArgument: + return visitNonisolatedSpecifierArgumentSyntaxImpl(node) + case .nonisolatedTypeSpecifier: + return visitNonisolatedTypeSpecifierSyntaxImpl(node) case .objCSelectorPieceList: return visitObjCSelectorPieceListSyntaxImpl(node) case .objCSelectorPiece: diff --git a/Sources/SwiftSyntax/generated/SyntaxTraits.swift b/Sources/SwiftSyntax/generated/SyntaxTraits.swift index bf4e7438523..368b529b45b 100644 --- a/Sources/SwiftSyntax/generated/SyntaxTraits.swift +++ b/Sources/SwiftSyntax/generated/SyntaxTraits.swift @@ -802,6 +802,8 @@ extension MissingSyntax: MissingNodeSyntax {} extension MissingTypeSyntax: MissingNodeSyntax {} +extension NonisolatedSpecifierArgumentSyntax: ParenthesizedSyntax {} + extension OperatorDeclSyntax: NamedDeclSyntax {} extension PatternBindingSyntax: WithTrailingCommaSyntax {} diff --git a/Sources/SwiftSyntax/generated/SyntaxVisitor.swift b/Sources/SwiftSyntax/generated/SyntaxVisitor.swift index f6c0bbd35f2..739a203874c 100644 --- a/Sources/SwiftSyntax/generated/SyntaxVisitor.swift +++ b/Sources/SwiftSyntax/generated/SyntaxVisitor.swift @@ -2315,6 +2315,30 @@ open class SyntaxVisitor { open func visitPost(_ node: NilLiteralExprSyntax) { } + /// Visiting ``NonisolatedSpecifierArgumentSyntax`` specifically. + /// - Parameter node: the node we are visiting. + /// - Returns: how should we continue visiting. + open func visit(_ node: NonisolatedSpecifierArgumentSyntax) -> SyntaxVisitorContinueKind { + return .visitChildren + } + + /// The function called after visiting ``NonisolatedSpecifierArgumentSyntax`` and its descendants. + /// - node: the node we just finished visiting. + open func visitPost(_ node: NonisolatedSpecifierArgumentSyntax) { + } + + /// Visiting ``NonisolatedTypeSpecifierSyntax`` specifically. + /// - Parameter node: the node we are visiting. + /// - Returns: how should we continue visiting. + open func visit(_ node: NonisolatedTypeSpecifierSyntax) -> SyntaxVisitorContinueKind { + return .visitChildren + } + + /// The function called after visiting ``NonisolatedTypeSpecifierSyntax`` and its descendants. + /// - node: the node we just finished visiting. + open func visitPost(_ node: NonisolatedTypeSpecifierSyntax) { + } + /// Visiting ``ObjCSelectorPieceListSyntax`` specifically. /// - Parameter node: the node we are visiting. /// - Returns: how should we continue visiting. @@ -5016,6 +5040,22 @@ open class SyntaxVisitor { visitPost(NilLiteralExprSyntax(unsafeCasting: node)) } + @inline(never) + private func visitNonisolatedSpecifierArgumentSyntaxImpl(_ node: Syntax) { + if visit(NonisolatedSpecifierArgumentSyntax(unsafeCasting: node)) == .visitChildren { + visitChildren(node) + } + visitPost(NonisolatedSpecifierArgumentSyntax(unsafeCasting: node)) + } + + @inline(never) + private func visitNonisolatedTypeSpecifierSyntaxImpl(_ node: Syntax) { + if visit(NonisolatedTypeSpecifierSyntax(unsafeCasting: node)) == .visitChildren { + visitChildren(node) + } + visitPost(NonisolatedTypeSpecifierSyntax(unsafeCasting: node)) + } + @inline(never) private func visitObjCSelectorPieceListSyntaxImpl(_ node: Syntax) { if visit(ObjCSelectorPieceListSyntax(unsafeCasting: node)) == .visitChildren { @@ -6204,6 +6244,10 @@ open class SyntaxVisitor { return self.visitNamedOpaqueReturnTypeSyntaxImpl(_:) case .nilLiteralExpr: return self.visitNilLiteralExprSyntaxImpl(_:) + case .nonisolatedSpecifierArgument: + return self.visitNonisolatedSpecifierArgumentSyntaxImpl(_:) + case .nonisolatedTypeSpecifier: + return self.visitNonisolatedTypeSpecifierSyntaxImpl(_:) case .objCSelectorPieceList: return self.visitObjCSelectorPieceListSyntaxImpl(_:) case .objCSelectorPiece: @@ -6786,6 +6830,10 @@ open class SyntaxVisitor { self.visitNamedOpaqueReturnTypeSyntaxImpl(node) case .nilLiteralExpr: self.visitNilLiteralExprSyntaxImpl(node) + case .nonisolatedSpecifierArgument: + self.visitNonisolatedSpecifierArgumentSyntaxImpl(node) + case .nonisolatedTypeSpecifier: + self.visitNonisolatedTypeSpecifierSyntaxImpl(node) case .objCSelectorPieceList: self.visitObjCSelectorPieceListSyntaxImpl(node) case .objCSelectorPiece: diff --git a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesJKLMN.swift b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesJKLMN.swift index 5f8cbf238d2..8e6766d2c4f 100644 --- a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesJKLMN.swift +++ b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesJKLMN.swift @@ -2957,3 +2957,155 @@ public struct RawNilLiteralExprSyntax: RawExprSyntaxNodeProtocol { layoutView.children[2].map(RawUnexpectedNodesSyntax.init(raw:)) } } + +@_spi(RawSyntax) +public struct RawNonisolatedSpecifierArgumentSyntax: RawSyntaxNodeProtocol { + @_spi(RawSyntax) + public var layoutView: RawSyntaxLayoutView { + return raw.layoutView! + } + + public static func isKindOf(_ raw: RawSyntax) -> Bool { + return raw.kind == .nonisolatedSpecifierArgument + } + + public var raw: RawSyntax + + init(raw: RawSyntax) { + precondition(Self.isKindOf(raw)) + self.raw = raw + } + + private init(unchecked raw: RawSyntax) { + self.raw = raw + } + + public init?(_ other: some RawSyntaxNodeProtocol) { + guard Self.isKindOf(other.raw) else { + return nil + } + self.init(unchecked: other.raw) + } + + public init( + _ unexpectedBeforeLeftParen: RawUnexpectedNodesSyntax? = nil, + leftParen: RawTokenSyntax, + _ unexpectedBetweenLeftParenAndNonsendingKeyword: RawUnexpectedNodesSyntax? = nil, + nonsendingKeyword: RawTokenSyntax, + _ unexpectedBetweenNonsendingKeywordAndRightParen: RawUnexpectedNodesSyntax? = nil, + rightParen: RawTokenSyntax, + _ unexpectedAfterRightParen: RawUnexpectedNodesSyntax? = nil, + arena: __shared RawSyntaxArena + ) { + let raw = RawSyntax.makeLayout( + kind: .nonisolatedSpecifierArgument, uninitializedCount: 7, arena: arena) { layout in + layout.initialize(repeating: nil) + layout[0] = unexpectedBeforeLeftParen?.raw + layout[1] = leftParen.raw + layout[2] = unexpectedBetweenLeftParenAndNonsendingKeyword?.raw + layout[3] = nonsendingKeyword.raw + layout[4] = unexpectedBetweenNonsendingKeywordAndRightParen?.raw + layout[5] = rightParen.raw + layout[6] = unexpectedAfterRightParen?.raw + } + self.init(unchecked: raw) + } + + public var unexpectedBeforeLeftParen: RawUnexpectedNodesSyntax? { + layoutView.children[0].map(RawUnexpectedNodesSyntax.init(raw:)) + } + + public var leftParen: RawTokenSyntax { + layoutView.children[1].map(RawTokenSyntax.init(raw:))! + } + + public var unexpectedBetweenLeftParenAndNonsendingKeyword: RawUnexpectedNodesSyntax? { + layoutView.children[2].map(RawUnexpectedNodesSyntax.init(raw:)) + } + + public var nonsendingKeyword: RawTokenSyntax { + layoutView.children[3].map(RawTokenSyntax.init(raw:))! + } + + public var unexpectedBetweenNonsendingKeywordAndRightParen: RawUnexpectedNodesSyntax? { + layoutView.children[4].map(RawUnexpectedNodesSyntax.init(raw:)) + } + + public var rightParen: RawTokenSyntax { + layoutView.children[5].map(RawTokenSyntax.init(raw:))! + } + + public var unexpectedAfterRightParen: RawUnexpectedNodesSyntax? { + layoutView.children[6].map(RawUnexpectedNodesSyntax.init(raw:)) + } +} + +@_spi(RawSyntax) +public struct RawNonisolatedTypeSpecifierSyntax: RawSyntaxNodeProtocol { + @_spi(RawSyntax) + public var layoutView: RawSyntaxLayoutView { + return raw.layoutView! + } + + public static func isKindOf(_ raw: RawSyntax) -> Bool { + return raw.kind == .nonisolatedTypeSpecifier + } + + public var raw: RawSyntax + + init(raw: RawSyntax) { + precondition(Self.isKindOf(raw)) + self.raw = raw + } + + private init(unchecked raw: RawSyntax) { + self.raw = raw + } + + public init?(_ other: some RawSyntaxNodeProtocol) { + guard Self.isKindOf(other.raw) else { + return nil + } + self.init(unchecked: other.raw) + } + + public init( + _ unexpectedBeforeNonisolatedKeyword: RawUnexpectedNodesSyntax? = nil, + nonisolatedKeyword: RawTokenSyntax, + _ unexpectedBetweenNonisolatedKeywordAndArgument: RawUnexpectedNodesSyntax? = nil, + argument: RawNonisolatedSpecifierArgumentSyntax?, + _ unexpectedAfterArgument: RawUnexpectedNodesSyntax? = nil, + arena: __shared RawSyntaxArena + ) { + let raw = RawSyntax.makeLayout( + kind: .nonisolatedTypeSpecifier, uninitializedCount: 5, arena: arena) { layout in + layout.initialize(repeating: nil) + layout[0] = unexpectedBeforeNonisolatedKeyword?.raw + layout[1] = nonisolatedKeyword.raw + layout[2] = unexpectedBetweenNonisolatedKeywordAndArgument?.raw + layout[3] = argument?.raw + layout[4] = unexpectedAfterArgument?.raw + } + self.init(unchecked: raw) + } + + public var unexpectedBeforeNonisolatedKeyword: RawUnexpectedNodesSyntax? { + layoutView.children[0].map(RawUnexpectedNodesSyntax.init(raw:)) + } + + public var nonisolatedKeyword: RawTokenSyntax { + layoutView.children[1].map(RawTokenSyntax.init(raw:))! + } + + public var unexpectedBetweenNonisolatedKeywordAndArgument: RawUnexpectedNodesSyntax? { + layoutView.children[2].map(RawUnexpectedNodesSyntax.init(raw:)) + } + + public var argument: RawNonisolatedSpecifierArgumentSyntax? { + layoutView.children[3].map(RawNonisolatedSpecifierArgumentSyntax.init(raw:)) + } + + public var unexpectedAfterArgument: RawUnexpectedNodesSyntax? { + layoutView.children[4].map(RawUnexpectedNodesSyntax.init(raw:)) + } +} diff --git a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesTUVWXYZ.swift b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesTUVWXYZ.swift index 9dafdb8a7bf..e4b5a2b69a0 100644 --- a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesTUVWXYZ.swift +++ b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesTUVWXYZ.swift @@ -1416,9 +1416,10 @@ public struct RawTypeSpecifierListSyntax: RawSyntaxNodeProtocol { /// - Note: Requires experimental feature `nonescapableTypes`. @_spi(ExperimentalLanguageFeatures) case lifetimeTypeSpecifier(RawLifetimeTypeSpecifierSyntax) + case nonisolatedTypeSpecifier(RawNonisolatedTypeSpecifierSyntax) public static func isKindOf(_ raw: RawSyntax) -> Bool { - RawSimpleTypeSpecifierSyntax.isKindOf(raw) || RawLifetimeTypeSpecifierSyntax.isKindOf(raw) + RawSimpleTypeSpecifierSyntax.isKindOf(raw) || RawLifetimeTypeSpecifierSyntax.isKindOf(raw) || RawNonisolatedTypeSpecifierSyntax.isKindOf(raw) } public var raw: RawSyntax { @@ -1427,6 +1428,8 @@ public struct RawTypeSpecifierListSyntax: RawSyntaxNodeProtocol { return node.raw case .lifetimeTypeSpecifier(let node): return node.raw + case .nonisolatedTypeSpecifier(let node): + return node.raw } } @@ -1435,6 +1438,8 @@ public struct RawTypeSpecifierListSyntax: RawSyntaxNodeProtocol { self = .simpleTypeSpecifier(node) } else if let node = node.as(RawLifetimeTypeSpecifierSyntax.self) { self = .lifetimeTypeSpecifier(node) + } else if let node = node.as(RawNonisolatedTypeSpecifierSyntax.self) { + self = .nonisolatedTypeSpecifier(node) } else { return nil } diff --git a/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift b/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift index bfbbbbe2053..0ca5359f6e7 100644 --- a/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift +++ b/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift @@ -2150,6 +2150,24 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { assertNoError(kind, 1, verify(layout[1], as: RawTokenSyntax.self, tokenChoices: [.keyword("nil")])) assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self)) } + func validateNonisolatedSpecifierArgumentSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) { + assert(layout.count == 7) + assertNoError(kind, 0, verify(layout[0], as: RawUnexpectedNodesSyntax?.self)) + assertNoError(kind, 1, verify(layout[1], as: RawTokenSyntax.self, tokenChoices: [.tokenKind(.leftParen)])) + assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self)) + assertNoError(kind, 3, verify(layout[3], as: RawTokenSyntax.self, tokenChoices: [.keyword("nonsending")])) + assertNoError(kind, 4, verify(layout[4], as: RawUnexpectedNodesSyntax?.self)) + assertNoError(kind, 5, verify(layout[5], as: RawTokenSyntax.self, tokenChoices: [.tokenKind(.rightParen)])) + assertNoError(kind, 6, verify(layout[6], as: RawUnexpectedNodesSyntax?.self)) + } + func validateNonisolatedTypeSpecifierSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) { + assert(layout.count == 5) + assertNoError(kind, 0, verify(layout[0], as: RawUnexpectedNodesSyntax?.self)) + assertNoError(kind, 1, verify(layout[1], as: RawTokenSyntax.self, tokenChoices: [.keyword("nonisolated")])) + assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self)) + assertNoError(kind, 3, verify(layout[3], as: RawNonisolatedSpecifierArgumentSyntax?.self)) + assertNoError(kind, 4, verify(layout[4], as: RawUnexpectedNodesSyntax?.self)) + } func validateObjCSelectorPieceListSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) { for (index, element) in layout.enumerated() { assertNoError(kind, index, verify(element, as: RawObjCSelectorPieceSyntax.self)) @@ -2561,7 +2579,6 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { .keyword("__shared"), .keyword("__owned"), .keyword("isolated"), - .keyword("nonisolated"), .keyword("_const"), .keyword("borrowing"), .keyword("consuming"), @@ -2957,7 +2974,8 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { for (index, element) in layout.enumerated() { assertAnyHasNoError(kind, index, [ verify(element, as: RawSimpleTypeSpecifierSyntax.self), - verify(element, as: RawLifetimeTypeSpecifierSyntax.self)]) + verify(element, as: RawLifetimeTypeSpecifierSyntax.self), + verify(element, as: RawNonisolatedTypeSpecifierSyntax.self)]) } } func validateUnexpectedNodesSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) { @@ -3488,6 +3506,10 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { validateNamedOpaqueReturnTypeSyntax(kind: kind, layout: layout) case .nilLiteralExpr: validateNilLiteralExprSyntax(kind: kind, layout: layout) + case .nonisolatedSpecifierArgument: + validateNonisolatedSpecifierArgumentSyntax(kind: kind, layout: layout) + case .nonisolatedTypeSpecifier: + validateNonisolatedTypeSpecifierSyntax(kind: kind, layout: layout) case .objCSelectorPieceList: validateObjCSelectorPieceListSyntax(kind: kind, layout: layout) case .objCSelectorPiece: diff --git a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesJKLMN.swift b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesJKLMN.swift index 686cb752a0b..fe4e496d5ab 100644 --- a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesJKLMN.swift +++ b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesJKLMN.swift @@ -5507,3 +5507,283 @@ public struct NilLiteralExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _LeafExp public static let structure: SyntaxNodeStructure = .layout([\Self.unexpectedBeforeNilKeyword, \Self.nilKeyword, \Self.unexpectedAfterNilKeyword]) } + +// MARK: - NonisolatedSpecifierArgumentSyntax + +/// A single argument that can be added to a nonisolated specifier: 'nonsending'. +/// +/// ### Example +/// `data` in `func foo(data: nonisolated(nonsending) () async -> Void) -> X` +/// +/// ### Children +/// +/// - `leftParen`: `(` +/// - `nonsendingKeyword`: `nonsending` +/// - `rightParen`: `)` +/// +/// ### Contained in +/// +/// - ``NonisolatedTypeSpecifierSyntax``.``NonisolatedTypeSpecifierSyntax/argument`` +public struct NonisolatedSpecifierArgumentSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNodeProtocol { + public let _syntaxNode: Syntax + + public init?(_ node: __shared some SyntaxProtocol) { + guard node.raw.kind == .nonisolatedSpecifierArgument else { + return nil + } + self._syntaxNode = node._syntaxNode + } + + @_transparent + init(unsafeCasting node: Syntax) { + self._syntaxNode = node + } + + /// - 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. + /// - 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, + _ unexpectedBeforeLeftParen: UnexpectedNodesSyntax? = nil, + leftParen: TokenSyntax = .leftParenToken(), + _ unexpectedBetweenLeftParenAndNonsendingKeyword: UnexpectedNodesSyntax? = nil, + nonsendingKeyword: TokenSyntax = .keyword(.nonsending), + _ unexpectedBetweenNonsendingKeywordAndRightParen: UnexpectedNodesSyntax? = nil, + rightParen: TokenSyntax = .rightParenToken(), + _ unexpectedAfterRightParen: UnexpectedNodesSyntax? = nil, + trailingTrivia: Trivia? = nil + ) { + // Extend the lifetime of all parameters so their arenas don't get destroyed + // before they can be added as children of the new arena. + self = withExtendedLifetime((RawSyntaxArena(), ( + unexpectedBeforeLeftParen, + leftParen, + unexpectedBetweenLeftParenAndNonsendingKeyword, + nonsendingKeyword, + unexpectedBetweenNonsendingKeywordAndRightParen, + rightParen, + unexpectedAfterRightParen + ))) { (arena, _) in + let layout: [RawSyntax?] = [ + unexpectedBeforeLeftParen?.raw, + leftParen.raw, + unexpectedBetweenLeftParenAndNonsendingKeyword?.raw, + nonsendingKeyword.raw, + unexpectedBetweenNonsendingKeywordAndRightParen?.raw, + rightParen.raw, + unexpectedAfterRightParen?.raw + ] + let raw = RawSyntax.makeLayout( + kind: SyntaxKind.nonisolatedSpecifierArgument, + from: layout, + arena: arena, + leadingTrivia: leadingTrivia, + trailingTrivia: trailingTrivia + ) + return Syntax.forRoot(raw, rawNodeArena: arena).cast(Self.self) + } + } + + public var unexpectedBeforeLeftParen: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 0)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 0, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(NonisolatedSpecifierArgumentSyntax.self) + } + } + + /// ### Tokens + /// + /// For syntax trees generated by the parser, this is guaranteed to be `(`. + public var leftParen: TokenSyntax { + get { + return Syntax(self).child(at: 1)!.cast(TokenSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 1, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(NonisolatedSpecifierArgumentSyntax.self) + } + } + + public var unexpectedBetweenLeftParenAndNonsendingKeyword: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 2)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 2, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(NonisolatedSpecifierArgumentSyntax.self) + } + } + + /// ### Tokens + /// + /// For syntax trees generated by the parser, this is guaranteed to be `nonsending`. + public var nonsendingKeyword: TokenSyntax { + get { + return Syntax(self).child(at: 3)!.cast(TokenSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 3, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(NonisolatedSpecifierArgumentSyntax.self) + } + } + + public var unexpectedBetweenNonsendingKeywordAndRightParen: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 4)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 4, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(NonisolatedSpecifierArgumentSyntax.self) + } + } + + /// ### Tokens + /// + /// For syntax trees generated by the parser, this is guaranteed to be `)`. + public var rightParen: TokenSyntax { + get { + return Syntax(self).child(at: 5)!.cast(TokenSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 5, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(NonisolatedSpecifierArgumentSyntax.self) + } + } + + public var unexpectedAfterRightParen: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 6)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 6, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(NonisolatedSpecifierArgumentSyntax.self) + } + } + + public static let structure: SyntaxNodeStructure = .layout([ + \Self.unexpectedBeforeLeftParen, + \Self.leftParen, + \Self.unexpectedBetweenLeftParenAndNonsendingKeyword, + \Self.nonsendingKeyword, + \Self.unexpectedBetweenNonsendingKeywordAndRightParen, + \Self.rightParen, + \Self.unexpectedAfterRightParen + ]) +} + +// MARK: - NonisolatedTypeSpecifierSyntax + +/// ### Children +/// +/// - `nonisolatedKeyword`: `nonisolated` +/// - `argument`: ``NonisolatedSpecifierArgumentSyntax``? +/// +/// ### Contained in +/// +/// - ``TypeSpecifierListSyntax`` +public struct NonisolatedTypeSpecifierSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNodeProtocol { + public let _syntaxNode: Syntax + + public init?(_ node: __shared some SyntaxProtocol) { + guard node.raw.kind == .nonisolatedTypeSpecifier else { + return nil + } + self._syntaxNode = node._syntaxNode + } + + @_transparent + init(unsafeCasting node: Syntax) { + self._syntaxNode = node + } + + /// - 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. + /// - 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, + _ unexpectedBeforeNonisolatedKeyword: UnexpectedNodesSyntax? = nil, + nonisolatedKeyword: TokenSyntax = .keyword(.nonisolated), + _ unexpectedBetweenNonisolatedKeywordAndArgument: UnexpectedNodesSyntax? = nil, + argument: NonisolatedSpecifierArgumentSyntax? = nil, + _ unexpectedAfterArgument: UnexpectedNodesSyntax? = nil, + trailingTrivia: Trivia? = nil + ) { + // Extend the lifetime of all parameters so their arenas don't get destroyed + // before they can be added as children of the new arena. + self = withExtendedLifetime((RawSyntaxArena(), ( + unexpectedBeforeNonisolatedKeyword, + nonisolatedKeyword, + unexpectedBetweenNonisolatedKeywordAndArgument, + argument, + unexpectedAfterArgument + ))) { (arena, _) in + let layout: [RawSyntax?] = [ + unexpectedBeforeNonisolatedKeyword?.raw, + nonisolatedKeyword.raw, + unexpectedBetweenNonisolatedKeywordAndArgument?.raw, + argument?.raw, + unexpectedAfterArgument?.raw + ] + let raw = RawSyntax.makeLayout( + kind: SyntaxKind.nonisolatedTypeSpecifier, + from: layout, + arena: arena, + leadingTrivia: leadingTrivia, + trailingTrivia: trailingTrivia + ) + return Syntax.forRoot(raw, rawNodeArena: arena).cast(Self.self) + } + } + + public var unexpectedBeforeNonisolatedKeyword: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 0)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 0, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(NonisolatedTypeSpecifierSyntax.self) + } + } + + /// ### Tokens + /// + /// For syntax trees generated by the parser, this is guaranteed to be `nonisolated`. + public var nonisolatedKeyword: TokenSyntax { + get { + return Syntax(self).child(at: 1)!.cast(TokenSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 1, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(NonisolatedTypeSpecifierSyntax.self) + } + } + + public var unexpectedBetweenNonisolatedKeywordAndArgument: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 2)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 2, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(NonisolatedTypeSpecifierSyntax.self) + } + } + + public var argument: NonisolatedSpecifierArgumentSyntax? { + get { + return Syntax(self).child(at: 3)?.cast(NonisolatedSpecifierArgumentSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 3, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(NonisolatedTypeSpecifierSyntax.self) + } + } + + public var unexpectedAfterArgument: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 4)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 4, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(NonisolatedTypeSpecifierSyntax.self) + } + } + + public static let structure: SyntaxNodeStructure = .layout([ + \Self.unexpectedBeforeNonisolatedKeyword, + \Self.nonisolatedKeyword, + \Self.unexpectedBetweenNonisolatedKeywordAndArgument, + \Self.argument, + \Self.unexpectedAfterArgument + ]) +} diff --git a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesQRS.swift b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesQRS.swift index 0be78ad525d..2431b585fc8 100644 --- a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesQRS.swift +++ b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesQRS.swift @@ -1267,7 +1267,7 @@ public struct SimpleStringLiteralExprSyntax: ExprSyntaxProtocol, SyntaxHashable, /// /// ### Children /// -/// - `specifier`: (`inout` | `__shared` | `__owned` | `isolated` | `nonisolated` | `_const` | `borrowing` | `consuming` | `sending`) +/// - `specifier`: (`inout` | `__shared` | `__owned` | `isolated` | `_const` | `borrowing` | `consuming` | `sending`) /// /// ### Contained in /// @@ -1331,7 +1331,6 @@ public struct SimpleTypeSpecifierSyntax: SyntaxProtocol, SyntaxHashable, _LeafSy /// - `__shared` /// - `__owned` /// - `isolated` - /// - `nonisolated` /// - `_const` /// - `borrowing` /// - `consuming` diff --git a/Sources/SwiftSyntaxBuilder/generated/ResultBuilders.swift b/Sources/SwiftSyntaxBuilder/generated/ResultBuilders.swift index c6c34003dfa..f1c1a598825 100644 --- a/Sources/SwiftSyntaxBuilder/generated/ResultBuilders.swift +++ b/Sources/SwiftSyntaxBuilder/generated/ResultBuilders.swift @@ -697,6 +697,10 @@ public struct TypeSpecifierListBuilder: ListBuilder { public static func buildExpression(_ expression: LifetimeTypeSpecifierSyntax) -> Component { buildExpression(.init(expression)) } + + public static func buildExpression(_ expression: NonisolatedTypeSpecifierSyntax) -> Component { + buildExpression(.init(expression)) + } } extension TypeSpecifierListSyntax { diff --git a/Tests/SwiftParserTest/TypeTests.swift b/Tests/SwiftParserTest/TypeTests.swift index 588c1be8beb..d74d919c073 100644 --- a/Tests/SwiftParserTest/TypeTests.swift +++ b/Tests/SwiftParserTest/TypeTests.swift @@ -537,6 +537,38 @@ final class TypeTests: ParserTestCase { experimentalFeatures: [.nonescapableTypes] ) } + + func testNonisolatedSpecifier() { + assertParse("let _: nonisolated(nonsending) () async -> Void = {}") + assertParse("let _: [nonisolated(nonsending) () async -> Void]") + assertParse("let _ = [String: (nonisolated(nonsending) () async -> Void)?].self") + assertParse("let _ = Array Void>()") + assertParse("func foo(test: nonisolated(nonsending) () async -> Void)") + assertParse("func foo(test: nonisolated(nonsending) @escaping () async -> Void) {}") + + assertParse( + "func foo(test: nonisolated(1️⃣) () async -> Void)", + diagnostics: [ + DiagnosticSpec( + message: "expected 'nonsending' in 'nonisolated' specifier", + fixIts: ["insert 'nonsending'"] + ) + ], + fixedSource: "func foo(test: nonisolated(nonsending) () async -> Void)" + ) + + assertParse( + "func foo(test: nonisolated(1️⃣hello) () async -> Void)", + diagnostics: [ + DiagnosticSpec( + message: "expected 'nonsending' in 'nonisolated' specifier", + fixIts: ["insert 'nonsending'"] + ), + DiagnosticSpec(message: "unexpected code 'hello' in 'nonisolated' specifier"), + ], + fixedSource: "func foo(test: nonisolated(nonsendinghello) () async -> Void)" + ) + } } final class InlineArrayTypeTests: ParserTestCase { From 3977abe2c08038467789b5526d9c9823f1be6404 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 7 Apr 2025 15:00:49 -0700 Subject: [PATCH 13/21] [SwiftParser] Make `nonisolated(nonsending)` parsing check before consuming '(' It's possible that `(nonsending)` was omitted and instead we are at the parameter list of a function type. Checking ahead allows for a better diagnostics and recovery. --- Sources/SwiftParser/Attributes.swift | 6 +++--- Sources/SwiftParser/Types.swift | 21 +++++++++++++++++++++ Tests/SwiftParserTest/TypeTests.swift | 14 ++++++++++++++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/Sources/SwiftParser/Attributes.swift b/Sources/SwiftParser/Attributes.swift index 72af60d312c..2f066fd6c39 100644 --- a/Sources/SwiftParser/Attributes.swift +++ b/Sources/SwiftParser/Attributes.swift @@ -157,7 +157,7 @@ extension Parser { shouldParseArgument = true case .customAttribute: shouldParseArgument = - self.withLookahead { $0.atCustomAttributeArgument() } + self.withLookahead { $0.atAttributeOrSpecifierArgument() } && self.at(TokenSpec(.leftParen, allowAtStartOfLine: false)) case .optional: shouldParseArgument = self.at(.leftParen) @@ -1002,7 +1002,7 @@ extension Parser { // MARK: Lookahead extension Parser.Lookahead { - mutating func atCustomAttributeArgument() -> Bool { + mutating func atAttributeOrSpecifierArgument() -> Bool { var lookahead = self.lookahead() lookahead.skipSingle() @@ -1036,7 +1036,7 @@ extension Parser.Lookahead { } if self.at(TokenSpec(.leftParen, allowAtStartOfLine: false)) - && self.withLookahead({ $0.atCustomAttributeArgument() }) + && self.withLookahead({ $0.atAttributeOrSpecifierArgument() }) { self.skipSingle() } diff --git a/Sources/SwiftParser/Types.swift b/Sources/SwiftParser/Types.swift index 11f7de1fc5a..0d2d2166f03 100644 --- a/Sources/SwiftParser/Types.swift +++ b/Sources/SwiftParser/Types.swift @@ -1059,6 +1059,27 @@ extension Parser { private mutating func parseNonisolatedTypeSpecifier() -> RawTypeSpecifierListSyntax.Element { let (unexpectedBeforeNonisolatedKeyword, nonisolatedKeyword) = self.expect(.keyword(.nonisolated)) + // Avoid being to greedy about `(` since this modifier should be associated with + // function types, it's possible that the argument is omitted and what follows + // is a function type i.e. `nonisolated () async -> Void`. + if self.at(.leftParen) && !withLookahead({ $0.atAttributeOrSpecifierArgument() }) { + let argument = RawNonisolatedSpecifierArgumentSyntax( + leftParen: missingToken(.leftParen), + nonsendingKeyword: missingToken(.keyword(.nonsending)), + rightParen: missingToken(.rightParen), + arena: self.arena + ) + + let nonisolatedSpecifier = RawNonisolatedTypeSpecifierSyntax( + unexpectedBeforeNonisolatedKeyword, + nonisolatedKeyword: nonisolatedKeyword, + argument: argument, + arena: self.arena + ) + return .nonisolatedTypeSpecifier(nonisolatedSpecifier) + } + + // nonisolated without an argument is valid in some positions i.e. inheritance clause. guard let leftParen = self.consume(if: .leftParen) else { let nonisolatedSpecifier = RawNonisolatedTypeSpecifierSyntax( unexpectedBeforeNonisolatedKeyword, diff --git a/Tests/SwiftParserTest/TypeTests.swift b/Tests/SwiftParserTest/TypeTests.swift index d74d919c073..1cd9ea36470 100644 --- a/Tests/SwiftParserTest/TypeTests.swift +++ b/Tests/SwiftParserTest/TypeTests.swift @@ -546,10 +546,23 @@ final class TypeTests: ParserTestCase { assertParse("func foo(test: nonisolated(nonsending) () async -> Void)") assertParse("func foo(test: nonisolated(nonsending) @escaping () async -> Void) {}") + assertParse( + "func foo(test: nonisolated1️⃣ () async -> Void)", + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "expected '(nonsending)' in 'nonisolated' specifier", + fixIts: ["insert '(nonsending)'"] + ) + ], + fixedSource: "func foo(test: nonisolated(nonsending) () async -> Void)" + ) + assertParse( "func foo(test: nonisolated(1️⃣) () async -> Void)", diagnostics: [ DiagnosticSpec( + locationMarker: "1️⃣", message: "expected 'nonsending' in 'nonisolated' specifier", fixIts: ["insert 'nonsending'"] ) @@ -561,6 +574,7 @@ final class TypeTests: ParserTestCase { "func foo(test: nonisolated(1️⃣hello) () async -> Void)", diagnostics: [ DiagnosticSpec( + locationMarker: "1️⃣", message: "expected 'nonsending' in 'nonisolated' specifier", fixIts: ["insert 'nonsending'"] ), From 9ffe4b81521fbe353bde08613cf2f928bead3127 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 8 Apr 2025 12:04:17 -0700 Subject: [PATCH 14/21] [SwiftParser] Lookahead: Teach `skipTypeAttributeList` about modifiers with arguments --- Sources/SwiftParser/Types.swift | 63 ++++++++++++++++++++++----- Tests/SwiftParserTest/TypeTests.swift | 31 +++++++++++++ 2 files changed, 84 insertions(+), 10 deletions(-) diff --git a/Sources/SwiftParser/Types.swift b/Sources/SwiftParser/Types.swift index 0d2d2166f03..535b6140bd7 100644 --- a/Sources/SwiftParser/Types.swift +++ b/Sources/SwiftParser/Types.swift @@ -691,11 +691,27 @@ extension Parser.Lookahead { var specifierProgress = LoopProgressCondition() // TODO: Can we model isolated/_const so that they're specified in both canParse* and parse*? while canHaveParameterSpecifier, - self.at(anyIn: SimpleTypeSpecifierSyntax.SpecifierOptions.self) != nil || self.at(.keyword(.isolated)) - || self.at(.keyword(._const)), + self.at(anyIn: SimpleTypeSpecifierSyntax.SpecifierOptions.self) != nil + || self.at(.keyword(.nonisolated), .keyword(.dependsOn)), self.hasProgressed(&specifierProgress) { - self.consumeAnyToken() + switch self.currentToken { + case .keyword(.nonisolated), .keyword(.dependsOn): + self.consumeAnyToken() + + // The argument is missing but it still could be a valid modifier, + // i.e. `nonisolated` in an inheritance clause. + guard self.at(.leftParen) else { + continue + } + + if self.withLookahead({ $0.atAttributeOrSpecifierArgument() }) { + skipSingle() + } + + default: + self.consumeAnyToken() + } } var attributeProgress = LoopProgressCondition() @@ -1059,10 +1075,24 @@ extension Parser { private mutating func parseNonisolatedTypeSpecifier() -> RawTypeSpecifierListSyntax.Element { let (unexpectedBeforeNonisolatedKeyword, nonisolatedKeyword) = self.expect(.keyword(.nonisolated)) - // Avoid being to greedy about `(` since this modifier should be associated with - // function types, it's possible that the argument is omitted and what follows - // is a function type i.e. `nonisolated () async -> Void`. - if self.at(.leftParen) && !withLookahead({ $0.atAttributeOrSpecifierArgument() }) { + // If the next token is not '(' this could mean two things: + // - What follows is a type and we should allow it because + // using `nonsisolated` without an argument is allowed in + // an inheritance clause. + // - The '(nonsending)' was omitted. + if !self.at(.leftParen) { + // `nonisolated P<...>` is allowed in an inheritance clause. + if withLookahead({ $0.canParseTypeIdentifier() }) { + let nonisolatedSpecifier = RawNonisolatedTypeSpecifierSyntax( + unexpectedBeforeNonisolatedKeyword, + nonisolatedKeyword: nonisolatedKeyword, + argument: nil, + arena: self.arena + ) + return .nonisolatedTypeSpecifier(nonisolatedSpecifier) + } + + // Otherwise require '(nonsending)' let argument = RawNonisolatedSpecifierArgumentSyntax( leftParen: missingToken(.leftParen), nonsendingKeyword: missingToken(.keyword(.nonsending)), @@ -1076,24 +1106,37 @@ extension Parser { argument: argument, arena: self.arena ) + return .nonisolatedTypeSpecifier(nonisolatedSpecifier) } - // nonisolated without an argument is valid in some positions i.e. inheritance clause. - guard let leftParen = self.consume(if: .leftParen) else { + // Avoid being to greedy about `(` since this modifier should be associated with + // function types, it's possible that the argument is omitted and what follows + // is a function type i.e. `nonisolated () async -> Void`. + if self.at(.leftParen) && !withLookahead({ $0.atAttributeOrSpecifierArgument() }) { + let argument = RawNonisolatedSpecifierArgumentSyntax( + leftParen: missingToken(.leftParen), + nonsendingKeyword: missingToken(.keyword(.nonsending)), + rightParen: missingToken(.rightParen), + arena: self.arena + ) + let nonisolatedSpecifier = RawNonisolatedTypeSpecifierSyntax( unexpectedBeforeNonisolatedKeyword, nonisolatedKeyword: nonisolatedKeyword, - argument: nil, + argument: argument, arena: self.arena ) + return .nonisolatedTypeSpecifier(nonisolatedSpecifier) } + let (unexpectedBeforeLeftParen, leftParen) = self.expect(.leftParen) let (unexpectedBeforeModifier, modifier) = self.expect(.keyword(.nonsending)) let (unexpectedBeforeRightParen, rightParen) = self.expect(.rightParen) let argument = RawNonisolatedSpecifierArgumentSyntax( + unexpectedBeforeLeftParen, leftParen: leftParen, unexpectedBeforeModifier, nonsendingKeyword: modifier, diff --git a/Tests/SwiftParserTest/TypeTests.swift b/Tests/SwiftParserTest/TypeTests.swift index 1cd9ea36470..b047fce523f 100644 --- a/Tests/SwiftParserTest/TypeTests.swift +++ b/Tests/SwiftParserTest/TypeTests.swift @@ -485,6 +485,20 @@ final class TypeTests: ParserTestCase { experimentalFeatures: [.nonescapableTypes] ) + assertParse( + "func foo() -> dependsOn1️⃣ @Sendable (Int, isolated (any Actor)?) async throws -> Void", + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "expected '(', parameter reference, and ')' in lifetime specifier", + fixIts: ["insert '(', parameter reference, and ')'"] + ) + ], + fixedSource: + "func foo() -> dependsOn(<#identifier#>) @Sendable (Int, isolated (any Actor)?) async throws -> Void", + experimentalFeatures: [.nonescapableTypes] + ) + assertParse( "func foo() -> dependsOn(1️⃣*) X", diagnostics: [ @@ -545,6 +559,23 @@ final class TypeTests: ParserTestCase { assertParse("let _ = Array Void>()") assertParse("func foo(test: nonisolated(nonsending) () async -> Void)") assertParse("func foo(test: nonisolated(nonsending) @escaping () async -> Void) {}") + assertParse("test(S Void>(), type(of: concurrentTest))") + assertParse("S Void>()") + assertParse("let _ = S Void>()") + assertParse("struct S : nonisolated P {}") + assertParse("let _ = [nonisolated()]") + + assertParse( + "Foo Void>()", + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "expected '(nonsending)' in 'nonisolated' specifier", + fixIts: ["insert '(nonsending)'"] + ) + ], + fixedSource: "Foo Void>()" + ) assertParse( "func foo(test: nonisolated1️⃣ () async -> Void)", From 2b13304c449f1cd81270f225d72cc41e35e989fc Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 9 Apr 2025 12:04:26 -0700 Subject: [PATCH 15/21] [SwiftParser] Account for a newline between `nonisolated` and `(` --- Sources/SwiftParser/Types.swift | 8 ++++++-- Tests/SwiftParserTest/TypeTests.swift | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Sources/SwiftParser/Types.swift b/Sources/SwiftParser/Types.swift index 535b6140bd7..3f5c15e0981 100644 --- a/Sources/SwiftParser/Types.swift +++ b/Sources/SwiftParser/Types.swift @@ -689,7 +689,6 @@ extension Parser.Lookahead { mutating func skipTypeAttributeList() { var specifierProgress = LoopProgressCondition() - // TODO: Can we model isolated/_const so that they're specified in both canParse* and parse*? while canHaveParameterSpecifier, self.at(anyIn: SimpleTypeSpecifierSyntax.SpecifierOptions.self) != nil || self.at(.keyword(.nonisolated), .keyword(.dependsOn)), @@ -701,7 +700,7 @@ extension Parser.Lookahead { // The argument is missing but it still could be a valid modifier, // i.e. `nonisolated` in an inheritance clause. - guard self.at(.leftParen) else { + guard self.at(TokenSpec(.leftParen, allowAtStartOfLine: false)) else { continue } @@ -1178,6 +1177,11 @@ extension Parser { break SPECIFIER_PARSING } } else if self.at(.keyword(.nonisolated)) { + // If '(' is located on the new line 'nonisolated' cannot be parsed + // as a specifier. + if self.peek(isAt: .leftParen) && self.peek().isAtStartOfLine { + break SPECIFIER_PARSING + } specifiers.append(parseNonisolatedTypeSpecifier()) } else { break SPECIFIER_PARSING diff --git a/Tests/SwiftParserTest/TypeTests.swift b/Tests/SwiftParserTest/TypeTests.swift index b047fce523f..057ae2dd3dd 100644 --- a/Tests/SwiftParserTest/TypeTests.swift +++ b/Tests/SwiftParserTest/TypeTests.swift @@ -565,6 +565,28 @@ final class TypeTests: ParserTestCase { assertParse("struct S : nonisolated P {}") assertParse("let _ = [nonisolated()]") + assertParse( + """ + let x: nonisolated + (hello) + """ + ) + + assertParse( + """ + struct S: nonisolated + P { + } + """ + ) + + assertParse( + """ + let x: nonisolated + (Int) async -> Void = {} + """ + ) + assertParse( "Foo Void>()", diagnostics: [ From ea776865d8b1862cfdd6b2e7aca6a27640a0f5c5 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Wed, 9 Apr 2025 15:13:28 -0700 Subject: [PATCH 16/21] Change default release version to 603.0.0 --- .github/workflows/publish_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish_release.yml b/.github/workflows/publish_release.yml index 969181fc799..44f68cce276 100644 --- a/.github/workflows/publish_release.yml +++ b/.github/workflows/publish_release.yml @@ -11,7 +11,7 @@ on: required: true swift_syntax_version: type: string - default: 601.0.0 + default: 603.0.0 description: "swift-syntax version" # The version of swift-syntax to tag. If this is a prerelease, `-prerelease-` is added to this version. required: true From 0ef1dc377a0060c919c3362f59c3ea772c3695f4 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Wed, 9 Apr 2025 15:27:23 -0700 Subject: [PATCH 17/21] Add SwiftSyntax603 version marker module --- BUILD.bazel | 8 ++++++++ Package.swift | 6 ++++++ Sources/VersionMarkerModules/CMakeLists.txt | 10 ++++++---- .../SwiftSyntax603/Empty.swift | 15 +++++++++++++++ 4 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 Sources/VersionMarkerModules/SwiftSyntax603/Empty.swift diff --git a/BUILD.bazel b/BUILD.bazel index 5a710ddeeb4..86301f4eec6 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -221,6 +221,7 @@ swift_syntax_library( ":SwiftSyntax600", ":SwiftSyntax601", ":SwiftSyntax602", + ":SwiftSyntax603", ":_SwiftSyntaxCShims", ], ) @@ -260,6 +261,13 @@ swift_syntax_library( ], ) +swift_syntax_library( + name = "SwiftSyntax603", + srcs = glob(["Sources/VersionMarkerModules/SwiftSyntax603/**/*.swift"]), + deps = [ + ], +) + swift_syntax_library( name = "SwiftSyntaxBuilder", deps = [ diff --git a/Package.swift b/Package.swift index 5c20ee04b86..844bfacc40a 100644 --- a/Package.swift +++ b/Package.swift @@ -212,6 +212,7 @@ let package = Package( name: "SwiftSyntax", dependencies: [ "_SwiftSyntaxCShims", "SwiftSyntax509", "SwiftSyntax510", "SwiftSyntax600", "SwiftSyntax601", "SwiftSyntax602", + "SwiftSyntax603", ], exclude: ["CMakeLists.txt"], swiftSettings: swiftSyntaxSwiftSettings @@ -250,6 +251,11 @@ let package = Package( path: "Sources/VersionMarkerModules/SwiftSyntax602" ), + .target( + name: "SwiftSyntax603", + path: "Sources/VersionMarkerModules/SwiftSyntax603" + ), + // MARK: SwiftSyntaxBuilder .target( diff --git a/Sources/VersionMarkerModules/CMakeLists.txt b/Sources/VersionMarkerModules/CMakeLists.txt index 467d4aa2202..609f1d308d4 100644 --- a/Sources/VersionMarkerModules/CMakeLists.txt +++ b/Sources/VersionMarkerModules/CMakeLists.txt @@ -11,10 +11,12 @@ add_library(${SWIFTSYNTAX_TARGET_NAMESPACE}SwiftSyntax509 STATIC SwiftSyntax509/Empty.swift) add_library(${SWIFTSYNTAX_TARGET_NAMESPACE}SwiftSyntax510 STATIC - SwiftSyntax509/Empty.swift) + SwiftSyntax510/Empty.swift) add_library(${SWIFTSYNTAX_TARGET_NAMESPACE}SwiftSyntax600 STATIC - SwiftSyntax509/Empty.swift) + SwiftSyntax600/Empty.swift) add_library(${SWIFTSYNTAX_TARGET_NAMESPACE}SwiftSyntax601 STATIC - SwiftSyntax509/Empty.swift) + SwiftSyntax601/Empty.swift) add_library(${SWIFTSYNTAX_TARGET_NAMESPACE}SwiftSyntax602 STATIC - SwiftSyntax509/Empty.swift) \ No newline at end of file + SwiftSyntax602/Empty.swift) +add_library(${SWIFTSYNTAX_TARGET_NAMESPACE}SwiftSyntax603 STATIC + SwiftSyntax603/Empty.swift) diff --git a/Sources/VersionMarkerModules/SwiftSyntax603/Empty.swift b/Sources/VersionMarkerModules/SwiftSyntax603/Empty.swift new file mode 100644 index 00000000000..cc2ffc3c32a --- /dev/null +++ b/Sources/VersionMarkerModules/SwiftSyntax603/Empty.swift @@ -0,0 +1,15 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +// The SwiftSyntax603 module is intentionally empty. +// It serves as an indicator which version of swift-syntax a package is building against. +// See the 'Macro Versioning.md' document for more details. From d0c3e616d59c4b5993da4422322665dfb99b4434 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Thu, 10 Apr 2025 09:00:23 -0700 Subject: [PATCH 18/21] Add GitHub action that automatically creates a PR to merge main into a release branch In the first period after branching the release branch, we want to include many changes from `main` also in the release branch. This workflow automatically creates a PR every Monday to merge main into the release branch. Later in the release cycle we should stop this practice to avoid landing risky changes by disabling this workflow. Similar to https://github.com/swiftlang/swift-format/pull/986 --- .github/workflows/automerge.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/workflows/automerge.yml diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml new file mode 100644 index 00000000000..3d8b1595646 --- /dev/null +++ b/.github/workflows/automerge.yml @@ -0,0 +1,14 @@ +name: Create PR to merge main into release branch +# In the first period after branching the release branch, we typically want to include many changes from `main` in the release branch. This workflow automatically creates a PR every Monday to merge main into the release branch. +# Later in the release cycle we should stop this practice to avoid landing risky changes by disabling this workflow. To do so, disable the workflow as described in https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/disabling-and-enabling-a-workflow +on: + schedule: + - cron: '0 9 * * MON' + workflow_dispatch: +jobs: + create_merge_pr: + name: Create PR to merge main into release branch + uses: swiftlang/github-workflows/.github/workflows/create_automerge_pr.yml@main + with: + base_branch: release/6.2 + if: (github.event_name == 'schedule' && github.repository == 'swiftlang/swift-syntax') || (github.event_name != 'schedule') # Ensure that we don't run this on a schedule in a fork From b9f80c744b0fd08bf6b123adad14f58e8472afd0 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 11 Apr 2025 12:12:20 -0700 Subject: [PATCH 19/21] [SwiftParser] Avoid consuming invalid `dependsOn` and `nonisolated` modifiers while skipping Otherwise in situations like `nonisolated()` `canParseType` would return `true` because `()` is valid type. --- Sources/SwiftParser/Types.swift | 65 +++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 7 deletions(-) diff --git a/Sources/SwiftParser/Types.swift b/Sources/SwiftParser/Types.swift index 3f5c15e0981..416f06ddc1f 100644 --- a/Sources/SwiftParser/Types.swift +++ b/Sources/SwiftParser/Types.swift @@ -687,7 +687,7 @@ extension Parser.Lookahead { return true } - mutating func skipTypeAttributeList() { + mutating func canParseTypeAttributeList() -> Bool { var specifierProgress = LoopProgressCondition() while canHaveParameterSpecifier, self.at(anyIn: SimpleTypeSpecifierSyntax.SpecifierOptions.self) != nil @@ -695,19 +695,66 @@ extension Parser.Lookahead { self.hasProgressed(&specifierProgress) { switch self.currentToken { - case .keyword(.nonisolated), .keyword(.dependsOn): + case .keyword(.nonisolated): + let canParseNonisolated = self.withLookahead({ + // Consume 'nonisolated' + $0.consumeAnyToken() + + // The argument is missing but it still could be a valid modifier, + // i.e. `nonisolated` in an inheritance clause. + guard $0.at(TokenSpec(.leftParen, allowAtStartOfLine: false)) else { + return true + } + + // Consume '(' + $0.consumeAnyToken() + + // nonisolated accepts a single modifier at the moment: 'nonsending' + // we need to check for that explicitly to avoid misinterpreting this + // keyword to be a modifier when it isn't i.e. `[nonisolated(42)]` + guard $0.consume(if: TokenSpec(.nonsending, allowAtStartOfLine: false)) != nil else { + return false + } + + return $0.consume(if: TokenSpec(.rightParen, allowAtStartOfLine: false)) != nil + }) + + guard canParseNonisolated else { + return false + } + self.consumeAnyToken() - // The argument is missing but it still could be a valid modifier, - // i.e. `nonisolated` in an inheritance clause. guard self.at(TokenSpec(.leftParen, allowAtStartOfLine: false)) else { continue } - if self.withLookahead({ $0.atAttributeOrSpecifierArgument() }) { - skipSingle() + self.skipSingle() + + case .keyword(.dependsOn): + let canParseDependsOn = self.withLookahead({ + // Consume 'dependsOn' + $0.consumeAnyToken() + + if $0.currentToken.isAtStartOfLine { + return false + } + + // `dependsOn` requires an argument list. + guard $0.atAttributeOrSpecifierArgument() else { + return false + } + + return true + }) + + guard canParseDependsOn else { + return false } + self.consumeAnyToken() + self.skipSingle() + default: self.consumeAnyToken() } @@ -718,10 +765,14 @@ extension Parser.Lookahead { self.consumeAnyToken() self.skipTypeAttribute() } + + return true } mutating func canParseTypeScalar() -> Bool { - self.skipTypeAttributeList() + guard self.canParseTypeAttributeList() else { + return false + } guard self.canParseSimpleOrCompositionType() else { return false From 2ae464ed7bda23468100cefd6e2257856ca38bcf Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 11 Apr 2025 12:14:19 -0700 Subject: [PATCH 20/21] [SwiftParser] Parse `nonisolated` as modifier only if it has `nonsending` in expression context --- Sources/SwiftParser/Expressions.swift | 30 +++++- Tests/SwiftParserTest/TypeTests.swift | 126 +++++++++++++++++++++++--- 2 files changed, 140 insertions(+), 16 deletions(-) diff --git a/Sources/SwiftParser/Expressions.swift b/Sources/SwiftParser/Expressions.swift index 11bcdf4ee1a..70e2a433f56 100644 --- a/Sources/SwiftParser/Expressions.swift +++ b/Sources/SwiftParser/Expressions.swift @@ -381,13 +381,39 @@ extension Parser { } } + /// Make sure that we only accept `nonisolated(nonsending)` as a valid type specifier + /// in expression context to minimize source compatibility impact. + func canParseNonisolatedAsSpecifierInExpressionContext() -> Bool { + return withLookahead { + guard $0.consume(if: .keyword(.nonisolated)) != nil else { + return false + } + + if $0.currentToken.isAtStartOfLine { + return false + } + + guard $0.consume(if: .leftParen) != nil else { + return false + } + + guard $0.consume(if: TokenSpec(.nonsending, allowAtStartOfLine: false)) != nil else { + return false + } + + return $0.at(TokenSpec(.rightParen, allowAtStartOfLine: false)) + } + } + /// Parse an expression sequence element. mutating func parseSequenceExpressionElement( flavor: ExprFlavor, pattern: PatternContext = .none ) -> RawExprSyntax { - // Try to parse '@' sign or 'inout' as an attributed typerepr. - if self.at(.atSign, .keyword(.inout)) { + // Try to parse '@' sign, 'inout', or 'nonisolated' as an attributed typerepr. + if self.at(.atSign, .keyword(.inout)) + || self.canParseNonisolatedAsSpecifierInExpressionContext() + { var lookahead = self.lookahead() if lookahead.canParseType() { let type = self.parseType() diff --git a/Tests/SwiftParserTest/TypeTests.swift b/Tests/SwiftParserTest/TypeTests.swift index 057ae2dd3dd..6e49d5fa75d 100644 --- a/Tests/SwiftParserTest/TypeTests.swift +++ b/Tests/SwiftParserTest/TypeTests.swift @@ -553,23 +553,117 @@ final class TypeTests: ParserTestCase { } func testNonisolatedSpecifier() { - assertParse("let _: nonisolated(nonsending) () async -> Void = {}") - assertParse("let _: [nonisolated(nonsending) () async -> Void]") - assertParse("let _ = [String: (nonisolated(nonsending) () async -> Void)?].self") - assertParse("let _ = Array Void>()") - assertParse("func foo(test: nonisolated(nonsending) () async -> Void)") - assertParse("func foo(test: nonisolated(nonsending) @escaping () async -> Void) {}") - assertParse("test(S Void>(), type(of: concurrentTest))") - assertParse("S Void>()") - assertParse("let _ = S Void>()") - assertParse("struct S : nonisolated P {}") - assertParse("let _ = [nonisolated()]") + assertParse( + """ + let x = nonisolated + print("hello") + """, + substructure: DeclReferenceExprSyntax( + baseName: .identifier("nonisolated") + ) + ) + + assertParse( + "let _: nonisolated(nonsending) () async -> Void = {}", + substructure: NonisolatedTypeSpecifierSyntax( + nonisolatedKeyword: .keyword(.nonisolated), + argument: NonisolatedSpecifierArgumentSyntax(nonsendingKeyword: .keyword(.nonsending)) + ) + ) + assertParse( + "let _: [nonisolated(nonsending) () async -> Void]", + substructure: NonisolatedTypeSpecifierSyntax( + nonisolatedKeyword: .keyword(.nonisolated), + argument: NonisolatedSpecifierArgumentSyntax(nonsendingKeyword: .keyword(.nonsending)) + ) + ) + + assertParse( + "let _ = [String: (nonisolated(nonsending) () async -> Void)?].self", + substructure: NonisolatedTypeSpecifierSyntax( + nonisolatedKeyword: .keyword(.nonisolated), + argument: NonisolatedSpecifierArgumentSyntax(nonsendingKeyword: .keyword(.nonsending)) + ) + ) + + assertParse( + "let _ = Array Void>()", + substructure: NonisolatedTypeSpecifierSyntax( + nonisolatedKeyword: .keyword(.nonisolated), + argument: NonisolatedSpecifierArgumentSyntax(nonsendingKeyword: .keyword(.nonsending)) + ) + ) + assertParse( + "func foo(test: nonisolated(nonsending) () async -> Void)", + substructure: NonisolatedTypeSpecifierSyntax( + nonisolatedKeyword: .keyword(.nonisolated), + argument: NonisolatedSpecifierArgumentSyntax(nonsendingKeyword: .keyword(.nonsending)) + ) + ) + assertParse( + "func foo(test: nonisolated(nonsending) @escaping () async -> Void) {}", + substructure: NonisolatedTypeSpecifierSyntax( + nonisolatedKeyword: .keyword(.nonisolated), + argument: NonisolatedSpecifierArgumentSyntax(nonsendingKeyword: .keyword(.nonsending)) + ) + ) + assertParse( + "test(S Void>(), type(of: concurrentTest))", + substructure: NonisolatedTypeSpecifierSyntax( + nonisolatedKeyword: .keyword(.nonisolated), + argument: NonisolatedSpecifierArgumentSyntax(nonsendingKeyword: .keyword(.nonsending)) + ) + ) + assertParse( + "S Void>()", + substructure: NonisolatedTypeSpecifierSyntax( + nonisolatedKeyword: .keyword(.nonisolated), + argument: NonisolatedSpecifierArgumentSyntax(nonsendingKeyword: .keyword(.nonsending)) + ) + ) + assertParse( + "let _ = S Void>()", + substructure: NonisolatedTypeSpecifierSyntax( + nonisolatedKeyword: .keyword(.nonisolated), + argument: NonisolatedSpecifierArgumentSyntax(nonsendingKeyword: .keyword(.nonsending)) + ) + ) + + assertParse( + "struct S : nonisolated P {}", + substructure: NonisolatedTypeSpecifierSyntax( + nonisolatedKeyword: .keyword(.nonisolated) + ) + ) + + assertParse( + "let _ = [nonisolated()]", + substructure: DeclReferenceExprSyntax( + baseName: .identifier("nonisolated") + ) + ) + + assertParse( + "let _ = [nonisolated(nonsending) () async -> Void]()", + substructure: NonisolatedTypeSpecifierSyntax( + nonisolatedKeyword: .keyword(.nonisolated), + argument: NonisolatedSpecifierArgumentSyntax(nonsendingKeyword: .keyword(.nonsending)) + ) + ) + + assertParse( + "_ = S()", + substructure: GenericArgumentSyntax.Argument( + IdentifierTypeSyntax(name: .identifier("nonisolated")) + ) + ) assertParse( """ let x: nonisolated (hello) - """ + """, + substructure: IdentifierTypeSyntax(name: .identifier("nonisolated")) ) assertParse( @@ -577,14 +671,18 @@ final class TypeTests: ParserTestCase { struct S: nonisolated P { } - """ + """, + substructure: NonisolatedTypeSpecifierSyntax( + nonisolatedKeyword: .keyword(.nonisolated) + ) ) assertParse( """ let x: nonisolated (Int) async -> Void = {} - """ + """, + substructure: IdentifierTypeSyntax(name: .identifier("nonisolated")) ) assertParse( From caa2d3b8bce961baa3bf2ca5010a63ae2a827d70 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Wed, 16 Apr 2025 15:39:56 -0700 Subject: [PATCH 21/21] Revert "Change default release version to 603.0.0" This reverts commit ea776865d8b1862cfdd6b2e7aca6a27640a0f5c5. --- .github/workflows/publish_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish_release.yml b/.github/workflows/publish_release.yml index 44f68cce276..969181fc799 100644 --- a/.github/workflows/publish_release.yml +++ b/.github/workflows/publish_release.yml @@ -11,7 +11,7 @@ on: required: true swift_syntax_version: type: string - default: 603.0.0 + default: 601.0.0 description: "swift-syntax version" # The version of swift-syntax to tag. If this is a prerelease, `-prerelease-` is added to this version. required: true