diff --git a/CodeGeneration/Sources/SyntaxSupport/DeclNodes.swift b/CodeGeneration/Sources/SyntaxSupport/DeclNodes.swift index 0dea2d63426..25ecbf84785 100644 --- a/CodeGeneration/Sources/SyntaxSupport/DeclNodes.swift +++ b/CodeGeneration/Sources/SyntaxSupport/DeclNodes.swift @@ -1307,6 +1307,37 @@ public let DECL_NODES: [Node] = [ ] ), + Node( + kind: .usingDecl, + base: .decl, + experimentalFeature: .defaultIsolationPerFile, + nameForDiagnostics: "using", + documentation: """ + A `using` declaration, currently used to control actor isolation within the current file. + + An example of a `using` declaration is + + ```swift + using @MainActor + ``` + """, + children: [ + Child( + name: "usingKeyword", + kind: .token(choices: [.keyword(.using)]), + documentation: "The `using` keyword for this declaration." + ), + Child( + name: "specifier", + kind: .nodeChoices(choices: [ + Child(name: "attribute", kind: .node(kind: .attribute)), + Child(name: "modifier", kind: .token(choices: [.token(.identifier)])), + ]), + documentation: "The specifier that could be either an attribute or a modifier." + ), + ] + ), + Node( kind: .inheritedTypeList, base: .syntaxCollection, diff --git a/CodeGeneration/Sources/SyntaxSupport/ExperimentalFeatures.swift b/CodeGeneration/Sources/SyntaxSupport/ExperimentalFeatures.swift index aa36cd8ba53..70cd36c9a7d 100644 --- a/CodeGeneration/Sources/SyntaxSupport/ExperimentalFeatures.swift +++ b/CodeGeneration/Sources/SyntaxSupport/ExperimentalFeatures.swift @@ -22,6 +22,7 @@ public enum ExperimentalFeature: String, CaseIterable { case keypathWithMethodMembers case oldOwnershipOperatorSpellings case inlineArrayTypeSugar + case defaultIsolationPerFile /// The name of the feature as it is written in the compiler's `Features.def` file. public var featureName: String { @@ -44,6 +45,8 @@ public enum ExperimentalFeature: String, CaseIterable { return "OldOwnershipOperatorSpellings" case .inlineArrayTypeSugar: return "InlineArrayTypeSugar" + case .defaultIsolationPerFile: + return "DefaultIsolationPerFile" } } @@ -68,6 +71,8 @@ public enum ExperimentalFeature: String, CaseIterable { return "`_move` and `_borrow` as ownership operators" case .inlineArrayTypeSugar: return "sugar type for InlineArray" + case .defaultIsolationPerFile: + return "set default actor isolation for a file" } } diff --git a/CodeGeneration/Sources/SyntaxSupport/KeywordSpec.swift b/CodeGeneration/Sources/SyntaxSupport/KeywordSpec.swift index d3c4cd7b3fd..524b216c655 100644 --- a/CodeGeneration/Sources/SyntaxSupport/KeywordSpec.swift +++ b/CodeGeneration/Sources/SyntaxSupport/KeywordSpec.swift @@ -268,6 +268,7 @@ public enum Keyword: CaseIterable { case unsafe case unsafeAddress case unsafeMutableAddress + case using case `var` case visibility case weak @@ -667,6 +668,8 @@ public enum Keyword: CaseIterable { return KeywordSpec("unsafeAddress") case .unsafeMutableAddress: return KeywordSpec("unsafeMutableAddress") + case .using: + return KeywordSpec("using") case .var: return KeywordSpec("var", isLexerClassified: true) case .visibility: diff --git a/CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift b/CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift index 6e50fe39500..40bd67d14bd 100644 --- a/CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift +++ b/CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift @@ -304,6 +304,7 @@ public enum SyntaxNodeKind: String, CaseIterable, IdentifierConvertible, TypeCon case unresolvedIsExpr case unresolvedTernaryExpr case unsafeExpr + case usingDecl case valueBindingPattern case variableDecl case versionComponent diff --git a/Sources/SwiftParser/Declarations.swift b/Sources/SwiftParser/Declarations.swift index a9e4a01f449..6ffffd81438 100644 --- a/Sources/SwiftParser/Declarations.swift +++ b/Sources/SwiftParser/Declarations.swift @@ -140,6 +140,29 @@ extension TokenConsumer { // Otherwise, parse it as an expression. return false + case .lhs(.using): + // This declaration doesn't support attributes or modifiers + if hasAttribute || hasModifier { + return false + } + + var lookahead = subparser.lookahead() + + // Consume 'using' + lookahead.consumeAnyToken() + + // Allow parsing 'using' as declaration only if + // it's immediately followed by either `@` or + // an identifier. + if lookahead.atStartOfLine { + return false + } + + guard lookahead.at(.atSign) || lookahead.at(.identifier) else { + return false + } + + return true case .some(_): // All other decl start keywords unconditionally start a decl. return true @@ -338,6 +361,8 @@ extension Parser { return RawDeclSyntax(self.parseMacroDeclaration(attrs: attrs, introducerHandle: handle)) case (.lhs(.pound), let handle)?: return RawDeclSyntax(self.parseMacroExpansionDeclaration(attrs, handle)) + case (.lhs(.using), let handle)?: + return RawDeclSyntax(self.parseUsingDeclaration(attrs: attrs, introducerHandle: handle)) case (.rhs, let handle)?: return RawDeclSyntax(self.parseBindingDeclaration(attrs, handle, in: context)) case nil: @@ -439,6 +464,54 @@ extension Parser { } } +extension Parser { + mutating func parseUsingDeclaration( + attrs: DeclAttributes, + introducerHandle handle: RecoveryConsumptionHandle + ) -> RawUsingDeclSyntax { + let unexpectedAttributes: RawUnexpectedNodesSyntax? = + if !attrs.attributes.isEmpty { + RawUnexpectedNodesSyntax(attrs.attributes.elements, arena: self.arena) + } else { + nil + } + + let unexpectedModifiers: RawUnexpectedNodesSyntax? = + if !attrs.modifiers.isEmpty { + RawUnexpectedNodesSyntax(attrs.modifiers.elements, arena: self.arena) + } else { + nil + } + + let (unexpectedBeforeKeyword, usingKeyword) = self.eat(handle) + + let unexpectedBeforeUsingKeyword = RawUnexpectedNodesSyntax( + combining: unexpectedAttributes, + unexpectedModifiers, + unexpectedBeforeKeyword, + arena: self.arena + ) + + if self.at(.atSign), case .attribute(let attribute) = self.parseAttribute() { + return RawUsingDeclSyntax( + unexpectedBeforeUsingKeyword, + usingKeyword: usingKeyword, + specifier: .attribute(attribute), + arena: self.arena + ) + } + + let modifier = self.expectWithoutRecovery(.identifier) + + return RawUsingDeclSyntax( + unexpectedBeforeUsingKeyword, + usingKeyword: usingKeyword, + specifier: .modifier(modifier), + arena: self.arena + ) + } +} + extension Parser { /// Parse an extension declaration. mutating func parseExtensionDeclaration( diff --git a/Sources/SwiftParser/TokenPrecedence.swift b/Sources/SwiftParser/TokenPrecedence.swift index dbe50a0f62b..6851702c419 100644 --- a/Sources/SwiftParser/TokenPrecedence.swift +++ b/Sources/SwiftParser/TokenPrecedence.swift @@ -241,7 +241,7 @@ enum TokenPrecedence: Comparable { .get, .set, .didSet, .willSet, .unsafeAddress, .addressWithOwner, .addressWithNativeOwner, .unsafeMutableAddress, .mutableAddressWithOwner, .mutableAddressWithNativeOwner, ._read, .read, ._modify, .modify, // Misc - .import: + .import, .using: self = .declKeyword case // `TypeAttribute` diff --git a/Sources/SwiftParser/TokenSpecSet.swift b/Sources/SwiftParser/TokenSpecSet.swift index 03294198301..b80c3794821 100644 --- a/Sources/SwiftParser/TokenSpecSet.swift +++ b/Sources/SwiftParser/TokenSpecSet.swift @@ -290,6 +290,7 @@ enum PureDeclarationKeyword: TokenSpecSet { case `subscript` case `typealias` case pound + case using init?(lexeme: Lexer.Lexeme, experimentalFeatures: Parser.ExperimentalFeatures) { switch PrepareForKeywordMatch(lexeme) { @@ -311,6 +312,7 @@ enum PureDeclarationKeyword: TokenSpecSet { case TokenSpec(.subscript): self = .subscript case TokenSpec(.typealias): self = .typealias case TokenSpec(.pound): self = .pound + case TokenSpec(.using) where experimentalFeatures.contains(.defaultIsolationPerFile): self = .using default: return nil } } @@ -335,6 +337,7 @@ enum PureDeclarationKeyword: TokenSpecSet { case .subscript: return .keyword(.subscript) case .typealias: return .keyword(.typealias) case .pound: return TokenSpec(.pound, recoveryPrecedence: .openingPoundIf) + case .using: return TokenSpec(.using) } } } diff --git a/Sources/SwiftParser/generated/ExperimentalFeatures.swift b/Sources/SwiftParser/generated/ExperimentalFeatures.swift index 9513b06aac7..c8c8c373856 100644 --- a/Sources/SwiftParser/generated/ExperimentalFeatures.swift +++ b/Sources/SwiftParser/generated/ExperimentalFeatures.swift @@ -52,6 +52,9 @@ extension Parser.ExperimentalFeatures { /// Whether to enable the parsing of sugar type for InlineArray. public static let inlineArrayTypeSugar = Self (rawValue: 1 << 8) + /// Whether to enable the parsing of set default actor isolation for a file. + public static let defaultIsolationPerFile = Self (rawValue: 1 << 9) + /// Creates a new value representing the experimental feature with the /// given name, or returns nil if the name is not recognized. public init?(name: String) { @@ -74,6 +77,8 @@ extension Parser.ExperimentalFeatures { self = .oldOwnershipOperatorSpellings case "InlineArrayTypeSugar": self = .inlineArrayTypeSugar + case "DefaultIsolationPerFile": + self = .defaultIsolationPerFile default: return nil } diff --git a/Sources/SwiftParserDiagnostics/generated/SyntaxKindNameForDiagnostics.swift b/Sources/SwiftParserDiagnostics/generated/SyntaxKindNameForDiagnostics.swift index 9a9d00393e7..45c76011b63 100644 --- a/Sources/SwiftParserDiagnostics/generated/SyntaxKindNameForDiagnostics.swift +++ b/Sources/SwiftParserDiagnostics/generated/SyntaxKindNameForDiagnostics.swift @@ -406,6 +406,8 @@ extension SyntaxKind { return "ternary operator" case .unsafeExpr: return "'unsafe' expression" + case .usingDecl: + return "using" case .valueBindingPattern: return "value binding pattern" case .variableDecl: diff --git a/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift b/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift index 802b6ae8003..f6ed8a61c82 100644 --- a/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift +++ b/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift @@ -3444,6 +3444,16 @@ public func childName(_ keyPath: AnyKeyPath) -> String? { return "expression" case \UnsafeExprSyntax.unexpectedAfterExpression: return "unexpectedAfterExpression" + case \UsingDeclSyntax.unexpectedBeforeUsingKeyword: + return "unexpectedBeforeUsingKeyword" + case \UsingDeclSyntax.usingKeyword: + return "usingKeyword" + case \UsingDeclSyntax.unexpectedBetweenUsingKeywordAndSpecifier: + return "unexpectedBetweenUsingKeywordAndSpecifier" + case \UsingDeclSyntax.specifier: + return "specifier" + case \UsingDeclSyntax.unexpectedAfterSpecifier: + return "unexpectedAfterSpecifier" case \ValueBindingPatternSyntax.unexpectedBeforeBindingSpecifier: return "unexpectedBeforeBindingSpecifier" case \ValueBindingPatternSyntax.bindingSpecifier: diff --git a/Sources/SwiftSyntax/generated/Keyword.swift b/Sources/SwiftSyntax/generated/Keyword.swift index f434e6ff9cc..c0a04cc4bed 100644 --- a/Sources/SwiftSyntax/generated/Keyword.swift +++ b/Sources/SwiftSyntax/generated/Keyword.swift @@ -214,6 +214,7 @@ public enum Keyword: UInt8, Hashable, Sendable { case unsafe case unsafeAddress case unsafeMutableAddress + case using case `var` case visibility case weak @@ -366,6 +367,8 @@ public enum Keyword: UInt8, Hashable, Sendable { self = .swift case "throw": self = .throw + case "using": + self = .using case "where": self = .where case "while": @@ -944,6 +947,7 @@ public enum Keyword: UInt8, Hashable, Sendable { "unsafe", "unsafeAddress", "unsafeMutableAddress", + "using", "var", "visibility", "weak", diff --git a/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift b/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift index 761e69e41d9..2c06e381fc6 100644 --- a/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift +++ b/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift @@ -2289,6 +2289,16 @@ open class SyntaxAnyVisitor: SyntaxVisitor { visitAnyPost(node._syntaxNode) } + @_spi(ExperimentalLanguageFeatures) + override open func visit(_ node: UsingDeclSyntax) -> SyntaxVisitorContinueKind { + return visitAny(node._syntaxNode) + } + + @_spi(ExperimentalLanguageFeatures) + override open func visitPost(_ node: UsingDeclSyntax) { + visitAnyPost(node._syntaxNode) + } + override open func visit(_ node: ValueBindingPatternSyntax) -> SyntaxVisitorContinueKind { return visitAny(node._syntaxNode) } diff --git a/Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift b/Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift index 8c1a8444f88..8102e59b2c3 100644 --- a/Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift +++ b/Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift @@ -214,7 +214,7 @@ public struct DeclSyntax: DeclSyntaxProtocol, SyntaxHashable { public init?(_ node: __shared some SyntaxProtocol) { switch node.raw.kind { - case .accessorDecl, .actorDecl, .associatedTypeDecl, .classDecl, .deinitializerDecl, .editorPlaceholderDecl, .enumCaseDecl, .enumDecl, .extensionDecl, .functionDecl, .ifConfigDecl, .importDecl, .initializerDecl, .macroDecl, .macroExpansionDecl, .missingDecl, .operatorDecl, .poundSourceLocation, .precedenceGroupDecl, .protocolDecl, .structDecl, .subscriptDecl, .typeAliasDecl, .variableDecl: + case .accessorDecl, .actorDecl, .associatedTypeDecl, .classDecl, .deinitializerDecl, .editorPlaceholderDecl, .enumCaseDecl, .enumDecl, .extensionDecl, .functionDecl, .ifConfigDecl, .importDecl, .initializerDecl, .macroDecl, .macroExpansionDecl, .missingDecl, .operatorDecl, .poundSourceLocation, .precedenceGroupDecl, .protocolDecl, .structDecl, .subscriptDecl, .typeAliasDecl, .usingDecl, .variableDecl: self._syntaxNode = node._syntaxNode default: return nil @@ -262,6 +262,7 @@ public struct DeclSyntax: DeclSyntaxProtocol, SyntaxHashable { .node(StructDeclSyntax.self), .node(SubscriptDeclSyntax.self), .node(TypeAliasDeclSyntax.self), + .node(UsingDeclSyntax.self), .node(VariableDeclSyntax.self) ]) } @@ -1787,6 +1788,7 @@ extension Syntax { .node(UnresolvedIsExprSyntax.self), .node(UnresolvedTernaryExprSyntax.self), .node(UnsafeExprSyntax.self), + .node(UsingDeclSyntax.self), .node(ValueBindingPatternSyntax.self), .node(VariableDeclSyntax.self), .node(VersionComponentListSyntax.self), diff --git a/Sources/SwiftSyntax/generated/SyntaxEnum.swift b/Sources/SwiftSyntax/generated/SyntaxEnum.swift index 5f70ddd32a8..da712828311 100644 --- a/Sources/SwiftSyntax/generated/SyntaxEnum.swift +++ b/Sources/SwiftSyntax/generated/SyntaxEnum.swift @@ -304,6 +304,8 @@ public enum SyntaxEnum: Sendable { case unresolvedIsExpr(UnresolvedIsExprSyntax) case unresolvedTernaryExpr(UnresolvedTernaryExprSyntax) case unsafeExpr(UnsafeExprSyntax) + @_spi(ExperimentalLanguageFeatures) + case usingDecl(UsingDeclSyntax) case valueBindingPattern(ValueBindingPatternSyntax) case variableDecl(VariableDeclSyntax) case versionComponentList(VersionComponentListSyntax) @@ -876,6 +878,8 @@ extension Syntax { return .unresolvedTernaryExpr(UnresolvedTernaryExprSyntax(self)!) case .unsafeExpr: return .unsafeExpr(UnsafeExprSyntax(self)!) + case .usingDecl: + return .usingDecl(UsingDeclSyntax(self)!) case .valueBindingPattern: return .valueBindingPattern(ValueBindingPatternSyntax(self)!) case .variableDecl: @@ -929,6 +933,8 @@ public enum DeclSyntaxEnum { case structDecl(StructDeclSyntax) case subscriptDecl(SubscriptDeclSyntax) case typeAliasDecl(TypeAliasDeclSyntax) + @_spi(ExperimentalLanguageFeatures) + case usingDecl(UsingDeclSyntax) case variableDecl(VariableDeclSyntax) } @@ -982,6 +988,8 @@ extension DeclSyntax { return .subscriptDecl(SubscriptDeclSyntax(self)!) case .typeAliasDecl: return .typeAliasDecl(TypeAliasDeclSyntax(self)!) + case .usingDecl: + return .usingDecl(UsingDeclSyntax(self)!) case .variableDecl: return .variableDecl(VariableDeclSyntax(self)!) default: diff --git a/Sources/SwiftSyntax/generated/SyntaxKind.swift b/Sources/SwiftSyntax/generated/SyntaxKind.swift index f1d00716562..d2998e079f5 100644 --- a/Sources/SwiftSyntax/generated/SyntaxKind.swift +++ b/Sources/SwiftSyntax/generated/SyntaxKind.swift @@ -304,6 +304,8 @@ public enum SyntaxKind: Sendable { case unresolvedIsExpr case unresolvedTernaryExpr case unsafeExpr + @_spi(ExperimentalLanguageFeatures) + case usingDecl case valueBindingPattern case variableDecl case versionComponentList @@ -1001,6 +1003,8 @@ public enum SyntaxKind: Sendable { return UnresolvedTernaryExprSyntax.self case .unsafeExpr: return UnsafeExprSyntax.self + case .usingDecl: + return UsingDeclSyntax.self case .valueBindingPattern: return ValueBindingPatternSyntax.self case .variableDecl: diff --git a/Sources/SwiftSyntax/generated/SyntaxRewriter.swift b/Sources/SwiftSyntax/generated/SyntaxRewriter.swift index 06778189fb7..bc976b3b244 100644 --- a/Sources/SwiftSyntax/generated/SyntaxRewriter.swift +++ b/Sources/SwiftSyntax/generated/SyntaxRewriter.swift @@ -2043,6 +2043,14 @@ open class SyntaxRewriter { return ExprSyntax(UnsafeExprSyntax(unsafeCasting: visitChildren(node._syntaxNode))) } + /// Visit a `UsingDeclSyntax`. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + @_spi(ExperimentalLanguageFeatures) + open func visit(_ node: UsingDeclSyntax) -> DeclSyntax { + return DeclSyntax(UsingDeclSyntax(unsafeCasting: visitChildren(node._syntaxNode))) + } + /// Visit a ``ValueBindingPatternSyntax``. /// - Parameter node: the node that is being visited /// - Returns: the rewritten node @@ -3547,6 +3555,11 @@ open class SyntaxRewriter { Syntax(visit(UnsafeExprSyntax(unsafeCasting: node))) } + @inline(never) + private func visitUsingDeclSyntaxImpl(_ node: Syntax) -> Syntax { + Syntax(visit(UsingDeclSyntax(unsafeCasting: node))) + } + @inline(never) private func visitValueBindingPatternSyntaxImpl(_ node: Syntax) -> Syntax { Syntax(visit(ValueBindingPatternSyntax(unsafeCasting: node))) @@ -4187,6 +4200,8 @@ open class SyntaxRewriter { return self.visitUnresolvedTernaryExprSyntaxImpl(_:) case .unsafeExpr: return self.visitUnsafeExprSyntaxImpl(_:) + case .usingDecl: + return self.visitUsingDeclSyntaxImpl(_:) case .valueBindingPattern: return self.visitValueBindingPatternSyntaxImpl(_:) case .variableDecl: @@ -4773,6 +4788,8 @@ open class SyntaxRewriter { return visitUnresolvedTernaryExprSyntaxImpl(node) case .unsafeExpr: return visitUnsafeExprSyntaxImpl(node) + case .usingDecl: + return visitUsingDeclSyntaxImpl(node) case .valueBindingPattern: return visitValueBindingPatternSyntaxImpl(node) case .variableDecl: diff --git a/Sources/SwiftSyntax/generated/SyntaxVisitor.swift b/Sources/SwiftSyntax/generated/SyntaxVisitor.swift index d600655152f..9ed668dd140 100644 --- a/Sources/SwiftSyntax/generated/SyntaxVisitor.swift +++ b/Sources/SwiftSyntax/generated/SyntaxVisitor.swift @@ -3371,6 +3371,20 @@ open class SyntaxVisitor { open func visitPost(_ node: UnsafeExprSyntax) { } + /// Visiting `UsingDeclSyntax` specifically. + /// - Parameter node: the node we are visiting. + /// - Returns: how should we continue visiting. + @_spi(ExperimentalLanguageFeatures) + open func visit(_ node: UsingDeclSyntax) -> SyntaxVisitorContinueKind { + return .visitChildren + } + + /// The function called after visiting `UsingDeclSyntax` and its descendants. + /// - node: the node we just finished visiting. + @_spi(ExperimentalLanguageFeatures) + open func visitPost(_ node: UsingDeclSyntax) { + } + /// Visiting ``ValueBindingPatternSyntax`` specifically. /// - Parameter node: the node we are visiting. /// - Returns: how should we continue visiting. @@ -5742,6 +5756,14 @@ open class SyntaxVisitor { visitPost(UnsafeExprSyntax(unsafeCasting: node)) } + @inline(never) + private func visitUsingDeclSyntaxImpl(_ node: Syntax) { + if visit(UsingDeclSyntax(unsafeCasting: node)) == .visitChildren { + visitChildren(node) + } + visitPost(UsingDeclSyntax(unsafeCasting: node)) + } + @inline(never) private func visitValueBindingPatternSyntaxImpl(_ node: Syntax) { if visit(ValueBindingPatternSyntax(unsafeCasting: node)) == .visitChildren { @@ -6418,6 +6440,8 @@ open class SyntaxVisitor { return self.visitUnresolvedTernaryExprSyntaxImpl(_:) case .unsafeExpr: return self.visitUnsafeExprSyntaxImpl(_:) + case .usingDecl: + return self.visitUsingDeclSyntaxImpl(_:) case .valueBindingPattern: return self.visitValueBindingPatternSyntaxImpl(_:) case .variableDecl: @@ -7004,6 +7028,8 @@ open class SyntaxVisitor { self.visitUnresolvedTernaryExprSyntaxImpl(node) case .unsafeExpr: self.visitUnsafeExprSyntaxImpl(node) + case .usingDecl: + self.visitUsingDeclSyntaxImpl(node) case .valueBindingPattern: self.visitValueBindingPatternSyntaxImpl(node) case .variableDecl: diff --git a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesD.swift b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesD.swift index d9f5b337aea..79480c99c6b 100644 --- a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesD.swift +++ b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesD.swift @@ -499,7 +499,7 @@ public struct RawDeclSyntax: RawDeclSyntaxNodeProtocol { public static func isKindOf(_ raw: RawSyntax) -> Bool { switch raw.kind { - case .accessorDecl, .actorDecl, .associatedTypeDecl, .classDecl, .deinitializerDecl, .editorPlaceholderDecl, .enumCaseDecl, .enumDecl, .extensionDecl, .functionDecl, .ifConfigDecl, .importDecl, .initializerDecl, .macroDecl, .macroExpansionDecl, .missingDecl, .operatorDecl, .poundSourceLocation, .precedenceGroupDecl, .protocolDecl, .structDecl, .subscriptDecl, .typeAliasDecl, .variableDecl: + case .accessorDecl, .actorDecl, .associatedTypeDecl, .classDecl, .deinitializerDecl, .editorPlaceholderDecl, .enumCaseDecl, .enumDecl, .extensionDecl, .functionDecl, .ifConfigDecl, .importDecl, .initializerDecl, .macroDecl, .macroExpansionDecl, .missingDecl, .operatorDecl, .poundSourceLocation, .precedenceGroupDecl, .protocolDecl, .structDecl, .subscriptDecl, .typeAliasDecl, .usingDecl, .variableDecl: return true default: return false diff --git a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesTUVWXYZ.swift b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesTUVWXYZ.swift index e4b5a2b69a0..704219505b9 100644 --- a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesTUVWXYZ.swift +++ b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesTUVWXYZ.swift @@ -1863,6 +1863,108 @@ public struct RawUnsafeExprSyntax: RawExprSyntaxNodeProtocol { } } +@_spi(ExperimentalLanguageFeatures) +@_spi(RawSyntax) +public struct RawUsingDeclSyntax: RawDeclSyntaxNodeProtocol { + public enum Specifier: RawSyntaxNodeProtocol { + case attribute(RawAttributeSyntax) + /// ### Tokens + /// + /// For syntax trees generated by the parser, this is guaranteed to be ``. + case modifier(RawTokenSyntax) + + public static func isKindOf(_ raw: RawSyntax) -> Bool { + RawAttributeSyntax.isKindOf(raw) || RawTokenSyntax.isKindOf(raw) + } + + public var raw: RawSyntax { + switch self { + case .attribute(let node): + return node.raw + case .modifier(let node): + return node.raw + } + } + + public init?(_ node: __shared some RawSyntaxNodeProtocol) { + if let node = node.as(RawAttributeSyntax.self) { + self = .attribute(node) + } else if let node = node.as(RawTokenSyntax.self) { + self = .modifier(node) + } else { + return nil + } + } + } + + @_spi(RawSyntax) + public var layoutView: RawSyntaxLayoutView { + return raw.layoutView! + } + + public static func isKindOf(_ raw: RawSyntax) -> Bool { + return raw.kind == .usingDecl + } + + 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( + _ unexpectedBeforeUsingKeyword: RawUnexpectedNodesSyntax? = nil, + usingKeyword: RawTokenSyntax, + _ unexpectedBetweenUsingKeywordAndSpecifier: RawUnexpectedNodesSyntax? = nil, + specifier: Specifier, + _ unexpectedAfterSpecifier: RawUnexpectedNodesSyntax? = nil, + arena: __shared RawSyntaxArena + ) { + let raw = RawSyntax.makeLayout( + kind: .usingDecl, uninitializedCount: 5, arena: arena) { layout in + layout.initialize(repeating: nil) + layout[0] = unexpectedBeforeUsingKeyword?.raw + layout[1] = usingKeyword.raw + layout[2] = unexpectedBetweenUsingKeywordAndSpecifier?.raw + layout[3] = specifier.raw + layout[4] = unexpectedAfterSpecifier?.raw + } + self.init(unchecked: raw) + } + + public var unexpectedBeforeUsingKeyword: RawUnexpectedNodesSyntax? { + layoutView.children[0].map(RawUnexpectedNodesSyntax.init(raw:)) + } + + public var usingKeyword: RawTokenSyntax { + layoutView.children[1].map(RawTokenSyntax.init(raw:))! + } + + public var unexpectedBetweenUsingKeywordAndSpecifier: RawUnexpectedNodesSyntax? { + layoutView.children[2].map(RawUnexpectedNodesSyntax.init(raw:)) + } + + public var specifier: RawSyntax { + layoutView.children[3]! + } + + public var unexpectedAfterSpecifier: RawUnexpectedNodesSyntax? { + layoutView.children[4].map(RawUnexpectedNodesSyntax.init(raw:)) + } +} + @_spi(RawSyntax) public struct RawValueBindingPatternSyntax: RawPatternSyntaxNodeProtocol { @_spi(RawSyntax) diff --git a/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift b/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift index 2a0122ad1cd..436e1e9acdc 100644 --- a/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift +++ b/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift @@ -3015,6 +3015,15 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { assertNoError(kind, 3, verify(layout[3], as: RawExprSyntax.self)) assertNoError(kind, 4, verify(layout[4], as: RawUnexpectedNodesSyntax?.self)) } + func validateUsingDeclSyntax(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("using")])) + assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self)) + assertAnyHasNoError(kind, 3, [ + verify(layout[3], as: RawSyntax.self)]) + assertNoError(kind, 4, verify(layout[4], as: RawUnexpectedNodesSyntax?.self)) + } func validateValueBindingPatternSyntax(kind: SyntaxKind, layout: RawSyntaxBuffer) { assert(layout.count == 5) assertNoError(kind, 0, verify(layout[0], as: RawUnexpectedNodesSyntax?.self)) @@ -3682,6 +3691,8 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { validateUnresolvedTernaryExprSyntax(kind: kind, layout: layout) case .unsafeExpr: validateUnsafeExprSyntax(kind: kind, layout: layout) + case .usingDecl: + validateUsingDeclSyntax(kind: kind, layout: layout) case .valueBindingPattern: validateValueBindingPatternSyntax(kind: kind, layout: layout) case .variableDecl: diff --git a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesTUVWXYZ.swift b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesTUVWXYZ.swift index cf0c4b33f19..bf5973151ba 100644 --- a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesTUVWXYZ.swift +++ b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesTUVWXYZ.swift @@ -3086,6 +3086,222 @@ public struct UnsafeExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _LeafExprSyn ]) } +// MARK: - UsingDeclSyntax + +/// A `using` declaration, currently used to control actor isolation within the current file. +/// +/// An example of a `using` declaration is +/// +/// ```swift +/// using @MainActor +/// ``` +/// +/// - Note: Requires experimental feature `defaultIsolationPerFile`. +/// +/// ### Children +/// +/// - `usingKeyword`: `using` +/// - `specifier`: (``AttributeSyntax`` | ``) +@_spi(ExperimentalLanguageFeatures) +public struct UsingDeclSyntax: DeclSyntaxProtocol, SyntaxHashable, _LeafDeclSyntaxNodeProtocol { + public enum Specifier: SyntaxChildChoices, SyntaxHashable { + case attribute(AttributeSyntax) + /// ### Tokens + /// + /// For syntax trees generated by the parser, this is guaranteed to be ``. + case modifier(TokenSyntax) + + public var _syntaxNode: Syntax { + switch self { + case .attribute(let node): + return node._syntaxNode + case .modifier(let node): + return node._syntaxNode + } + } + + public init(_ node: AttributeSyntax) { + self = .attribute(node) + } + + public init(_ node: TokenSyntax) { + self = .modifier(node) + } + + public init?(_ node: __shared some SyntaxProtocol) { + if let node = node.as(AttributeSyntax.self) { + self = .attribute(node) + } else if let node = node.as(TokenSyntax.self) { + self = .modifier(node) + } else { + return nil + } + } + + public static var structure: SyntaxNodeStructure { + return .choices([.node(AttributeSyntax.self), .node(TokenSyntax.self)]) + } + + /// Checks if the current syntax node can be cast to ``AttributeSyntax``. + /// + /// - Returns: `true` if the node can be cast, `false` otherwise. + public func `is`(_ syntaxType: AttributeSyntax.Type) -> Bool { + return self.as(syntaxType) != nil + } + + /// Attempts to cast the current syntax node to ``AttributeSyntax``. + /// + /// - Returns: An instance of ``AttributeSyntax``, or `nil` if the cast fails. + public func `as`(_ syntaxType: AttributeSyntax.Type) -> AttributeSyntax? { + return AttributeSyntax.init(self) + } + + /// Force-casts the current syntax node to ``AttributeSyntax``. + /// + /// - Returns: An instance of ``AttributeSyntax``. + /// - Warning: This function will crash if the cast is not possible. Use `as` to safely attempt a cast. + public func cast(_ syntaxType: AttributeSyntax.Type) -> AttributeSyntax { + return self.as(AttributeSyntax.self)! + } + + /// Checks if the current syntax node can be cast to ``TokenSyntax``. + /// + /// - Returns: `true` if the node can be cast, `false` otherwise. + public func `is`(_ syntaxType: TokenSyntax.Type) -> Bool { + return self.as(syntaxType) != nil + } + + /// Attempts to cast the current syntax node to ``TokenSyntax``. + /// + /// - Returns: An instance of ``TokenSyntax``, or `nil` if the cast fails. + public func `as`(_ syntaxType: TokenSyntax.Type) -> TokenSyntax? { + return TokenSyntax.init(self) + } + + /// Force-casts the current syntax node to ``TokenSyntax``. + /// + /// - Returns: An instance of ``TokenSyntax``. + /// - Warning: This function will crash if the cast is not possible. Use `as` to safely attempt a cast. + public func cast(_ syntaxType: TokenSyntax.Type) -> TokenSyntax { + return self.as(TokenSyntax.self)! + } + } + + public let _syntaxNode: Syntax + + public init?(_ node: __shared some SyntaxProtocol) { + guard node.raw.kind == .usingDecl 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. + /// - usingKeyword: The `using` keyword for this declaration. + /// - specifier: The specifier that could be either an attribute or a modifier. + /// - 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, + _ unexpectedBeforeUsingKeyword: UnexpectedNodesSyntax? = nil, + usingKeyword: TokenSyntax = .keyword(.using), + _ unexpectedBetweenUsingKeywordAndSpecifier: UnexpectedNodesSyntax? = nil, + specifier: Specifier, + _ unexpectedAfterSpecifier: 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(), ( + unexpectedBeforeUsingKeyword, + usingKeyword, + unexpectedBetweenUsingKeywordAndSpecifier, + specifier, + unexpectedAfterSpecifier + ))) { (arena, _) in + let layout: [RawSyntax?] = [ + unexpectedBeforeUsingKeyword?.raw, + usingKeyword.raw, + unexpectedBetweenUsingKeywordAndSpecifier?.raw, + specifier.raw, + unexpectedAfterSpecifier?.raw + ] + let raw = RawSyntax.makeLayout( + kind: SyntaxKind.usingDecl, + from: layout, + arena: arena, + leadingTrivia: leadingTrivia, + trailingTrivia: trailingTrivia + ) + return Syntax.forRoot(raw, rawNodeArena: arena).cast(Self.self) + } + } + + public var unexpectedBeforeUsingKeyword: 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(UsingDeclSyntax.self) + } + } + + /// The `using` keyword for this declaration. + /// + /// ### Tokens + /// + /// For syntax trees generated by the parser, this is guaranteed to be `using`. + public var usingKeyword: 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(UsingDeclSyntax.self) + } + } + + public var unexpectedBetweenUsingKeywordAndSpecifier: 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(UsingDeclSyntax.self) + } + } + + /// The specifier that could be either an attribute or a modifier. + public var specifier: Specifier { + get { + return Syntax(self).child(at: 3)!.cast(Specifier.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 3, with: Syntax(value), rawAllocationArena: RawSyntaxArena()).cast(UsingDeclSyntax.self) + } + } + + public var unexpectedAfterSpecifier: 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(UsingDeclSyntax.self) + } + } + + public static let structure: SyntaxNodeStructure = .layout([ + \Self.unexpectedBeforeUsingKeyword, + \Self.usingKeyword, + \Self.unexpectedBetweenUsingKeywordAndSpecifier, + \Self.specifier, + \Self.unexpectedAfterSpecifier + ]) +} + // MARK: - ValueBindingPatternSyntax /// ### Children diff --git a/Tests/SwiftParserTest/DeclarationTests.swift b/Tests/SwiftParserTest/DeclarationTests.swift index f55aa4c1a3d..3f5367df4f0 100644 --- a/Tests/SwiftParserTest/DeclarationTests.swift +++ b/Tests/SwiftParserTest/DeclarationTests.swift @@ -12,7 +12,7 @@ import SwiftBasicFormat @_spi(Testing) @_spi(RawSyntax) @_spi(ExperimentalLanguageFeatures) import SwiftParser -@_spi(RawSyntax) import SwiftSyntax +@_spi(RawSyntax) @_spi(ExperimentalLanguageFeatures) import SwiftSyntax import SwiftSyntaxBuilder import XCTest @@ -3557,3 +3557,134 @@ final class DeclarationTests: ParserTestCase { ) } } + +final class UsingDeclarationTests: ParserTestCase { + override var experimentalFeatures: Parser.ExperimentalFeatures { + [.defaultIsolationPerFile] + } + + func testUsing() { + assertParse( + "using @MainActor", + substructure: UsingDeclSyntax( + usingKeyword: .keyword(.using), + specifier: .attribute( + AttributeSyntax( + attributeName: IdentifierTypeSyntax( + name: .identifier("MainActor") + ) + ) + ) + ) + ) + assertParse( + "using nonisolated", + substructure: UsingDeclSyntax( + usingKeyword: .keyword(.using), + specifier: .modifier(.identifier("nonisolated")) + ) + ) + + assertParse( + "using @Test", + substructure: UsingDeclSyntax( + usingKeyword: .keyword(.using), + specifier: .attribute( + AttributeSyntax( + attributeName: IdentifierTypeSyntax( + name: .identifier("Test") + ) + ) + ) + ) + ) + + assertParse( + "using test", + substructure: UsingDeclSyntax( + usingKeyword: .keyword(.using), + specifier: .modifier(.identifier("test")) + ) + ) + + assertParse( + """ + nonisolated + using + """, + substructure: CodeBlockSyntax( + DeclReferenceExprSyntax(baseName: .identifier("using")) + ) + ) + + assertParse( + """ + @MainActor + using + """, + substructure: CodeBlockSyntax( + DeclReferenceExprSyntax(baseName: .identifier("using")) + ) + ) + + assertParse( + """ + using + @MainActor 1️⃣ + """, + diagnostics: [ + DiagnosticSpec( + message: "expected declaration after attribute", + fixIts: ["insert declaration"] + ) + ], + fixedSource: + """ + using + @MainActor <#declaration#> + """ + ) + + assertParse( + """ + using + nonisolated + """, + substructure: CodeBlockSyntax( + DeclReferenceExprSyntax(baseName: .identifier("using")) + ) + ) + + assertParse( + """ + func + using (x: Int) {} + """ + ) + + assertParse( + """ + func + using + (x: Int) {} + """ + ) + + assertParse( + """ + let + using = 42 + """ + ) + + assertParse("let (x: Int, using: String) = (x: 42, using: \"\")") + + assertParse( + """ + do { + using @MainActor + } + """ + ) + } +}