@@ -81,10 +81,13 @@ public struct Syntax: SyntaxProtocol, SyntaxHashable {
8181 }
8282 }
8383
84- private var root : Syntax {
85- switch info. info! {
86- case . root( _) : return self
87- case . nonRoot( let info) : return info. parent. root
84+ public var root : Syntax {
85+ return self . withUnownedSyntax {
86+ var node = $0
87+ while let parent = node. parent {
88+ node = parent
89+ }
90+ return node. value
8891 }
8992 }
9093
@@ -129,7 +132,10 @@ public struct Syntax: SyntaxProtocol, SyntaxHashable {
129132 }
130133
131134 /// "designated" memberwise initializer of `Syntax`.
132- init ( _ raw: RawSyntax , info: Info ) {
135+ // transparent because normal inlining is too late for eliminating ARC traffic for Info.
136+ // FIXME: Remove @_transparent after OSSA enabled.
137+ @_transparent
138+ init ( _ raw: RawSyntax , info: __shared Info) {
133139 self . raw = raw
134140 self . info = info
135141 }
@@ -309,7 +315,7 @@ public struct Syntax: SyntaxProtocol, SyntaxHashable {
309315 /// Create a ``Syntax`` node from a specialized syntax node.
310316 // Inline always so the optimizer can optimize this to a member access on `syntax` without having to go through
311317 // generics.
312- @inline ( __always )
318+ @_transparent
313319 public init ( _ syntax: __shared some SyntaxProtocol ) {
314320 self = syntax. _syntaxNode
315321 }
@@ -380,6 +386,59 @@ extension Syntax {
380386 }
381387}
382388
389+ /// Temporary non-owning Syntax.
390+ ///
391+ /// This can be used for handling Syntax node without ARC traffic.
392+ struct UnownedSyntax {
393+ private let raw : RawSyntax
394+ private let info : Unmanaged < Syntax . Info >
395+
396+ @_transparent
397+ init ( _ node: __shared Syntax) {
398+ self . raw = node. raw
399+ self . info = . passUnretained( node. info. unsafelyUnwrapped)
400+ }
401+
402+ /// Extract the Syntax value.
403+ @inline ( __always)
404+ var value : Syntax {
405+ Syntax ( raw, info: info. takeUnretainedValue ( ) )
406+ }
407+
408+ /// Get the parent of the Syntax value, but without retaining it.
409+ @inline ( __always)
410+ var parent : UnownedSyntax ? {
411+ return info. _withUnsafeGuaranteedRef {
412+ switch $0. info. unsafelyUnwrapped {
413+ case . nonRoot( let info) :
414+ return UnownedSyntax ( info. parent)
415+ case . root( _) :
416+ return nil
417+ }
418+ }
419+ }
420+
421+ /// Temporarily use the Syntax value.
422+ @inline ( __always)
423+ func withValue< T> ( _ body: ( Syntax ) -> T ) -> T {
424+ info. _withUnsafeGuaranteedRef {
425+ body ( Syntax ( self . raw, info: $0) )
426+ }
427+ }
428+ }
429+
430+ extension SyntaxProtocol {
431+ /// Execute the `body` with ``UnownedSyntax`` of `node`.
432+ ///
433+ /// This guarantees the life time of the `node` during the `body` is executed.
434+ @inline ( __always)
435+ func withUnownedSyntax< T> ( _ body: ( UnownedSyntax ) -> T ) -> T {
436+ return withExtendedLifetime ( self ) {
437+ body ( UnownedSyntax ( Syntax ( $0) ) )
438+ }
439+ }
440+ }
441+
383442/// ``SyntaxNode`` used to be a pervasive type name in SwiftSyntax that has been
384443/// replaced by the ``Syntax`` type.
385444@available ( * , unavailable, message: " use 'Syntax' instead " )
0 commit comments