From 8a2b2a614b76c70f433dd80a99eb2236bf703826 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 23 Jun 2025 11:23:11 -0700 Subject: [PATCH 1/7] Add "late specifiers" to attributed types. This is needed to account for 'nonisolated' occuring after type attributes within a protocol conformance. (cherry picked from commit c186fe348e2160771c6dc664361ceb249216c431) --- CodeGeneration/Sources/SyntaxSupport/Child.swift | 9 +++++---- .../Sources/SyntaxSupport/GrammarGenerator.swift | 2 +- CodeGeneration/Sources/SyntaxSupport/Node.swift | 2 +- CodeGeneration/Sources/SyntaxSupport/TypeNodes.swift | 7 +++++++ CodeGeneration/Sources/Utils/SyntaxBuildableChild.swift | 4 ++-- .../swiftsyntax/RenamedChildrenCompatibilityFile.swift | 3 ++- .../templates/swiftsyntax/SyntaxNodesFile.swift | 2 +- 7 files changed, 19 insertions(+), 10 deletions(-) diff --git a/CodeGeneration/Sources/SyntaxSupport/Child.swift b/CodeGeneration/Sources/SyntaxSupport/Child.swift index 683c0441953..ce631b5950e 100644 --- a/CodeGeneration/Sources/SyntaxSupport/Child.swift +++ b/CodeGeneration/Sources/SyntaxSupport/Child.swift @@ -47,7 +47,8 @@ public enum ChildKind { kind: SyntaxNodeKind, collectionElementName: String? = nil, defaultsToEmpty: Bool = false, - deprecatedCollectionElementName: String? = nil + deprecatedCollectionElementName: String? = nil, + generateDeprecatedAddFunction: Bool = true ) /// The child is a token that matches one of the given `choices`. /// If `requiresLeadingSpace` or `requiresTrailingSpace` is not `nil`, it @@ -132,7 +133,7 @@ public class Child: NodeChoiceConvertible { return kind case .nodeChoices: return .syntax - case .collection(kind: let kind, _, _, _): + case .collection(kind: let kind, _, _, _, _): return kind case .token: return .token @@ -268,7 +269,7 @@ public class Child: NodeChoiceConvertible { /// Whether this child has syntax kind `UnexpectedNodes`. public var isUnexpectedNodes: Bool { switch kind { - case .collection(kind: .unexpectedNodes, _, _, _): + case .collection(kind: .unexpectedNodes, _, _, _, _): return true default: return false @@ -283,7 +284,7 @@ public class Child: NodeChoiceConvertible { return choices.isEmpty case .node(let kind): return kind.isBase - case .collection(kind: let kind, _, _, _): + case .collection(kind: let kind, _, _, _, _): return kind.isBase case .token: return false diff --git a/CodeGeneration/Sources/SyntaxSupport/GrammarGenerator.swift b/CodeGeneration/Sources/SyntaxSupport/GrammarGenerator.swift index ae84735dbf9..4d28dff2564 100644 --- a/CodeGeneration/Sources/SyntaxSupport/GrammarGenerator.swift +++ b/CodeGeneration/Sources/SyntaxSupport/GrammarGenerator.swift @@ -41,7 +41,7 @@ struct GrammarGenerator { case .nodeChoices(let choices, _): let choicesDescriptions = choices.map { grammar(for: $0) } return "(\(choicesDescriptions.joined(separator: " | ")))\(optionality)" - case .collection(kind: let kind, _, _, _): + case .collection(kind: let kind, _, _, _, _): return "\(kind.doccLink)\(optionality)" case .token(let choices, _, _): if choices.count == 1 { diff --git a/CodeGeneration/Sources/SyntaxSupport/Node.swift b/CodeGeneration/Sources/SyntaxSupport/Node.swift index 913473c1c85..f1341f7e355 100644 --- a/CodeGeneration/Sources/SyntaxSupport/Node.swift +++ b/CodeGeneration/Sources/SyntaxSupport/Node.swift @@ -391,7 +391,7 @@ fileprivate extension Child { return [kind] case .nodeChoices(let choices, _): return choices.flatMap(\.kinds) - case .collection(kind: let kind, _, _, _): + case .collection(kind: let kind, _, _, _, _): return [kind] case .token: return [.token] diff --git a/CodeGeneration/Sources/SyntaxSupport/TypeNodes.swift b/CodeGeneration/Sources/SyntaxSupport/TypeNodes.swift index ab81dde2a2d..1c19e2018d4 100644 --- a/CodeGeneration/Sources/SyntaxSupport/TypeNodes.swift +++ b/CodeGeneration/Sources/SyntaxSupport/TypeNodes.swift @@ -57,6 +57,13 @@ public let TYPE_NODES: [Node] = [ kind: .collection(kind: .attributeList, collectionElementName: "Attribute", defaultsToEmpty: true), documentation: "A list of attributes that can be attached to the type, such as `@escaping`." ), + Child( + name: "lateSpecifiers", + kind: .collection(kind: .typeSpecifierList, collectionElementName: "Specifier", defaultsToEmpty: true, + generateDeprecatedAddFunction: false), + documentation: + "A list of specifiers that can be attached to the type after the attributes, such as 'nonisolated'." + ), Child( name: "baseType", kind: .node(kind: .type), diff --git a/CodeGeneration/Sources/Utils/SyntaxBuildableChild.swift b/CodeGeneration/Sources/Utils/SyntaxBuildableChild.swift index 9ad78761e04..6e4fb79941a 100644 --- a/CodeGeneration/Sources/Utils/SyntaxBuildableChild.swift +++ b/CodeGeneration/Sources/Utils/SyntaxBuildableChild.swift @@ -33,7 +33,7 @@ extension Child { buildableKind = .node(kind: kind) case .nodeChoices: buildableKind = .node(kind: .syntax) - case .collection(kind: let kind, _, _, _): + case .collection(kind: let kind, _, _, _, _): buildableKind = .node(kind: kind) case .token: buildableKind = .token(self.tokenKind!) @@ -65,7 +65,7 @@ extension Child { return ExprSyntax("nil") } } - if case .collection(_, _, defaultsToEmpty: true, _) = kind { + if case .collection(_, _, defaultsToEmpty: true, _, _) = kind { return ExprSyntax("[]") } guard let token = token, isToken else { diff --git a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/RenamedChildrenCompatibilityFile.swift b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/RenamedChildrenCompatibilityFile.swift index aea1375e54c..77f4548117b 100644 --- a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/RenamedChildrenCompatibilityFile.swift +++ b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/RenamedChildrenCompatibilityFile.swift @@ -85,7 +85,8 @@ func makeCompatibilityAddMethod(for child: Child) -> DeclSyntax? { kind: _, collectionElementName: let collectionElementName?, defaultsToEmpty: _, - deprecatedCollectionElementName: let deprecatedCollectionElementName? + deprecatedCollectionElementName: let deprecatedCollectionElementName?, + generateDeprecatedAddFunction: _ ) = child.kind { let childEltType = childNode.collectionElementType.syntaxBaseName diff --git a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxNodesFile.swift b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxNodesFile.swift index 6c5bd8bad67..703448adfd0 100644 --- a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxNodesFile.swift +++ b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxNodesFile.swift @@ -176,7 +176,7 @@ func syntaxNode(nodesStartingWith: [Character]) -> SourceFileSyntax { // If needed, this could be added in the future, but for now withUnexpected should be sufficient. if let childNode = SYNTAX_NODE_MAP[child.syntaxNodeKind]?.collectionNode, !child.isUnexpectedNodes, - case .collection(_, collectionElementName: let childElt?, _, _) = child.kind + case .collection(_, collectionElementName: let childElt?, _, _, generateDeprecatedAddFunction: true) = child.kind { let childEltType = childNode.collectionElementType.syntaxBaseName From 8bab9ce9939d07cdae25129ce91ef8b1d41c69a9 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 23 Jun 2025 11:29:58 -0700 Subject: [PATCH 2/7] Regenerate syntax nodes (cherry picked from commit 6ecd03296ab0dca889a739f71cf1dca9cb8abdf2) --- .../generated/ChildNameForKeyPath.swift | 8 +++- .../generated/SyntaxCollections.swift | 1 + .../generated/raw/RawSyntaxNodesAB.swift | 28 +++++++---- .../generated/raw/RawSyntaxValidation.swift | 6 ++- .../generated/syntaxNodes/SyntaxNodesAB.swift | 47 +++++++++++++++---- 5 files changed, 69 insertions(+), 21 deletions(-) diff --git a/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift b/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift index f6ed8a61c82..00016bc72d1 100644 --- a/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift +++ b/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift @@ -286,8 +286,12 @@ public func childName(_ keyPath: AnyKeyPath) -> String? { return "unexpectedBetweenSpecifiersAndAttributes" case \AttributedTypeSyntax.attributes: return "attributes" - case \AttributedTypeSyntax.unexpectedBetweenAttributesAndBaseType: - return "unexpectedBetweenAttributesAndBaseType" + case \AttributedTypeSyntax.unexpectedBetweenAttributesAndLateSpecifiers: + return "unexpectedBetweenAttributesAndLateSpecifiers" + case \AttributedTypeSyntax.lateSpecifiers: + return "lateSpecifiers" + case \AttributedTypeSyntax.unexpectedBetweenLateSpecifiersAndBaseType: + return "unexpectedBetweenLateSpecifiersAndBaseType" case \AttributedTypeSyntax.baseType: return "baseType" case \AttributedTypeSyntax.unexpectedAfterBaseType: diff --git a/Sources/SwiftSyntax/generated/SyntaxCollections.swift b/Sources/SwiftSyntax/generated/SyntaxCollections.swift index a34717855b7..529785471e9 100644 --- a/Sources/SwiftSyntax/generated/SyntaxCollections.swift +++ b/Sources/SwiftSyntax/generated/SyntaxCollections.swift @@ -1918,6 +1918,7 @@ public struct TupleTypeElementListSyntax: SyntaxCollection, SyntaxHashable { /// ### Contained in /// /// - ``AttributedTypeSyntax``.``AttributedTypeSyntax/specifiers`` +/// - ``AttributedTypeSyntax``.``AttributedTypeSyntax/lateSpecifiers`` public struct TypeSpecifierListSyntax: SyntaxCollection, SyntaxHashable { public enum Element: SyntaxChildChoices, SyntaxHashable { /// A specifier that can be attached to a type to eg. mark a parameter as `inout` or `consuming` diff --git a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesAB.swift b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesAB.swift index 2c820290b8b..029b4a3493c 100644 --- a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesAB.swift +++ b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesAB.swift @@ -1822,21 +1822,25 @@ public struct RawAttributedTypeSyntax: RawTypeSyntaxNodeProtocol { specifiers: RawTypeSpecifierListSyntax, _ unexpectedBetweenSpecifiersAndAttributes: RawUnexpectedNodesSyntax? = nil, attributes: RawAttributeListSyntax, - _ unexpectedBetweenAttributesAndBaseType: RawUnexpectedNodesSyntax? = nil, + _ unexpectedBetweenAttributesAndLateSpecifiers: RawUnexpectedNodesSyntax? = nil, + lateSpecifiers: RawTypeSpecifierListSyntax, + _ unexpectedBetweenLateSpecifiersAndBaseType: RawUnexpectedNodesSyntax? = nil, baseType: some RawTypeSyntaxNodeProtocol, _ unexpectedAfterBaseType: RawUnexpectedNodesSyntax? = nil, arena: __shared RawSyntaxArena ) { let raw = RawSyntax.makeLayout( - kind: .attributedType, uninitializedCount: 7, arena: arena) { layout in + kind: .attributedType, uninitializedCount: 9, arena: arena) { layout in layout.initialize(repeating: nil) layout[0] = unexpectedBeforeSpecifiers?.raw layout[1] = specifiers.raw layout[2] = unexpectedBetweenSpecifiersAndAttributes?.raw layout[3] = attributes.raw - layout[4] = unexpectedBetweenAttributesAndBaseType?.raw - layout[5] = baseType.raw - layout[6] = unexpectedAfterBaseType?.raw + layout[4] = unexpectedBetweenAttributesAndLateSpecifiers?.raw + layout[5] = lateSpecifiers.raw + layout[6] = unexpectedBetweenLateSpecifiersAndBaseType?.raw + layout[7] = baseType.raw + layout[8] = unexpectedAfterBaseType?.raw } self.init(unchecked: raw) } @@ -1857,16 +1861,24 @@ public struct RawAttributedTypeSyntax: RawTypeSyntaxNodeProtocol { layoutView.children[3].map(RawAttributeListSyntax.init(raw:))! } - public var unexpectedBetweenAttributesAndBaseType: RawUnexpectedNodesSyntax? { + public var unexpectedBetweenAttributesAndLateSpecifiers: RawUnexpectedNodesSyntax? { layoutView.children[4].map(RawUnexpectedNodesSyntax.init(raw:)) } + public var lateSpecifiers: RawTypeSpecifierListSyntax { + layoutView.children[5].map(RawTypeSpecifierListSyntax.init(raw:))! + } + + public var unexpectedBetweenLateSpecifiersAndBaseType: RawUnexpectedNodesSyntax? { + layoutView.children[6].map(RawUnexpectedNodesSyntax.init(raw:)) + } + public var baseType: RawTypeSyntax { - layoutView.children[5].map(RawTypeSyntax.init(raw:))! + layoutView.children[7].map(RawTypeSyntax.init(raw:))! } public var unexpectedAfterBaseType: RawUnexpectedNodesSyntax? { - layoutView.children[6].map(RawUnexpectedNodesSyntax.init(raw:)) + layoutView.children[8].map(RawUnexpectedNodesSyntax.init(raw:)) } } diff --git a/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift b/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift index 436e1e9acdc..6bd1c7a4294 100644 --- a/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift +++ b/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift @@ -422,14 +422,16 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { assertNoError(kind, 10, verify(layout[10], as: RawUnexpectedNodesSyntax?.self)) } func validateAttributedTypeSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) { - assert(layout.count == 7) + assert(layout.count == 9) assertNoError(kind, 0, verify(layout[0], as: RawUnexpectedNodesSyntax?.self)) assertNoError(kind, 1, verify(layout[1], as: RawTypeSpecifierListSyntax.self)) assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self)) assertNoError(kind, 3, verify(layout[3], as: RawAttributeListSyntax.self)) assertNoError(kind, 4, verify(layout[4], as: RawUnexpectedNodesSyntax?.self)) - assertNoError(kind, 5, verify(layout[5], as: RawTypeSyntax.self)) + assertNoError(kind, 5, verify(layout[5], as: RawTypeSpecifierListSyntax.self)) assertNoError(kind, 6, verify(layout[6], as: RawUnexpectedNodesSyntax?.self)) + assertNoError(kind, 7, verify(layout[7], as: RawTypeSyntax.self)) + assertNoError(kind, 8, verify(layout[8], as: RawUnexpectedNodesSyntax?.self)) } func validateAvailabilityArgumentListSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) { for (index, element) in layout.enumerated() { diff --git a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesAB.swift b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesAB.swift index 6baae95f2e3..565c58d8843 100644 --- a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesAB.swift +++ b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesAB.swift @@ -3757,6 +3757,7 @@ public struct AttributeSyntax: SyntaxProtocol, SyntaxHashable, _LeafSyntaxNodePr /// /// - `specifiers`: ``TypeSpecifierListSyntax`` /// - `attributes`: ``AttributeListSyntax`` +/// - `lateSpecifiers`: ``TypeSpecifierListSyntax`` /// - `baseType`: ``TypeSyntax`` public struct AttributedTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTypeSyntaxNodeProtocol { public let _syntaxNode: Syntax @@ -3777,6 +3778,7 @@ public struct AttributedTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTyp /// - 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. /// - specifiers: A list of specifiers that can be attached to the type, such as `inout`, `isolated`, or `consuming`. /// - attributes: A list of attributes that can be attached to the type, such as `@escaping`. + /// - lateSpecifiers: A list of specifiers that can be attached to the type after the attributes, such as 'nonisolated'. /// - baseType: The type to with the specifiers and attributes are applied. /// - 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( @@ -3785,7 +3787,9 @@ public struct AttributedTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTyp specifiers: TypeSpecifierListSyntax = [], _ unexpectedBetweenSpecifiersAndAttributes: UnexpectedNodesSyntax? = nil, attributes: AttributeListSyntax = [], - _ unexpectedBetweenAttributesAndBaseType: UnexpectedNodesSyntax? = nil, + _ unexpectedBetweenAttributesAndLateSpecifiers: UnexpectedNodesSyntax? = nil, + lateSpecifiers: TypeSpecifierListSyntax = [], + _ unexpectedBetweenLateSpecifiersAndBaseType: UnexpectedNodesSyntax? = nil, baseType: some TypeSyntaxProtocol, _ unexpectedAfterBaseType: UnexpectedNodesSyntax? = nil, trailingTrivia: Trivia? = nil @@ -3797,7 +3801,9 @@ public struct AttributedTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTyp specifiers, unexpectedBetweenSpecifiersAndAttributes, attributes, - unexpectedBetweenAttributesAndBaseType, + unexpectedBetweenAttributesAndLateSpecifiers, + lateSpecifiers, + unexpectedBetweenLateSpecifiersAndBaseType, baseType, unexpectedAfterBaseType ))) { (arena, _) in @@ -3806,7 +3812,9 @@ public struct AttributedTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTyp specifiers.raw, unexpectedBetweenSpecifiersAndAttributes?.raw, attributes.raw, - unexpectedBetweenAttributesAndBaseType?.raw, + unexpectedBetweenAttributesAndLateSpecifiers?.raw, + lateSpecifiers.raw, + unexpectedBetweenLateSpecifiersAndBaseType?.raw, baseType.raw, unexpectedAfterBaseType?.raw ] @@ -3913,7 +3921,7 @@ public struct AttributedTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTyp .cast(AttributedTypeSyntax.self) } - public var unexpectedBetweenAttributesAndBaseType: UnexpectedNodesSyntax? { + public var unexpectedBetweenAttributesAndLateSpecifiers: UnexpectedNodesSyntax? { get { return Syntax(self).child(at: 4)?.cast(UnexpectedNodesSyntax.self) } @@ -3922,17 +3930,17 @@ public struct AttributedTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTyp } } - /// The type to with the specifiers and attributes are applied. - public var baseType: TypeSyntax { + /// A list of specifiers that can be attached to the type after the attributes, such as 'nonisolated'. + public var lateSpecifiers: TypeSpecifierListSyntax { get { - return Syntax(self).child(at: 5)!.cast(TypeSyntax.self) + return Syntax(self).child(at: 5)!.cast(TypeSpecifierListSyntax.self) } set(value) { self = Syntax(self).replacingChild(at: 5, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(AttributedTypeSyntax.self) } } - public var unexpectedAfterBaseType: UnexpectedNodesSyntax? { + public var unexpectedBetweenLateSpecifiersAndBaseType: UnexpectedNodesSyntax? { get { return Syntax(self).child(at: 6)?.cast(UnexpectedNodesSyntax.self) } @@ -3941,12 +3949,33 @@ public struct AttributedTypeSyntax: TypeSyntaxProtocol, SyntaxHashable, _LeafTyp } } + /// The type to with the specifiers and attributes are applied. + public var baseType: TypeSyntax { + get { + return Syntax(self).child(at: 7)!.cast(TypeSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 7, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(AttributedTypeSyntax.self) + } + } + + public var unexpectedAfterBaseType: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 8)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 8, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(AttributedTypeSyntax.self) + } + } + public static let structure: SyntaxNodeStructure = .layout([ \Self.unexpectedBeforeSpecifiers, \Self.specifiers, \Self.unexpectedBetweenSpecifiersAndAttributes, \Self.attributes, - \Self.unexpectedBetweenAttributesAndBaseType, + \Self.unexpectedBetweenAttributesAndLateSpecifiers, + \Self.lateSpecifiers, + \Self.unexpectedBetweenLateSpecifiersAndBaseType, \Self.baseType, \Self.unexpectedAfterBaseType ]) From 97299fc17e083ad422e2e4a5f97bf8d5b862b6c7 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 23 Jun 2025 12:07:26 -0700 Subject: [PATCH 3/7] Parse "late" specifiers for type attributes The only "late" specifier at the moment is `nonisolated`, used by protocol conformances. Fixes issue #3109. (cherry picked from commit 1856087f45e9463bd55a73a0568628c1e265b5eb) --- Sources/SwiftParser/Types.swift | 23 +++++++++++++++++--- Tests/SwiftParserTest/DeclarationTests.swift | 11 ++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/Sources/SwiftParser/Types.swift b/Sources/SwiftParser/Types.swift index 67ad666c5df..d1ee40187fa 100644 --- a/Sources/SwiftParser/Types.swift +++ b/Sources/SwiftParser/Types.swift @@ -100,6 +100,7 @@ extension Parser { RawAttributedTypeSyntax( specifiers: specifiersAndAttributes.specifiers, attributes: specifiersAndAttributes.attributes, + lateSpecifiers: specifiersAndAttributes.lateSpecifiers, baseType: base, arena: self.arena ) @@ -1221,7 +1222,8 @@ extension Parser { misplacedSpecifiers: [RawTokenSyntax] = [] ) -> ( specifiers: RawTypeSpecifierListSyntax, - attributes: RawAttributeListSyntax + attributes: RawAttributeListSyntax, + lateSpecifiers: RawTypeSpecifierListSyntax )? { var specifiers: [RawTypeSpecifierListSyntax.Element] = [] SPECIFIER_PARSING: while canHaveParameterSpecifier { @@ -1260,7 +1262,14 @@ extension Parser { attributes = nil } - guard !specifiers.isEmpty || attributes != nil else { + // Only handle `nonisolated` as a late specifier. + var lateSpecifiers: [RawTypeSpecifierListSyntax.Element] = [] + if self.at(.keyword(.nonisolated)) && + !(self.peek(isAt: .leftParen) && self.peek().isAtStartOfLine) { + lateSpecifiers.append(parseNonisolatedTypeSpecifier()) + } + + guard !specifiers.isEmpty || attributes != nil || !lateSpecifiers.isEmpty else { // No specifiers or attributes on this type return nil } @@ -1271,9 +1280,17 @@ extension Parser { specifierList = RawTypeSpecifierListSyntax(elements: specifiers, arena: arena) } + let lateSpecifierList: RawTypeSpecifierListSyntax + if lateSpecifiers.isEmpty { + lateSpecifierList = self.emptyCollection(RawTypeSpecifierListSyntax.self) + } else { + lateSpecifierList = RawTypeSpecifierListSyntax(elements: lateSpecifiers, arena: arena) + } + return ( specifierList, - attributes ?? self.emptyCollection(RawAttributeListSyntax.self) + attributes ?? self.emptyCollection(RawAttributeListSyntax.self), + lateSpecifierList ) } diff --git a/Tests/SwiftParserTest/DeclarationTests.swift b/Tests/SwiftParserTest/DeclarationTests.swift index 3f5367df4f0..19b0e428dd3 100644 --- a/Tests/SwiftParserTest/DeclarationTests.swift +++ b/Tests/SwiftParserTest/DeclarationTests.swift @@ -807,6 +807,17 @@ final class DeclarationTests: ParserTestCase { """ ) + assertParse( + """ + extension Int: @preconcurrency nonisolated Q {} + """ + ) + + assertParse( + """ + extension Int: @unsafe nonisolated Q {} + """ + ) } func testParseDynamicReplacement() { From 8f3671775c9444019374ebd116d2b68551d73d69 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 23 Jun 2025 12:20:06 -0700 Subject: [PATCH 4/7] Formatting (cherry picked from commit 1710601d5fe34d7ea9ce0a5473ddbe8aad1e6b16) --- CodeGeneration/Sources/SyntaxSupport/TypeNodes.swift | 8 ++++++-- .../templates/swiftsyntax/SyntaxNodesFile.swift | 3 ++- Sources/SwiftParser/Types.swift | 3 +-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CodeGeneration/Sources/SyntaxSupport/TypeNodes.swift b/CodeGeneration/Sources/SyntaxSupport/TypeNodes.swift index 1c19e2018d4..12dd1d385cc 100644 --- a/CodeGeneration/Sources/SyntaxSupport/TypeNodes.swift +++ b/CodeGeneration/Sources/SyntaxSupport/TypeNodes.swift @@ -59,8 +59,12 @@ public let TYPE_NODES: [Node] = [ ), Child( name: "lateSpecifiers", - kind: .collection(kind: .typeSpecifierList, collectionElementName: "Specifier", defaultsToEmpty: true, - generateDeprecatedAddFunction: false), + kind: .collection( + kind: .typeSpecifierList, + collectionElementName: "Specifier", + defaultsToEmpty: true, + generateDeprecatedAddFunction: false + ), documentation: "A list of specifiers that can be attached to the type after the attributes, such as 'nonisolated'." ), diff --git a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxNodesFile.swift b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxNodesFile.swift index 703448adfd0..3cfc96d85fd 100644 --- a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxNodesFile.swift +++ b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxNodesFile.swift @@ -176,7 +176,8 @@ func syntaxNode(nodesStartingWith: [Character]) -> SourceFileSyntax { // If needed, this could be added in the future, but for now withUnexpected should be sufficient. if let childNode = SYNTAX_NODE_MAP[child.syntaxNodeKind]?.collectionNode, !child.isUnexpectedNodes, - case .collection(_, collectionElementName: let childElt?, _, _, generateDeprecatedAddFunction: true) = child.kind + case .collection(_, collectionElementName: let childElt?, _, _, generateDeprecatedAddFunction: true) = child + .kind { let childEltType = childNode.collectionElementType.syntaxBaseName diff --git a/Sources/SwiftParser/Types.swift b/Sources/SwiftParser/Types.swift index d1ee40187fa..16654b66aef 100644 --- a/Sources/SwiftParser/Types.swift +++ b/Sources/SwiftParser/Types.swift @@ -1264,8 +1264,7 @@ extension Parser { // Only handle `nonisolated` as a late specifier. var lateSpecifiers: [RawTypeSpecifierListSyntax.Element] = [] - if self.at(.keyword(.nonisolated)) && - !(self.peek(isAt: .leftParen) && self.peek().isAtStartOfLine) { + if self.at(.keyword(.nonisolated)) && !(self.peek(isAt: .leftParen) && self.peek().isAtStartOfLine) { lateSpecifiers.append(parseNonisolatedTypeSpecifier()) } From 9bbeebc98d1ab69df9addd5b47e8319569961727 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 23 Jun 2025 12:31:46 -0700 Subject: [PATCH 5/7] Only parse 'nonisolated' as a specifier when we can have specifiers (cherry picked from commit 75583ab2b91ab6a3bada1d7a3034f26b5799fbe4) --- Sources/SwiftParser/Types.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Sources/SwiftParser/Types.swift b/Sources/SwiftParser/Types.swift index 16654b66aef..deb3ef9a617 100644 --- a/Sources/SwiftParser/Types.swift +++ b/Sources/SwiftParser/Types.swift @@ -1264,7 +1264,9 @@ extension Parser { // Only handle `nonisolated` as a late specifier. var lateSpecifiers: [RawTypeSpecifierListSyntax.Element] = [] - if self.at(.keyword(.nonisolated)) && !(self.peek(isAt: .leftParen) && self.peek().isAtStartOfLine) { + if self.at(.keyword(.nonisolated)) && !(self.peek(isAt: .leftParen) && self.peek().isAtStartOfLine) + && canHaveParameterSpecifier + { lateSpecifiers.append(parseNonisolatedTypeSpecifier()) } From e5c08f788eb43bf83ad5f06a839997d043dfb553 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 23 Jun 2025 14:11:18 -0700 Subject: [PATCH 6/7] Fix tests (cherry picked from commit ec9139ea0b6117ec19eeffbb2667864acd34200b) --- .../Tests/ValidateSyntaxNodes/ValidateSyntaxNodes.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CodeGeneration/Tests/ValidateSyntaxNodes/ValidateSyntaxNodes.swift b/CodeGeneration/Tests/ValidateSyntaxNodes/ValidateSyntaxNodes.swift index 164b1505895..e1b9a73df15 100644 --- a/CodeGeneration/Tests/ValidateSyntaxNodes/ValidateSyntaxNodes.swift +++ b/CodeGeneration/Tests/ValidateSyntaxNodes/ValidateSyntaxNodes.swift @@ -55,13 +55,13 @@ fileprivate extension ChildKind { return kind == otherKind case (.nodeChoices(let choices, _), .nodeChoices(let otherChoices, _)): return choices.count == otherChoices.count && zip(choices, otherChoices).allSatisfy { $0.hasSameType(as: $1) } - case (.collection(kind: let kind, _, _, _), .collection(kind: let otherKind, _, _, _)): + case (.collection(kind: let kind, _, _, _, _), .collection(kind: let otherKind, _, _, _, _)): return kind == otherKind case (.token(let choices, _, _), .token(let otherChoices, _, _)): return choices == otherChoices - case (.node(let kind), .collection(kind: let otherKind, _, _, _)): + case (.node(let kind), .collection(kind: let otherKind, _, _, _, _)): return kind == otherKind - case (.collection(kind: let kind, _, _, _), .node(let otherKind)): + case (.collection(kind: let kind, _, _, _, _), .node(let otherKind)): return kind == otherKind default: return false From 34821878e4e11c55f4590648aafeb4c180069bee Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 24 Jun 2025 06:35:50 -0700 Subject: [PATCH 7/7] Improve formatting (cherry picked from commit a298b41033122e865d59b757abb97a59bde6f8b9) --- .../templates/swiftsyntax/SyntaxNodesFile.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxNodesFile.swift b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxNodesFile.swift index 3cfc96d85fd..cb3a5f184eb 100644 --- a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxNodesFile.swift +++ b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxNodesFile.swift @@ -176,8 +176,8 @@ func syntaxNode(nodesStartingWith: [Character]) -> SourceFileSyntax { // If needed, this could be added in the future, but for now withUnexpected should be sufficient. if let childNode = SYNTAX_NODE_MAP[child.syntaxNodeKind]?.collectionNode, !child.isUnexpectedNodes, - case .collection(_, collectionElementName: let childElt?, _, _, generateDeprecatedAddFunction: true) = child - .kind + case .collection(_, collectionElementName: let childElt?, _, _, generateDeprecatedAddFunction: true) = + child.kind { let childEltType = childNode.collectionElementType.syntaxBaseName