@@ -48,11 +48,13 @@ public class BasicMacroExpansionContext {
4848 /// information about that source file.
4949 private var sourceFiles : [ SourceFileSyntax : KnownSourceFile ] = [ : ]
5050
51- /// Mapping from intentionally-disconnected syntax node roots to the
52- /// absolute offsets that have within a given source file, which is used
53- /// to establish the link between a node that been intentionally disconnected
54- /// from a source file to hide information from the macro implementation.
55- private var disconnectedNodes : [ Syntax : ( SourceFileSyntax , Int ) ] = [ : ]
51+ /// Mapping from intentionally-disconnected syntax nodes to the corresponding
52+ /// nodes in the original source file.
53+ ///
54+ /// This is used to establish the link between a node that been intentionally
55+ /// disconnected from a source file to hide information from the macro
56+ /// implementation.
57+ private var detachedNodes : [ Syntax : Syntax ] = [ : ]
5658
5759 /// The macro expansion discriminator, which is used to form unique names
5860 /// when requested.
@@ -69,24 +71,10 @@ public class BasicMacroExpansionContext {
6971}
7072
7173extension BasicMacroExpansionContext {
72- /// Note that the given node that was at the given position in the provided
73- /// source file has been disconnected and is now a new root.
74- private func addDisconnected(
75- _ node: some SyntaxProtocol ,
76- at offset: AbsolutePosition ,
77- in sourceFile: SourceFileSyntax
78- ) {
79- disconnectedNodes [ Syntax ( node) ] = ( sourceFile, offset. utf8Offset)
80- }
81-
8274 /// Detach the given node, and record where it came from.
8375 public func detach< Node: SyntaxProtocol > ( _ node: Node ) -> Node {
8476 let detached = node. detached
85-
86- if let rootSourceFile = node. root. as ( SourceFileSyntax . self) {
87- addDisconnected ( detached, at: node. position, in: rootSourceFile)
88- }
89-
77+ detachedNodes [ Syntax ( detached) ] = Syntax ( node)
9078 return detached
9179 }
9280}
@@ -136,28 +124,49 @@ extension BasicMacroExpansionContext: MacroExpansionContext {
136124 diagnostics. append ( diagnostic)
137125 }
138126
127+ /// Translates a position from a detached node to the corresponding location
128+ /// in the original source file.
129+ ///
130+ /// - Parameters:
131+ /// - position: The position to translate
132+ /// - node: The node at which the position is anchored. This node is used to
133+ /// find the offset in the original source file
134+ /// - fileName: The file name that should be used in the `SourceLocation`
135+ /// - Returns: The location in the original source file
136+ public func location(
137+ for position: AbsolutePosition ,
138+ anchoredAt node: Syntax ,
139+ fileName: String
140+ ) -> SourceLocation {
141+ guard let nodeInOriginalTree = detachedNodes [ node. root] else {
142+ return SourceLocationConverter ( file: fileName, tree: node. root) . location ( for: position)
143+ }
144+ let adjustedPosition = position + SourceLength( utf8Length: nodeInOriginalTree. position. utf8Offset)
145+ return SourceLocationConverter ( file: fileName, tree: nodeInOriginalTree. root) . location ( for: adjustedPosition)
146+ }
147+
139148 public func location(
140149 of node: some SyntaxProtocol ,
141150 at position: PositionInSyntaxNode ,
142151 filePathMode: SourceLocationFilePathMode
143152 ) -> AbstractSourceLocation ? {
144153 // Dig out the root source file and figure out how we need to adjust the
145154 // offset of the given syntax node to adjust for it.
146- let rootSourceFile : SourceFileSyntax
147- let offsetAdjustment : Int
155+ let rootSourceFile : SourceFileSyntax ?
156+ let offsetAdjustment : SourceLength
148157 if let directRootSourceFile = node. root. as ( SourceFileSyntax . self) {
149158 // The syntax node came from the source file itself.
150159 rootSourceFile = directRootSourceFile
151- offsetAdjustment = 0
152- } else if let ( adjustedSourceFile , offset ) = disconnectedNodes [ Syntax ( node) ] {
160+ offsetAdjustment = . zero
161+ } else if let nodeInOriginalTree = detachedNodes [ Syntax ( node) ] {
153162 // The syntax node came from a disconnected root, so adjust for that.
154- rootSourceFile = adjustedSourceFile
155- offsetAdjustment = offset
163+ rootSourceFile = nodeInOriginalTree . root . as ( SourceFileSyntax . self )
164+ offsetAdjustment = SourceLength ( utf8Length : nodeInOriginalTree . position . utf8Offset )
156165 } else {
157166 return nil
158167 }
159168
160- guard let knownRoot = sourceFiles [ rootSourceFile] else {
169+ guard let rootSourceFile , let knownRoot = sourceFiles [ rootSourceFile] else {
161170 return nil
162171 }
163172
@@ -189,6 +198,6 @@ extension BasicMacroExpansionContext: MacroExpansionContext {
189198
190199 // Do the location lookup.
191200 let converter = SourceLocationConverter ( file: fileName, tree: rootSourceFile)
192- return AbstractSourceLocation ( converter. location ( for: rawPosition. advanced ( by : offsetAdjustment) ) )
201+ return AbstractSourceLocation ( converter. location ( for: rawPosition + offsetAdjustment) )
193202 }
194203}
0 commit comments