Skip to content

Commit 1c9f1c4

Browse files
authored
SwiftSyntax: allow any Syntax nodes to access their leading/trailing trivia. (#15278)
Although libSyntax is designed in a way that trivia is attached to tokens, we shouldn't restrict clients to access trivia only from a token. An example can be doc-comment, which conceptually attaches to a declaration rather than the start token of a declaration, like at-attributes.
1 parent 9c00c38 commit 1c9f1c4

File tree

3 files changed

+78
-9
lines changed

3 files changed

+78
-9
lines changed

test/SwiftSyntax/AbsolutePosition.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,35 @@ PositionTests.test("Recursion") {
101101
})
102102
}
103103

104+
PositionTests.test("Trivias") {
105+
expectDoesNotThrow({
106+
let leading = Trivia(pieces: [
107+
.newlines(1),
108+
.backticks(1),
109+
.docLineComment("/// some comment")
110+
])
111+
let trailing = Trivia.docLineComment("/// This is comment\n")
112+
let idx = 5
113+
let items : [CodeBlockItemSyntax] =
114+
[CodeBlockItemSyntax](repeating: CodeBlockItemSyntax {
115+
$0.useItem(ReturnStmtSyntax {
116+
$0.useReturnKeyword(
117+
SyntaxFactory.makeReturnKeyword(
118+
leadingTrivia: leading,
119+
trailingTrivia: trailing))
120+
})}, count: idx + 1)
121+
let root = SyntaxFactory.makeSourceFile(
122+
statements: SyntaxFactory.makeCodeBlockItemList(items),
123+
eofToken: SyntaxFactory.makeToken(.eof, presence: .present))
124+
125+
expectEqual(root.leadingTrivia!.count, 3)
126+
expectEqual(root.trailingTrivia!.count, 0)
127+
let state = root.statements[idx]
128+
expectEqual(state.leadingTrivia!.count, 3)
129+
expectEqual(state.trailingTrivia!.count, 1)
130+
expectEqual(state.leadingTrivia!.byteSize + state.trailingTrivia!.byteSize
131+
+ state.byteSizeAfterTrimmingTrivia, state.byteSize)
132+
})
133+
}
134+
104135
runAllTests()

tools/SwiftSyntax/RawSyntax.swift

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -214,22 +214,40 @@ extension RawSyntax {
214214
}
215215
}
216216

217-
func accumulateLeadingTrivia(_ pos: AbsolutePosition) -> Bool {
217+
var leadingTrivia: Trivia? {
218218
switch self {
219219
case .node(_, let layout, _):
220220
for child in layout {
221221
guard let child = child else { continue }
222-
if child.accumulateLeadingTrivia(pos) {
223-
return true
224-
}
222+
guard let result = child.leadingTrivia else { continue }
223+
return result
225224
}
226-
return false
225+
return nil
227226
case let .token(_, leadingTrivia, _, presence):
228-
guard case .present = presence else { return false }
229-
for piece in leadingTrivia {
230-
piece.accumulateAbsolutePosition(pos)
227+
guard case .present = presence else { return nil }
228+
return leadingTrivia
229+
}
230+
}
231+
232+
var trailingTrivia: Trivia? {
233+
switch self {
234+
case .node(_, let layout, _):
235+
for child in layout.reversed() {
236+
guard let child = child else { continue }
237+
guard let result = child.trailingTrivia else { continue }
238+
return result
231239
}
232-
return true
240+
return nil
241+
case let .token(_, _, trailingTrivia, presence):
242+
guard case .present = presence else { return nil }
243+
return trailingTrivia
244+
}
245+
}
246+
247+
func accumulateLeadingTrivia(_ pos: AbsolutePosition) {
248+
guard let trivia = leadingTrivia else { return }
249+
for piece in trivia {
250+
piece.accumulateAbsolutePosition(pos)
233251
}
234252
}
235253

tools/SwiftSyntax/Syntax.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,26 @@ extension Syntax {
138138
return data.byteSize
139139
}
140140

141+
/// The leading trivia of this syntax node. Leading trivia is attached to
142+
/// the first token syntax contained by this node. Without such token, this
143+
/// property will return nil.
144+
public var leadingTrivia: Trivia? {
145+
return raw.leadingTrivia
146+
}
147+
148+
/// The trailing trivia of this syntax node. Trailing trivia is attached to
149+
/// the last token syntax contained by this node. Without such token, this
150+
/// property will return nil.
151+
public var trailingTrivia: Trivia? {
152+
return raw.trailingTrivia
153+
}
154+
155+
/// The textual byte length of this node exluding leading and trailing trivia.
156+
public var byteSizeAfterTrimmingTrivia: Int {
157+
return data.byteSize - (leadingTrivia?.byteSize ?? 0) -
158+
(trailingTrivia?.byteSize ?? 0)
159+
}
160+
141161
/// The root of the tree in which this node resides.
142162
public var root: Syntax {
143163
return makeSyntax(root: _root, data: _root)

0 commit comments

Comments
 (0)