Skip to content

Commit ae19235

Browse files
authored
Merge pull request #1 from apple/master
merge upstream
2 parents 8302e82 + 1e1c305 commit ae19235

File tree

5 files changed

+88
-11
lines changed

5 files changed

+88
-11
lines changed

Sources/SwiftDriver/Driver/ToolExecutionDelegate.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,10 @@ public struct ToolExecutionDelegate: JobExecutorDelegate {
5151
public func jobFinished(job: Job, result: ProcessResult, pid: Int) {
5252
switch mode {
5353
case .regular, .verbose:
54-
// FIXME: Check what the current driver does.
5554
let output = (try? result.utf8Output() + result.utf8stderrOutput()) ?? ""
5655
if !output.isEmpty {
57-
stdoutStream <<< output
58-
stdoutStream.flush()
56+
stderrStream <<< output
57+
stderrStream.flush()
5958
}
6059

6160
case .parsableOutput:

Sources/SwiftDriver/Options/OptionParsing.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
1010
//
1111
//===----------------------------------------------------------------------===//
12-
public enum OptionParseError : Error {
12+
public enum OptionParseError : Error, Equatable {
1313
case unknownOption(index: Int, argument: String)
1414
case missingArgument(index: Int, argument: String)
1515
}

Sources/SwiftDriver/Utilities/PrefixTrie.swift

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,21 +57,36 @@ public struct PrefixTrie<Key: Collection, Value> where Key.Element: Hashable {
5757
set {
5858
var index = key.startIndex
5959
var current = root
60+
// Keep track of which nodes we've visited along the way,
61+
// so we can walk back up this if we need to prune dead branches.
62+
// Note: This is only used (and is only appended to) if the new
63+
// value being stored is `nil`.
64+
var traversed: [(parent: Node, step: Key.Element)] = []
6065

6166
// Traverse as much of the prefix as we can, keeping track of the index
6267
// we ended on
6368
while index < key.endIndex, let next = current.next[key[index]] {
69+
if newValue == nil {
70+
traversed.append((current, key[index]))
71+
}
72+
6473
key.formIndex(after: &index)
6574
current = next
6675
}
6776

6877
// We're matching a prefix of an existing key in the trie
6978
if index == key.endIndex {
7079
// Update the value in the trie with the new value
71-
72-
// FIXME: If `newValue` is `nil`, we won't clean up intermediate entries
73-
// in the trie.
7480
current.value = newValue
81+
// remove dead nodes if the current child is a leaf
82+
if newValue == nil && current.next.keys.count == 0 {
83+
self.pruneEmptyBranchesIfNeeded(traversed)
84+
}
85+
return
86+
}
87+
88+
// If the value we're adding is `nil` just return and don't create the trie
89+
guard newValue != nil else {
7590
return
7691
}
7792

@@ -87,4 +102,32 @@ public struct PrefixTrie<Key: Collection, Value> where Key.Element: Hashable {
87102
}
88103
}
89104
}
105+
106+
107+
/// Given a list of nodes starting from a root node to any other node along a branch,
108+
/// prune the branch of any dead nodes along the way.
109+
private func pruneEmptyBranchesIfNeeded(_ traversed: [(parent: Node, step: Key.Element)]) {
110+
for (parent, step) in traversed.reversed() {
111+
// find the first parent with a value or more than one child.
112+
// If we traversed up to the root then chop everything regarless.
113+
if parent.value != nil || parent.next.keys.count > 1 || parent === traversed.first!.parent {
114+
parent.next[step] = nil
115+
break
116+
}
117+
}
118+
}
119+
120+
/// Returns the number of nodes in the trie
121+
public var nodeCount: Int {
122+
123+
var count = 0
124+
125+
var nodes = [self.root]
126+
while let currentNode = nodes.popLast() {
127+
nodes.append(contentsOf: currentNode.next.values)
128+
count += currentNode.next.keys.count
129+
}
130+
131+
return count
132+
}
90133
}

Tests/SwiftDriverTests/PrefixTrieTests.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,17 @@ import SwiftDriver
44
final class PrefixTrieTests: XCTestCase {
55
func testSimpleTrie() {
66
var trie = PrefixTrie<String, Int>()
7+
8+
trie["1234"] = nil
9+
XCTAssertEqual(trie.nodeCount, 0)
10+
711
trie["a"] = 0
812
trie["b"] = 1
913
trie["abcd"] = 2
1014
trie["abc"] = 3
1115

16+
XCTAssertEqual(trie.nodeCount, 5)
17+
1218
XCTAssertEqual(trie["a"], 0)
1319
XCTAssertEqual(trie["ab"], 0)
1420
XCTAssertEqual(trie["b"], 1)
@@ -37,6 +43,7 @@ final class PrefixTrieTests: XCTestCase {
3743
}
3844

3945
func testUpdating() {
46+
4047
var trie = PrefixTrie<String, Int>()
4148
trie["garbage"] = 0
4249
XCTAssertEqual(trie["garbage"], 0)
@@ -46,6 +53,23 @@ final class PrefixTrieTests: XCTestCase {
4653

4754
trie["garbage"] = nil
4855
XCTAssertNil(trie["garbage"])
56+
XCTAssertEqual(trie.nodeCount, 0)
57+
58+
trie["12345"] = 12345 // 5 nodes
59+
trie["12367"] = 12367 // 3 common nodes, 2 new nodes
60+
XCTAssertEqual(trie.nodeCount, 7)
61+
trie["123890"] = 123890 // 3 common nodes, 3 new nodes
62+
XCTAssertEqual(trie.nodeCount, 10)
63+
trie["123890"] = nil
64+
XCTAssertEqual(trie.nodeCount, 7)
65+
XCTAssertNil(trie["123890"])
66+
trie["abc"] = 979899 // 3 new nodes, 0 common nodes
67+
XCTAssertEqual(trie.nodeCount, 10)
68+
// existing prefix that cannot be deleted since
69+
// 12345 & 12367 exist
70+
trie["123"] = nil
71+
XCTAssertEqual(trie.nodeCount, 10)
72+
4973
}
5074

5175
func testCollectionMatching() {

Tests/SwiftDriverTests/SwiftDriverTests.swift

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,21 @@ final class SwiftDriverTests: XCTestCase {
3030
func testParseErrors() {
3131
let options = OptionTable()
3232

33-
// FIXME: Check for the exact form of the error
34-
XCTAssertThrowsError(try options.parse(["-unrecognized"]))
35-
XCTAssertThrowsError(try options.parse(["-I"]))
36-
XCTAssertThrowsError(try options.parse(["-module-name"]))
33+
XCTAssertThrowsError(try options.parse(["-unrecognized"])) { error in
34+
XCTAssertEqual(error as? OptionParseError, .unknownOption(index: 0, argument: "-unrecognized"))
35+
}
36+
37+
XCTAssertThrowsError(try options.parse(["-I"])) { error in
38+
XCTAssertEqual(error as? OptionParseError, .missingArgument(index: 0, argument: "-I"))
39+
}
40+
41+
XCTAssertThrowsError(try options.parse(["-color-diagnostics", "-I"])) { error in
42+
XCTAssertEqual(error as? OptionParseError, .missingArgument(index: 1, argument: "-I"))
43+
}
44+
45+
XCTAssertThrowsError(try options.parse(["-module-name"])) { error in
46+
XCTAssertEqual(error as? OptionParseError, .missingArgument(index: 0, argument: "-module-name"))
47+
}
3748
}
3849

3950
func testDriverKindParsing() throws {

0 commit comments

Comments
 (0)