8000 Merge pull request #42 from apple/master · swiftlang/swift-syntax@df26ae7 · GitHub
[go: up one dir, main page]

Skip to content

Commit df26ae7

Browse files
authored
Merge pull request #42 from apple/master
Sync swift-5.0-branch with master.
2 parents 25f7358 + 9004282 commit df26ae7

12 files changed

+162
-36
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@ code.
1313
Add this repository to the `Package.swift` manifest of your project:
1414

1515
```swift
16+
// swift-tools-version:4.2
1617
import PackageDescription
1718

1819
let package = Package(
1920
name: "MyTool",
2021
dependencies: [
21-
.package(url: "https://github.com/apple/swift-syntax.git", .branch("<#Specify Release tag#>")),
22+
.package(url: "https://github.com/apple/swift-syntax.git", .exact("<#Specify Release tag#>")),
2223
],
2324
targets: [
2425
.target(name: "MyTool", dependencies: ["SwiftSyntax"]),

Sources/SwiftSyntax/RawSyntax.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ extension RawSyntax {
478478
if shouldVisit {
479479
// Visit this node realizes a syntax node.
480480
visitor.visitPre()
481-
visitChildren = visitor.visit()
481+
visitChildren = visitor.visit() == .visitChildren
482482
}
483483
if visitChildren {
484484
for (offset, element) in layout.enumerated() {

Sources/SwiftSyntax/Syntax.swift

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,11 @@ extension Syntax {
105105
return raw.kind.isPattern
106106
}
107107

108+
/// Whether or not this node represents an unknown node.
109+
public var isUnknown: Bool {
110+
return raw.kind.isUnknown
111+
}
112+
108113
/// The parent of this syntax node, or `nil` if this node is the root.
109114
public var parent: Syntax? {
110115
guard let parentData = data.parent else { return nil }
@@ -239,9 +244,9 @@ extension Syntax {
239244
in file: URL,
240245
afterLeadingTrivia: Bool = true
241246
) -> SourceLocation {
242-
let pos = afterLeadingTrivia ?
243-
data.position :
244-
data.positionAfterSkippingLeadingTrivia
247+
let pos = afterLeadingTrivia ?
248+
data.positionAfterSkippingLeadingTrivia :
249+
data.position
245250
return SourceLocation(file: file.path, position: pos)
246251
}
247252

Sources/SwiftSyntax/SyntaxClassifier.swift.gyb

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ fileprivate class _SyntaxClassifier: SyntaxVisitor {
7070
}
7171
}
7272

73-
override func visit(_ token: TokenSyntax) {
73+
override func visit(_ token: TokenSyntax) -> SyntaxVisitorContinueKind {
7474
assert(token.isPresent)
7575
// FIXME: We need to come up with some way in which the SyntaxClassifier can
7676
// classify trivia (i.e. comments). In particular we need to be able to
@@ -91,16 +91,17 @@ fileprivate class _SyntaxClassifier: SyntaxVisitor {
9191
assert(classifications[token] == nil,
9292
"\(token) has already been classified")
9393
classifications[token] = classification
94+
return .skipChildren
9495
}
9596

9697
% for node in SYNTAX_NODES:
9798
% if is_visitable(node):
98-
override func visit(_ node: ${node.name}) -> Bool {
99+
override func visit(_ node: ${node.name}) -> SyntaxVisitorContinueKind {
99100
if skipNodeIds.contains(node.raw.id) {
100-
return false
101+
return .skipChildren
101102
}
102103
% if node.is_unknown() or node.is_syntax_collection():
103-
return true
104+
return .visitChildren
104105
% else:
105106
% for child in node.children:
106107
% if child.is_optional:
@@ -123,7 +124,7 @@ fileprivate class _SyntaxClassifier: SyntaxVisitor {
123124
% end
124125
% end
125126
% end
126-
return false
127+
return .skipChildren
127128
% end
128129
}
129130
% end

Sources/SwiftSyntax/SyntaxKind.swift.gyb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,18 @@ public enum SyntaxKind: String, Codable {
4545
% end
4646
% end
4747

48+
public var isUnknown: Bool {
49+
switch self {
50+
% for name, nodes in grouped_nodes.items():
51+
% if name not in ["Syntax", "SyntaxCollection"]:
52+
case .unknown${name}: return true
53+
% end
54+
% end
55+
case .unknown: return true
56+
default: return false
57+
}
58+
}
59+
4860
public init(from decoder: Decoder) throws {
4961
let container = try decoder.singleValueContainer()
5062
let kind = try container.decode(String.self)

Sources/SwiftSyntax/SyntaxRewriter.swift.gyb

Lines changed: 85 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -94,28 +94,54 @@ open class SyntaxRewriter {
9494
}
9595
}
9696

97+
/// The enum describes how the SyntaxVistor should continue after visiting
98+
/// the current node.
99+
public enum SyntaxVisitorContinueKind {
100+
101+
/// The visitor should visit the descendents of the current node.
102+
case visitChildren
103+
104+
/// The visitor should avoid visiting the descendents of the current node.
105+
case skipChildren
106+
}
107+
97108
open class SyntaxVisitor {
98109
public init() {}
99110
% for node in SYNTAX_NODES:
100111
% if is_visitable(node):
101-
open func visit(_ node: ${node.name}) -> Bool {
102-
return true
112+
/// Visting ${node.name} specifically.
113+
/// - Parameter node: the node we are visiting.
114+
/// - Returns: how should we continue visiting.
115+
open func visit(_ node: ${node.name}) -> SyntaxVisitorContinueKind {
116+
return .visitChildren
103117
}
104118
% end
105119
% end
106120

107-
open func visit(_ node: UnknownSyntax) -> Bool {
108-
return true
121+
/// Visting UnknownSyntax specifically.
122+
/// - Parameter node: the node we are visiting.
123+
/// - Returns: how should we continue visiting.
124+
open func visit(_ node: UnknownSyntax) -> SyntaxVisitorContinueKind {
125+
return .visitChildren
109126
}
110127

128+
/// Whether we should ever visit a given syntax kind.
129+
/// - Parameter kind: the input kind we're checking.
130+
/// - Returns: whether we should visit syntax nodes of this kind.
111131
open func shouldVisit(_ kind: SyntaxKind) -> Bool {
112132
return true
113133
}
134+
135+
/// Whether we should ever visit a given token kind.
136+
/// - Parameter kind: the input token kind we're checking.
137+
/// - Returns: whether we should visit tokens of this kind.
114138
open func shouldVisit(_ kind: TokenKind) -> Bool {
115139
return true
116140
}
117141

118-
open func visit(_ token: TokenSyntax) {}
142+
open func visit(_ token: TokenSyntax) -> SyntaxVisitorContinueKind {
143+
return .skipChildren
144+
}
119145

120146
/// The function called before visiting the node and its descendents.
121147
/// - node: the node we are about to visit.
@@ -125,18 +151,17 @@ open class SyntaxVisitor {
125151
/// - node: the node we just finished visiting.
126152
open func visitPost(_ node: Syntax) {}
127153

128-
public func visit(_ node: Syntax) -> Bool {
154+
public func visit(_ node: Syntax) -> SyntaxVisitorContinueKind {
129155
switch node.raw.kind {
130-
case .token: visit(node as! TokenSyntax)
156+
case .token: return visit(node as! TokenSyntax)
131157
% for node in SYNTAX_NODES:
132158
% if is_visitable(node):
133159
case .${node.swift_syntax_kind}: return visit(node as! ${node.name})
134160
% end
135161
% end
136162
case .unknown: return visit(node as! UnknownSyntax)
137-
default: break
163+
default: return .skipChildren
138164
}
139-
return false
140165
}
141166
}
142167

@@ -145,7 +170,7 @@ open class SyntaxVisitor {
145170
/// otherwise the node is represented as a child index list from a realized
146171
/// ancestor.
147172
class PendingSyntaxNode {
148-
let parent: PendingSyntaxNode?
173+
let parent: PendingSyntaxNode!
149174
private var kind: PendingSyntaxNodeKind
150175

151176
private enum PendingSyntaxNodeKind {
@@ -161,7 +186,7 @@ class PendingSyntaxNode {
161186
case .realized(let node):
162187
return node
163188
case .virtual(let index):
164-
let _node = parent!.node.child(at: index)!
189+
let _node = parent.node.child(at: index)!
165190
kind = .realized(node: _node)
166191
return _node
167192
}
@@ -186,7 +211,7 @@ class PendingSyntaxNode {
186211
/// not interesting to users' SyntaxVisitor.
187212
class RawSyntaxVisitor {
188213
private let visitor: SyntaxVisitor
189-
private var currentNode: PendingSyntaxNode?
214+
private var currentNode: PendingSyntaxNode!
190215

191216
required init(_ visitor: SyntaxVisitor, _ root: Syntax) {
192217
self.visitor = visitor
@@ -202,25 +227,25 @@ class RawSyntaxVisitor {
202227
}
203228

204229
func addChildIdx(_ idx: Int) {
205-
currentNode = PendingSyntaxNode(currentNode!, idx)
230+
currentNode = PendingSyntaxNode(currentNode, idx)
206231
}
207232

208233
func moveUp() {
209-
currentNode = currentNode!.parent
234+
currentNode = currentNode.parent
210235
}
211236

212237
func visitPre() {
213-
visitor.visitPre(currentNode!.node)
238+
visitor.visitPre(currentNode.node)
214239
}
215240

216241
func visitPost() {
217-
visitor.visitPost(currentNode!.node)
242+
visitor.visitPost(currentNode.node)
218243
}
219244

220245
// The current raw syntax node is interesting for the user, so realize a
221246
// correponding syntax node and feed it into the visitor.
222-
func visit() -> Bool {
223-
return visitor.visit(currentNode!.node)
247+
func visit() -> SyntaxVisitorContinueKind {
248+
return visitor.visit(currentNode.node)
224249
}
225250
}
226251

@@ -231,3 +256,45 @@ extension Syntax {
231256
data.raw.accept(RawSyntaxVisitor(visitor, self))
232257
}
233258
}
259+
260+
public enum SyntaxVerifierError: Error, CustomStringConvertible {
261+
case unknownSyntaxFound(node: Syntax)
262+
263+
public var description: String {
264+
switch self {
265+
case .unknownSyntaxFound(let node):
266+
return "unknown syntax node for \"\(node)\""
267+
}
268+
}
269+
}
270+
271+
public class SyntaxVerifier: SyntaxVisitor {
272+
273+
var unknownNodes: [Syntax] = []
274+
275+
override public func shouldVisit(_ node: SyntaxKind) -> Bool {
276+
return node.isUnknown
277+
}
278+
279+
override public func shouldVisit(_ node: TokenKind) -> Bool {
280+
return false
281+
}
282+
283+
override public func visitPre(_ node: Syntax) {
284+
assert(node.isUnknown)
285+
unknownNodes.append(node)
286+
}
287+
288+
private func verify(_ node: Syntax) throws {
289+
node.walk(self)
290+
if let unknownNode = unknownNodes.first {
291+
throw SyntaxVerifierError.unknownSyntaxFound(node: unknownNode)
292+
}
293+
}
294+
295+
private override init() {}
296+
297+
public static func verify(_ node: Syntax) throws {
298+
try SyntaxVerifier().verify(node)
299+
}
300+
}

Sources/lit-test-helper/ClassifiedSyntaxTreePrinter.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,12 @@ class ClassifiedSyntaxTreePrinter: SyntaxVisitor {
115115
}
116116
}
117117

118-
override func visit(_ node: TokenSyntax) {
118+
override func visit(_ node: TokenSyntax) -> SyntaxVisitorContinueKind {
119119
visit(node.leadingTrivia)
120120
let classification = classifications[node] ?? SyntaxClassification.none
121121
recordCurrentClassification(classification)
122122
result += node.text
123123
visit(node.trailingTrivia)
124+
return .skipChildren
124125
}
125126
}

Tests/SwiftSyntaxTest/AbsolutePosition.swift

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public class AbsolutePositionTestCase: XCTestCase {
1919
("testTrivias", testTrivias),
2020
("testImplicit", testImplicit),
2121
("testWithoutSourceFileRoot", testWithoutSourceFileRoot),
22+
("testSourceLocation", testSourceLocation),
2223
]
2324

2425
public func testVisitor() {
@@ -64,9 +65,10 @@ public class AbsolutePositionTestCase: XCTestCase {
6465
_ = node.byteSize
6566
_ = node.positionAfterSkippingLeadingTrivia
6667
}
67-
override func visit(_ node: TokenSyntax) {
68+
override func visit(_ node: TokenSyntax) -> SyntaxVisitorContinueKind {
6869
XCTAssertEqual(node.positionAfterSkippingLeadingTrivia.utf8Offset,
6970
node.position.utf8Offset + node.leadingTrivia.byteSize)
71+
return .skipChildren
7072
}
7173
}
7274
parsed.walk(Visitor())
@@ -138,4 +140,29 @@ public class AbsolutePositionTestCase: XCTestCase {
138140
XCTAssertEqual(0, item.position.utf8Offset)
139141
XCTAssertEqual(1, item.positionAfterSkippingLeadingTrivia.utf8Offset)
140142
}
143+
144+
public func testSourceLocation() {
145+
let url = URL(fileURLWithPath: "/tmp/test.swift")
146+
let root = self.createSourceFile(2)
147+
guard let secondReturnStmt = root.child(at: 0)?.child(at: 1) else {
148+
fatalError("out of sync with createSourceFile")
149+
}
150+
let startLoc = secondReturnStmt.startLocation(in: url)
151+
XCTAssertEqual(startLoc.line, 4)
152+
XCTAssertEqual(startLoc.column, 18)
153+
154+
let startLocBeforeTrivia =
155+
secondReturnStmt.startLocation(in: url, afterLeadingTrivia: false)
156+
XCTAssertEqual(startLocBeforeTrivia.line, 3)
157+
XCTAssertEqual(startLocBeforeTrivia.column, 1)
158+
159+
let endLoc = secondReturnStmt.endLocation(in: url)
160+
XCTAssertEqual(endLoc.line, 4)
161+
XCTAssertEqual(endLoc.column, 24)
162+
163+
let endLocAfterTrivia =
164+
secondReturnStmt.endLocation(in: url, afterTrailingTrivia: true)
165+
XCTAssertEqual(endLocAfterTrivia.line, 5)
166+
XCTAssertEqual(endLocAfterTrivia.column, 1)
167+
}
141168
}

Tests/SwiftSyntaxTest/DiagnosticTest.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,14 @@ public class DiagnosticTestCase: XCTestCase {
7676
self.url = url
7777
self.engine = engine
7878
}
79-
override func visit(_ function: FunctionDeclSyntax) -> Bool {
79+
override func visit(_ function: FunctionDeclSyntax) -> SyntaxVisitorContinueKind {
8080
let startLoc = function.identifier.startLocation(in: url)
8181
let endLoc = function.endLocation(in: url)
8282
engine.diagnose(.badFunction(function.identifier), location: startLoc) {
8383
$0.highlight(function.identifier.sourceRange(in: self.url))
8484
}
8585
engine.diagnose(.endOfFunction(function.identifier), location: endLoc)
86-
return true
86+
return .visitChildren
8787
}
8888
}
8989

Tests/SwiftSyntaxTest/ParseFile.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public class ParseFileTestCase: XCTestCase {
3030
let fileContents = try String(contentsOf: currentFile)
3131
let parsed = try SyntaxTreeParser.parse(currentFile)
3232
XCTAssertEqual("\(parsed)", fileContents)
33+
try SyntaxVerifier.verify(parsed)
3334
}())
3435
}
3536
}

0 commit comments

Comments
 (0)
0