diff --git a/Sources/SwiftSyntax/SyntaxBaseNodes.swift.gyb b/Sources/SwiftSyntax/SyntaxBaseNodes.swift.gyb index c874304913a..faef5ef274c 100644 --- a/Sources/SwiftSyntax/SyntaxBaseNodes.swift.gyb +++ b/Sources/SwiftSyntax/SyntaxBaseNodes.swift.gyb @@ -51,6 +51,7 @@ public extension Syntax { public struct ${node.name}: ${node.name}Protocol, SyntaxHashable { public let _syntaxNode: Syntax + /// Create a `${node.name}` node from a specialized syntax node. public init(_ syntax: S) { // We know this cast is going to succeed. Go through init(_: SyntaxData) // to do a sanity check and verify the kind matches in debug builds and get @@ -58,6 +59,12 @@ public struct ${node.name}: ${node.name}Protocol, SyntaxHashable { self.init(syntax._syntaxNode.data) } + /// Create a `${node.name}` node from a specialized optional syntax node. + public init?(_ syntax: S?) { + guard let syntax = syntax else { return nil } + self.init(syntax) + } + /// Converts the given `Syntax` node to a `${node.name}` if possible. Returns /// `nil` if the conversion is not possible. public init?(_ syntax: Syntax) { diff --git a/Sources/SwiftSyntax/SyntaxData.swift b/Sources/SwiftSyntax/SyntaxData.swift index dd2acea849b..7bde994d607 100644 --- a/Sources/SwiftSyntax/SyntaxData.swift +++ b/Sources/SwiftSyntax/SyntaxData.swift @@ -188,8 +188,10 @@ struct AbsoluteRawSyntax { return nil } - func replacingSelf(_ newRaw: RawSyntax) -> AbsoluteRawSyntax { - return .init(raw: newRaw, info: info) + func replacingSelf(_ newRaw: RawSyntax, newRootId: UInt32) -> AbsoluteRawSyntax { + let nodeId = SyntaxIdentifier(rootId: newRootId, indexInTree: info.nodeId.indexInTree) + let newInfo = AbsoluteSyntaxInfo(position: info.position, nodeId: nodeId) + return .init(raw: newRaw, info: newInfo) } static func forRoot(_ raw: RawSyntax) -> AbsoluteRawSyntax { @@ -334,7 +336,7 @@ struct SyntaxData { if let parent = parent { let parentData = parent.data.replacingChild(newRaw, at: indexInParent) let newParent = Syntax(parentData) - return SyntaxData(absoluteRaw.replacingSelf(newRaw), parent: newParent) + return SyntaxData(absoluteRaw.replacingSelf(newRaw, newRootId: parentData.nodeId.rootId), parent: newParent) } else { // Otherwise, we're already the root, so return the new root data. return .forRoot(newRaw) diff --git a/Sources/SwiftSyntax/gyb_generated/SyntaxBaseNodes.swift b/Sources/SwiftSyntax/gyb_generated/SyntaxBaseNodes.swift index a29b53fe15b..d5294809ccb 100644 --- a/Sources/SwiftSyntax/gyb_generated/SyntaxBaseNodes.swift +++ b/Sources/SwiftSyntax/gyb_generated/SyntaxBaseNodes.swift @@ -38,6 +38,7 @@ public extension Syntax { public struct DeclSyntax: DeclSyntaxProtocol, SyntaxHashable { public let _syntaxNode: Syntax + /// Create a `DeclSyntax` node from a specialized syntax node. public init(_ syntax: S) { // We know this cast is going to succeed. Go through init(_: SyntaxData) // to do a sanity check and verify the kind matches in debug builds and get @@ -45,6 +46,12 @@ public struct DeclSyntax: DeclSyntaxProtocol, SyntaxHashable { self.init(syntax._syntaxNode.data) } + /// Create a `DeclSyntax` node from a specialized optional syntax node. + public init?(_ syntax: S?) { + guard let syntax = syntax else { return nil } + self.init(syntax) + } + /// Converts the given `Syntax` node to a `DeclSyntax` if possible. Returns /// `nil` if the conversion is not possible. public init?(_ syntax: Syntax) { @@ -135,6 +142,7 @@ public extension Syntax { public struct ExprSyntax: ExprSyntaxProtocol, SyntaxHashable { public let _syntaxNode: Syntax + /// Create a `ExprSyntax` node from a specialized syntax node. public init(_ syntax: S) { // We know this cast is going to succeed. Go through init(_: SyntaxData) // to do a sanity check and verify the kind matches in debug builds and get @@ -142,6 +150,12 @@ public struct ExprSyntax: ExprSyntaxProtocol, SyntaxHashable { self.init(syntax._syntaxNode.data) } + /// Create a `ExprSyntax` node from a specialized optional syntax node. + public init?(_ syntax: S?) { + guard let syntax = syntax else { return nil } + self.init(syntax) + } + /// Converts the given `Syntax` node to a `ExprSyntax` if possible. Returns /// `nil` if the conversion is not possible. public init?(_ syntax: Syntax) { @@ -232,6 +246,7 @@ public extension Syntax { public struct StmtSyntax: StmtSyntaxProtocol, SyntaxHashable { public let _syntaxNode: Syntax + /// Create a `StmtSyntax` node from a specialized syntax node. public init(_ syntax: S) { // We know this cast is going to succeed. Go through init(_: SyntaxData) // to do a sanity check and verify the kind matches in debug builds and get @@ -239,6 +254,12 @@ public struct StmtSyntax: StmtSyntaxProtocol, SyntaxHashable { self.init(syntax._syntaxNode.data) } + /// Create a `StmtSyntax` node from a specialized optional syntax node. + public init?(_ syntax: S?) { + guard let syntax = syntax else { return nil } + self.init(syntax) + } + /// Converts the given `Syntax` node to a `StmtSyntax` if possible. Returns /// `nil` if the conversion is not possible. public init?(_ syntax: Syntax) { @@ -329,6 +350,7 @@ public extension Syntax { public struct TypeSyntax: TypeSyntaxProtocol, SyntaxHashable { public let _syntaxNode: Syntax + /// Create a `TypeSyntax` node from a specialized syntax node. public init(_ syntax: S) { // We know this cast is going to succeed. Go through init(_: SyntaxData) // to do a sanity check and verify the kind matches in debug builds and get @@ -336,6 +358,12 @@ public struct TypeSyntax: TypeSyntaxProtocol, SyntaxHashable { self.init(syntax._syntaxNode.data) } + /// Create a `TypeSyntax` node from a specialized optional syntax node. + public init?(_ syntax: S?) { + guard let syntax = syntax else { return nil } + self.init(syntax) + } + /// Converts the given `Syntax` node to a `TypeSyntax` if possible. Returns /// `nil` if the conversion is not possible. public init?(_ syntax: Syntax) { @@ -426,6 +454,7 @@ public extension Syntax { public struct PatternSyntax: PatternSyntaxProtocol, SyntaxHashable { public let _syntaxNode: Syntax + /// Create a `PatternSyntax` node from a specialized syntax node. public init(_ syntax: S) { // We know this cast is going to succeed. Go through init(_: SyntaxData) // to do a sanity check and verify the kind matches in debug builds and get @@ -433,6 +462,12 @@ public struct PatternSyntax: PatternSyntaxProtocol, SyntaxHashable { self.init(syntax._syntaxNode.data) } + /// Create a `PatternSyntax` node from a specialized optional syntax node. + public init?(_ syntax: S?) { + guard let syntax = syntax else { return nil } + self.init(syntax) + } + /// Converts the given `Syntax` node to a `PatternSyntax` if possible. Returns /// `nil` if the conversion is not possible. public init?(_ syntax: Syntax) { diff --git a/Tests/SwiftSyntaxTest/SyntaxTests.swift b/Tests/SwiftSyntaxTest/SyntaxTests.swift index 92ed527bd3c..40ac0921a29 100644 --- a/Tests/SwiftSyntaxTest/SyntaxTests.swift +++ b/Tests/SwiftSyntaxTest/SyntaxTests.swift @@ -119,5 +119,8 @@ public class SyntaxTests: XCTestCase { case .integerLiteralExpr: break default: XCTFail("failed to convert to SyntaxEnum") } + + XCTAssertNil(ExprSyntax(nil as IntegerLiteralExprSyntax?)) + XCTAssertEqual(ExprSyntax(integerExpr).as(IntegerLiteralExprSyntax.self)!, integerExpr) } } diff --git a/Tests/SwiftSyntaxTest/SyntaxVisitorTests.swift b/Tests/SwiftSyntaxTest/SyntaxVisitorTests.swift index 0d4f58595cd..d2a829d4ef2 100644 --- a/Tests/SwiftSyntaxTest/SyntaxVisitorTests.swift +++ b/Tests/SwiftSyntaxTest/SyntaxVisitorTests.swift @@ -94,4 +94,19 @@ public class SyntaxVisitorTests: XCTestCase { XCTAssertEqual(hashBefore, parsed.hashValue) }()) } + + public func testRewriteTrivia() { + class TriviaRemover: SyntaxRewriter { + override func visit(_ token: TokenSyntax) -> Syntax { + return Syntax(token.withTrailingTrivia(.zero)) + } + } + + XCTAssertNoThrow(try { + let parsed = try SyntaxParser.parse(source: "let a = 5") + let visitor = TriviaRemover() + let rewritten = visitor.visit(parsed) + XCTAssertEqual(rewritten.description, "leta=5") + }()) + } }