diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index aacfaa37d9d..d03d7b22926 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -84,11 +84,13 @@ Once you've pushed your branch, you should see an option on this repository's pa > [!TIP] > If you are stuck, it’s encouraged to submit a PR that describes the issue you’re having, e.g. if there are tests that are failing, build failures you can’t resolve, or if you have architectural questions. We’re happy to work with you to resolve those issue. -## Opening a PR for Release Branch +### Opening a PR for Release Branch -See the [dedicated section][section] on the Swift project website. +See the [dedicated section](https://www.swift.org/contributing/#release-branch-pull-requests) on the Swift project website. -[section]: https://www.swift.org/contributing/#release-branch-pull-requests +## Changing public API + +If you are modifying public API, request feedback for these changes from the community by opening a RFC as described by the [RFC Process](Contributor%20Documentation/RFC%20Process.md). ## Review and CI Testing diff --git a/CodeGeneration/Sources/SyntaxSupport/AttributeNodes.swift b/CodeGeneration/Sources/SyntaxSupport/AttributeNodes.swift index ca49a20150c..a92cb990b9e 100644 --- a/CodeGeneration/Sources/SyntaxSupport/AttributeNodes.swift +++ b/CodeGeneration/Sources/SyntaxSupport/AttributeNodes.swift @@ -385,7 +385,7 @@ public let ATTRIBUTE_NODES: [Node] = [ ), Child( name: "accessorSpecifier", - kind: .token(choices: [.keyword(.get), .keyword(.set)]), + kind: .token(choices: [.keyword(.get), .keyword(.set), .keyword(._modify)]), documentation: "The accessor name.", isOptional: true ), diff --git a/CodeGeneration/Sources/SyntaxSupport/Child.swift b/CodeGeneration/Sources/SyntaxSupport/Child.swift index e486ef1598b..2c299b429de 100644 --- a/CodeGeneration/Sources/SyntaxSupport/Child.swift +++ b/CodeGeneration/Sources/SyntaxSupport/Child.swift @@ -93,6 +93,9 @@ public class Child: NodeChoiceConvertible { /// Whether this child is optional and can be `nil`. public let isOptional: Bool + /// Whether this child provides a default value when used as a parameter in a function. + public let providesDefaultInitialization: Bool + public let experimentalFeature: ExperimentalFeature? /// A name of this child that can be shown in diagnostics. @@ -303,6 +306,7 @@ public class Child: NodeChoiceConvertible { nameForDiagnostics: String? = nil, documentation: String? = nil, isOptional: Bool = false, + providesDefaultInitialization: Bool = true, newerChildPath: [Child] = [] ) { precondition(name.first?.isLowercase ?? true, "The first letter of a child’s name should be lowercase") @@ -314,6 +318,7 @@ public class Child: NodeChoiceConvertible { self.documentationSummary = SwiftSyntax.Trivia.docCommentTrivia(from: documentation) self.documentationAbstract = String(documentation?.split(whereSeparator: \.isNewline).first ?? "") self.isOptional = isOptional + self.providesDefaultInitialization = providesDefaultInitialization } /// Create a node that is a copy of the last node in `newerChildPath`, but @@ -329,6 +334,7 @@ public class Child: NodeChoiceConvertible { self.documentationSummary = other.documentationSummary self.documentationAbstract = other.documentationAbstract self.isOptional = other.isOptional + self.providesDefaultInitialization = other.providesDefaultInitialization } /// Create a child for the unexpected nodes between two children (either or @@ -353,6 +359,7 @@ public class Child: NodeChoiceConvertible { nameForDiagnostics: nil, documentation: nil, isOptional: true, + providesDefaultInitialization: true, newerChildPath: newerChildPath ) } diff --git a/CodeGeneration/Sources/SyntaxSupport/ExperimentalFeatures.swift b/CodeGeneration/Sources/SyntaxSupport/ExperimentalFeatures.swift index 6da75f84254..4b6381d1e77 100644 --- a/CodeGeneration/Sources/SyntaxSupport/ExperimentalFeatures.swift +++ b/CodeGeneration/Sources/SyntaxSupport/ExperimentalFeatures.swift @@ -21,6 +21,7 @@ public enum ExperimentalFeature: String, CaseIterable { case coroutineAccessors case valueGenerics case abiAttribute + case unsafeExpression /// The name of the feature as it is written in the compiler's `Features.def` file. public var featureName: String { @@ -41,6 +42,8 @@ public enum ExperimentalFeature: String, CaseIterable { return "ValueGenerics" case .abiAttribute: return "ABIAttribute" + case .unsafeExpression: + return "WarnUnsafe" } } @@ -63,6 +66,8 @@ public enum ExperimentalFeature: String, CaseIterable { return "value generics" case .abiAttribute: return "@abi attribute" + case .unsafeExpression: + return "'unsafe' expression" } } diff --git a/CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift b/CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift index fdad15af88d..7aad34d15ec 100644 --- a/CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift +++ b/CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift @@ -180,6 +180,23 @@ public let EXPR_NODES: [Node] = [ ] ), + Node( + kind: .unsafeExpr, + base: .expr, + experimentalFeature: .unsafeExpression, + nameForDiagnostics: "'unsafe' expression", + children: [ + Child( + name: "unsafeKeyword", + kind: .token(choices: [.keyword(.unsafe)]) + ), + Child( + name: "expression", + kind: .node(kind: .expr) + ), + ] + ), + Node( kind: .binaryOperatorExpr, base: .expr, @@ -944,7 +961,8 @@ public let EXPR_NODES: [Node] = [ Child( name: "leftParen", kind: .token(choices: [.token(.leftParen)]), - isOptional: true + isOptional: true, + providesDefaultInitialization: false ), Child( name: "arguments", @@ -954,7 +972,8 @@ public let EXPR_NODES: [Node] = [ Child( name: "rightParen", kind: .token(choices: [.token(.rightParen)]), - isOptional: true + isOptional: true, + providesDefaultInitialization: false ), Child( name: "trailingClosure", diff --git a/CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift b/CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift index 1d9e97b0e29..5038fa3d39c 100644 --- a/CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift +++ b/CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift @@ -299,6 +299,7 @@ public enum SyntaxNodeKind: String, CaseIterable, IdentifierConvertible, TypeCon case unresolvedAsExpr case unresolvedIsExpr case unresolvedTernaryExpr + case unsafeExpr case valueBindingPattern case variableDecl case versionComponent diff --git a/CodeGeneration/Sources/Utils/SyntaxBuildableChild.swift b/CodeGeneration/Sources/Utils/SyntaxBuildableChild.swift index fe73c60d237..9ad78761e04 100644 --- a/CodeGeneration/Sources/Utils/SyntaxBuildableChild.swift +++ b/CodeGeneration/Sources/Utils/SyntaxBuildableChild.swift @@ -86,7 +86,7 @@ extension Child { /// ` = default_value` that can be used as the default value to for a /// function parameter. Otherwise, return `nil`. public var defaultInitialization: InitializerClauseSyntax? { - if let defaultValue { + if providesDefaultInitialization, let defaultValue { return InitializerClauseSyntax( equal: .equalToken(leadingTrivia: .space, trailingTrivia: .space), value: defaultValue diff --git a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SwiftSyntaxDoccIndexTemplate.md b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SwiftSyntaxDoccIndexTemplate.md index c100fb2eb1c..46014f31202 100644 --- a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SwiftSyntaxDoccIndexTemplate.md +++ b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SwiftSyntaxDoccIndexTemplate.md @@ -66,7 +66,6 @@ allows Swift tools to parse, inspect, generate, and transform Swift source code. ### Internals - -- - - - diff --git a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxRewriterFile.swift b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxRewriterFile.swift index 86f2cbaaea1..d8005fde045 100644 --- a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxRewriterFile.swift +++ b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxRewriterFile.swift @@ -42,13 +42,6 @@ let syntaxRewriterFile = SourceFileSyntax(leadingTrivia: copyrightHeader) { """ ) - DeclSyntax( - """ - /// 'Syntax' object factory recycling 'Syntax.Info' instances. - private let nodeFactory: SyntaxNodeFactory = SyntaxNodeFactory() - """ - ) - DeclSyntax( """ public init(viewMode: SyntaxTreeViewMode = .sourceAccurate) { @@ -330,11 +323,11 @@ let syntaxRewriterFile = SourceFileSyntax(leadingTrivia: copyrightHeader) { // with 'Syntax' var rewrittens: ContiguousArray = [] - for case let (child?, info) in RawSyntaxChildren(node) where viewMode.shouldTraverse(node: child) { + for case let childDataRef? in node.layoutBuffer where viewMode.shouldTraverse(node: childDataRef.pointee.raw) { // Build the Syntax node to rewrite - var childNode = visitImpl(nodeFactory.create(parent: node, raw: child, absoluteInfo: info)) - if childNode.raw.id != child.id { + let childNode = visitImpl(Syntax(arena: node.arena, dataRef: childDataRef)) + if childNode.raw.id != childDataRef.pointee.raw.id { // The node was rewritten, let's handle it if newLayout.baseAddress == nil { @@ -345,13 +338,10 @@ let syntaxRewriterFile = SourceFileSyntax(leadingTrivia: copyrightHeader) { } // Update the rewritten child. - newLayout[Int(info.indexInParent)] = childNode.raw + newLayout[Int(childDataRef.pointee.absoluteInfo.layoutIndexInParent)] = childNode.raw // Retain the syntax arena of the new node until it's wrapped with Syntax node. rewrittens.append(childNode.raw.arenaReference.retained) } - - // Recycle 'childNode.info' - nodeFactory.dispose(&childNode) } if newLayout.baseAddress != nil { diff --git a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxVisitorFile.swift b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxVisitorFile.swift index 430feaa6393..00dac528f50 100644 --- a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxVisitorFile.swift +++ b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxVisitorFile.swift @@ -32,13 +32,6 @@ let syntaxVisitorFile = SourceFileSyntax(leadingTrivia: copyrightHeader) { try! ClassDeclSyntax("open class SyntaxVisitor") { DeclSyntax("public let viewMode: SyntaxTreeViewMode") - DeclSyntax( - """ - /// 'Syntax' object factory recycling 'Syntax.Info' instances. - private let nodeFactory: SyntaxNodeFactory = SyntaxNodeFactory() - """ - ) - DeclSyntax( """ public init(viewMode: SyntaxTreeViewMode) { @@ -221,10 +214,8 @@ let syntaxVisitorFile = SourceFileSyntax(leadingTrivia: copyrightHeader) { DeclSyntax( """ private func visitChildren(_ node: Syntax) { - for case let (child?, info) in RawSyntaxChildren(node) where viewMode.shouldTraverse(node: child) { - var childNode = nodeFactory.create(parent: node, raw: child, absoluteInfo: info) - dispatchVisit(childNode) - nodeFactory.dispose(&childNode) + for case let childDataRef? in node.layoutBuffer where viewMode.shouldTraverse(node: childDataRef.pointee.raw) { + dispatchVisit(Syntax(arena: node.arena, dataRef: childDataRef)) } } """ diff --git a/Contributor Documentation/RFC Process.md b/Contributor Documentation/RFC Process.md new file mode 100644 index 00000000000..d9d7b0e66c6 --- /dev/null +++ b/Contributor Documentation/RFC Process.md @@ -0,0 +1,33 @@ +# RFC Process + +We gather community feedback for all changes to swift-syntax’s public API to keep it clean and usable. Everybody is encouraged to share their thoughts on the RFC posts. + +Public API changes are: +- New APIs +- Changes to API behavior that are not considered bugfixes +- Deprecations of public API +- API-incompatible changes, like changing the type of a public variable +- Declarations marked with `@_spi` are not considered part of the public API and don’t require an RFC. + +## The RFC post + +The RFC process should contain the following. +- **Summary**: A short summary of the changes. If it is not obvious, what motivated this change? +- **Link to PR**: Link to the pull request that implements the API change. +- **Swift Interface**: The changed API in a Swift Interface style notation. + - For new API this should contain the new API’s signature and its doc comment. + - If the change affects existing API, the old API’s signature should also be included in the RFC post for reference. +- **For new API**: If this is new API, justify why this is this a worthwhile addition to swift-syntax. A good guideline is to answer the following questions. + - **Commonality**: Do you expect this API to be widely used? Is it applicable in a variety of contexts? + - **Discoverability**: Will users of swift-syntax easily find this new API for the operations they want to achieve? + - **Not trivially composable**: Can the desired behavior be achieved using existing public API? If yes, is this new API superior because one of the following reasons? + - **Readability**: Is the proposed new API easier to understand than the composed alternative? + - **Correctness and performance issues**: Does the naive implementation using existing API have any correctness or performance issues that would be covered correctly by the new API. +- **For existing API**: If this is modifying existing API, do you expect many clients to be affected by the change? + - **Migration**: What are the migration steps needed to migrate to the new API? + +Make sure to also include the change in `Release Notes/.md`. + +## The RFC process + +After posting the RFC in the [swift-syntax category on the Swift forums](https://forums.swift.org/c/development/swift-syntax/112), you can merge the PR. You are expected to incorporate any feedback from the RFC in a timely manner. diff --git a/Release Notes/602.md b/Release Notes/602.md index b114de7e317..e4a072d5f0d 100644 --- a/Release Notes/602.md +++ b/Release Notes/602.md @@ -14,6 +14,18 @@ - Migration steps: Replace uses of `ExpandEditorPlaceholdersToTrailingClosures` with `ExpandEditorPlaceholdersToLiteralClosures`. The initializer does not need to change: `.init(indentationWidth:)` on the new type provides the same behavior as the old type. - Notes: This improves code completion in a SourceKitLSP session where the trailing closure form may be undesirable. The nested placeholders offer more flexibility to end users, in editors that support it. +- `SyntaxArena` and `ParsingSyntaxArena` has changed to SPI + - Description: `SyntaxArena` and the subclasses were only meant to be used when dealing with `RawSyntax` which is also SPI. + - Pull Request: https://github.com/swiftlang/swift-syntax/pull/2930 + - Migration steps: Do not use `SyntaxArena` or `ParsingSyntaxArena` directly. + - Notes: Although the type itself was `public`, most initializers were already SPI and there was no way to retrive them from existing types via public API. + +- `SyntaxChildrenIndex` is no longer `ExpressibleByNilLiteral` + - Description: `nil` used to represent the end index. However, due to a change in the internal structure, the end index must now be retrieved from the collection. + - Pull Request: https://github.com/swiftlang/swift-syntax/pull/2925 + - Migration steps: Use `SyntaxChildren.endIndex` instead. + - Notes: `ExpressibleByNilLiteral` was a mistake. In general, `Collection.Index` should only be created and managed by the collection itself. For example, `Collection.index(after:)` exists, but `Index.advanced(by:)` does not. + ## Template - *Affected API or two word description* diff --git a/Sources/SwiftOperators/OperatorTable+Folding.swift b/Sources/SwiftOperators/OperatorTable+Folding.swift index 121e5f6146f..06371c9f50a 100644 --- a/Sources/SwiftOperators/OperatorTable+Folding.swift +++ b/Sources/SwiftOperators/OperatorTable+Folding.swift @@ -11,9 +11,9 @@ //===----------------------------------------------------------------------===// #if compiler(>=6) -public import SwiftSyntax +@_spi(ExperimentalLanguageFeatures) public import SwiftSyntax #else -import SwiftSyntax +@_spi(ExperimentalLanguageFeatures) import SwiftSyntax #endif extension ExprSyntax { @@ -104,8 +104,8 @@ extension OperatorTable { op: ExprSyntax, rhs: ExprSyntax ) -> ExprSyntax { - // If the left-hand side is a "try" or "await", hoist it up to encompass - // the right-hand side as well. + // If the left-hand side is a "try", "await", or "unsafe", hoist it up to + // encompass the right-hand side as well. if let tryExpr = lhs.as(TryExprSyntax.self) { return ExprSyntax( TryExprSyntax( @@ -138,6 +138,24 @@ extension OperatorTable { ) } + if let unsafeExpr = lhs.as(UnsafeExprSyntax.self) { + return ExprSyntax( + UnsafeExprSyntax( + leadingTrivia: unsafeExpr.leadingTrivia, + unsafeExpr.unexpectedBeforeUnsafeKeyword, + unsafeKeyword: unsafeExpr.unsafeKeyword, + unsafeExpr.unexpectedBetweenUnsafeKeywordAndExpression, + expression: makeBinaryOperationExpr( + lhs: unsafeExpr.expression, + op: op, + rhs: rhs + ), + unsafeExpr.unexpectedAfterExpression, + trailingTrivia: unsafeExpr.trailingTrivia + ) + ) + } + // The form of the binary operation depends on the operator itself, // which will be one of the unresolved infix operators. diff --git a/Sources/SwiftParser/Attributes.swift b/Sources/SwiftParser/Attributes.swift index b042dc6a78b..65ea4646ff4 100644 --- a/Sources/SwiftParser/Attributes.swift +++ b/Sources/SwiftParser/Attributes.swift @@ -650,7 +650,12 @@ extension Parser { let unexpectedBeforeAccessor: RawUnexpectedNodesSyntax? let accessor: RawTokenSyntax? if period != nil { - (unexpectedBeforeAccessor, accessor) = self.expect(.keyword(.get), .keyword(.set), default: .keyword(.get)) + (unexpectedBeforeAccessor, accessor) = self.expect( + .keyword(.get), + .keyword(.set), + .keyword(._modify), + default: .keyword(.get) + ) } else { (unexpectedBeforeAccessor, accessor) = (nil, nil) } diff --git a/Sources/SwiftParser/Expressions.swift b/Sources/SwiftParser/Expressions.swift index 620912bcaea..bf1c9fba52d 100644 --- a/Sources/SwiftParser/Expressions.swift +++ b/Sources/SwiftParser/Expressions.swift @@ -444,6 +444,19 @@ extension Parser { arena: self.arena ) ) + case (.unsafe, let handle)?: + let unsafeTok = self.eat(handle) + let sub = self.parseSequenceExpressionElement( + flavor: flavor, + pattern: pattern + ) + return RawExprSyntax( + RawUnsafeExprSyntax( + unsafeKeyword: unsafeTok, + expression: sub, + arena: self.arena + ) + ) case (._move, let handle)?: let moveKeyword = self.eat(handle) let sub = self.parseSequenceExpressionElement( diff --git a/Sources/SwiftParser/Parser.swift b/Sources/SwiftParser/Parser.swift index 89d21612a18..2f2205d6dbf 100644 --- a/Sources/SwiftParser/Parser.swift +++ b/Sources/SwiftParser/Parser.swift @@ -303,15 +303,10 @@ public struct Parser { /// if this is `nil`. /// - parseTransition: The previously recorded state for an incremental /// parse, or `nil`. - /// - arena: Arena the parsing syntax are made into. If it's `nil`, a new - /// arena is created automatically, and `input` copied into the - /// arena. If non-`nil`, `input` must be within its registered - /// source buffer or allocator. public init( _ input: UnsafeBufferPointer, maximumNestingLevel: Int? = nil, parseTransition: IncrementalParseTransition? = nil, - arena: ParsingSyntaxArena? = nil, swiftVersion: SwiftVersion? = nil ) { // Chain to the private buffer initializer. @@ -319,7 +314,7 @@ public struct Parser { buffer: input, maximumNestingLevel: maximumNestingLevel, parseTransition: parseTransition, - arena: arena, + arena: nil, swiftVersion: swiftVersion, experimentalFeatures: [] ) diff --git a/Sources/SwiftParser/TokenSpecSet.swift b/Sources/SwiftParser/TokenSpecSet.swift index e3eb5126ffa..2806c15440d 100644 --- a/Sources/SwiftParser/TokenSpecSet.swift +++ b/Sources/SwiftParser/TokenSpecSet.swift @@ -700,6 +700,7 @@ enum ExpressionModifierKeyword: TokenSpecSet { case `repeat` case each case any + case unsafe init?(lexeme: Lexer.Lexeme, experimentalFeatures: Parser.ExperimentalFeatures) { switch PrepareForKeywordMatch(lexeme) { @@ -713,6 +714,7 @@ enum ExpressionModifierKeyword: TokenSpecSet { case TokenSpec(.repeat): self = .repeat case TokenSpec(.each): self = .each case TokenSpec(.any): self = .any + case TokenSpec(.unsafe) where experimentalFeatures.contains(.unsafeExpression): self = .unsafe default: return nil } } @@ -729,6 +731,7 @@ enum ExpressionModifierKeyword: TokenSpecSet { case .repeat: return .keyword(.repeat) case .each: return .keyword(.each) case .any: return .keyword(.any) + case .unsafe: return .keyword(.unsafe) } } } diff --git a/Sources/SwiftParser/generated/ExperimentalFeatures.swift b/Sources/SwiftParser/generated/ExperimentalFeatures.swift index c999428f365..a5a8e2c21d1 100644 --- a/Sources/SwiftParser/generated/ExperimentalFeatures.swift +++ b/Sources/SwiftParser/generated/ExperimentalFeatures.swift @@ -48,6 +48,9 @@ extension Parser.ExperimentalFeatures { /// Whether to enable the parsing of @abi attribute. public static let abiAttribute = Self (rawValue: 1 << 7) + /// Whether to enable the parsing of 'unsafe' expression. + public static let unsafeExpression = Self (rawValue: 1 << 8) + /// 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) { @@ -68,6 +71,8 @@ extension Parser.ExperimentalFeatures { self = .valueGenerics case "ABIAttribute": self = .abiAttribute + case "WarnUnsafe": + self = .unsafeExpression default: return nil } diff --git a/Sources/SwiftParser/generated/Parser+TokenSpecSet.swift b/Sources/SwiftParser/generated/Parser+TokenSpecSet.swift index 0b4ac402f2e..a36fe84b9df 100644 --- a/Sources/SwiftParser/generated/Parser+TokenSpecSet.swift +++ b/Sources/SwiftParser/generated/Parser+TokenSpecSet.swift @@ -1374,6 +1374,7 @@ extension DerivativeAttributeArgumentsSyntax { public enum AccessorSpecifierOptions: TokenSpecSet { case get case set + case _modify init?(lexeme: Lexer.Lexeme, experimentalFeatures: Parser.ExperimentalFeatures) { switch PrepareForKeywordMatch(lexeme) { @@ -1381,6 +1382,8 @@ extension DerivativeAttributeArgumentsSyntax { self = .get case TokenSpec(.set): self = .set + case TokenSpec(._modify): + self = ._modify default: return nil } @@ -1392,6 +1395,8 @@ extension DerivativeAttributeArgumentsSyntax { self = .get case TokenSpec(.set): self = .set + case TokenSpec(._modify): + self = ._modify default: return nil } @@ -1403,6 +1408,8 @@ extension DerivativeAttributeArgumentsSyntax { return .keyword(.get) case .set: return .keyword(.set) + case ._modify: + return .keyword(._modify) } } @@ -1416,6 +1423,8 @@ extension DerivativeAttributeArgumentsSyntax { return .keyword(.get) case .set: return .keyword(.set) + case ._modify: + return .keyword(._modify) } } } diff --git a/Sources/SwiftParserDiagnostics/generated/SyntaxKindNameForDiagnostics.swift b/Sources/SwiftParserDiagnostics/generated/SyntaxKindNameForDiagnostics.swift index 094815a0d2d..3bab4943b4f 100644 --- a/Sources/SwiftParserDiagnostics/generated/SyntaxKindNameForDiagnostics.swift +++ b/Sources/SwiftParserDiagnostics/generated/SyntaxKindNameForDiagnostics.swift @@ -399,6 +399,8 @@ extension SyntaxKind { return "'is'" case .unresolvedTernaryExpr: return "ternary operator" + case .unsafeExpr: + return "'unsafe' expression" case .valueBindingPattern: return "value binding pattern" case .variableDecl: diff --git a/Sources/SwiftSyntax/AbsoluteRawSyntax.swift b/Sources/SwiftSyntax/AbsoluteRawSyntax.swift deleted file mode 100644 index a3f5aec25e7..00000000000 --- a/Sources/SwiftSyntax/AbsoluteRawSyntax.swift +++ /dev/null @@ -1,47 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 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 -// -//===----------------------------------------------------------------------===// - -struct AbsoluteRawSyntax: Sendable { - let raw: RawSyntax - let info: AbsoluteSyntaxInfo - - /// Returns first `present` child. - func firstChild(viewMode: SyntaxTreeViewMode) -> AbsoluteRawSyntax? { - guard let layoutView = raw.layoutView else { return nil } - var curInfo = info.advancedToFirstChild() - for childOpt in layoutView.children { - if let child = childOpt, viewMode.shouldTraverse(node: child) { - return AbsoluteRawSyntax(raw: child, info: curInfo) - } - curInfo = curInfo.advancedBySibling(childOpt) - } - return nil - } - - /// Returns next `present` sibling. - func nextSibling(parent: AbsoluteRawSyntax, viewMode: SyntaxTreeViewMode) -> AbsoluteRawSyntax? { - var curInfo = info.advancedBySibling(raw) - for siblingOpt in parent.raw.layoutView!.children.dropFirst(Int(info.indexInParent + 1)) { - if let sibling = siblingOpt, viewMode.shouldTraverse(node: sibling) { - return AbsoluteRawSyntax(raw: sibling, info: curInfo) - } - curInfo = curInfo.advancedBySibling(siblingOpt) - } - return nil - } - - func replacingSelf(_ newRaw: RawSyntax, newRootId: UInt) -> AbsoluteRawSyntax { - let nodeId = SyntaxIdentifier(rootId: newRootId, indexInTree: info.nodeId.indexInTree) - let newInfo = AbsoluteSyntaxInfo(position: info.position, nodeId: nodeId) - return .init(raw: newRaw, info: newInfo) - } -} diff --git a/Sources/SwiftSyntax/AbsoluteSyntaxInfo.swift b/Sources/SwiftSyntax/AbsoluteSyntaxInfo.swift index 0d281f63a8a..8f21dfd9d11 100644 --- a/Sources/SwiftSyntax/AbsoluteSyntaxInfo.swift +++ b/Sources/SwiftSyntax/AbsoluteSyntaxInfo.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2024 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 @@ -10,49 +10,52 @@ // //===----------------------------------------------------------------------===// -struct AbsoluteSyntaxPosition: Sendable { - /// The UTF-8 offset of the syntax node in the source file - let offset: UInt32 - let indexInParent: UInt32 - - func advancedBySibling(_ raw: RawSyntax?) -> AbsoluteSyntaxPosition { - let newOffset = self.offset + UInt32(truncatingIfNeeded: raw?.totalLength.utf8Length ?? 0) - let newIndexInParent = self.indexInParent + 1 - return .init(offset: newOffset, indexInParent: newIndexInParent) - } - - func advancedToFirstChild() -> AbsoluteSyntaxPosition { - return .init(offset: self.offset, indexInParent: 0) - } - - static var forRoot: AbsoluteSyntaxPosition { - return .init(offset: 0, indexInParent: 0) - } -} - /// `AbsoluteSyntaxInfo` represents the information that relates a `RawSyntax` /// to a source file tree, like its absolute source offset. struct AbsoluteSyntaxInfo: Sendable { - let position: AbsoluteSyntaxPosition - let nodeId: SyntaxIdentifier + /// The UTF-8 offset at which the syntax node’s leading trivia start in the source file. + let offset: UInt32 + + /// Index in parent's layout. Note that this counts `nil` children. + let layoutIndexInParent: UInt32 - /// The UTF-8 offset of the syntax node in the source file - var offset: UInt32 { return position.offset } - var indexInParent: UInt32 { return position.indexInParent } + /// Index of the node when traversing the syntax tree using a depth-first traversal. + /// This skips `nil` children in the parent's layout. + let indexInTree: UInt32 func advancedBySibling(_ raw: RawSyntax?) -> AbsoluteSyntaxInfo { - let newPosition = position.advancedBySibling(raw) - let newNodeId = nodeId.advancedBySibling(raw) - return .init(position: newPosition, nodeId: newNodeId) + if let raw { + // '&+' operations are safe because we have the preconditions in 'forRoot(_:)'. + return AbsoluteSyntaxInfo( + offset: offset &+ UInt32(truncatingIfNeeded: raw.totalLength.utf8Length), + layoutIndexInParent: layoutIndexInParent &+ 1, + indexInTree: indexInTree &+ UInt32(truncatingIfNeeded: raw.totalNodes) + ) + } else { + return AbsoluteSyntaxInfo( + offset: offset, + layoutIndexInParent: layoutIndexInParent &+ 1, + indexInTree: indexInTree + ) + } } func advancedToFirstChild() -> AbsoluteSyntaxInfo { - let newPosition = position.advancedToFirstChild() - let newNodeId = nodeId.advancedToFirstChild() - return .init(position: newPosition, nodeId: newNodeId) + return AbsoluteSyntaxInfo( + offset: offset, + layoutIndexInParent: 0, + indexInTree: indexInTree &+ 1 + ) } static func forRoot(_ raw: RawSyntax) -> AbsoluteSyntaxInfo { - return .init(position: .forRoot, nodeId: .forRoot(raw)) + // These checks ensure the safety of the unchecked arithmetic operations in 'advancedBySibling(_:)'. + precondition(raw.totalLength.utf8Length <= UInt32.max, "too long") + precondition(raw.totalNodes <= UInt32.max, "too many nodes") + return AbsoluteSyntaxInfo( + offset: 0, + layoutIndexInParent: 0, + indexInTree: 0 + ) } } diff --git a/Sources/SwiftSyntax/CMakeLists.txt b/Sources/SwiftSyntax/CMakeLists.txt index dd77da30625..8001de25c4e 100644 --- a/Sources/SwiftSyntax/CMakeLists.txt +++ b/Sources/SwiftSyntax/CMakeLists.txt @@ -8,7 +8,6 @@ add_swift_syntax_library(SwiftSyntax AbsolutePosition.swift - AbsoluteRawSyntax.swift AbsoluteSyntaxInfo.swift Assert.swift BumpPtrAllocator.swift @@ -31,7 +30,6 @@ add_swift_syntax_library(SwiftSyntax SyntaxCollection.swift SyntaxHashable.swift SyntaxIdentifier.swift - SyntaxNodeFactory.swift SyntaxNodeStructure.swift SyntaxProtocol.swift SyntaxText.swift diff --git a/Sources/SwiftSyntax/Documentation.docc/Glossary.md b/Sources/SwiftSyntax/Documentation.docc/Glossary.md index 362d7878b0b..9d7a8a335a0 100644 --- a/Sources/SwiftSyntax/Documentation.docc/Glossary.md +++ b/Sources/SwiftSyntax/Documentation.docc/Glossary.md @@ -7,8 +7,6 @@ Glossary of terms and abbreviations used in SwiftSyntax To avoid ongoing repetition of common long terms, SwiftSyntax uses a couple of abbreviations that are common in compiler projects. -**Arena** See ``SyntaxArena`` - **Decl** Abbreviation for *Declaration* **Expr** Abbreviation for *Expression* diff --git a/Sources/SwiftSyntax/Documentation.docc/generated/SwiftSyntax.md b/Sources/SwiftSyntax/Documentation.docc/generated/SwiftSyntax.md index 10a71a658da..7e45d4c8dd1 100644 --- a/Sources/SwiftSyntax/Documentation.docc/generated/SwiftSyntax.md +++ b/Sources/SwiftSyntax/Documentation.docc/generated/SwiftSyntax.md @@ -411,7 +411,6 @@ allows Swift tools to parse, inspect, generate, and transform Swift source code. ### Internals - -- - - - diff --git a/Sources/SwiftSyntax/MemoryLayout.swift b/Sources/SwiftSyntax/MemoryLayout.swift index e4666e52dd1..89e35bc1109 100644 --- a/Sources/SwiftSyntax/MemoryLayout.swift +++ b/Sources/SwiftSyntax/MemoryLayout.swift @@ -13,9 +13,9 @@ // See `MemoryLayoutTest.swift`. @_spi(Testing) public enum SyntaxMemoryLayout: Sendable { public struct Value: Equatable, Sendable { - var size: Int - var stride: Int - var alignment: Int + public let size: Int + public let stride: Int + public let alignment: Int public init(size: Int, stride: Int, alignment: Int) { self.size = size diff --git a/Sources/SwiftSyntax/MissingNodeInitializers.swift b/Sources/SwiftSyntax/MissingNodeInitializers.swift index 09aa58c1a51..89507878422 100644 --- a/Sources/SwiftSyntax/MissingNodeInitializers.swift +++ b/Sources/SwiftSyntax/MissingNodeInitializers.swift @@ -13,8 +13,7 @@ extension MissingDeclSyntax { public init( attributes: AttributeListSyntax, - modifiers: DeclModifierListSyntax, - arena: __shared SyntaxArena + modifiers: DeclModifierListSyntax ) { self.init( attributes: attributes, diff --git a/Sources/SwiftSyntax/Raw/RawSyntax.swift b/Sources/SwiftSyntax/Raw/RawSyntax.swift index d271f5ea7bc..77db819d449 100644 --- a/Sources/SwiftSyntax/Raw/RawSyntax.swift +++ b/Sources/SwiftSyntax/Raw/RawSyntax.swift @@ -186,10 +186,10 @@ extension RawSyntaxData.ParsedToken { extension RawSyntaxData.MaterializedToken { var leadingTrivia: RawTriviaPieceBuffer { - triviaPieces[..=6) +@_implementationOnly private import _SwiftSyntaxCShims +#else +@_implementationOnly import _SwiftSyntaxCShims +#endif + +// `Syntax` is a user facing tree wrapping `RawSyntax` tree. A value is a pair +// of strong reference to the `SyntaxDataArena` and a pointer to the `SyntaxData` +// allocated in the arena. +// The arena is shared between the all node in the tree, but only in the tree. +// Whenever the tree is modified, a new arena is created and it forms a new "tree". + /// A Syntax node represents a tree of nodes with tokens at the leaves. /// Each node has accessors for its known children, and allows efficient /// iteration over the children through its `children` property. public struct Syntax: SyntaxProtocol, SyntaxHashable { - /// We need a heap indirection to store a syntax node's parent. We could use an indirect enum here but explicitly - /// modelling it using a class allows us to re-use these heap-allocated objects in `SyntaxVisitor`. - /// - /// - Note: `@unchecked Sendable` because `info` is mutable. In Swift 6 and above the variable can be declared as - /// `nonisolated(unsafe)` but that attribute doesn't exist in previous Swift versions and a checked Sendable - /// conformance generates a warning. - final class Info: @unchecked Sendable { - // For root node. - struct Root: Sendable { - private var arena: RetainedSyntaxArena - - init(arena: RetainedSyntaxArena) { - self.arena = arena - } - } - - // For non-root nodes. - struct NonRoot: Sendable { - var parent: Syntax - var absoluteInfo: AbsoluteSyntaxInfo - } + let arena: SyntaxDataArena + let dataRef: SyntaxDataReference - enum InfoImpl: Sendable { - case root(Root) - case nonRoot(NonRoot) - } - - init(_ info: InfoImpl) { - self.info = info - } - - /// The actual stored information that references the parent or the tree's root. - /// - /// - Important: Must only be set to `nil` when `Syntax.Info` is used in a memory recycling pool - /// (eg. in `SyntaxVisitor`). In that case the `Syntax.Info` is considered garbage memory that can be re-used - /// later. `info` needs to be set to a real value when `Syntax.Info` is recycled from the memory recycling pool. - #if compiler(>=6.0) - nonisolated(unsafe) var info: InfoImpl! - #else - var info: InfoImpl! - #endif + /// "designated" memberwise initializer of `Syntax`. + @_transparent // Inline early to enable certain optimzation. + init(arena: __shared SyntaxDataArena, dataRef: SyntaxDataReference) { + self.arena = arena + self.dataRef = dataRef } - /// Reference to the node's parent or, if this node is the root of a tree, a reference to the `SyntaxArena` to keep - /// the syntax tree alive. - /// - /// - Important: In almost all use cases you should not access this directly. Prefer accessors like `parent`. - /// - Important: Must only be set to `nil` when this `Syntax` node is known to get destroyed and the `Info` should be - /// stored in a memory recycling pool (eg. in `SyntaxVisitor`). After setting `info` to `nil`, this `Syntax` node - /// is considered garbage and should not be accessed anymore in any way. - var info: Info! - let raw: RawSyntax - - private var rootInfo: Info.Root { - switch info.info! { - case .root(let info): return info - case .nonRoot(let info): return info.parent.rootInfo - } + var data: SyntaxData { + @_transparent unsafeAddress { dataRef.pointer } } - private var nonRootInfo: Info.NonRoot? { - switch info.info! { - case .root(_): return nil - case .nonRoot(let info): return info - } + @inline(__always) + var raw: RawSyntax { + data.raw } - public var root: Syntax { - return self.withUnownedSyntax { - var node = $0 - while let parent = node.parent { - node = parent - } - return node.value - } + @inline(__always) + public var root: Self { + return Self(arena: arena, dataRef: arena.root) } + @inline(__always) public var parent: Syntax? { - nonRootInfo?.parent + guard let parentDataRef = data.parent else { + return nil + } + return Syntax(arena: arena, dataRef: parentDataRef) } - var absoluteInfo: AbsoluteSyntaxInfo { - nonRootInfo?.absoluteInfo ?? .forRoot(raw) + @inline(__always) + public var hasParent: Bool { + data.parent != nil } - var absoluteRaw: AbsoluteRawSyntax { - AbsoluteRawSyntax(raw: raw, info: absoluteInfo) + var absoluteInfo: AbsoluteSyntaxInfo { + data.absoluteInfo } - var indexInParent: Int { - Int(absoluteInfo.indexInParent) + /// Index in parent's layout. `nil` slots are counted. + var layoutIndexInParent: Int { + Int(absoluteInfo.layoutIndexInParent) } public var id: SyntaxIdentifier { - absoluteInfo.nodeId + // This var is a workaround for a potential compiler bug (rdar://141977987) + let rootDataRef = arena.root + return SyntaxIdentifier( + rootId: rootDataRef.pointee.raw.id, + indexInTree: SyntaxIdentifier.SyntaxIndexInTree(indexInTree: absoluteInfo.indexInTree) + ) } /// The position of the start of this node's leading trivia @@ -131,27 +101,6 @@ public struct Syntax: SyntaxProtocol, SyntaxHashable { position + raw.totalLength } - /// "designated" memberwise initializer of `Syntax`. - // transparent because normal inlining is too late for eliminating ARC traffic for Info. - // FIXME: Remove @_transparent after OSSA enabled. - @_transparent - init(_ raw: RawSyntax, info: __shared Info) { - self.raw = raw - self.info = info - } - - init(_ raw: RawSyntax, parent: Syntax, absoluteInfo: AbsoluteSyntaxInfo) { - self.init(raw, info: Info(.nonRoot(.init(parent: parent, absoluteInfo: absoluteInfo)))) - } - - /// Creates a `Syntax` with the provided raw syntax and parent. - /// - Parameters: - /// - absoluteRaw: The underlying `AbsoluteRawSyntax` of this node. - /// - parent: The parent of this node, or `nil` if this node is the root. - init(_ absoluteRaw: AbsoluteRawSyntax, parent: Syntax) { - self.init(absoluteRaw.raw, parent: parent, absoluteInfo: absoluteRaw.info) - } - /// Creates a ``Syntax`` for a root raw node. /// /// - Parameters: @@ -161,30 +110,32 @@ public struct Syntax: SyntaxProtocol, SyntaxHashable { /// has a chance to retain it. static func forRoot(_ raw: RawSyntax, rawNodeArena: RetainedSyntaxArena) -> Syntax { precondition(rawNodeArena == raw.arenaReference) - return Syntax(raw, info: Info(.root(.init(arena: rawNodeArena)))) + let arena = SyntaxDataArena(raw: raw, rawNodeArena: rawNodeArena) + return Self(arena: arena, dataRef: arena.root) } static func forRoot(_ raw: RawSyntax, rawNodeArena: SyntaxArena) -> Syntax { - precondition(rawNodeArena == raw.arenaReference) - return Syntax(raw, info: Info(.root(.init(arena: RetainedSyntaxArena(rawNodeArena))))) + return forRoot(raw, rawNodeArena: RetainedSyntaxArena(rawNodeArena)) + } + + /// References to the children data. + /// + /// - Note: The buffer is managed by the arena, and thus only valid while the arena is alive. + var layoutBuffer: SyntaxDataReferenceBuffer { + self.arena.layout(for: self.dataRef) } - /// Returns the child data at the provided index in this data's layout. - /// - Note: This has O(n) performance, prefer using a proper Sequence type - /// if applicable, instead of this. - /// - Note: This function traps if the index is out of the bounds of the - /// data's layout. + /// Returns the child node at the provided index in this node's layout. + /// - Note: This function traps if the node is a token, or the index is out of the bounds of the layout. /// /// - Parameter index: The index to create and cache. - /// - Parameter parent: The parent to associate the child with. This is - /// normally the Syntax node that this `Syntax` belongs to. - /// - Returns: The child's data at the provided index. + /// - Returns: The child's node at the provided index. func child(at index: Int) -> Syntax? { - if raw.layoutView!.children[index] == nil { return nil } - var iter = RawSyntaxChildren(absoluteRaw).makeIterator() - for _ in 0..(mapping map: (Syntax) -> T?) -> T? { + var dataRef = self.dataRef + while true { + if let mapped = map(Syntax(arena: self.arena, dataRef: dataRef)) { + return mapped + } + guard let parent = dataRef.pointee.parent else { + return nil + } + dataRef = parent + } + } + /// Needed for the conformance to ``SyntaxProtocol``. Just returns `self`. public var _syntaxNode: Syntax { return self @@ -372,56 +334,184 @@ extension Syntax { } } -/// Temporary non-owning Syntax. +typealias SyntaxDataReference = SyntaxArenaAllocatedPointer +typealias SyntaxDataReferenceBuffer = SyntaxArenaAllocatedBufferPointer + +/// Node data for a `Syntax`, allocated and managed by `SyntaxDataArena`. /// -/// This can be used for handling Syntax node without ARC traffic. -struct UnownedSyntax { - private let raw: RawSyntax - private let info: Unmanaged +/// - Note: This type must be trivial as it is allocated by ‘BumpPtrAllocator’. +struct SyntaxData: Sendable { + /// Underlying node of the `Syntax` node. + let raw: RawSyntax + /// Reference to the parent data. Or `nil` if this is the root. + let parent: SyntaxDataReference? + /// Index and position info in the tree. + let absoluteInfo: AbsoluteSyntaxInfo + /// Length of the children layout. + /// This is a cached information, equals to `raw.layoutView?.children.count ?? 0`. + /// This 4 bytes fits nicely after the 12 bytes `absoluteInfo`. + let childCount: UInt32 + + // If the childCount > 0, a pointer to the layout buffer (`UnsafePointer?`) is tail allocated. +} - @_transparent - init(_ node: __shared Syntax) { - self.raw = node.raw - self.info = .passUnretained(node.info.unsafelyUnwrapped) +/// `SyntaxDataArena` manages the entire data of a `Syntax` tree. +final class SyntaxDataArena: @unchecked Sendable { + /// Mutex for locking the data when populating layout buffers. + private let mutex: PlatformMutex + + /// Allocator. + private let allocator: BumpPtrAllocator + + /// Retaining reference to the arena of the _root_ ``RawSyntax`` + private let rawArena: RetainedSyntaxArena + + /// Root node. + let root: SyntaxDataReference + + init(raw: RawSyntax, rawNodeArena: RetainedSyntaxArena) { + precondition(rawNodeArena == raw.arenaReference) + + self.mutex = PlatformMutex.create() + self.allocator = BumpPtrAllocator(initialSlabSize: Self.slabSize(for: raw)) + self.rawArena = rawNodeArena + self.root = Self.createDataImpl(allocator: allocator, raw: raw, parent: nil, absoluteInfo: .forRoot(raw)) } - /// Extract the Syntax value. - @inline(__always) - var value: Syntax { - Syntax(raw, info: info.takeUnretainedValue()) + deinit { + // Debug print for re-evaluating `slabSize(for:)` + // print("nodeCount: \(root.pointee.raw.totalNodes), slabSize: \(Self.slabSize(for: root.pointee.raw)), allocated: \(allocator.totalByteSizeAllocated), overflowed: \(Self.slabSize(for: root.pointee.raw) < allocator.totalByteSizeAllocated)") + self.mutex.destroy() } - /// Get the parent of the Syntax value, but without retaining it. - @inline(__always) - var parent: UnownedSyntax? { - return info._withUnsafeGuaranteedRef { - switch $0.info.unsafelyUnwrapped { - case .nonRoot(let info): - return UnownedSyntax(info.parent) - case .root(_): - return nil - } + /// Return the childen data of the given node. + /// + /// The layout buffer is created and cached when it's first accessed. + func layout(for parent: SyntaxDataReference) -> SyntaxDataReferenceBuffer { + let childCount = Int(truncatingIfNeeded: parent.pointee.childCount) + + // Return empty buffer for the node with no children. + guard childCount != 0 else { + return SyntaxDataReferenceBuffer() } + + // The storage of the buffer address is allocated next to the SyntaxData. + let baseAddressRef = parent.advanced(by: 1) + .unsafeRawPointer + .assumingMemoryBound(to: AtomicPointer.self) + + // If the buffer is already populated, return it. + if let baseAddress = swiftsyntax_atomic_pointer_get(baseAddressRef)?.assumingMemoryBound( + to: SyntaxDataReference?.self + ) { + return SyntaxDataReferenceBuffer(UnsafeBufferPointer(start: baseAddress, count: childCount)) + } + + mutex.lock() + defer { mutex.unlock() } + + // Recheck, maybe some other thread has populated the buffer during acquiring the lock. + if let baseAddress = swiftsyntax_atomic_pointer_get(baseAddressRef)?.assumingMemoryBound( + to: SyntaxDataReference?.self + ) { + return SyntaxDataReferenceBuffer(UnsafeBufferPointer(start: baseAddress, count: childCount)) + } + + let buffer = createLayoutDataImpl(parent) + assert(buffer.count == childCount, "childCount doesn't match with RawSyntax layout length?") + // Remeber the base address of the created buffer. + swiftsyntax_atomic_pointer_set( + UnsafeMutablePointer(mutating: baseAddressRef), + UnsafeRawPointer(buffer.baseAddress) + ) + + return SyntaxDataReferenceBuffer(buffer) } - /// Temporarily use the Syntax value. - @inline(__always) - func withValue(_ body: (Syntax) -> T) -> T { - info._withUnsafeGuaranteedRef { - body(Syntax(self.raw, info: $0)) + /// Create the layout buffer of the node. + private func createLayoutDataImpl(_ parent: SyntaxDataReference) -> UnsafeBufferPointer { + let rawChildren = parent.pointee.raw.layoutView!.children + let allocated = self.allocator.allocate(SyntaxDataReference?.self, count: rawChildren.count) + + var ptr = allocated.baseAddress! + var absoluteInfo = parent.pointee.absoluteInfo.advancedToFirstChild() + for raw in rawChildren { + let dataRef: SyntaxDataReference? + if let raw { + dataRef = Self.createDataImpl(allocator: self.allocator, raw: raw, parent: parent, absoluteInfo: absoluteInfo) + } else { + dataRef = nil + } + ptr.initialize(to: dataRef) + absoluteInfo = absoluteInfo.advancedBySibling(raw) + ptr += 1 } + return UnsafeBufferPointer(allocated) } -} -extension SyntaxProtocol { - /// Execute the `body` with ``UnownedSyntax`` of `node`. + /// Calculate the recommended slab size of `BumpPtrAllocator`. /// - /// This guarantees the life time of the `node` during the `body` is executed. - @inline(__always) - func withUnownedSyntax(_ body: (UnownedSyntax) -> T) -> T { - return withExtendedLifetime(self) { - body(UnownedSyntax(Syntax($0))) + /// Estimate the total allocation size assuming the client visits every node in + /// the tree. Return the estimated size, or 4096 if it's larger than 4096. + /// + /// Each node consumes `SyntaxData` size at least. Non-empty layout node tail + /// allocates a pointer storage for the base address of the layout buffer. + /// + /// For layout buffers, each child element consumes a `SyntaxDataReference` in + /// the parent's layout. But non-collection layout nodes, the layout is usually + /// sparse, so we can't calculate the exact memory size until we see the RawSyntax. + /// That being said, `SytnaxData` + 4 pointer size looks like an enough estimation. + private static func slabSize(for raw: RawSyntax) -> Int { + let dataSize = MemoryLayout.stride + let pointerSize = MemoryLayout.stride + + let nodeCount = raw.totalNodes + assert(nodeCount != 0, "The tree needs to contain at least the root node") + let totalSize = dataSize + (dataSize + pointerSize * 4) * (nodeCount &- 1) + // Power of 2 might look nicer, but 'BumpPtrAllocator' doesn't require that. + return min(totalSize, 4096) + } + + /// Allocate and initialize `SyntaxData` with the trailing pointer storage for the references to the children. + private static func createDataImpl( + allocator: BumpPtrAllocator, + raw: RawSyntax, + parent: SyntaxDataReference?, + absoluteInfo: AbsoluteSyntaxInfo + ) -> SyntaxDataReference { + let childCount = raw.layoutView?.children.count ?? 0 + + // Allocate 'SyntaxData' + storage for the reference to the children data. + // NOTE: If you change the memory layout, revisit 'slabSize(for:)' too. + var totalSize = MemoryLayout.stride + if childCount != 0 { + // Tail allocate the storage for the pointer to the lazily allocated layout data. + totalSize &+= MemoryLayout.size } + let alignment = MemoryLayout.alignment + let allocated = allocator.allocate(byteCount: totalSize, alignment: alignment).baseAddress! + + // Initialize the data. + let dataRef = allocated.bindMemory(to: SyntaxData.self, capacity: 1) + dataRef.initialize( + to: SyntaxData( + raw: raw, + parent: parent, + absoluteInfo: absoluteInfo, + childCount: UInt32(truncatingIfNeeded: childCount) + ) + ) + + if childCount != 0 { + // Initialize the tail allocated storage with 'nil'. + let layoutBufferBaseAddressRef = + allocated + .advanced(by: MemoryLayout.stride) + .bindMemory(to: AtomicPointer.self, capacity: 1) + swiftsyntax_atomic_pointer_set(layoutBufferBaseAddressRef, nil) + } + + return SyntaxDataReference(UnsafePointer(dataRef)) } } @@ -433,7 +523,8 @@ public struct SyntaxNode {} /// See `SyntaxMemoryLayout`. let SyntaxMemoryLayouts: [String: SyntaxMemoryLayout.Value] = [ "Syntax": .init(Syntax.self), - "Syntax.Info": .init(Syntax.Info.self), - "Syntax.Info.Root": .init(Syntax.Info.Root.self), - "Syntax.Info.NonRoot": .init(Syntax.Info.NonRoot.self), + "SyntaxData": .init(SyntaxData.self), + "AbsoluteSyntaxInfo": .init(AbsoluteSyntaxInfo.self), + "SyntaxDataReference?": .init(SyntaxDataReference?.self), + "AtomicPointer": .init(AtomicPointer.self), ] diff --git a/Sources/SwiftSyntax/SyntaxArena.swift b/Sources/SwiftSyntax/SyntaxArena.swift index 8021819475c..6e2550adaeb 100644 --- a/Sources/SwiftSyntax/SyntaxArena.swift +++ b/Sources/SwiftSyntax/SyntaxArena.swift @@ -43,6 +43,7 @@ /// As an added benefit of the ``SyntaxArena``, `RawSyntax` nodes don’t need to /// be reference-counted, further improving the performance of ``SwiftSyntax`` /// when worked with at that level. +@_spi(RawSyntax) public class SyntaxArena { /// Bump-pointer allocator for all "intern" methods. fileprivate let allocator: BumpPtrAllocator @@ -105,7 +106,6 @@ public class SyntaxArena { /// Copies the contents of a ``SyntaxText`` to the memory this arena manages, /// and return the ``SyntaxText`` in the destination. - @_spi(RawSyntax) public func intern(_ value: SyntaxText) -> SyntaxText { // Return the passed-in value if it's already managed by this arena. if self.contains(text: value) { @@ -119,7 +119,6 @@ public class SyntaxArena { /// Copies a UTF8 sequence of `String` to the memory this arena manages, and /// returns the copied string as a ``SyntaxText`` - @_spi(RawSyntax) public func intern(_ value: String) -> SyntaxText { if value.isEmpty { return SyntaxText() } var value = value @@ -173,8 +172,8 @@ public class SyntaxArena { } /// SyntaxArena for parsing. +@_spi(RawSyntax) public class ParsingSyntaxArena: SyntaxArena { - @_spi(RawSyntax) public typealias ParseTriviaFunction = (_ source: SyntaxText, _ position: TriviaPosition) -> [RawTriviaPiece] /// Source file buffer the Syntax tree represents. @@ -185,7 +184,6 @@ public class ParsingSyntaxArena: SyntaxArena { /// - Important: Must never be changed to a mutable value. See `SyntaxArenaRef.parseTrivia`. private let parseTriviaFunction: ParseTriviaFunction - @_spi(RawSyntax) public init(parseTriviaFunction: @escaping ParseTriviaFunction) { self.sourceBuffer = .init(start: nil, count: 0) self.parseTriviaFunction = parseTriviaFunction @@ -198,7 +196,6 @@ public class ParsingSyntaxArena: SyntaxArena { /// The interned buffer is guaranteed to be null-terminated. /// `contains(address _:)` is faster if the address is inside the memory /// range this function returned. - @_spi(RawSyntax) public func internSourceBuffer(_ buffer: UnsafeBufferPointer) -> UnsafeBufferPointer { let allocated = allocator.allocate( UInt8.self, @@ -214,7 +211,6 @@ public class ParsingSyntaxArena: SyntaxArena { return sourceBuffer } - @_spi(RawSyntax) public override func contains(text: SyntaxText) -> Bool { if let addr = text.baseAddress, self.sourceBufferContains(addr) { return true @@ -230,7 +226,6 @@ public class ParsingSyntaxArena: SyntaxArena { } /// Parse `source` into a list of ``RawTriviaPiece`` using `parseTriviaFunction`. - @_spi(RawSyntax) public func parseTrivia(source: SyntaxText, position: TriviaPosition) -> [RawTriviaPiece] { // Must never access mutable state. See `SyntaxArenaRef.parseTrivia`. return self.parseTriviaFunction(source, position) diff --git a/Sources/SwiftSyntax/SyntaxArenaAllocatedBuffer.swift b/Sources/SwiftSyntax/SyntaxArenaAllocatedBuffer.swift index d2dd2412b6d..3a2fbfe6342 100644 --- a/Sources/SwiftSyntax/SyntaxArenaAllocatedBuffer.swift +++ b/Sources/SwiftSyntax/SyntaxArenaAllocatedBuffer.swift @@ -27,14 +27,18 @@ public struct SyntaxArenaAllocatedPointer: @unchecked Sendabl /// - Important: The client needs to ensure sure that the buffer is indeed /// allocated by a ``SyntaxArena`` and that the ``SyntaxArena`` will outlive /// any users of this ``SyntaxArenaAllocatedBufferPointer``. - init(_ buffer: UnsafePointer) { - self.pointer = buffer + init(_ pointer: UnsafePointer) { + self.pointer = pointer } var pointee: Element { @_transparent unsafeAddress { pointer } } + func advanced(by n: Int) -> Self { + Self(pointer.advanced(by: n)) + } + var unsafeRawPointer: UnsafeRawPointer { return UnsafeRawPointer(pointer) } @@ -66,10 +70,12 @@ public struct SyntaxArenaAllocatedBufferPointer: RandomAccess self.buffer = buffer } - public subscript>( - range: RangeType - ) -> SyntaxArenaAllocatedBufferPointer { - return SyntaxArenaAllocatedBufferPointer(UnsafeBufferPointer(rebasing: self.buffer[range])) + /// Create a buffer over the same memory as the given buffer slice. + public init(rebasing slice: Self.SubSequence) { + self.buffer = UnsafeBufferPointer( + start: slice.base.baseAddress?.advanced(by: slice.startIndex), + count: slice.count + ) } public subscript(_ index: Int) -> Element { diff --git a/Sources/SwiftSyntax/SyntaxChildren.swift b/Sources/SwiftSyntax/SyntaxChildren.swift index 5457797679a..e02bdb4fda9 100644 --- a/Sources/SwiftSyntax/SyntaxChildren.swift +++ b/Sources/SwiftSyntax/SyntaxChildren.swift @@ -12,415 +12,23 @@ // MARK: - Index -/// The data for an index in a syntax children collection that is not the end -/// index. See ``SyntaxChildrenIndex`` for the representation of the end index. -struct SyntaxChildrenIndexData: Hashable, Comparable, Sendable { - /// The UTF-8 offset of the item at this index in the source file - /// See `AbsoluteSyntaxPosition.offset` - let offset: UInt32 - /// The index of the node in the parent. - /// See `AbsoluteSyntaxPosition.indexInParent` - let indexInParent: UInt32 - /// Unique value for a node within its own tree. - /// See ``SyntaxIdentifier/indexInTree`` - let indexInTree: SyntaxIdentifier.SyntaxIndexInTree - - static func < ( - lhs: SyntaxChildrenIndexData, - rhs: SyntaxChildrenIndexData - ) -> Bool { - return lhs.indexInParent < rhs.indexInParent - } - - fileprivate init( - offset: UInt32, - indexInParent: UInt32, - indexInTree: SyntaxIdentifier.SyntaxIndexInTree - ) { - self.offset = offset - self.indexInParent = indexInParent - self.indexInTree = indexInTree - } - - init(_ absoluteSyntaxInfo: AbsoluteSyntaxInfo) { - self.offset = absoluteSyntaxInfo.offset - self.indexInParent = absoluteSyntaxInfo.indexInParent - self.indexInTree = absoluteSyntaxInfo.nodeId.indexInTree - } -} - /// An index in a syntax children collection. -public struct SyntaxChildrenIndex: Hashable, Comparable, ExpressibleByNilLiteral, Sendable { - /// Construct the `endIndex` of a ``SyntaxChildren`` collection. - public init(nilLiteral: ()) { - self.data = nil - } - - // Performance considerations: - // It is faster to use a special end index than computing a materialized index - // that stores past the end of the collection if forward iteration is the only - // thing that's needed. - - /// `nil` represents the end index and `.some` represents a materialized index - /// that points into a collection. - /// It is faster to use `Optional` here rather than making - /// `SyntaxChildrenIndex` an enum because the optional value can be - /// force-unwrapped when we know that an index is not the end index, saving a - /// switch case comparison. - let data: SyntaxChildrenIndexData? - - fileprivate init( - offset: UInt32, - indexInParent: UInt32, - indexInTree: SyntaxIdentifier.SyntaxIndexInTree - ) { - self.data = SyntaxChildrenIndexData( - offset: offset, - indexInParent: indexInParent, - indexInTree: indexInTree - ) - } +public struct SyntaxChildrenIndex: Hashable, Comparable, Sendable { + /// The index in a `SyntaxDataReferenceBuffer` that is being referenced by this `SyntaxChildrenIndex`. + let value: Int - internal init(_ absoluteSyntaxInfo: AbsoluteSyntaxInfo) { - self.data = SyntaxChildrenIndexData(absoluteSyntaxInfo) + init(value: Int) { + self.value = value } /// Returns `true` if `lhs` occurs before `rhs`. public static func < (lhs: SyntaxChildrenIndex, rhs: SyntaxChildrenIndex) -> Bool { - switch (lhs.data, rhs.data) { - case (.some(let lhsData), .some(let rhsData)): - return lhsData < rhsData - case (.some(_), .none): - return true - case (.none, .some(_)): - return false - case (.none, .none): - return false - } - } -} - -fileprivate extension AbsoluteSyntaxInfo { - /// Construct `AbsoluteSyntaxInfo` from the given index data and a `rootId`. - init(index: SyntaxChildrenIndexData, rootId: UInt) { - let position = AbsoluteSyntaxPosition( - offset: index.offset, - indexInParent: index.indexInParent - ) - let identifier = SyntaxIdentifier( - rootId: rootId, - indexInTree: index.indexInTree - ) - self.init(position: position, nodeId: identifier) + lhs.value < rhs.value } } // MARK: - Collections -/// Collection that contains the child nodes of a raw node (including missing -/// nodes), along with their associated `AbsoluteSyntaxInfo`. -struct RawSyntaxChildren: BidirectionalCollection, Sendable { - typealias Element = (raw: RawSyntax?, syntaxInfo: AbsoluteSyntaxInfo) - typealias Index = SyntaxChildrenIndex - - struct Iterator: IteratorProtocol { - let collection: RawSyntaxChildren - var nextIndex: SyntaxChildrenIndex - - init(collection: RawSyntaxChildren) { - self.collection = collection - self.nextIndex = collection.startIndex - } - - mutating func next() -> RawSyntaxChildren.Element? { - guard nextIndex != collection.endIndex else { - return nil - } - // Re-use the fetched child to compute the next index, eliminating one - // access to the underlying collection - let child = collection[nextIndex] - nextIndex = collection.index(nextIndex, advancedBy: child.raw) - return child - } - } - - /// The node whose children shall be accessed - private let parent: RawSyntax - - private var parentLayoutView: RawSyntaxLayoutView { - return parent.layoutView! - } - - /// The rootId of the tree the child nodes belong to - private let rootId: UInt - - /// The number of children in `parent`. Cached to avoid reaching into `parent` for every index - /// advancement - private let numberOfChildren: Int - - let startIndex: SyntaxChildrenIndex - var endIndex: SyntaxChildrenIndex { - return nil - } - - func makeIterator() -> Iterator { - return Iterator(collection: self) - } - - /// Advance the given index by the given ``RawSyntax`` node. - func index(_ index: Index, advancedBy node: RawSyntax?) -> Index { - // We can assume a non-end index since advancing the end index is undefined - // behaviour. - let index = index.data! - - if index.indexInParent + 1 < numberOfChildren { - // Compute the next materialized index - let nodeLength = UInt32(node?.totalLength.utf8Length ?? 0) - let advancedIndexInTree = index.indexInTree.advancedBy(node) - return SyntaxChildrenIndex( - offset: index.offset + nodeLength, - indexInParent: index.indexInParent + 1, - indexInTree: advancedIndexInTree - ) - } else { - // We have reached the end of the collection. Return the end index. - return nil - } - } - - func index(after index: SyntaxChildrenIndex) -> SyntaxChildrenIndex { - guard let indexData = index.data else { - preconditionFailure("Cannot get the index after the end index") - } - let node = parent.layoutView!.children[Int(indexData.indexInParent)] - return self.index(index, advancedBy: node) - } - - func index(before index: SyntaxChildrenIndex) -> SyntaxChildrenIndex { - if let index = index.data { - // We are reversing a non-end index. - let previousNode = parent.layoutView!.children[Int(index.indexInParent - 1)] - let previousNodeLength = UInt32(previousNode?.totalLength.utf8Length ?? 0) - let reversedIndexInTree = index.indexInTree.reversedBy(previousNode) - return SyntaxChildrenIndex( - offset: index.offset - previousNodeLength, - indexInParent: index.indexInParent - 1, - indexInTree: reversedIndexInTree - ) - } else { - // We need to reverse the end index. For this we need to compute a - // materialized version of the end index that points one past the end of - // the collection. After we have that materialized index, we can reverse - // it using the above logic. - - // If the start index is nil, then the collection is empty and we are - // reversing before the start index. That is undefined behaviour, so we - // can assume a non-end index. - let startIndex = self.startIndex.data! - - // Compute a materialized index. - let offset = startIndex.offset + UInt32(parent.totalLength.utf8Length) - let indexInParent = startIndex.indexInParent + UInt32(parentLayoutView.children.count) - let indexInTree = startIndex.indexInTree.indexInTree + UInt32(parent.totalNodes) - 1 - let syntaxIndexInTree = SyntaxIdentifier.SyntaxIndexInTree(indexInTree: indexInTree) - let materialized = SyntaxChildrenIndex( - offset: offset, - indexInParent: indexInParent, - indexInTree: syntaxIndexInTree - ) - - // Reverse index based on the above logic - return self.index(before: materialized) - } - } - - func distance(from start: SyntaxChildrenIndex, to end: SyntaxChildrenIndex) -> Int { - switch (start.data, end.data) { - case (.some(let start), .some(let end)): - return Int(end.indexInParent - start.indexInParent) - case (.some(let start), .none): - return parentLayoutView.children.count - Int(start.indexInParent) - case (.none, .some(let end)): - return Int(end.indexInParent) - parentLayoutView.children.count - case (.none, .none): - return 0 - } - } - - subscript(index: SyntaxChildrenIndex) -> (raw: RawSyntax?, syntaxInfo: AbsoluteSyntaxInfo) { - // Accessing the end index is undefined. So we can assume a non-end index. - let index = index.data! - - let child = parent.layoutView!.children[Int(index.indexInParent)] - let info = AbsoluteSyntaxInfo(index: index, rootId: rootId) - return (child, info) - } - - init(_ parent: AbsoluteRawSyntax) { - self.parent = parent.raw - self.rootId = parent.info.nodeId.rootId - switch parent.raw.view { - case .layout(let layoutView): - self.numberOfChildren = layoutView.children.count - case .token: - self.numberOfChildren = 0 - } - - if self.numberOfChildren == 0 { - self.startIndex = nil - } else { - let startPosition = parent.info.advancedToFirstChild() - self.startIndex = SyntaxChildrenIndex(startPosition) - } - } - - init(_ base: __shared Syntax) { - self.init(base.absoluteRaw) - } -} - -/// Collection that contains the `present` child nodes of an -/// `AbsoluteRawSyntax` node. -struct NonNilRawSyntaxChildren: BidirectionalCollection, Sendable { - typealias Element = AbsoluteRawSyntax - typealias Index = SyntaxChildrenIndex - - struct Iterator: IteratorProtocol { - var iterator: RawSyntaxChildren.Iterator - let viewMode: SyntaxTreeViewMode - - init(allChildren: RawSyntaxChildren, viewMode: SyntaxTreeViewMode) { - self.iterator = allChildren.makeIterator() - self.viewMode = viewMode - } - - mutating func next() -> AbsoluteRawSyntax? { - // Advance the underlying RawSyntaxChildren.Iterator until we find a - // present node. - while true { - guard let (node, info) = self.iterator.next() else { - return nil - } - if let node, viewMode.shouldTraverse(node: node) { - return AbsoluteRawSyntax(raw: node, info: info) - } - } - } - } - - let viewMode: SyntaxTreeViewMode - - /// The underlying collection which contains all children. The present - /// children are filtered from this collection. - private var allChildren: RawSyntaxChildren - - let startIndex: SyntaxChildrenIndex - var endIndex: SyntaxChildrenIndex { - return allChildren.endIndex - } - - func makeIterator() -> Iterator { - return Iterator(allChildren: allChildren, viewMode: viewMode) - } - - /// Advances the index to the next present node in the given collection. If - /// the node at the given index is already present, it is not advanced. - /// If no present node exists in the given collection after the index, the - /// collection's `endIndex` is returned. - private static func presentIndex( - after index: SyntaxChildrenIndex, - in children: RawSyntaxChildren, - viewMode: SyntaxTreeViewMode - ) -> SyntaxChildrenIndex { - var advancedIndex = index - while true { - if advancedIndex != children.endIndex { - let node = children[advancedIndex].raw - if let node = node, viewMode.shouldTraverse(node: node) { - // Found a present node. Return its index. - return advancedIndex - } - // Continue advancing - advancedIndex = children.index(advancedIndex, advancedBy: node) - } else { - // Reached the end of the collection. Don't advance further. - return advancedIndex - } - } - } - - /// Reverses the index to the previous present node in the given collection. - /// If the node at the given index is already present, it is not reversed. - /// Behavior is undefined if no present index exists before the given index. - private static func presentIndex( - before index: SyntaxChildrenIndex, - in children: RawSyntaxChildren, - viewMode: SyntaxTreeViewMode - ) -> SyntaxChildrenIndex { - var reversedIndex = index - while true { - if reversedIndex < children.endIndex, - let node = children[reversedIndex].raw, - viewMode.shouldTraverse(node: node) - { - return reversedIndex - } - // Reversing any further would result in undefined behaviour of - // index(before:) - precondition( - reversedIndex != children.startIndex, - "presentIndex(before:) must not be called if there is no " + "present index before the given one" - ) - reversedIndex = children.index(before: reversedIndex) - } - } - - func index(after index: SyntaxChildrenIndex) -> SyntaxChildrenIndex { - let nextIndex = allChildren.index(after: index) - return Self.presentIndex( - after: nextIndex, - in: allChildren, - viewMode: viewMode - ) - } - - func index(before index: SyntaxChildrenIndex) -> SyntaxChildrenIndex { - // presentIndex(before:) must have a valid previous present node. By - // contract of the index(before:) function we are not called on the start - // index. The start index points to the first present node. Hence there is - // a present node before us. - return Self.presentIndex( - before: allChildren.index(before: index), - in: allChildren, - viewMode: viewMode - ) - } - - subscript(position: SyntaxChildrenIndex) -> AbsoluteRawSyntax { - let (node, info) = allChildren[position] - // Valid indices always point to present nodes. Thus safe to force unwrap. - return AbsoluteRawSyntax(raw: node!, info: info) - } - - init(_ parent: AbsoluteRawSyntax, viewMode: SyntaxTreeViewMode) { - let allChildren = RawSyntaxChildren(parent) - - self.allChildren = allChildren - self.startIndex = Self.presentIndex( - after: allChildren.startIndex, - in: allChildren, - viewMode: viewMode - ) - self.viewMode = viewMode - } - - /// - Note: Inline so we don't retain `Syntax.Info` when creating `NonNilRawSyntaxChildren` from a `Syntax`. - @inline(__always) - init(_ node: Syntax, viewMode: SyntaxTreeViewMode) { - self.init(node.absoluteRaw, viewMode: viewMode) - } -} - /// Collection that contains the present child ``Syntax`` nodes of the given node. public struct SyntaxChildren: BidirectionalCollection, Sendable { /// ``SyntaxChildren`` is indexed by ``SyntaxChildrenIndex``. @@ -429,38 +37,57 @@ public struct SyntaxChildren: BidirectionalCollection, Sendable { /// ``SyntaxChildren`` contains ``Syntax`` nodes. public typealias Element = Syntax - /// The collection that contains the raw child nodes. ``Syntax`` nodes are - /// generated from these. - private let rawChildren: NonNilRawSyntaxChildren + private let viewMode: SyntaxTreeViewMode /// The parent node of the children. Used to build the ``Syntax`` nodes. private let parent: Syntax + /// Pre-fetched layout buffer. Because accessing `Syntax.layoutBuffer` may not be trivial. + private let layoutBuffer: SyntaxDataReferenceBuffer + /// The index of the first child in this collection. - public var startIndex: SyntaxChildrenIndex { return rawChildren.startIndex } + public let startIndex: SyntaxChildrenIndex /// The index that’s one after the last element in the collection. - public var endIndex: SyntaxChildrenIndex { return rawChildren.endIndex } + public var endIndex: SyntaxChildrenIndex { Index(value: layoutBuffer.endIndex) } + + private static func shouldTraverse(_ dataRef: SyntaxDataReference?, viewMode: SyntaxTreeViewMode) -> Bool { + guard let dataRef else { + return false + } + return viewMode.shouldTraverse(node: dataRef.pointee.raw) + } /// The index for the child that’s after the child at `index`. public func index(after index: SyntaxChildrenIndex) -> SyntaxChildrenIndex { - return rawChildren.index(after: index) + precondition(index < endIndex) + return Index( + value: layoutBuffer[(index.value + 1)...] + .firstIndex(where: { Self.shouldTraverse($0, viewMode: viewMode) }) ?? layoutBuffer.endIndex + ) } /// The index for the child that’s before the child at `index`. public func index(before index: SyntaxChildrenIndex) -> SyntaxChildrenIndex { - return rawChildren.index(before: index) + precondition(index > startIndex) + return Index( + value: layoutBuffer[.. Syntax { - let child = rawChildren[index] - return Syntax(child, parent: parent) + return Syntax(arena: parent.arena, dataRef: layoutBuffer[index.value]!) } internal init(_ node: Syntax, viewMode: SyntaxTreeViewMode) { - self.rawChildren = NonNilRawSyntaxChildren(node, viewMode: viewMode) self.parent = node + self.layoutBuffer = node.layoutBuffer + self.viewMode = viewMode + self.startIndex = Index( + value: layoutBuffer.firstIndex(where: { Self.shouldTraverse($0, viewMode: viewMode) }) ?? layoutBuffer.endIndex + ) } /// Return the index of `node` within this collection. diff --git a/Sources/SwiftSyntax/SyntaxCollection.swift b/Sources/SwiftSyntax/SyntaxCollection.swift index e17be71f842..a6af1f39597 100644 --- a/Sources/SwiftSyntax/SyntaxCollection.swift +++ b/Sources/SwiftSyntax/SyntaxCollection.swift @@ -224,22 +224,33 @@ extension SyntaxCollection { /// An iterator over a ``SyntaxCollection``. public struct SyntaxCollectionIterator: IteratorProtocol { - private let parent: Syntax public typealias Element = E - private var iterator: RawSyntaxChildren.Iterator + /// The arena in which `SyntaxData` get allocated as the children are traversed. + private let arena: SyntaxDataArena - init(parent: Syntax, rawChildren: RawSyntaxChildren) { - self.parent = parent - self.iterator = rawChildren.makeIterator() + /// The buffer containing the children that are iterated by this iterator. + private let layoutBuffer: SyntaxDataReferenceBuffer + + /// The index in `layoutBuffer` that will be returned when `next` is called. + private var index: Int + + init(_ node: Node) where Node.Element == Element { + let syntax = Syntax(node) + self.arena = syntax.arena + self.layoutBuffer = syntax.layoutBuffer + self.index = layoutBuffer.startIndex } public mutating func next() -> Element? { - guard let (raw, info) = self.iterator.next() else { + guard self.index < self.layoutBuffer.count else { return nil } - let absoluteRaw = AbsoluteRawSyntax(raw: raw!, info: info) - return Syntax(absoluteRaw, parent: parent).cast(Element.self) + defer { + self.index += 1 + } + // 'SyntaxCollection' always has non-nil children. We can thus force-unwrap the element at 'index'. + return Syntax(arena: arena, dataRef: layoutBuffer[self.index]!).cast(Element.self) } } @@ -263,8 +274,8 @@ extension SyntaxCollection { // Keep `newElements` alive so their arena doesn't get deallocated. withExtendedLifetime(newElements) { var newLayout = layoutView.formLayoutArray() - let layoutRangeLowerBound = (subrange.lowerBound.data?.indexInParent).map(Int.init) ?? newLayout.endIndex - let layoutRangeUpperBound = (subrange.upperBound.data?.indexInParent).map(Int.init) ?? newLayout.endIndex + let layoutRangeLowerBound = subrange.lowerBound.value + let layoutRangeUpperBound = subrange.upperBound.value newLayout.replaceSubrange(layoutRangeLowerBound.. SyntaxCollectionIterator { - return SyntaxCollectionIterator(parent: Syntax(self), rawChildren: rawChildren) + return SyntaxCollectionIterator(self) } - private var rawChildren: RawSyntaxChildren { - // We know children in a syntax collection cannot be missing. So we can - // use the low-level and faster RawSyntaxChildren collection instead of - // NonNilRawSyntaxChildren. - return RawSyntaxChildren(Syntax(self).absoluteRaw) + var elements: SyntaxDataReferenceBuffer { + Syntax(self).layoutBuffer } public var startIndex: SyntaxChildrenIndex { - return rawChildren.startIndex + return SyntaxChildrenIndex(value: 0) } public var endIndex: SyntaxChildrenIndex { - return rawChildren.endIndex + return SyntaxChildrenIndex(value: elements.count) } public func index(after index: SyntaxChildrenIndex) -> SyntaxChildrenIndex { - return rawChildren.index(after: index) + return Index(value: elements.index(after: index.value)) } public func index(before index: SyntaxChildrenIndex) -> SyntaxChildrenIndex { - return rawChildren.index(before: index) + return Index(value: elements.index(before: index.value)) } public func distance(from start: SyntaxChildrenIndex, to end: SyntaxChildrenIndex) -> Int { - return rawChildren.distance(from: start, to: end) + return elements.distance(from: start.value, to: end.value) } public subscript(position: SyntaxChildrenIndex) -> Element { get { - let (raw, info) = rawChildren[position] - let absoluteRaw = AbsoluteRawSyntax(raw: raw!, info: info) - return Syntax(absoluteRaw, parent: Syntax(self)).cast(Element.self) + // 'SyntaxCollection' always has non-nil children. We can thus force-unwrap the element at 'position.value' + return Syntax(arena: Syntax(self).arena, dataRef: elements[position.value]!).cast(Element.self) } set { - guard let indexToReplace = (position.data?.indexInParent).map(Int.init) else { - preconditionFailure("Cannot replace element at the end index") - } + let indexToReplace = position.value var newLayout = layoutView.formLayoutArray() /// Make sure the index is a valid index for replacing precondition( diff --git a/Sources/SwiftSyntax/SyntaxIdentifier.swift b/Sources/SwiftSyntax/SyntaxIdentifier.swift index 55d7f52d105..74c3f3b5ef3 100644 --- a/Sources/SwiftSyntax/SyntaxIdentifier.swift +++ b/Sources/SwiftSyntax/SyntaxIdentifier.swift @@ -31,25 +31,6 @@ public struct SyntaxIdentifier: Comparable, Hashable, Sendable { /// When traversing the syntax tree using a depth-first traversal, the index at which the node will be visited. let indexInTree: UInt32 - /// Assuming that this index points to the start of `raw`, advance it so that it points to the next sibling of - /// `raw`. - func advancedBy(_ raw: RawSyntax?) -> SyntaxIndexInTree { - let newIndexInTree = self.indexInTree + UInt32(truncatingIfNeeded: raw?.totalNodes ?? 0) - return .init(indexInTree: newIndexInTree) - } - - /// Assuming that this index points to the next sibling of `raw`, reverse it so that it points to the start of - /// `raw`. - func reversedBy(_ raw: RawSyntax?) -> SyntaxIndexInTree { - let newIndexInTree = self.indexInTree - UInt32(truncatingIfNeeded: raw?.totalNodes ?? 0) - return .init(indexInTree: newIndexInTree) - } - - func advancedToFirstChild() -> SyntaxIndexInTree { - let newIndexInTree = self.indexInTree + 1 - return .init(indexInTree: newIndexInTree) - } - init(indexInTree: UInt32) { self.indexInTree = indexInTree } @@ -74,33 +55,11 @@ public struct SyntaxIdentifier: Comparable, Hashable, Sendable { /// same instance. This guarantees that the trees with the same 'rootId' have /// exact the same structure. But, two trees with exactly the same structure /// might still have different 'rootId's. - let rootId: UInt + let rootId: RawSyntax.ID /// Unique value for a node within its own tree. public let indexInTree: SyntaxIndexInTree - /// Returns the `UInt` that is used as the root ID for the given raw syntax node. - private static func rootId(of raw: RawSyntax) -> UInt { - return UInt(bitPattern: raw.pointer.unsafeRawPointer) - } - - func advancedBySibling(_ raw: RawSyntax?) -> SyntaxIdentifier { - let newIndexInTree = indexInTree.advancedBy(raw) - return SyntaxIdentifier(rootId: self.rootId, indexInTree: newIndexInTree) - } - - func advancedToFirstChild() -> SyntaxIdentifier { - let newIndexInTree = self.indexInTree.advancedToFirstChild() - return SyntaxIdentifier(rootId: self.rootId, indexInTree: newIndexInTree) - } - - static func forRoot(_ raw: RawSyntax) -> SyntaxIdentifier { - return SyntaxIdentifier( - rootId: Self.rootId(of: raw), - indexInTree: SyntaxIndexInTree(indexInTree: 0) - ) - } - /// Forms a ``SyntaxIdentifier`` from an ``SyntaxIdentifier/SyntaxIndexInTree`` inside a ``Syntax`` node that /// constitutes the tree's root. /// @@ -118,14 +77,11 @@ public struct SyntaxIdentifier: Comparable, Hashable, Sendable { _ indexInTree: SyntaxIndexInTree, relativeToRoot root: some SyntaxProtocol ) -> SyntaxIdentifier? { - guard !root.hasParent else { - return nil - } - guard indexInTree.indexInTree < SyntaxIndexInTree(indexInTree: 0).advancedBy(root.raw).indexInTree else { + guard !root.hasParent, Int(truncatingIfNeeded: indexInTree.indexInTree) < root.raw.totalNodes else { return nil } - return SyntaxIdentifier(rootId: Self.rootId(of: root.raw), indexInTree: indexInTree) + return SyntaxIdentifier(rootId: root.raw.id, indexInTree: indexInTree) } /// A ``SyntaxIdentifier`` compares less than another ``SyntaxIdentifier`` if the node at that identifier occurs first diff --git a/Sources/SwiftSyntax/SyntaxNodeFactory.swift b/Sources/SwiftSyntax/SyntaxNodeFactory.swift deleted file mode 100644 index 9246603e892..00000000000 --- a/Sources/SwiftSyntax/SyntaxNodeFactory.swift +++ /dev/null @@ -1,85 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2024 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 -// -//===----------------------------------------------------------------------===// - -/// Reusable 'Syntax.Info' storage. -private struct SyntaxInfoRepository { - private final class _Buffer: ManagedBuffer { - deinit { - self.withUnsafeMutablePointers { headerPtr, elementPtr in - _ = elementPtr.deinitialize(count: headerPtr.pointee) - } - } - } - - /// Fixed capacity which is enough for most use cases. - static var capacity: Int { 64 } - - private let buffer: _Buffer - - init() { - let buffer = _Buffer.create(minimumCapacity: Self.capacity, makingHeaderWith: { _ in 0 }) - self.buffer = buffer as! _Buffer - } - - /// Take the 'Syntax.Info' object from the address. - func push(_ info: UnsafeMutablePointer) { - buffer.withUnsafeMutablePointers { headerPtr, elementPtr in - guard headerPtr.pointee < Self.capacity else { - return - } - assert(info.pointee != nil, "tried to push 'nil' info") - elementPtr.advanced(by: headerPtr.pointee).moveInitialize(from: info, count: 1) - info.initialize(to: nil) - headerPtr.pointee += 1 - } - } - - /// Vend a 'Swift.Info' object if available. - func pop() -> Syntax.Info? { - return buffer.withUnsafeMutablePointers { headerPtr, elementPtr in - guard headerPtr.pointee > 0 else { - return nil - } - headerPtr.pointee -= 1 - return elementPtr.advanced(by: headerPtr.pointee).move() - } - } -} - -/// 'Syntax' object factory. This may hold some stocks of recycled 'Syntax.Info'. -struct SyntaxNodeFactory { - private let syntaxInfoRepo: SyntaxInfoRepository = SyntaxInfoRepository() - - /// Create a 'Syntax' instance using the supplied info. - /// - /// If this factory has a recycled 'Syntax.Info', use one of them. Otherwise, just create a instance by allocating a new one. - @inline(__always) - func create(parent: Syntax, raw: RawSyntax, absoluteInfo: AbsoluteSyntaxInfo) -> Syntax { - if let info = syntaxInfoRepo.pop() { - info.info = .nonRoot(.init(parent: parent, absoluteInfo: absoluteInfo)) - return Syntax(raw, info: info) - } else { - return Syntax(raw, parent: parent, absoluteInfo: absoluteInfo) - } - } - - /// Dispose a 'Syntax' object. - /// - /// 'node.info' is collected for future reuse. 'node' is not usable after calling this. - @inline(__always) - func dispose(_ node: inout Syntax) { - if isKnownUniquelyReferenced(&node.info) { - node.info.unsafelyUnwrapped.info = nil - syntaxInfoRepo.push(&node.info) - } - } -} diff --git a/Sources/SwiftSyntax/SyntaxProtocol.swift b/Sources/SwiftSyntax/SyntaxProtocol.swift index 1eba89b48f5..a870594f8a7 100644 --- a/Sources/SwiftSyntax/SyntaxProtocol.swift +++ b/Sources/SwiftSyntax/SyntaxProtocol.swift @@ -156,6 +156,9 @@ extension SyntaxProtocol { /// Return this subtree with this node as the root, ie. detach this node /// from its parent. public var detached: Self { + if !self.hasParent { + return self + } // Make sure `self` (and thus the arena of `self.raw`) can’t get deallocated // before the detached node can be created. return withExtendedLifetime(self) { @@ -202,7 +205,7 @@ extension SyntaxProtocol { /// The index of this node in a ``SyntaxChildren`` collection. internal var indexInParent: SyntaxChildrenIndex { - return SyntaxChildrenIndex(Syntax(self).absoluteInfo) + return SyntaxChildrenIndex(value: Syntax(self).layoutIndexInParent) } /// The parent of this syntax node, or `nil` if this node is the root. @@ -217,7 +220,7 @@ extension SyntaxProtocol { /// Whether or not this node has a parent. public var hasParent: Bool { - return parent != nil + return Syntax(self).hasParent } public var keyPathInParent: AnyKeyPath? { @@ -227,7 +230,7 @@ extension SyntaxProtocol { guard case .layout(let childrenKeyPaths) = parent.kind.syntaxNodeType.structure else { return nil } - return childrenKeyPaths[Syntax(self).indexInParent] + return childrenKeyPaths[Syntax(self).layoutIndexInParent] } @available(*, deprecated, message: "Use previousToken(viewMode:) instead") @@ -240,18 +243,7 @@ extension SyntaxProtocol { /// /// If no node has a non-`nil` mapping, returns `nil`. public func ancestorOrSelf(mapping map: (Syntax) -> T?) -> T? { - return self.withUnownedSyntax { - var node = $0 - while true { - if let mapped = node.withValue(map) { - return mapped - } - guard let parent = node.parent else { - return nil - } - node = parent - } - } + Syntax(self).ancestorOrSelf(mapping: map) } } @@ -261,21 +253,7 @@ extension SyntaxProtocol { /// Recursively walks through the tree to find the token semantically before /// this node. public func previousToken(viewMode: SyntaxTreeViewMode) -> TokenSyntax? { - guard let parent = self.parent else { - return nil - } - let siblings = NonNilRawSyntaxChildren(parent, viewMode: viewMode) - // `self` could be a missing node at index 0 and `viewMode` be `.sourceAccurate`. - // In that case `siblings` skips over the missing `self` node and has a `startIndex > 0`. - if self.indexInParent >= siblings.startIndex { - for absoluteRaw in siblings[.. TokenSyntax? { - guard let parent = self.parent else { - return nil - } - let siblings = NonNilRawSyntaxChildren(parent, viewMode: viewMode) - let nextSiblingIndex = siblings.index(after: self.indexInParent) - for absoluteRaw in siblings[nextSiblingIndex...] { - let child = Syntax(absoluteRaw, parent: parent) - if let token = child.firstToken(viewMode: viewMode) { - return token - } - } - return parent.nextToken(viewMode: viewMode) + return self._syntaxNode.nextToken(viewMode: viewMode) } @available(*, deprecated, message: "Use firstToken(viewMode: .sourceAccurate) instead") @@ -307,17 +274,7 @@ extension SyntaxProtocol { /// Returns the first token node that is part of this syntax node. public func firstToken(viewMode: SyntaxTreeViewMode) -> TokenSyntax? { - guard viewMode.shouldTraverse(node: raw) else { return nil } - if let token = _syntaxNode.as(TokenSyntax.self) { - return token - } - - for child in children(viewMode: viewMode) { - if let token = child.firstToken(viewMode: viewMode) { - return token - } - } - return nil + return self._syntaxNode.firstToken(viewMode: viewMode) } @available(*, deprecated, message: "Use lastToken(viewMode: .sourceAccurate) instead") @@ -327,17 +284,7 @@ extension SyntaxProtocol { /// Returns the last token node that is part of this syntax node. public func lastToken(viewMode: SyntaxTreeViewMode) -> TokenSyntax? { - guard viewMode.shouldTraverse(node: raw) else { return nil } - if let token = _syntaxNode.as(TokenSyntax.self) { - return token - } - - for child in children(viewMode: viewMode).reversed() { - if let tok = child.lastToken(viewMode: viewMode) { - return tok - } - } - return nil + return self._syntaxNode.lastToken(viewMode: viewMode) } /// Sequence of tokens that are part of this Syntax node. @@ -374,20 +321,27 @@ extension SyntaxProtocol { /// If the identifier references a node from a different tree (ie. one that has a different root ID in the /// ``SyntaxIdentifier``) or if no node with the given identifier is a child of this syntax node, returns `nil`. public func node(at syntaxIdentifier: SyntaxIdentifier) -> Syntax? { - guard self.id <= syntaxIdentifier && syntaxIdentifier < self.id.advancedBySibling(self.raw) else { - // The syntax identifier is not part of this tree. + let syntax = Syntax(self) + guard syntax.raw.id == syntaxIdentifier.rootId else { return nil } - if self.id == syntaxIdentifier { - return Syntax(self) - } - for child in children(viewMode: .all) { - if let node = child.node(at: syntaxIdentifier) { + + func _node(at indexInTree: UInt32, in node: Syntax) -> Syntax? { + let i = node.absoluteInfo.indexInTree + if i == indexInTree { return node } + guard i < indexInTree, indexInTree < i &+ UInt32(truncatingIfNeeded: node.raw.totalNodes) else { + return nil + } + for child in node.children(viewMode: .all) { + if let node = _node(at: indexInTree, in: child) { + return node + } + } + preconditionFailure("syntaxIdentifier is covered by this node but not any of its children?") } - - preconditionFailure("syntaxIdentifier is covered by this node but not any of its children?") + return _node(at: syntaxIdentifier.indexInTree.indexInTree, in: syntax) } } diff --git a/Sources/SwiftSyntax/TokenSequence.swift b/Sources/SwiftSyntax/TokenSequence.swift index ef31c2c129c..1567919c451 100644 --- a/Sources/SwiftSyntax/TokenSequence.swift +++ b/Sources/SwiftSyntax/TokenSequence.swift @@ -10,6 +10,70 @@ // //===----------------------------------------------------------------------===// +extension Syntax { + /// Implementation of 'SyntaxProtocol.previousToken(viewMode:)' + func previousToken(viewMode: SyntaxTreeViewMode) -> TokenSyntax? { + guard let parentDataRef = data.parent else { + return nil + } + for case let childDataRef? in arena.layout(for: parentDataRef)[.. TokenSyntax? { + guard let parentDataRef = data.parent else { + return nil + } + for case let childDataRef? in arena.layout(for: parentDataRef)[(layoutIndexInParent &+ 1)...] { + if let token = Syntax(arena: arena, dataRef: childDataRef).firstToken(viewMode: viewMode) { + return token + } + } + return Syntax(arena: arena, dataRef: parentDataRef).nextToken(viewMode: viewMode) + } + + /// Implementation of 'SyntaxProtocol.firstToken(viewMode:)' + /// + /// - Note: Can't use 'RawSyntax.firstToken(viewMode:)' because it loses absolute info. + func firstToken(viewMode: SyntaxTreeViewMode) -> TokenSyntax? { + guard viewMode.shouldTraverse(node: raw) else { + return nil + } + if raw.isToken { + return TokenSyntax(self)! + } + for case let childDataRef? in layoutBuffer { + if let token = Syntax(arena: arena, dataRef: childDataRef).firstToken(viewMode: viewMode) { + return token + } + } + return nil + } + + /// Implementation of 'SyntaxProtocol.lastToken(viewMode:)' + /// + /// - Note: Can't use 'RawSyntax.lastToken(viewMode:)' because it loses absolute info. + func lastToken(viewMode: SyntaxTreeViewMode) -> TokenSyntax? { + guard viewMode.shouldTraverse(node: raw) else { + return nil + } + if raw.isToken { + return TokenSyntax(self)! + } + for case let childDataRef? in layoutBuffer.reversed() { + if let token = Syntax(arena: arena, dataRef: childDataRef).lastToken(viewMode: viewMode) { + return token + } + } + return nil + } +} + /// Sequence of tokens that are part of the provided Syntax node. public struct TokenSequence: Sequence, Sendable { /// Iterates over a ``TokenSequence``. diff --git a/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift b/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift index 75ae283f668..566b0142125 100644 --- a/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift +++ b/Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift @@ -3387,6 +3387,16 @@ public func childName(_ keyPath: AnyKeyPath) -> String? { return "colon" case \UnresolvedTernaryExprSyntax.unexpectedAfterColon: return "unexpectedAfterColon" + case \UnsafeExprSyntax.unexpectedBeforeUnsafeKeyword: + return "unexpectedBeforeUnsafeKeyword" + case \UnsafeExprSyntax.unsafeKeyword: + return "unsafeKeyword" + case \UnsafeExprSyntax.unexpectedBetweenUnsafeKeywordAndExpression: + return "unexpectedBetweenUnsafeKeywordAndExpression" + case \UnsafeExprSyntax.expression: + return "expression" + case \UnsafeExprSyntax.unexpectedAfterExpression: + return "unexpectedAfterExpression" case \ValueBindingPatternSyntax.unexpectedBeforeBindingSpecifier: return "unexpectedBeforeBindingSpecifier" case \ValueBindingPatternSyntax.bindingSpecifier: diff --git a/Sources/SwiftSyntax/generated/RenamedChildrenCompatibility.swift b/Sources/SwiftSyntax/generated/RenamedChildrenCompatibility.swift index c0b55967d15..a3b1e896b37 100644 --- a/Sources/SwiftSyntax/generated/RenamedChildrenCompatibility.swift +++ b/Sources/SwiftSyntax/generated/RenamedChildrenCompatibility.swift @@ -2911,11 +2911,11 @@ extension FunctionCallExprSyntax { _ unexpectedBeforeCalledExpression: UnexpectedNodesSyntax? = nil, calledExpression: some ExprSyntaxProtocol, _ unexpectedBetweenCalledExpressionAndLeftParen: UnexpectedNodesSyntax? = nil, - leftParen: TokenSyntax? = nil, + leftParen: TokenSyntax?, _ unexpectedBetweenLeftParenAndArgumentList: UnexpectedNodesSyntax? = nil, argumentList: LabeledExprListSyntax, _ unexpectedBetweenArgumentListAndRightParen: UnexpectedNodesSyntax? = nil, - rightParen: TokenSyntax? = nil, + rightParen: TokenSyntax?, _ unexpectedBetweenRightParenAndTrailingClosure: UnexpectedNodesSyntax? = nil, trailingClosure: ClosureExprSyntax? = nil, _ unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures: UnexpectedNodesSyntax? = nil, diff --git a/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift b/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift index dc6991cd0f5..7289ed7f8e2 100644 --- a/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift +++ b/Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift @@ -2268,6 +2268,20 @@ open class SyntaxAnyVisitor: SyntaxVisitor { visitAnyPost(node._syntaxNode) } + #if compiler(>=5.8) + @_spi(ExperimentalLanguageFeatures) + #endif + override open func visit(_ node: UnsafeExprSyntax) -> SyntaxVisitorContinueKind { + return visitAny(node._syntaxNode) + } + + #if compiler(>=5.8) + @_spi(ExperimentalLanguageFeatures) + #endif + override open func visitPost(_ node: UnsafeExprSyntax) { + 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 1dd414f2cd5..210d83d4fea 100644 --- a/Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift +++ b/Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift @@ -542,7 +542,7 @@ public struct ExprSyntax: ExprSyntaxProtocol, SyntaxHashable { public init?(_ node: __shared some SyntaxProtocol) { switch node.raw.kind { - case .arrayExpr, .arrowExpr, .asExpr, .assignmentExpr, .awaitExpr, .binaryOperatorExpr, .booleanLiteralExpr, .borrowExpr, ._canImportExpr, ._canImportVersionInfo, .closureExpr, .consumeExpr, .copyExpr, .declReferenceExpr, .dictionaryExpr, .discardAssignmentExpr, .doExpr, .editorPlaceholderExpr, .floatLiteralExpr, .forceUnwrapExpr, .functionCallExpr, .genericSpecializationExpr, .ifExpr, .inOutExpr, .infixOperatorExpr, .integerLiteralExpr, .isExpr, .keyPathExpr, .macroExpansionExpr, .memberAccessExpr, .missingExpr, .nilLiteralExpr, .optionalChainingExpr, .packElementExpr, .packExpansionExpr, .patternExpr, .postfixIfConfigExpr, .postfixOperatorExpr, .prefixOperatorExpr, .regexLiteralExpr, .sequenceExpr, .simpleStringLiteralExpr, .stringLiteralExpr, .subscriptCallExpr, .superExpr, .switchExpr, .ternaryExpr, .tryExpr, .tupleExpr, .typeExpr, .unresolvedAsExpr, .unresolvedIsExpr, .unresolvedTernaryExpr: + case .arrayExpr, .arrowExpr, .asExpr, .assignmentExpr, .awaitExpr, .binaryOperatorExpr, .booleanLiteralExpr, .borrowExpr, ._canImportExpr, ._canImportVersionInfo, .closureExpr, .consumeExpr, .copyExpr, .declReferenceExpr, .dictionaryExpr, .discardAssignmentExpr, .doExpr, .editorPlaceholderExpr, .floatLiteralExpr, .forceUnwrapExpr, .functionCallExpr, .genericSpecializationExpr, .ifExpr, .inOutExpr, .infixOperatorExpr, .integerLiteralExpr, .isExpr, .keyPathExpr, .macroExpansionExpr, .memberAccessExpr, .missingExpr, .nilLiteralExpr, .optionalChainingExpr, .packElementExpr, .packExpansionExpr, .patternExpr, .postfixIfConfigExpr, .postfixOperatorExpr, .prefixOperatorExpr, .regexLiteralExpr, .sequenceExpr, .simpleStringLiteralExpr, .stringLiteralExpr, .subscriptCallExpr, .superExpr, .switchExpr, .ternaryExpr, .tryExpr, .tupleExpr, .typeExpr, .unresolvedAsExpr, .unresolvedIsExpr, .unresolvedTernaryExpr, .unsafeExpr: self._syntaxNode = node._syntaxNode default: return nil @@ -619,7 +619,8 @@ public struct ExprSyntax: ExprSyntaxProtocol, SyntaxHashable { .node(TypeExprSyntax.self), .node(UnresolvedAsExprSyntax.self), .node(UnresolvedIsExprSyntax.self), - .node(UnresolvedTernaryExprSyntax.self) + .node(UnresolvedTernaryExprSyntax.self), + .node(UnsafeExprSyntax.self) ]) } } @@ -1789,6 +1790,7 @@ extension Syntax { .node(UnresolvedAsExprSyntax.self), .node(UnresolvedIsExprSyntax.self), .node(UnresolvedTernaryExprSyntax.self), + .node(UnsafeExprSyntax.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 439e8dca904..b801ba4011b 100644 --- a/Sources/SwiftSyntax/generated/SyntaxEnum.swift +++ b/Sources/SwiftSyntax/generated/SyntaxEnum.swift @@ -305,6 +305,10 @@ public enum SyntaxEnum: Sendable { case unresolvedAsExpr(UnresolvedAsExprSyntax) case unresolvedIsExpr(UnresolvedIsExprSyntax) case unresolvedTernaryExpr(UnresolvedTernaryExprSyntax) + #if compiler(>=5.8) + @_spi(ExperimentalLanguageFeatures) + #endif + case unsafeExpr(UnsafeExprSyntax) case valueBindingPattern(ValueBindingPatternSyntax) case variableDecl(VariableDeclSyntax) case versionComponentList(VersionComponentListSyntax) @@ -869,6 +873,8 @@ extension Syntax { return .unresolvedIsExpr(UnresolvedIsExprSyntax(self)!) case .unresolvedTernaryExpr: return .unresolvedTernaryExpr(UnresolvedTernaryExprSyntax(self)!) + case .unsafeExpr: + return .unsafeExpr(UnsafeExprSyntax(self)!) case .valueBindingPattern: return .valueBindingPattern(ValueBindingPatternSyntax(self)!) case .variableDecl: @@ -1041,6 +1047,10 @@ public enum ExprSyntaxEnum { case unresolvedAsExpr(UnresolvedAsExprSyntax) case unresolvedIsExpr(UnresolvedIsExprSyntax) case unresolvedTernaryExpr(UnresolvedTernaryExprSyntax) + #if compiler(>=5.8) + @_spi(ExperimentalLanguageFeatures) + #endif + case unsafeExpr(UnsafeExprSyntax) } extension ExprSyntax { @@ -1153,6 +1163,8 @@ extension ExprSyntax { return .unresolvedIsExpr(UnresolvedIsExprSyntax(self)!) case .unresolvedTernaryExpr: return .unresolvedTernaryExpr(UnresolvedTernaryExprSyntax(self)!) + case .unsafeExpr: + return .unsafeExpr(UnsafeExprSyntax(self)!) default: preconditionFailure("unknown Expr syntax kind") } diff --git a/Sources/SwiftSyntax/generated/SyntaxKind.swift b/Sources/SwiftSyntax/generated/SyntaxKind.swift index c88765ee1f6..b3bd4e49111 100644 --- a/Sources/SwiftSyntax/generated/SyntaxKind.swift +++ b/Sources/SwiftSyntax/generated/SyntaxKind.swift @@ -305,6 +305,10 @@ public enum SyntaxKind: Sendable { case unresolvedAsExpr case unresolvedIsExpr case unresolvedTernaryExpr + #if compiler(>=5.8) + @_spi(ExperimentalLanguageFeatures) + #endif + case unsafeExpr case valueBindingPattern case variableDecl case versionComponentList @@ -994,6 +998,8 @@ public enum SyntaxKind: Sendable { return UnresolvedIsExprSyntax.self case .unresolvedTernaryExpr: return UnresolvedTernaryExprSyntax.self + case .unsafeExpr: + return UnsafeExprSyntax.self case .valueBindingPattern: return ValueBindingPatternSyntax.self case .variableDecl: diff --git a/Sources/SwiftSyntax/generated/SyntaxRewriter.swift b/Sources/SwiftSyntax/generated/SyntaxRewriter.swift index 8c503ed4910..e91d2adb0c8 100644 --- a/Sources/SwiftSyntax/generated/SyntaxRewriter.swift +++ b/Sources/SwiftSyntax/generated/SyntaxRewriter.swift @@ -31,9 +31,6 @@ open class SyntaxRewriter { /// intermediate nodes should be allocated. private let arena: SyntaxArena? - /// 'Syntax' object factory recycling 'Syntax.Info' instances. - private let nodeFactory: SyntaxNodeFactory = SyntaxNodeFactory() - public init(viewMode: SyntaxTreeViewMode = .sourceAccurate) { self.viewMode = viewMode self.arena = nil @@ -2023,6 +2020,16 @@ open class SyntaxRewriter { return ExprSyntax(UnresolvedTernaryExprSyntax(unsafeCasting: visitChildren(node._syntaxNode))) } + /// Visit a `UnsafeExprSyntax`. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + #if compiler(>=5.8) + @_spi(ExperimentalLanguageFeatures) + #endif + open func visit(_ node: UnsafeExprSyntax) -> ExprSyntax { + return ExprSyntax(UnsafeExprSyntax(unsafeCasting: visitChildren(node._syntaxNode))) + } + /// Visit a ``ValueBindingPatternSyntax``. /// - Parameter node: the node that is being visited /// - Returns: the rewritten node @@ -3507,6 +3514,11 @@ open class SyntaxRewriter { Syntax(visit(UnresolvedTernaryExprSyntax(unsafeCasting: node))) } + @inline(never) + private func visitUnsafeExprSyntaxImpl(_ node: Syntax) -> Syntax { + Syntax(visit(UnsafeExprSyntax(unsafeCasting: node))) + } + @inline(never) private func visitValueBindingPatternSyntaxImpl(_ node: Syntax) -> Syntax { Syntax(visit(ValueBindingPatternSyntax(unsafeCasting: node))) @@ -4139,6 +4151,8 @@ open class SyntaxRewriter { return self.visitUnresolvedIsExprSyntaxImpl(_:) case .unresolvedTernaryExpr: return self.visitUnresolvedTernaryExprSyntaxImpl(_:) + case .unsafeExpr: + return self.visitUnsafeExprSyntaxImpl(_:) case .valueBindingPattern: return self.visitValueBindingPatternSyntaxImpl(_:) case .variableDecl: @@ -4717,6 +4731,8 @@ open class SyntaxRewriter { return visitUnresolvedIsExprSyntaxImpl(node) case .unresolvedTernaryExpr: return visitUnresolvedTernaryExprSyntaxImpl(node) + case .unsafeExpr: + return visitUnsafeExprSyntaxImpl(node) case .valueBindingPattern: return visitValueBindingPatternSyntaxImpl(node) case .variableDecl: @@ -4769,11 +4785,11 @@ open class SyntaxRewriter { // with 'Syntax' var rewrittens: ContiguousArray = [] - for case let (child?, info) in RawSyntaxChildren(node) where viewMode.shouldTraverse(node: child) { + for case let childDataRef? in node.layoutBuffer where viewMode.shouldTraverse(node: childDataRef.pointee.raw) { // Build the Syntax node to rewrite - var childNode = visitImpl(nodeFactory.create(parent: node, raw: child, absoluteInfo: info)) - if childNode.raw.id != child.id { + let childNode = visitImpl(Syntax(arena: node.arena, dataRef: childDataRef)) + if childNode.raw.id != childDataRef.pointee.raw.id { // The node was rewritten, let's handle it if newLayout.baseAddress == nil { @@ -4784,13 +4800,10 @@ open class SyntaxRewriter { } // Update the rewritten child. - newLayout[Int(info.indexInParent)] = childNode.raw + newLayout[Int(childDataRef.pointee.absoluteInfo.layoutIndexInParent)] = childNode.raw // Retain the syntax arena of the new node until it's wrapped with Syntax node. rewrittens.append(childNode.raw.arenaReference.retained) } - - // Recycle 'childNode.info' - nodeFactory.dispose(&childNode) } if newLayout.baseAddress != nil { diff --git a/Sources/SwiftSyntax/generated/SyntaxVisitor.swift b/Sources/SwiftSyntax/generated/SyntaxVisitor.swift index 23ca802e1c9..89dbf80a3ad 100644 --- a/Sources/SwiftSyntax/generated/SyntaxVisitor.swift +++ b/Sources/SwiftSyntax/generated/SyntaxVisitor.swift @@ -24,9 +24,6 @@ public enum SyntaxVisitorContinueKind { open class SyntaxVisitor { public let viewMode: SyntaxTreeViewMode - /// 'Syntax' object factory recycling 'Syntax.Info' instances. - private let nodeFactory: SyntaxNodeFactory = SyntaxNodeFactory() - public init(viewMode: SyntaxTreeViewMode) { self.viewMode = viewMode } @@ -3337,6 +3334,24 @@ open class SyntaxVisitor { open func visitPost(_ node: UnresolvedTernaryExprSyntax) { } + /// Visiting `UnsafeExprSyntax` specifically. + /// - Parameter node: the node we are visiting. + /// - Returns: how should we continue visiting. + #if compiler(>=5.8) + @_spi(ExperimentalLanguageFeatures) + #endif + open func visit(_ node: UnsafeExprSyntax) -> SyntaxVisitorContinueKind { + return .visitChildren + } + + /// The function called after visiting `UnsafeExprSyntax` and its descendants. + /// - node: the node we just finished visiting. + #if compiler(>=5.8) + @_spi(ExperimentalLanguageFeatures) + #endif + open func visitPost(_ node: UnsafeExprSyntax) { + } + /// Visiting ``ValueBindingPatternSyntax`` specifically. /// - Parameter node: the node we are visiting. /// - Returns: how should we continue visiting. @@ -5676,6 +5691,14 @@ open class SyntaxVisitor { visitPost(UnresolvedTernaryExprSyntax(unsafeCasting: node)) } + @inline(never) + private func visitUnsafeExprSyntaxImpl(_ node: Syntax) { + if visit(UnsafeExprSyntax(unsafeCasting: node)) == .visitChildren { + visitChildren(node) + } + visitPost(UnsafeExprSyntax(unsafeCasting: node)) + } + @inline(never) private func visitValueBindingPatternSyntaxImpl(_ node: Syntax) { if visit(ValueBindingPatternSyntax(unsafeCasting: node)) == .visitChildren { @@ -6344,6 +6367,8 @@ open class SyntaxVisitor { return self.visitUnresolvedIsExprSyntaxImpl(_:) case .unresolvedTernaryExpr: return self.visitUnresolvedTernaryExprSyntaxImpl(_:) + case .unsafeExpr: + return self.visitUnsafeExprSyntaxImpl(_:) case .valueBindingPattern: return self.visitValueBindingPatternSyntaxImpl(_:) case .variableDecl: @@ -6922,6 +6947,8 @@ open class SyntaxVisitor { self.visitUnresolvedIsExprSyntaxImpl(node) case .unresolvedTernaryExpr: self.visitUnresolvedTernaryExprSyntaxImpl(node) + case .unsafeExpr: + self.visitUnsafeExprSyntaxImpl(node) case .valueBindingPattern: self.visitValueBindingPatternSyntaxImpl(node) case .variableDecl: @@ -6951,10 +6978,8 @@ open class SyntaxVisitor { #endif private func visitChildren(_ node: Syntax) { - for case let (child?, info) in RawSyntaxChildren(node) where viewMode.shouldTraverse(node: child) { - var childNode = nodeFactory.create(parent: node, raw: child, absoluteInfo: info) - dispatchVisit(childNode) - nodeFactory.dispose(&childNode) + for case let childDataRef? in node.layoutBuffer where viewMode.shouldTraverse(node: childDataRef.pointee.raw) { + dispatchVisit(Syntax(arena: node.arena, dataRef: childDataRef)) } } } diff --git a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesEF.swift b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesEF.swift index 376db16d670..7ac0c48d843 100644 --- a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesEF.swift +++ b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesEF.swift @@ -988,7 +988,7 @@ public struct RawExprSyntax: RawExprSyntaxNodeProtocol { public static func isKindOf(_ raw: RawSyntax) -> Bool { switch raw.kind { - case .arrayExpr, .arrowExpr, .asExpr, .assignmentExpr, .awaitExpr, .binaryOperatorExpr, .booleanLiteralExpr, .borrowExpr, ._canImportExpr, ._canImportVersionInfo, .closureExpr, .consumeExpr, .copyExpr, .declReferenceExpr, .dictionaryExpr, .discardAssignmentExpr, .doExpr, .editorPlaceholderExpr, .floatLiteralExpr, .forceUnwrapExpr, .functionCallExpr, .genericSpecializationExpr, .ifExpr, .inOutExpr, .infixOperatorExpr, .integerLiteralExpr, .isExpr, .keyPathExpr, .macroExpansionExpr, .memberAccessExpr, .missingExpr, .nilLiteralExpr, .optionalChainingExpr, .packElementExpr, .packExpansionExpr, .patternExpr, .postfixIfConfigExpr, .postfixOperatorExpr, .prefixOperatorExpr, .regexLiteralExpr, .sequenceExpr, .simpleStringLiteralExpr, .stringLiteralExpr, .subscriptCallExpr, .superExpr, .switchExpr, .ternaryExpr, .tryExpr, .tupleExpr, .typeExpr, .unresolvedAsExpr, .unresolvedIsExpr, .unresolvedTernaryExpr: + case .arrayExpr, .arrowExpr, .asExpr, .assignmentExpr, .awaitExpr, .binaryOperatorExpr, .booleanLiteralExpr, .borrowExpr, ._canImportExpr, ._canImportVersionInfo, .closureExpr, .consumeExpr, .copyExpr, .declReferenceExpr, .dictionaryExpr, .discardAssignmentExpr, .doExpr, .editorPlaceholderExpr, .floatLiteralExpr, .forceUnwrapExpr, .functionCallExpr, .genericSpecializationExpr, .ifExpr, .inOutExpr, .infixOperatorExpr, .integerLiteralExpr, .isExpr, .keyPathExpr, .macroExpansionExpr, .memberAccessExpr, .missingExpr, .nilLiteralExpr, .optionalChainingExpr, .packElementExpr, .packExpansionExpr, .patternExpr, .postfixIfConfigExpr, .postfixOperatorExpr, .prefixOperatorExpr, .regexLiteralExpr, .sequenceExpr, .simpleStringLiteralExpr, .stringLiteralExpr, .subscriptCallExpr, .superExpr, .switchExpr, .ternaryExpr, .tryExpr, .tupleExpr, .typeExpr, .unresolvedAsExpr, .unresolvedIsExpr, .unresolvedTernaryExpr, .unsafeExpr: return true default: return false diff --git a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesTUVWXYZ.swift b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesTUVWXYZ.swift index 4a2489c4ba0..0de57b84777 100644 --- a/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesTUVWXYZ.swift +++ b/Sources/SwiftSyntax/generated/raw/RawSyntaxNodesTUVWXYZ.swift @@ -1955,6 +1955,79 @@ public struct RawUnresolvedTernaryExprSyntax: RawExprSyntaxNodeProtocol { } } +#if compiler(>=5.8) +@_spi(ExperimentalLanguageFeatures) +#endif +@_spi(RawSyntax) +public struct RawUnsafeExprSyntax: RawExprSyntaxNodeProtocol { + @_spi(RawSyntax) + public var layoutView: RawSyntaxLayoutView { + return raw.layoutView! + } + + public static func isKindOf(_ raw: RawSyntax) -> Bool { + return raw.kind == .unsafeExpr + } + + 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( + _ unexpectedBeforeUnsafeKeyword: RawUnexpectedNodesSyntax? = nil, + unsafeKeyword: RawTokenSyntax, + _ unexpectedBetweenUnsafeKeywordAndExpression: RawUnexpectedNodesSyntax? = nil, + expression: some RawExprSyntaxNodeProtocol, + _ unexpectedAfterExpression: RawUnexpectedNodesSyntax? = nil, + arena: __shared SyntaxArena + ) { + let raw = RawSyntax.makeLayout( + kind: .unsafeExpr, uninitializedCount: 5, arena: arena) { layout in + layout.initialize(repeating: nil) + layout[0] = unexpectedBeforeUnsafeKeyword?.raw + layout[1] = unsafeKeyword.raw + layout[2] = unexpectedBetweenUnsafeKeywordAndExpression?.raw + layout[3] = expression.raw + layout[4] = unexpectedAfterExpression?.raw + } + self.init(unchecked: raw) + } + + public var unexpectedBeforeUnsafeKeyword: RawUnexpectedNodesSyntax? { + layoutView.children[0].map(RawUnexpectedNodesSyntax.init(raw:)) + } + + public var unsafeKeyword: RawTokenSyntax { + layoutView.children[1].map(RawTokenSyntax.init(raw:))! + } + + public var unexpectedBetweenUnsafeKeywordAndExpression: RawUnexpectedNodesSyntax? { + layoutView.children[2].map(RawUnexpectedNodesSyntax.init(raw:)) + } + + public var expression: RawExprSyntax { + layoutView.children[3].map(RawExprSyntax.init(raw:))! + } + + public var unexpectedAfterExpression: 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 3a04edbea02..c46645b2dc1 100644 --- a/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift +++ b/Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift @@ -950,7 +950,7 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { assertNoError(kind, 6, verify(layout[6], as: RawUnexpectedNodesSyntax?.self)) assertNoError(kind, 7, verify(layout[7], as: RawTokenSyntax?.self, tokenChoices: [.tokenKind(.period)])) assertNoError(kind, 8, verify(layout[8], as: RawUnexpectedNodesSyntax?.self)) - assertNoError(kind, 9, verify(layout[9], as: RawTokenSyntax?.self, tokenChoices: [.keyword("get"), .keyword("set")])) + assertNoError(kind, 9, verify(layout[9], as: RawTokenSyntax?.self, tokenChoices: [.keyword("get"), .keyword("set"), .keyword("_modify")])) assertNoError(kind, 10, verify(layout[10], as: RawUnexpectedNodesSyntax?.self)) assertNoError(kind, 11, verify(layout[11], as: RawTokenSyntax?.self, tokenChoices: [.tokenKind(.comma)])) assertNoError(kind, 12, verify(layout[12], as: RawUnexpectedNodesSyntax?.self)) @@ -2973,6 +2973,14 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { assertNoError(kind, 5, verify(layout[5], as: RawTokenSyntax.self, tokenChoices: [.tokenKind(.colon)])) assertNoError(kind, 6, verify(layout[6], as: RawUnexpectedNodesSyntax?.self)) } + func validateUnsafeExprSyntax(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("unsafe")])) + assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self)) + assertNoError(kind, 3, verify(layout[3], as: RawExprSyntax.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)) @@ -3632,6 +3640,8 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) { validateUnresolvedIsExprSyntax(kind: kind, layout: layout) case .unresolvedTernaryExpr: validateUnresolvedTernaryExprSyntax(kind: kind, layout: layout) + case .unsafeExpr: + validateUnsafeExprSyntax(kind: kind, layout: layout) case .valueBindingPattern: validateValueBindingPatternSyntax(kind: kind, layout: layout) case .variableDecl: diff --git a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesD.swift b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesD.swift index 4977ad6958a..ce581322301 100644 --- a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesD.swift +++ b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesD.swift @@ -1239,7 +1239,7 @@ public struct DeinitializerEffectSpecifiersSyntax: SyntaxProtocol, SyntaxHashabl /// - `colon`: `:` /// - `originalDeclName`: ``ExprSyntax`` /// - `period`: `.`? -/// - `accessorSpecifier`: (`get` | `set`)? +/// - `accessorSpecifier`: (`get` | `set` | `_modify`)? /// - `comma`: `,`? /// - `arguments`: ``DifferentiabilityWithRespectToArgumentSyntax``? /// @@ -1439,6 +1439,7 @@ public struct DerivativeAttributeArgumentsSyntax: SyntaxProtocol, SyntaxHashable /// For syntax trees generated by the parser, this is guaranteed to be one of the following kinds: /// - `get` /// - `set` + /// - `_modify` public var accessorSpecifier: TokenSyntax? { get { return Syntax(self).child(at: 9)?.cast(TokenSyntax.self) diff --git a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesEF.swift b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesEF.swift index cddd3ae6fda..01ac92dda70 100644 --- a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesEF.swift +++ b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesEF.swift @@ -3150,11 +3150,11 @@ public struct FunctionCallExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _LeafE _ unexpectedBeforeCalledExpression: UnexpectedNodesSyntax? = nil, calledExpression: some ExprSyntaxProtocol, _ unexpectedBetweenCalledExpressionAndLeftParen: UnexpectedNodesSyntax? = nil, - leftParen: TokenSyntax? = nil, + leftParen: TokenSyntax?, _ unexpectedBetweenLeftParenAndArguments: UnexpectedNodesSyntax? = nil, arguments: LabeledExprListSyntax, _ unexpectedBetweenArgumentsAndRightParen: UnexpectedNodesSyntax? = nil, - rightParen: TokenSyntax? = nil, + rightParen: TokenSyntax?, _ unexpectedBetweenRightParenAndTrailingClosure: UnexpectedNodesSyntax? = nil, trailingClosure: ClosureExprSyntax? = nil, _ unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures: UnexpectedNodesSyntax? = nil, diff --git a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesTUVWXYZ.swift b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesTUVWXYZ.swift index 1f06653f5b2..2c9705a89c0 100644 --- a/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesTUVWXYZ.swift +++ b/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesTUVWXYZ.swift @@ -3272,6 +3272,128 @@ public struct UnresolvedTernaryExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _ ]) } +// MARK: - UnsafeExprSyntax + +/// - Note: Requires experimental feature `unsafeExpression`. +/// +/// ### Children +/// +/// - `unsafeKeyword`: `unsafe` +/// - `expression`: ``ExprSyntax`` +#if compiler(>=5.8) +@_spi(ExperimentalLanguageFeatures) +#endif +public struct UnsafeExprSyntax: ExprSyntaxProtocol, SyntaxHashable, _LeafExprSyntaxNodeProtocol { + public let _syntaxNode: Syntax + + public init?(_ node: __shared some SyntaxProtocol) { + guard node.raw.kind == .unsafeExpr 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, + _ unexpectedBeforeUnsafeKeyword: UnexpectedNodesSyntax? = nil, + unsafeKeyword: TokenSyntax = .keyword(.unsafe), + _ unexpectedBetweenUnsafeKeywordAndExpression: UnexpectedNodesSyntax? = nil, + expression: some ExprSyntaxProtocol, + _ unexpectedAfterExpression: 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((SyntaxArena(), ( + unexpectedBeforeUnsafeKeyword, + unsafeKeyword, + unexpectedBetweenUnsafeKeywordAndExpression, + expression, + unexpectedAfterExpression + ))) { (arena, _) in + let layout: [RawSyntax?] = [ + unexpectedBeforeUnsafeKeyword?.raw, + unsafeKeyword.raw, + unexpectedBetweenUnsafeKeywordAndExpression?.raw, + expression.raw, + unexpectedAfterExpression?.raw + ] + let raw = RawSyntax.makeLayout( + kind: SyntaxKind.unsafeExpr, + from: layout, + arena: arena, + leadingTrivia: leadingTrivia, + trailingTrivia: trailingTrivia + ) + return Syntax.forRoot(raw, rawNodeArena: arena).cast(Self.self) + } + } + + public var unexpectedBeforeUnsafeKeyword: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 0)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 0, with: Syntax(value), arena: SyntaxArena()).cast(UnsafeExprSyntax.self) + } + } + + /// ### Tokens + /// + /// For syntax trees generated by the parser, this is guaranteed to be `unsafe`. + public var unsafeKeyword: TokenSyntax { + get { + return Syntax(self).child(at: 1)!.cast(TokenSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 1, with: Syntax(value), arena: SyntaxArena()).cast(UnsafeExprSyntax.self) + } + } + + public var unexpectedBetweenUnsafeKeywordAndExpression: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 2)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 2, with: Syntax(value), arena: SyntaxArena()).cast(UnsafeExprSyntax.self) + } + } + + public var expression: ExprSyntax { + get { + return Syntax(self).child(at: 3)!.cast(ExprSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 3, with: Syntax(value), arena: SyntaxArena()).cast(UnsafeExprSyntax.self) + } + } + + public var unexpectedAfterExpression: UnexpectedNodesSyntax? { + get { + return Syntax(self).child(at: 4)?.cast(UnexpectedNodesSyntax.self) + } + set(value) { + self = Syntax(self).replacingChild(at: 4, with: Syntax(value), arena: SyntaxArena()).cast(UnsafeExprSyntax.self) + } + } + + public static let structure: SyntaxNodeStructure = .layout([ + \Self.unexpectedBeforeUnsafeKeyword, + \Self.unsafeKeyword, + \Self.unexpectedBetweenUnsafeKeywordAndExpression, + \Self.expression, + \Self.unexpectedAfterExpression + ]) +} + // MARK: - ValueBindingPatternSyntax /// ### Children diff --git a/Sources/SwiftSyntaxBuilder/SwiftSyntaxBuilderCompatibility.swift b/Sources/SwiftSyntaxBuilder/SwiftSyntaxBuilderCompatibility.swift index 89f2009a050..f40eca51f30 100644 --- a/Sources/SwiftSyntaxBuilder/SwiftSyntaxBuilderCompatibility.swift +++ b/Sources/SwiftSyntaxBuilder/SwiftSyntaxBuilderCompatibility.swift @@ -14,7 +14,7 @@ // All users of the declarations in this file should transition away from them ASAP. #if compiler(>=6) -internal import SwiftSyntax +public import SwiftSyntax #else import SwiftSyntax #endif @@ -22,6 +22,253 @@ import SwiftSyntax @available(*, deprecated, renamed: "ImportPathComponentListBuilder") public typealias AccessPathBuilder = ImportPathComponentListBuilder +/// Written here to enable the use of `@LabeledExprListBuilder`. +extension FunctionCallExprSyntax { + @_disfavoredOverload + @available( + *, + deprecated, + message: "Use the initializer that does not provide default values for leftParen and rightParen." + ) + public init( + leadingTrivia: Trivia? = nil, + unexpectedBeforeCalledExpression: UnexpectedNodesSyntax? = nil, + calledExpression: ExprSyntaxProtocol, + unexpectedBetweenCalledExpressionAndLeftParen: UnexpectedNodesSyntax? = nil, + unexpectedBetweenLeftParenAndArguments: UnexpectedNodesSyntax? = nil, + unexpectedBetweenArgumentsAndRightParen: UnexpectedNodesSyntax? = nil, + rightParen: TokenSyntax? = nil, + unexpectedBetweenRightParenAndTrailingClosure: UnexpectedNodesSyntax? = nil, + trailingClosure: ClosureExprSyntax? = nil, + unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures: UnexpectedNodesSyntax? = nil, + additionalTrailingClosures: MultipleTrailingClosureElementListSyntax = [], + unexpectedAfterAdditionalTrailingClosures: UnexpectedNodesSyntax? = nil, + @LabeledExprListBuilder argumentsBuilder: () throws -> LabeledExprListSyntax, + trailingTrivia: Trivia? = nil + ) rethrows { + try self.init( + leadingTrivia: leadingTrivia, + unexpectedBeforeCalledExpression, + calledExpression: ExprSyntax(fromProtocol: calledExpression), + unexpectedBetweenCalledExpressionAndLeftParen, + leftParen: nil, + unexpectedBetweenLeftParenAndArguments, + arguments: argumentsBuilder(), + unexpectedBetweenArgumentsAndRightParen, + rightParen: rightParen, + unexpectedBetweenRightParenAndTrailingClosure, + trailingClosure: trailingClosure, + unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures, + additionalTrailingClosures: additionalTrailingClosures, + unexpectedAfterAdditionalTrailingClosures, + trailingTrivia: trailingTrivia + ) + } + + @_disfavoredOverload + @available( + *, + deprecated, + message: "Use the initializer that does not provide default values for leftParen and rightParen." + ) + public init( + leadingTrivia: Trivia? = nil, + unexpectedBeforeCalledExpression: UnexpectedNodesSyntax? = nil, + calledExpression: ExprSyntaxProtocol, + unexpectedBetweenCalledExpressionAndLeftParen: UnexpectedNodesSyntax? = nil, + leftParen: TokenSyntax? = nil, + unexpectedBetweenLeftParenAndArguments: UnexpectedNodesSyntax? = nil, + unexpectedBetweenArgumentsAndRightParen: UnexpectedNodesSyntax? = nil, + unexpectedBetweenRightParenAndTrailingClosure: UnexpectedNodesSyntax? = nil, + trailingClosure: ClosureExprSyntax? = nil, + unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures: UnexpectedNodesSyntax? = nil, + additionalTrailingClosures: MultipleTrailingClosureElementListSyntax = [], + unexpectedAfterAdditionalTrailingClosures: UnexpectedNodesSyntax? = nil, + @LabeledExprListBuilder argumentsBuilder: () throws -> LabeledExprListSyntax, + trailingTrivia: Trivia? = nil + ) rethrows { + try self.init( + leadingTrivia: leadingTrivia, + unexpectedBeforeCalledExpression, + calledExpression: ExprSyntax(fromProtocol: calledExpression), + unexpectedBetweenCalledExpressionAndLeftParen, + leftParen: leftParen, + unexpectedBetweenLeftParenAndArguments, + arguments: argumentsBuilder(), + unexpectedBetweenArgumentsAndRightParen, + rightParen: nil, + unexpectedBetweenRightParenAndTrailingClosure, + trailingClosure: trailingClosure, + unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures, + additionalTrailingClosures: additionalTrailingClosures, + unexpectedAfterAdditionalTrailingClosures, + trailingTrivia: trailingTrivia + ) + } + + @_disfavoredOverload + @available( + *, + deprecated, + message: "Use the initializer that does not provide default values for leftParen and rightParen." + ) + public init( + leadingTrivia: Trivia? = nil, + unexpectedBeforeCalledExpression: UnexpectedNodesSyntax? = nil, + calledExpression: ExprSyntaxProtocol, + unexpectedBetweenCalledExpressionAndLeftParen: UnexpectedNodesSyntax? = nil, + unexpectedBetweenLeftParenAndArguments: UnexpectedNodesSyntax? = nil, + unexpectedBetweenArgumentsAndRightParen: UnexpectedNodesSyntax? = nil, + unexpectedBetweenRightParenAndTrailingClosure: UnexpectedNodesSyntax? = nil, + trailingClosure: ClosureExprSyntax? = nil, + unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures: UnexpectedNodesSyntax? = nil, + additionalTrailingClosures: MultipleTrailingClosureElementListSyntax = [], + unexpectedAfterAdditionalTrailingClosures: UnexpectedNodesSyntax? = nil, + @LabeledExprListBuilder argumentsBuilder: () throws -> LabeledExprListSyntax, + trailingTrivia: Trivia? = nil + ) rethrows { + try self.init( + leadingTrivia: leadingTrivia, + unexpectedBeforeCalledExpression, + calledExpression: ExprSyntax(fromProtocol: calledExpression), + unexpectedBetweenCalledExpressionAndLeftParen, + leftParen: nil, + unexpectedBetweenLeftParenAndArguments, + arguments: argumentsBuilder(), + unexpectedBetweenArgumentsAndRightParen, + rightParen: nil, + unexpectedBetweenRightParenAndTrailingClosure, + trailingClosure: trailingClosure, + unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures, + additionalTrailingClosures: additionalTrailingClosures, + unexpectedAfterAdditionalTrailingClosures, + trailingTrivia: trailingTrivia + ) + } + + @_disfavoredOverload + @available( + *, + deprecated, + message: "Use the initializer that does not provide default values for leftParen and rightParen." + ) + public init( + leadingTrivia: Trivia? = nil, + unexpectedBeforeCalledExpression: UnexpectedNodesSyntax? = nil, + calledExpression: ExprSyntaxProtocol, + unexpectedBetweenCalledExpressionAndLeftParen: UnexpectedNodesSyntax? = nil, + unexpectedBetweenLeftParenAndArguments: UnexpectedNodesSyntax? = nil, + unexpectedBetweenArgumentsAndRightParen: UnexpectedNodesSyntax? = nil, + rightParen: TokenSyntax? = nil, + unexpectedBetweenRightParenAndTrailingClosure: UnexpectedNodesSyntax? = nil, + trailingClosure: ClosureExprSyntax? = nil, + unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures: UnexpectedNodesSyntax? = nil, + additionalTrailingClosures: MultipleTrailingClosureElementListSyntax = [], + unexpectedAfterAdditionalTrailingClosures: UnexpectedNodesSyntax? = nil, + @LabeledExprListBuilder argumentListBuilder: () throws -> LabeledExprListSyntax, + trailingTrivia: Trivia? = nil + ) rethrows { + try self.init( + leadingTrivia: leadingTrivia, + unexpectedBeforeCalledExpression, + calledExpression: ExprSyntax(fromProtocol: calledExpression), + unexpectedBetweenCalledExpressionAndLeftParen, + leftParen: nil, + unexpectedBetweenLeftParenAndArguments, + arguments: argumentListBuilder(), + unexpectedBetweenArgumentsAndRightParen, + rightParen: rightParen, + unexpectedBetweenRightParenAndTrailingClosure, + trailingClosure: trailingClosure, + unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures, + additionalTrailingClosures: additionalTrailingClosures, + unexpectedAfterAdditionalTrailingClosures, + trailingTrivia: trailingTrivia + ) + } + + @_disfavoredOverload + @available( + *, + deprecated, + message: "Use the initializer that does not provide default values for leftParen and rightParen." + ) + public init( + leadingTrivia: Trivia? = nil, + unexpectedBeforeCalledExpression: UnexpectedNodesSyntax? = nil, + calledExpression: ExprSyntaxProtocol, + unexpectedBetweenCalledExpressionAndLeftParen: UnexpectedNodesSyntax? = nil, + leftParen: TokenSyntax? = nil, + unexpectedBetweenLeftParenAndArguments: UnexpectedNodesSyntax? = nil, + unexpectedBetweenArgumentsAndRightParen: UnexpectedNodesSyntax? = nil, + unexpectedBetweenRightParenAndTrailingClosure: UnexpectedNodesSyntax? = nil, + trailingClosure: ClosureExprSyntax? = nil, + unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures: UnexpectedNodesSyntax? = nil, + additionalTrailingClosures: MultipleTrailingClosureElementListSyntax = [], + unexpectedAfterAdditionalTrailingClosures: UnexpectedNodesSyntax? = nil, + @LabeledExprListBuilder argumentListBuilder: () throws -> LabeledExprListSyntax, + trailingTrivia: Trivia? = nil + ) rethrows { + try self.init( + leadingTrivia: leadingTrivia, + unexpectedBeforeCalledExpression, + calledExpression: ExprSyntax(fromProtocol: calledExpression), + unexpectedBetweenCalledExpressionAndLeftParen, + leftParen: leftParen, + unexpectedBetweenLeftParenAndArguments, + arguments: argumentListBuilder(), + unexpectedBetweenArgumentsAndRightParen, + rightParen: nil, + unexpectedBetweenRightParenAndTrailingClosure, + trailingClosure: trailingClosure, + unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures, + additionalTrailingClosures: additionalTrailingClosures, + unexpectedAfterAdditionalTrailingClosures, + trailingTrivia: trailingTrivia + ) + } + + @_disfavoredOverload + @available( + *, + deprecated, + message: "Use the initializer that does not provide default values for leftParen and rightParen." + ) + public init( + leadingTrivia: Trivia? = nil, + unexpectedBeforeCalledExpression: UnexpectedNodesSyntax? = nil, + calledExpression: ExprSyntaxProtocol, + unexpectedBetweenCalledExpressionAndLeftParen: UnexpectedNodesSyntax? = nil, + unexpectedBetweenLeftParenAndArguments: UnexpectedNodesSyntax? = nil, + unexpectedBetweenArgumentsAndRightParen: UnexpectedNodesSyntax? = nil, + unexpectedBetweenRightParenAndTrailingClosure: UnexpectedNodesSyntax? = nil, + trailingClosure: ClosureExprSyntax? = nil, + unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures: UnexpectedNodesSyntax? = nil, + additionalTrailingClosures: MultipleTrailingClosureElementListSyntax = [], + unexpectedAfterAdditionalTrailingClosures: UnexpectedNodesSyntax? = nil, + @LabeledExprListBuilder argumentListBuilder: () throws -> LabeledExprListSyntax, + trailingTrivia: Trivia? = nil + ) rethrows { + try self.init( + leadingTrivia: leadingTrivia, + unexpectedBeforeCalledExpression, + calledExpression: ExprSyntax(fromProtocol: calledExpression), + unexpectedBetweenCalledExpressionAndLeftParen, + leftParen: nil, + unexpectedBetweenLeftParenAndArguments, + arguments: argumentListBuilder(), + unexpectedBetweenArgumentsAndRightParen, + rightParen: nil, + unexpectedBetweenRightParenAndTrailingClosure, + trailingClosure: trailingClosure, + unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures, + additionalTrailingClosures: additionalTrailingClosures, + unexpectedAfterAdditionalTrailingClosures, + trailingTrivia: trailingTrivia + ) + } +} + @available(*, deprecated, renamed: "LabeledExprListBuilder") public typealias TupleExprElementListBuilder = LabeledExprListBuilder diff --git a/Sources/SwiftSyntaxBuilder/generated/BuildableNodes.swift b/Sources/SwiftSyntaxBuilder/generated/BuildableNodes.swift index 17f43279cb0..fae1d39d232 100644 --- a/Sources/SwiftSyntaxBuilder/generated/BuildableNodes.swift +++ b/Sources/SwiftSyntaxBuilder/generated/BuildableNodes.swift @@ -628,10 +628,10 @@ extension FunctionCallExprSyntax { unexpectedBeforeCalledExpression: UnexpectedNodesSyntax? = nil, calledExpression: ExprSyntaxProtocol, unexpectedBetweenCalledExpressionAndLeftParen: UnexpectedNodesSyntax? = nil, - leftParen: TokenSyntax? = nil, + leftParen: TokenSyntax?, unexpectedBetweenLeftParenAndArguments: UnexpectedNodesSyntax? = nil, unexpectedBetweenArgumentsAndRightParen: UnexpectedNodesSyntax? = nil, - rightParen: TokenSyntax? = nil, + rightParen: TokenSyntax?, unexpectedBetweenRightParenAndTrailingClosure: UnexpectedNodesSyntax? = nil, trailingClosure: ClosureExprSyntax? = nil, unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures: UnexpectedNodesSyntax? = nil, diff --git a/Sources/SwiftSyntaxBuilder/generated/RenamedChildrenBuilderCompatibility.swift b/Sources/SwiftSyntaxBuilder/generated/RenamedChildrenBuilderCompatibility.swift index b775c3eda01..f7f344ef9a9 100644 --- a/Sources/SwiftSyntaxBuilder/generated/RenamedChildrenBuilderCompatibility.swift +++ b/Sources/SwiftSyntaxBuilder/generated/RenamedChildrenBuilderCompatibility.swift @@ -340,10 +340,10 @@ extension FunctionCallExprSyntax { unexpectedBeforeCalledExpression: UnexpectedNodesSyntax? = nil, calledExpression: ExprSyntaxProtocol, unexpectedBetweenCalledExpressionAndLeftParen: UnexpectedNodesSyntax? = nil, - leftParen: TokenSyntax? = nil, + leftParen: TokenSyntax?, unexpectedBetweenLeftParenAndArgumentList: UnexpectedNodesSyntax? = nil, unexpectedBetweenArgumentListAndRightParen: UnexpectedNodesSyntax? = nil, - rightParen: TokenSyntax? = nil, + rightParen: TokenSyntax?, unexpectedBetweenRightParenAndTrailingClosure: UnexpectedNodesSyntax? = nil, trailingClosure: ClosureExprSyntax? = nil, unexpectedBetweenTrailingClosureAndAdditionalTrailingClosures: UnexpectedNodesSyntax? = nil, diff --git a/Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift b/Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift index d858103cc0a..e4e923f794b 100644 --- a/Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift +++ b/Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift @@ -930,23 +930,30 @@ private class MacroApplication: SyntaxRewriter { } override func visit(_ node: VariableDeclSyntax) -> DeclSyntax { - var node = super.visit(node).cast(VariableDeclSyntax.self) + var rewrittenNode = super.visit(node).cast(VariableDeclSyntax.self) - guard !macroAttributes(attachedTo: DeclSyntax(node), ofType: AccessorMacro.Type.self).isEmpty else { - return DeclSyntax(node) + guard !macroAttributes(attachedTo: DeclSyntax(rewrittenNode), ofType: AccessorMacro.Type.self).isEmpty else { + return DeclSyntax(rewrittenNode) } - guard node.bindings.count == 1, - var binding = node.bindings.first + guard rewrittenNode.bindings.count == 1, + var binding = rewrittenNode.bindings.first else { contextGenerator(Syntax(node)).addDiagnostics( from: MacroApplicationError.accessorMacroOnVariableWithMultipleBindings, - node: node + node: rewrittenNode ) - return DeclSyntax(node) + return DeclSyntax(rewrittenNode) } - var expansion = expandAccessors(of: node, existingAccessors: binding.accessorBlock) + // Generate the context based on the node before it was rewritten by calling `super.visit`. If the node was modified + // by `super.visit`, it will not have any parents, which would cause the lexical context to be empty. + let context = contextGenerator(Syntax(node)) + var expansion = expandAccessors( + of: rewrittenNode, + context: context, + existingAccessors: binding.accessorBlock + ) if expansion.accessors != binding.accessorBlock { if binding.accessorBlock == nil { @@ -966,16 +973,25 @@ private class MacroApplication: SyntaxRewriter { binding.accessorBlock = expansion.accessors } - node.bindings = [binding] + rewrittenNode.bindings = [binding] } - return DeclSyntax(node) + return DeclSyntax(rewrittenNode) } override func visit(_ node: SubscriptDeclSyntax) -> DeclSyntax { - var node = super.visit(node).cast(SubscriptDeclSyntax.self) - node.accessorBlock = expandAccessors(of: node, existingAccessors: node.accessorBlock).accessors - return DeclSyntax(node) + var rewrittenNode = super.visit(node).cast(SubscriptDeclSyntax.self) + // Generate the context based on the node before it was rewritten by calling `super.visit`. If the node was modified + // by `super.visit`, it will not have any parents, which would cause the lexical context to be empty. + let context = contextGenerator(Syntax(node)) + rewrittenNode.accessorBlock = + expandAccessors( + of: rewrittenNode, + context: context, + existingAccessors: rewrittenNode.accessorBlock + ) + .accessors + return DeclSyntax(rewrittenNode) } } @@ -1160,6 +1176,7 @@ extension MacroApplication { /// removed). private func expandAccessors( of storage: some DeclSyntaxProtocol, + context: Context, existingAccessors: AccessorBlockSyntax? ) -> (accessors: AccessorBlockSyntax?, expandsGetSet: Bool) { let accessorMacros = macroAttributes(attachedTo: DeclSyntax(storage), ofType: AccessorMacro.Type.self) @@ -1184,7 +1201,7 @@ extension MacroApplication { definition: macro.definition, attributeNode: macro.attributeNode, attachedTo: DeclSyntax(storage), - in: contextGenerator(Syntax(storage)), + in: context, indentationWidth: indentationWidth ) { checkExpansions(newAccessors) @@ -1201,7 +1218,7 @@ extension MacroApplication { definition: macro.definition, attributeNode: macro.attributeNode, attachedTo: DeclSyntax(storage), - in: contextGenerator(Syntax(storage)), + in: context, indentationWidth: indentationWidth ) { guard case .accessors(let accessorList) = newAccessors.accessors else { @@ -1220,7 +1237,7 @@ extension MacroApplication { } } } catch { - contextGenerator(Syntax(storage)).addDiagnostics(from: error, node: macro.attributeNode) + context.addDiagnostics(from: error, node: macro.attributeNode) } } return (newAccessorsBlock, expandsGetSet) diff --git a/Sources/_SwiftSyntaxCShims/CMakeLists.txt b/Sources/_SwiftSyntaxCShims/CMakeLists.txt index 39cfa9294c2..9505468103b 100644 --- a/Sources/_SwiftSyntaxCShims/CMakeLists.txt +++ b/Sources/_SwiftSyntaxCShims/CMakeLists.txt @@ -1,5 +1,7 @@ set(target ${SWIFTSYNTAX_TARGET_NAMESPACE}_SwiftSyntaxCShims) -add_library(${target} INTERFACE) -target_include_directories(${target} INTERFACE "include") +add_library(${target} STATIC + PlatformMutex.c +) +target_include_directories(${target} PUBLIC "include") set_property(GLOBAL APPEND PROPERTY SWIFT_EXPORTS ${target}) install(TARGETS ${target} EXPORT SwiftSyntaxTargets) diff --git a/Sources/_SwiftSyntaxCShims/PlatformMutex.c b/Sources/_SwiftSyntaxCShims/PlatformMutex.c new file mode 100644 index 00000000000..a3ceede13ef --- /dev/null +++ b/Sources/_SwiftSyntaxCShims/PlatformMutex.c @@ -0,0 +1,107 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 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 +// +//===----------------------------------------------------------------------===// + +#include "PlatformMutex.h" +#include + +#if defined(__wasi__) && !defined(_REENTRANT) +#define SWIFTSYNTAX_HAS_THREAD 0 +#else +#define SWIFTSYNTAX_HAS_THREAD 1 +#endif + +#if SWIFTSYNTAX_HAS_THREAD +#if defined(__APPLE__) +#include + +PlatformMutex swiftsyntax_platform_mutex_create() { + PlatformMutex m; + m.opaque = malloc(sizeof(os_unfair_lock)); + *(os_unfair_lock_t)m.opaque = OS_UNFAIR_LOCK_INIT; + return m; +} + +void swiftsyntax_platform_mutex_lock(PlatformMutex m) { + os_unfair_lock_lock(m.opaque); +} + +void swiftsyntax_platform_mutex_unlock(PlatformMutex m) { + os_unfair_lock_unlock(m.opaque); +} + +void swiftsyntax_platform_mutex_destroy(PlatformMutex m) { + free(m.opaque); +} + +#elif defined(_WIN32) +#include + +PlatformMutex swiftsyntax_platform_mutex_create() { + PlatformMutex m; + m.opaque = malloc(sizeof(SRWLOCK)); + InitializeSRWLock(m.opaque); + return m; +} + +void swiftsyntax_platform_mutex_lock(PlatformMutex m) { + AcquireSRWLockExclusive(m.opaque); +} + +void swiftsyntax_platform_mutex_unlock(PlatformMutex m) { + ReleaseSRWLockExclusive(m.opaque); +} + +void swiftsyntax_platform_mutex_destroy(PlatformMutex m) { + free(m.opaque); +} + +#elif __has_include() +#include + +PlatformMutex swiftsyntax_platform_mutex_create() { + PlatformMutex m; + m.opaque = malloc(sizeof(pthread_mutex_t)); + pthread_mutex_init(m.opaque, 0); + return m; +} + +void swiftsyntax_platform_mutex_lock(PlatformMutex m) { + pthread_mutex_lock(m.opaque); +} + +void swiftsyntax_platform_mutex_unlock(PlatformMutex m) { + pthread_mutex_unlock(m.opaque); +} + +void swiftsyntax_platform_mutex_destroy(PlatformMutex m) { + pthread_mutex_destroy(m.opaque); + free(m.opaque); +} + +#else +#error "platform mutex implementation is not available" +// Add platform specific implementation above, or set SWIFTSYNTAX_HAS_THREAD to 0. +#endif + +#else // SWIFTSYNTAX_HAS_THREAD + +PlatformMutex swiftsyntax_platform_mutex_create() { + PlatformMutex m; + m.opaque = 0; + return m; +} + +void swiftsyntax_platform_mutex_lock(PlatformMutex m) {} +void swiftsyntax_platform_mutex_unlock(PlatformMutex m) {} +void swiftsyntax_platform_mutex_destroy(PlatformMutex m) {} + +#endif // SWIFTSYNTAX_HAS_THREAD diff --git a/Sources/_SwiftSyntaxCShims/dummy.c b/Sources/_SwiftSyntaxCShims/dummy.c deleted file mode 100644 index 8b137891791..00000000000 --- a/Sources/_SwiftSyntaxCShims/dummy.c +++ /dev/null @@ -1 +0,0 @@ - diff --git a/Sources/_SwiftSyntaxCShims/include/AtomicBool.h b/Sources/_SwiftSyntaxCShims/include/Atomics.h similarity index 77% rename from Sources/_SwiftSyntaxCShims/include/AtomicBool.h rename to Sources/_SwiftSyntaxCShims/include/Atomics.h index afa3caf4db9..cdbc4b68f7b 100644 --- a/Sources/_SwiftSyntaxCShims/include/AtomicBool.h +++ b/Sources/_SwiftSyntaxCShims/include/Atomics.h @@ -38,4 +38,16 @@ static inline void swiftsyntax_atomic_bool_destroy(AtomicBool *_Nonnull atomic) free(atomic); } +typedef struct { + _Atomic(const void *_Nullable) value; +} AtomicPointer; + +static inline const void *_Nullable swiftsyntax_atomic_pointer_get(const AtomicPointer *_Nonnull atomic) { + return atomic->value; +} + +static inline void swiftsyntax_atomic_pointer_set(AtomicPointer *_Nonnull atomic, const void *_Nullable newValue) { + atomic->value = newValue; +} + #endif // SWIFTSYNTAX_ATOMICBOOL_H diff --git a/Sources/_SwiftSyntaxCShims/include/PlatformMutex.h b/Sources/_SwiftSyntaxCShims/include/PlatformMutex.h new file mode 100644 index 00000000000..3288a0f1746 --- /dev/null +++ b/Sources/_SwiftSyntaxCShims/include/PlatformMutex.h @@ -0,0 +1,34 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFTSYNTAX_PLATFORMMUTEX_H +#define SWIFTSYNTAX_PLATFORMMUTEX_H + +#include "_bridging.h" + +typedef struct PlatformMutex { + void *opaque; +} PlatformMutex; + +SWIFT_NAME_S("PlatformMutex.create()") +PlatformMutex swiftsyntax_platform_mutex_create(void); + +SWIFT_NAME_S("PlatformMutex.lock(self:)") +void swiftsyntax_platform_mutex_lock(PlatformMutex m); + +SWIFT_NAME_S("PlatformMutex.unlock(self:)") +void swiftsyntax_platform_mutex_unlock(PlatformMutex m); + +SWIFT_NAME_S("PlatformMutex.destroy(self:)") +void swiftsyntax_platform_mutex_destroy(PlatformMutex m); + +#endif // SWIFTSYNTAX_PLATFORMMUTEX_H diff --git a/Sources/_SwiftSyntaxCShims/include/SwiftSyntaxCShims.h b/Sources/_SwiftSyntaxCShims/include/SwiftSyntaxCShims.h index 1db9283cdfb..cbe705c5808 100644 --- a/Sources/_SwiftSyntaxCShims/include/SwiftSyntaxCShims.h +++ b/Sources/_SwiftSyntaxCShims/include/SwiftSyntaxCShims.h @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "_includes.h" -#include "AtomicBool.h" +#include "Atomics.h" +#include "PlatformMutex.h" #include "swiftsyntax_errno.h" #include "swiftsyntax_stdio.h" diff --git a/Sources/_SwiftSyntaxCShims/include/_bridging.h b/Sources/_SwiftSyntaxCShims/include/_bridging.h new file mode 100644 index 00000000000..5d4c83f2771 --- /dev/null +++ b/Sources/_SwiftSyntaxCShims/include/_bridging.h @@ -0,0 +1,22 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFTSYNTAX_BRIDGING_H +#define SWIFTSYNTAX_BRIDGING_H + +#if __has_attribute(swift_name) +#define SWIFT_NAME_S(NAME) __attribute__((swift_name(NAME))) +#else +#define SWIFT_NAME_S(NAME) +#endif + +#endif // SWIFTSYNTAX_BRIDGING_H diff --git a/Sources/_SwiftSyntaxCShims/include/module.modulemap b/Sources/_SwiftSyntaxCShims/include/module.modulemap index 010d951f292..a37c41fa2f9 100644 --- a/Sources/_SwiftSyntaxCShims/include/module.modulemap +++ b/Sources/_SwiftSyntaxCShims/include/module.modulemap @@ -1,7 +1,11 @@ module _SwiftSyntaxCShims { header "_includes.h" - header "AtomicBool.h" + header "Atomics.h" + header "PlatformMutex.h" header "swiftsyntax_errno.h" header "swiftsyntax_stdio.h" + + textual header "_bridging.h" + export * } diff --git a/Sources/_SwiftSyntaxCShims/include/swiftsyntax_errno.h b/Sources/_SwiftSyntaxCShims/include/swiftsyntax_errno.h index 589fde7eaed..f21a17793c0 100644 --- a/Sources/_SwiftSyntaxCShims/include/swiftsyntax_errno.h +++ b/Sources/_SwiftSyntaxCShims/include/swiftsyntax_errno.h @@ -13,9 +13,11 @@ #ifndef SWIFTSYNTAX_ERRNO_H #define SWIFTSYNTAX_ERRNO_H +#include "_bridging.h" + #include -__attribute__((swift_name("getter:_errno()"))) +SWIFT_NAME_S("getter:_errno()") static inline int swiftsyntax_errno(void) { return errno; } diff --git a/Sources/_SwiftSyntaxCShims/include/swiftsyntax_stdio.h b/Sources/_SwiftSyntaxCShims/include/swiftsyntax_stdio.h index a7f24c3dfee..8bcc29e2a34 100644 --- a/Sources/_SwiftSyntaxCShims/include/swiftsyntax_stdio.h +++ b/Sources/_SwiftSyntaxCShims/include/swiftsyntax_stdio.h @@ -13,19 +13,21 @@ #ifndef SWIFTSYNTAX_STDIO_H #define SWIFTSYNTAX_STDIO_H +#include "_bridging.h" + #include -__attribute__((swift_name("getter:_stdout()"))) +SWIFT_NAME_S("getter:_stdout()") static inline FILE *swiftsyntax_stdout(void) { return stdout; } -__attribute__((swift_name("getter:_stdin()"))) +SWIFT_NAME_S("getter:_stdin()") static inline FILE *swiftsyntax_stdin(void) { return stdin; } -__attribute__((swift_name("getter:_stderr()"))) +SWIFT_NAME_S("getter:_stderr()") static inline FILE *swiftsyntax_stderr(void) { return stderr; } diff --git a/Tests/SwiftParserTest/ExpressionTests.swift b/Tests/SwiftParserTest/ExpressionTests.swift index 90498d7179a..1038026eb26 100644 --- a/Tests/SwiftParserTest/ExpressionTests.swift +++ b/Tests/SwiftParserTest/ExpressionTests.swift @@ -2188,6 +2188,40 @@ final class StatementExpressionTests: ParserTestCase { ) } + func testUnsafeExpr() { + assertParse( + """ + func f() { + let x = unsafe y + } + """, + experimentalFeatures: .unsafeExpression + ) + + assertParse( + """ + func f() { + let x = unsafe1️⃣ y + } + """, + diagnostics: [ + DiagnosticSpec( + message: "consecutive statements on a line must be separated by newline or ';'", + fixIts: [ + "insert newline", + "insert ';'", + ] + ) + ], + fixedSource: """ + func f() { + let x = unsafe + y + } + """ + ) + } + func testUnterminatedInterpolationAtEndOfMultilineStringLiteral() { assertParse( #""" diff --git a/Tests/SwiftParserTest/ThenStatementTests.swift b/Tests/SwiftParserTest/ThenStatementTests.swift index 6435b60a304..0fa963a18b0 100644 --- a/Tests/SwiftParserTest/ThenStatementTests.swift +++ b/Tests/SwiftParserTest/ThenStatementTests.swift @@ -267,7 +267,9 @@ final class ThenStatementTests: ParserTestCase { substructure: FunctionCallExprSyntax( calledExpression: DeclReferenceExprSyntax(baseName: .identifier("then")), + leftParen: nil, arguments: .init([]), + rightParen: nil, trailingClosure: ClosureExprSyntax(statements: .init([])) ) ) @@ -282,7 +284,9 @@ final class ThenStatementTests: ParserTestCase { substructure: FunctionCallExprSyntax( calledExpression: DeclReferenceExprSyntax(baseName: .identifier("then")), + leftParen: nil, arguments: .init([]), + rightParen: nil, trailingClosure: ClosureExprSyntax(statements: .init([])) ) ) diff --git a/Tests/SwiftSyntaxMacroExpansionTest/AccessorMacroTests.swift b/Tests/SwiftSyntaxMacroExpansionTest/AccessorMacroTests.swift index bd5858389c1..cf1c0b6267a 100644 --- a/Tests/SwiftSyntaxMacroExpansionTest/AccessorMacroTests.swift +++ b/Tests/SwiftSyntaxMacroExpansionTest/AccessorMacroTests.swift @@ -532,4 +532,37 @@ final class AccessorMacroTests: XCTestCase { indentationWidth: indentationWidth ) } + + func testClosureInAccessorMacro() { + enum PropertyWrapperMacro: AccessorMacro { + public static func expansion( + of node: AttributeSyntax, + providingAccessorsOf declaration: some DeclSyntaxProtocol, + in context: some MacroExpansionContext + ) throws -> [AccessorDeclSyntax] { + guard let structDecl = context.lexicalContext.first?.as(StructDeclSyntax.self) else { + return [] + } + + return ["get { \(literal: structDecl.name.text) }"] + } + } + assertMacroExpansion( + """ + struct Foo { + @TestWrapper(b: { a in 1 }) var test3: Thing + } + """, + expandedSource: """ + struct Foo { + var test3: Thing { + get { + "Foo" + } + } + } + """, + macros: ["TestWrapper": PropertyWrapperMacro.self] + ) + } } diff --git a/Tests/SwiftSyntaxTest/MemoryLayoutTest.swift b/Tests/SwiftSyntaxTest/MemoryLayoutTest.swift index af45e08f7fb..ae8317f3411 100644 --- a/Tests/SwiftSyntaxTest/MemoryLayoutTest.swift +++ b/Tests/SwiftSyntaxTest/MemoryLayoutTest.swift @@ -33,9 +33,10 @@ final class MemoryLayoutTest: XCTestCase { "RawSyntax?": .init(size: 8, stride: 8, alignment: 8), "Syntax": .init(size: 16, stride: 16, alignment: 8), - "Syntax.Info": .init(size: 8, stride: 8, alignment: 8), - "Syntax.Info.Root": .init(size: 8, stride: 8, alignment: 8), - "Syntax.Info.NonRoot": .init(size: 36, stride: 40, alignment: 8), + "SyntaxData": .init(size: 32, stride: 32, alignment: 8), + "AbsoluteSyntaxInfo": .init(size: 12, stride: 12, alignment: 4), + "SyntaxDataReference?": .init(size: 8, stride: 8, alignment: 8), + "AtomicPointer": .init(size: 8, stride: 8, alignment: 8), ] let values = SyntaxMemoryLayout.values @@ -45,4 +46,13 @@ final class MemoryLayoutTest: XCTestCase { XCTAssertEqual(actualValue, exp.value, "Matching '\(exp.key)' values") } } + + func testSyntaxDataTailAllocation() throws { + #if !arch(x86_64) && !arch(arm64) + throw XCTSkip("Only runs on x86_64 and arm64") + #endif + let values = SyntaxMemoryLayout.values + // This ensures 'AtomicPointer' is safe to tail allocate right after 'SyntaxData.stride' + XCTAssertGreaterThanOrEqual(values["SyntaxData"]!.alignment, values["AtomicPointer"]!.alignment) + } } diff --git a/Tests/SwiftSyntaxTest/SyntaxCreationTests.swift b/Tests/SwiftSyntaxTest/SyntaxCreationTests.swift index bde8943a31f..a355341c245 100644 --- a/Tests/SwiftSyntaxTest/SyntaxCreationTests.swift +++ b/Tests/SwiftSyntaxTest/SyntaxCreationTests.swift @@ -179,7 +179,9 @@ class SyntaxCreationTests: XCTestCase { let call3 = FunctionCallExprSyntax( calledExpression: printID, - arguments: LabeledExprListSyntax([arg]) + leftParen: nil, + arguments: LabeledExprListSyntax([arg]), + rightParen: nil ) XCTAssertNil(call3.leftParen) XCTAssertNil(call3.rightParen)