Skip to content

Commit a72fea3

Browse files
committed
SourceLocation: refactor user-facing part of SourceLocation out and allow it to be computed on demand.
Always computing the user-facing part of source location (line/column) can be inefficient and unnecessary. This patch refactors the user-facing part of SourceLocation to a separate data structure and allows it to be computed on demand.
1 parent fc36c46 commit a72fea3

File tree

3 files changed

+63
-14
lines changed

3 files changed

+63
-14
lines changed

Sources/SwiftSyntax/PrintingDiagnosticConsumer.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public class PrintingDiagnosticConsumer: DiagnosticConsumer {
3838
/// Prints each of the fields in a diagnositic to stderr.
3939
public func write(_ diagnostic: Diagnostic) {
4040
if let loc = diagnostic.location {
41-
write("\(loc.file):\(loc.line):\(loc.column): ")
41+
write("\(loc.file!):\(loc.line!):\(loc.column!): ")
4242
} else {
4343
write("<unknown>:0:0: ")
4444
}

Sources/SwiftSyntax/SourceLocation.swift

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,75 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
/// Represents a source location in a Swift file.
14-
public struct SourceLocation: Codable {
13+
/// Represent the user-facing part of SourceLocation that can be calculated
14+
/// on demand.
15+
struct ComputedLocation: Codable {
1516
/// The line in the file where this location resides. 1-based.
16-
public let line: Int
17+
let line: Int
1718

1819
/// The UTF-8 byte offset from the beginning of the line where this location
1920
/// resides. 1-based.
20-
public let column: Int
21+
let column: Int
22+
23+
/// The file in which this location resides.
24+
let file: String
25+
26+
init(line: Int, column: Int, file: String) {
27+
self.line = line
28+
self.column = column
29+
self.file = file
30+
}
31+
init(offset: Int, using converter: SourceLocationConverter) {
32+
let loc = converter.location(for: AbsolutePosition(utf8Offset: offset))
33+
assert(loc.offset == offset)
34+
self.line = loc.line!
35+
self.column = loc.column!
36+
self.file = loc.file!
37+
}
38+
}
39+
40+
/// Represents a source location in a Swift file.
41+
public struct SourceLocation: Codable {
42+
43+
/// Line and column that can be computed on demand.
44+
private var compLoc: ComputedLocation?
2145

2246
/// The UTF-8 byte offset into the file where this location resides.
2347
public let offset: Int
2448

49+
/// The line in the file where this location resides. 1-based.
50+
public var line: Int? {
51+
return compLoc?.line
52+
}
53+
54+
/// The UTF-8 byte offset from the beginning of the line where this location
55+
/// resides. 1-based.
56+
public var column: Int? {
57+
return compLoc?.column
58+
}
59+
2560
/// The file in which this location resides.
26-
public let file: String
61+
public var file: String? {
62+
return compLoc?.file
63+
}
2764

2865
public init(line: Int, column: Int, offset: Int, file: String) {
29-
self.line = line
30-
self.column = column
3166
self.offset = offset
32-
self.file = file
67+
self.compLoc = ComputedLocation(line: line, column: column, file: file)
68+
}
69+
70+
/// Initialize SourceLocation with a utf8 offset. Line, column and file will
71+
/// be nil after invoking this initializer. Client can call computeLineColumn(using)
72+
/// later to populate these fields.
73+
public init(offset: Int) {
74+
self.offset = offset
75+
}
76+
77+
/// Populate line, column and file by using a SourceLocationConverter.
78+
/// It will assert if these fileds have already been populated.
79+
public mutating func computeLineColumn(using converter: SourceLocationConverter) {
80+
assert(compLoc == nil, "line and column have already been calculated")
81+
self.compLoc = ComputedLocation(offset: offset, using: converter)
3382
}
3483
}
3584

Tests/SwiftSyntaxTest/AbsolutePosition.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -149,15 +149,15 @@ public class AbsolutePositionTestCase: XCTestCase {
149149
let startLoc = secondReturnStmt.startLocation(converter: converter)
150150
XCTAssertEqual(startLoc.line, 8)
151151
XCTAssertEqual(startLoc.column, 1)
152-
XCTAssertEqual(converter.position(ofLine: startLoc.line, column: startLoc.column),
152+
XCTAssertEqual(converter.position(ofLine: startLoc.line!, column: startLoc.column!),
153153
secondReturnStmt.positionAfterSkippingLeadingTrivia)
154154

155155
let startLocBeforeTrivia =
156156
secondReturnStmt.startLocation(converter: converter,
157157
afterLeadingTrivia: false)
158158
XCTAssertEqual(startLocBeforeTrivia.line, 6)
159159
XCTAssertEqual(startLocBeforeTrivia.column, 1)
160-
XCTAssertEqual(converter.position(ofLine: startLocBeforeTrivia.line, column: startLocBeforeTrivia.column),
160+
XCTAssertEqual(converter.position(ofLine: startLocBeforeTrivia.line!, column: startLocBeforeTrivia.column!),
161161
secondReturnStmt.position)
162162

163163
let endLoc = secondReturnStmt.endLocation(converter: converter)
@@ -170,9 +170,9 @@ public class AbsolutePositionTestCase: XCTestCase {
170170
XCTAssertEqual(endLocAfterTrivia.line, 11)
171171
XCTAssertEqual(endLocAfterTrivia.column, 1)
172172

173-
XCTAssertTrue(converter.isValid(line: startLoc.line, column: startLoc.column))
174-
XCTAssertFalse(converter.isValid(line: startLoc.line, column: startLoc.column+50))
175-
XCTAssertFalse(converter.isValid(line: 0, column: startLoc.column))
173+
XCTAssertTrue(converter.isValid(line: startLoc.line!, column: startLoc.column!))
174+
XCTAssertFalse(converter.isValid(line: startLoc.line!, column: startLoc.column!+50))
175+
XCTAssertFalse(converter.isValid(line: 0, column: startLoc.column!))
176176
XCTAssertTrue(converter.isValid(position: secondReturnStmt.position))
177177
XCTAssertFalse(converter.isValid(position: secondReturnStmt.position+SourceLength(utf8Length: 100)))
178178
}

0 commit comments

Comments
 (0)