mirror of
https://github.com/apple/swift.git
synced 2026-06-27 12:25:55 +02:00
ee36dbeda3
Syntax nodes are designed as reusable blocks to construct Swift source code. For this reason, we don't track absolute position in each node; instead, the absolute position should be calculated on the fly when needed. We recently found absolute positions are useful to bridge with sourcekitd, which typically speaks in the language of line, column and offset. Therefore, this patch tries to add a computed property on SyntaxNode to get its absolute position. To compute the absolute position of a SyntaxNode from scratch requires tree traversal. However, getting the absolute position from these added APIs doesn't necessarily mean we'll traverse since SyntaxData will actively cache the computed position. Also, since we recursively compute the absolute position, all the position caches for a SyntaxNode's previous siblings and ancestors will be populated as well during the calculation of this SyntaxNode.
101 lines
3.2 KiB
Swift
101 lines
3.2 KiB
Swift
// RUN: %target-run-simple-swift
|
|
// REQUIRES: executable_test
|
|
// REQUIRES: OS=macosx
|
|
// REQUIRES: objc_interop
|
|
|
|
import StdlibUnittest
|
|
import Foundation
|
|
import SwiftSyntax
|
|
import SwiftLang
|
|
|
|
func getInput(_ file: String) -> URL {
|
|
var result = URL(fileURLWithPath: #file)
|
|
result.deleteLastPathComponent()
|
|
result.appendPathComponent("Inputs")
|
|
result.appendPathComponent(file)
|
|
return result
|
|
}
|
|
|
|
|
|
class FuncRenamer: SyntaxRewriter {
|
|
override func visit(_ node: FunctionDeclSyntax) ->DeclSyntax {
|
|
return (super.visit(node) as! FunctionDeclSyntax).withIdentifier(
|
|
SyntaxFactory.makeIdentifier("anotherName"))
|
|
}
|
|
}
|
|
|
|
var PositionTests = TestSuite("AbsolutePositionTests")
|
|
|
|
PositionTests.test("Visitor") {
|
|
expectDoesNotThrow({
|
|
let content = try SwiftLang.parse(getInput("visitor.swift"))
|
|
let source = try String(contentsOf: getInput("visitor.swift"))
|
|
let parsed = try SourceFileSyntax.decodeSourceFileSyntax(content)
|
|
expectEqual(parsed.position.byteOffset, 0)
|
|
expectEqual(parsed.eofToken.positionAfterSkippingLeadingTrivia.byteOffset,
|
|
source.count)
|
|
expectEqual(parsed.position.byteOffset, 0)
|
|
expectEqual(parsed.byteSize, source.count)
|
|
})
|
|
}
|
|
|
|
PositionTests.test("Closure") {
|
|
expectDoesNotThrow({
|
|
let content = try SwiftLang.parse(getInput("closure.swift"))
|
|
let source = try String(contentsOf: getInput("closure.swift"))
|
|
let parsed = try SourceFileSyntax.decodeSourceFileSyntax(content)
|
|
expectEqual(parsed.eofToken.positionAfterSkippingLeadingTrivia.byteOffset,
|
|
source.count)
|
|
expectEqual(parsed.position.byteOffset, 0)
|
|
expectEqual(parsed.byteSize, source.count)
|
|
})
|
|
}
|
|
|
|
PositionTests.test("Rename") {
|
|
expectDoesNotThrow({
|
|
let content = try SwiftLang.parse(getInput("visitor.swift"))
|
|
let parsed = try SourceFileSyntax.decodeSourceFileSyntax(content)
|
|
let renamed = FuncRenamer().visit(parsed) as! SourceFileSyntax
|
|
let renamedSource = renamed.description
|
|
expectEqual(renamed.eofToken.positionAfterSkippingLeadingTrivia.byteOffset,
|
|
renamedSource.count)
|
|
expectEqual(renamed.byteSize, renamedSource.count)
|
|
})
|
|
}
|
|
|
|
PositionTests.test("CurrentFile") {
|
|
expectDoesNotThrow({
|
|
let content = try SwiftLang.parse(URL(fileURLWithPath: #file))
|
|
let parsed = try SourceFileSyntax.decodeSourceFileSyntax(content)
|
|
class Visitor: SyntaxVisitor {
|
|
override func visitPre(_ node: Syntax) {
|
|
_ = node.position
|
|
_ = node.byteSize
|
|
_ = node.positionAfterSkippingLeadingTrivia
|
|
}
|
|
}
|
|
Visitor().visit(parsed)
|
|
})
|
|
}
|
|
|
|
PositionTests.test("Recursion") {
|
|
expectDoesNotThrow({
|
|
var l = [CodeBlockItemSyntax]()
|
|
let idx = 2000
|
|
for _ in 0...idx {
|
|
l.append(SyntaxFactory.makeCodeBlockItem(
|
|
item: SyntaxFactory.makeReturnStmt(
|
|
returnKeyword: SyntaxFactory.makeToken(.returnKeyword, presence: .present)
|
|
.withTrailingTrivia(.newlines(1)), expression: nil), semicolon: nil))
|
|
}
|
|
let root = SyntaxFactory.makeSourceFile(
|
|
statements: SyntaxFactory.makeCodeBlockItemList(l),
|
|
eofToken: SyntaxFactory.makeToken(.eof, presence: .present))
|
|
_ = root.statements[idx].position
|
|
_ = root.statements[idx].byteSize
|
|
_ = root.statements[idx].positionAfterSkippingLeadingTrivia
|
|
})
|
|
}
|
|
|
|
runAllTests()
|