@@ -71,7 +71,6 @@ public struct SourceLocation: Sendable {
7171 }
7272
7373 /// The path to the source file.
74- @_spi ( Experimental)
7574 public var filePath : String
7675
7776 /// The line in the source file.
@@ -212,14 +211,61 @@ extension SourceLocation: Codable {
212211
213212 public init ( from decoder: any Decoder ) throws {
214213 let container = try decoder. container ( keyedBy: _CodingKeys. self)
215- fileID = try container. decode ( String . self, forKey: . fileID)
216- line = try container. decode ( Int . self, forKey: . line)
217- column = try container. decode ( Int . self, forKey: . column)
214+ let fileID = try container. decode ( String . self, forKey: . fileID)
215+ let line = try container. decode ( Int . self, forKey: . line)
216+ let column = try container. decode ( Int . self, forKey: . column)
218217
219218 // For simplicity's sake, we won't be picky about which key contains the
220219 // file path.
221- filePath = try container. decodeIfPresent ( String . self, forKey: . filePath)
220+ let filePath = try container. decodeIfPresent ( String . self, forKey: . filePath)
222221 ?? container. decode ( String . self, forKey: . _filePath)
222+
223+ self . init ( fileID: fileID, filePath: filePath, line: line, column: column)
224+ }
225+
226+ init ( fileIDSynthesizingIfNeeded fileID: String ? , filePath: String , line: Int , column: Int ) {
227+ // Synthesize the file ID if needed.
228+ let fileID = fileID ?? Self . _synthesizeFileID ( fromFilePath: filePath)
229+ self . init ( fileID: fileID, filePath: filePath, line: line, column: column)
230+ }
231+
232+ /// The name of the ersatz Swift module used for synthesized file IDs.
233+ static var synthesizedModuleName : String {
234+ " __C "
235+ }
236+
237+ /// Synthesize a file ID from the given file path and module name.
238+ ///
239+ /// - Parameters:
240+ /// - filePath: The file path.
241+ /// - moduleName: The module name.
242+ ///
243+ /// - Returns: A file path constructed from `filePath` and `moduleName`.
244+ private static func _synthesizeFileID( fromFilePath filePath: String , inModuleNamed moduleName: String = synthesizedModuleName) -> String {
245+ let fileName : String ? = {
246+ var filePath = filePath [ ... ]
247+
248+ #if os(Windows)
249+ // On Windows, replace backslashes in the path with slashes. (This is an
250+ // admittedly naïve approach, but this function is not a hot path.)
251+ do {
252+ let characters = filePath. map { $0 == #"\"# ? " / " : $0 }
253+ filePath = String ( characters) [ ... ]
254+ }
255+ #endif
256+
257+ // Trim any trailing slashes, then take the substring following the last
258+ // (remaining) slash, if any.
259+ if let lastNonSlashCharacter = filePath. lastIndex ( where: { $0 != " / " } ) {
260+ filePath = filePath [ ... lastNonSlashCharacter]
261+ if let lastSlashCharacter = filePath. lastIndex ( of: " / " ) {
262+ filePath = filePath [ lastSlashCharacter... ] . dropFirst ( )
263+ }
264+ return String ( filePath)
265+ }
266+ return nil
267+ } ( )
268+ return " \( moduleName) / \( fileName ?? filePath) "
223269 }
224270}
225271
@@ -231,7 +277,7 @@ extension SourceLocation {
231277 /// - Warning: This property is provided temporarily to aid in integrating the
232278 /// testing library with existing tools such as Swift Package Manager. It
233279 /// will be removed in a future release.
234- @available ( swift, deprecated: 100000.0 , renamed: " filePath " )
280+ @available ( swift, deprecated: 6.3 , renamed: " filePath " )
235281 public var _filePath : String {
236282 get {
237283 filePath
0 commit comments