@@ -103,22 +103,6 @@ public struct SourceRange: Hashable, Codable, Sendable {
103
103
}
104
104
}
105
105
106
- /// Collects all `PoundSourceLocationSyntax` directives in a file.
107
- fileprivate class SourceLocationCollector : SyntaxVisitor {
108
- private var sourceLocationDirectives : [ PoundSourceLocationSyntax ] = [ ]
109
-
110
- override func visit( _ node: PoundSourceLocationSyntax ) -> SyntaxVisitorContinueKind {
111
- sourceLocationDirectives. append ( node)
112
- return . skipChildren
113
- }
114
-
115
- static func collectSourceLocations( in tree: some SyntaxProtocol ) -> [ PoundSourceLocationSyntax ] {
116
- let collector = SourceLocationCollector ( viewMode: . sourceAccurate)
117
- collector. walk ( tree)
118
- return collector. sourceLocationDirectives
119
- }
120
- }
121
-
122
106
fileprivate struct SourceLocationDirectiveArguments {
123
107
enum Error : Swift . Error , CustomStringConvertible {
124
108
case nonDecimalLineNumber( TokenSyntax )
@@ -169,8 +153,8 @@ public final class SourceLocationConverter {
169
153
/// The information from all `#sourceLocation` directives in the file
170
154
/// necessary to compute presumed locations.
171
155
///
172
- /// - `sourceLine` is the line at which the `#sourceLocation` statement occurs
173
- /// within the current file.
156
+ /// - `sourceLine` is the physical line number of the end of the last token of
157
+ /// `#sourceLocation(...)` directive within the current file.
174
158
/// - `arguments` are the `file` and `line` arguments of the directive or `nil`
175
159
/// if spelled as `#sourceLocation()` to reset the source location directive.
176
160
private var sourceLocationDirectives : [ ( sourceLine: Int , arguments: SourceLocationDirectiveArguments ? ) ] = [ ]
@@ -189,21 +173,7 @@ public final class SourceLocationConverter {
189
173
precondition ( tree. parent == nil , " SourceLocationConverter must be passed the root of the syntax tree " )
190
174
self . fileName = fileName
191
175
self . source = tree. syntaxTextBytes
192
- ( self . lines, endOfFile) = computeLines ( tree: Syntax ( tree) )
193
- precondition ( tree. totalLength. utf8Length == endOfFile. utf8Offset)
194
-
195
- for directive in SourceLocationCollector . collectSourceLocations ( in: tree) {
196
- let location = self . physicalLocation ( for: directive. positionAfterSkippingLeadingTrivia)
197
- if let args = directive. arguments {
198
- if let parsedArgs = try ? SourceLocationDirectiveArguments ( args) {
199
- // Ignore any malformed `#sourceLocation` directives.
200
- sourceLocationDirectives. append ( ( sourceLine: location. line, arguments: parsedArgs) )
201
- }
202
- } else {
203
- // `#sourceLocation()` without any arguments resets the `#sourceLocation` directive.
204
- sourceLocationDirectives. append ( ( sourceLine: location. line, arguments: nil ) )
205
- }
206
- }
176
+ ( self . lines, self . endOfFile, self . sourceLocationDirectives) = computeLines ( tree: Syntax ( tree) )
207
177
}
208
178
209
179
/// Create a new ``SourceLocationConverter`` to convert between ``AbsolutePosition``
@@ -511,15 +481,21 @@ extension SyntaxProtocol {
511
481
/// the end-of-file position.
512
482
fileprivate func computeLines(
513
483
tree: Syntax
514
- ) -> ( [ AbsolutePosition ] , AbsolutePosition ) {
484
+ ) -> (
485
+ lines: [ AbsolutePosition ] ,
486
+ endOfFile: AbsolutePosition ,
487
+ sourceLocationDirectives: [ ( sourceLine: Int , arguments: SourceLocationDirectiveArguments ? ) ]
488
+ ) {
515
489
var lines : [ AbsolutePosition ] = [ . startOfFile]
516
490
var position : AbsolutePosition = . startOfFile
517
-
491
+ var sourceLocationDirectives : [ ( sourceLine : Int , arguments : SourceLocationDirectiveArguments ? ) ] = [ ]
518
492
let lastLineLength = tree. raw. forEachLineLength { lineLength in
519
493
position += lineLength
520
494
lines. append ( position)
495
+ } handleSourceLocationDirective: { lineOffset, args in
496
+ sourceLocationDirectives. append ( ( sourceLine: lines. count + lineOffset, arguments: args) )
521
497
}
522
- return ( lines, position + lastLineLength)
498
+ return ( lines, position + lastLineLength, sourceLocationDirectives )
523
499
}
524
500
525
501
fileprivate func computeLines( _ source: SyntaxText ) -> ( [ AbsolutePosition ] , AbsolutePosition ) {
@@ -652,7 +628,8 @@ fileprivate extension RawSyntax {
652
628
/// - Returns: The leftover ``SourceLength`` at the end of the walk.
653
629
func forEachLineLength(
654
630
prefix: SourceLength = . zero,
655
- body: ( SourceLength ) -> ( )
631
+ body: ( SourceLength ) -> ( ) ,
632
+ handleSourceLocationDirective: ( _ lineOffset: Int , _ arguments: SourceLocationDirectiveArguments ? ) -> ( )
656
633
) -> SourceLength {
657
634
var curPrefix = prefix
658
635
switch self . rawData. payload {
@@ -664,7 +641,40 @@ fileprivate extension RawSyntax {
664
641
curPrefix = dat. trailingTrivia. forEachLineLength ( prefix: curPrefix, body: body)
665
642
case . layout( let dat) :
666
643
for case let node? in dat. layout where SyntaxTreeViewMode . sourceAccurate. shouldTraverse ( node: node) {
667
- curPrefix = node. forEachLineLength ( prefix: curPrefix, body: body)
644
+ curPrefix = node. forEachLineLength (
645
+ prefix: curPrefix,
646
+ body: body,
647
+ handleSourceLocationDirective: handleSourceLocationDirective
648
+ )
649
+ }
650
+
651
+ // Handle '#sourceLocation' directive.
652
+ if dat. kind == . poundSourceLocation {
653
+ // Count newlines in the trailing trivia. The client want to get the
654
+ // line of the _end_ of '#sourceLocation()' directive.
655
+ var lineOffset = 0
656
+ if let lastTok = self . lastToken ( viewMode: . sourceAccurate) {
657
+ switch lastTok. raw. rawData. payload {
658
+ case . parsedToken( let dat) :
659
+ _ = dat. trailingTriviaText. forEachLineLength ( body: { _ in lineOffset -= 1 } )
660
+ case . materializedToken( let dat) :
661
+ _ = dat. trailingTrivia. forEachLineLength ( body: { _ in lineOffset -= 1 } )
662
+ case . layout( _) :
663
+ preconditionFailure ( " lastToken(viewMode:) returned non-token " )
664
+ }
665
+ }
666
+
667
+ let directive = Syntax . forRoot ( self , rawNodeArena: self . arenaReference. retained)
668
+ . cast ( PoundSourceLocationSyntax . self)
669
+ if let args = directive. arguments {
670
+ if let parsedArgs = try ? SourceLocationDirectiveArguments ( args) {
671
+ // Ignore any malformed `#sourceLocation` directives.
672
+ handleSourceLocationDirective ( lineOffset, parsedArgs)
673
+ }
674
+ } else {
675
+ // `#sourceLocation()` without any arguments resets the `#sourceLocation` directive.
676
+ handleSourceLocationDirective ( lineOffset, nil )
677
+ }
668
678
}
669
679
}
670
680
return curPrefix
0 commit comments