Skip to content

Commit c52da97

Browse files
author
Stephane Magne
authored
Merge pull request #174 from scribd/stephane/fix_runtime_parse
Fix two bugs
2 parents b4bbe2d + 3664b18 commit c52da97

File tree

4 files changed

+125
-60
lines changed

4 files changed

+125
-60
lines changed

Sources/WeaverCodeGen/Inspector.swift

+10-3
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,18 @@ public final class Inspector {
2929
}
3030
}
3131

32+
try validateRuntime()
33+
}
34+
35+
@discardableResult
36+
public func validateRuntime() throws -> [TreeNode] {
37+
var treeNodes: [TreeNode] = []
3238
for rootContainer in dependencyGraph.rootContainers {
33-
if let treeInspector = RuntimeTreeInspector(rootContainer: rootContainer, dependencyGraph: dependencyGraph) {
34-
try treeInspector.validate()
35-
}
39+
let treeInspector = RuntimeTreeInspector(rootContainer: rootContainer, dependencyGraph: dependencyGraph)
40+
try treeInspector.validate()
41+
treeNodes.append(treeInspector.rootNode)
3642
}
43+
return treeNodes
3744
}
3845
}
3946

Sources/WeaverCodeGen/RuntimeTreeInspector.swift

+49-55
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,11 @@ import Foundation
1111

1212
public final class RuntimeTreeInspector {
1313

14-
private let rootNode: TreeNode
14+
public let rootNode: TreeNode
1515

16-
init?(rootContainer: DependencyContainer,
17-
dependencyGraph: DependencyGraph) {
18-
guard let treeNode = TreeNode(rootContainer: rootContainer, dependencyGraph: dependencyGraph) else { return nil }
19-
self.rootNode = treeNode
16+
init(rootContainer: DependencyContainer,
17+
dependencyGraph: DependencyGraph) {
18+
self.rootNode = TreeNode(rootContainer: rootContainer, dependencyGraph: dependencyGraph)
2019
}
2120

2221
public func validate() throws {
@@ -97,8 +96,9 @@ private extension RuntimeTreeInspector {
9796

9897
// dependencyChain
9998
var stepCount = 1
100-
for parentContainer in node.dependencyChain.reversed() {
101-
if let earlyReturn = resolveStep(stepCount, parentContainer) {
99+
var _parent = node.parent
100+
while let parent = _parent {
101+
if let earlyReturn = resolveStep(stepCount, parent.inspectingContainer) {
102102
switch earlyReturn {
103103
case .empty:
104104
return nil
@@ -107,49 +107,52 @@ private extension RuntimeTreeInspector {
107107
}
108108
}
109109

110+
_parent = parent.parent
110111
stepCount += 1
111112
}
112113

113-
if let rootContainer = node.dependencyChain.first {
114-
history.append(.dependencyNotFound(dependency, in: rootContainer))
115-
}
114+
history.append(.dependencyNotFound(dependency, in: rootNode.inspectingContainer))
116115

117116
return history
118117
}
119118
}
120119

121120
// MARK: - Full Tree Iterator
122121

123-
final class TreeNode: Sequence, IteratorProtocol {
122+
public final class TreeNode: Sequence, IteratorProtocol {
124123

125124
let inspectingContainer: DependencyContainer
126125

127-
let dependencyChain: [DependencyContainer]
126+
private(set) var children: [TreeNode] = []
128127

129-
let dependencyGraph: DependencyGraph
128+
weak var parent: TreeNode?
130129

131-
let dependencyPointer: Dependency
130+
// do not encode
131+
private(set) var dependencyPointer: Dependency?
132132

133-
convenience init?(rootContainer: DependencyContainer,
134-
dependencyGraph: DependencyGraph) {
135-
guard let firstDependency = rootContainer.dependencies.orderedValues.first else {
136-
return nil
137-
}
133+
private(set) var previousDependencyPointer: Dependency?
134+
135+
let dependencyGraph: DependencyGraph
138136

137+
convenience init(rootContainer: DependencyContainer,
138+
dependencyGraph: DependencyGraph) {
139139
self.init(inspectingContainer: rootContainer,
140-
dependencyChain: [],
141140
dependencyGraph: dependencyGraph,
142-
dependencyPointer: firstDependency)
141+
dependencyPointer: rootContainer.dependencies.orderedValues.first,
142+
previousDependencyPointer: nil,
143+
parent: nil)
143144
}
144145

145146
private init(inspectingContainer: DependencyContainer,
146-
dependencyChain: [DependencyContainer],
147-
dependencyGraph: DependencyGraph,
148-
dependencyPointer: Dependency) {
147+
dependencyGraph: DependencyGraph,
148+
dependencyPointer: Dependency?,
149+
previousDependencyPointer: Dependency?,
150+
parent: TreeNode?) {
149151
self.inspectingContainer = inspectingContainer
150-
self.dependencyChain = dependencyChain
151152
self.dependencyGraph = dependencyGraph
152153
self.dependencyPointer = dependencyPointer
154+
self.previousDependencyPointer = previousDependencyPointer
155+
self.parent = parent
153156
}
154157

155158
/*
@@ -160,18 +163,26 @@ final class TreeNode: Sequence, IteratorProtocol {
160163
- If none, then move to the next sibling dependency in the current node
161164
- If none, then pop up to the parent node
162165
*/
163-
func next() -> TreeNode? {
166+
public func next() -> TreeNode? {
167+
168+
guard let dependencyPointer = dependencyPointer else {
169+
return nextByPopping()
170+
}
164171

165172
guard dependencyPointer.kind.isRegistration,
166-
let nextContainer = try? dependencyGraph.dependencyContainer(for: dependencyPointer),
167-
let firstNestedDependency = nextContainer.dependencies.orderedValues.first else {
168-
return nextByIterating(after: dependencyPointer)
173+
let nextContainer = try? dependencyGraph.dependencyContainer(for: dependencyPointer) else {
174+
return nextByIterating(after: dependencyPointer)
169175
}
170176

171-
return TreeNode(inspectingContainer: nextContainer,
172-
dependencyChain: dependencyChain + [inspectingContainer],
173-
dependencyGraph: dependencyGraph,
174-
dependencyPointer: firstNestedDependency)
177+
let node = TreeNode(inspectingContainer: nextContainer,
178+
dependencyGraph: dependencyGraph,
179+
dependencyPointer: nextContainer.dependencies.orderedValues.first,
180+
previousDependencyPointer: dependencyPointer,
181+
parent: self)
182+
183+
children.append(node)
184+
185+
return node
175186
}
176187

177188
private func nextByIterating(after dependency: Dependency) -> TreeNode? {
@@ -183,34 +194,17 @@ final class TreeNode: Sequence, IteratorProtocol {
183194
}
184195

185196
let nextIndex = values.index(after: firstIndex)
186-
guard nextIndex < values.count else {
187-
return nextByPopping()
188-
}
189-
190-
let nextDependency: Dependency = values[nextIndex]
191-
return TreeNode(inspectingContainer: inspectingContainer,
192-
dependencyChain: dependencyChain,
193-
dependencyGraph: dependencyGraph,
194-
dependencyPointer: nextDependency).next()
197+
dependencyPointer = nextIndex < values.count ? values[nextIndex] : nil
198+
return next()
195199
}
196200

197201
private func nextByPopping() -> TreeNode? {
198-
guard let parentContainer: DependencyContainer = dependencyChain.last else {
199-
return nil
200-
}
201-
202-
guard let dependencyNames = parentContainer.dependencyNamesByConcreteType[inspectingContainer.type] else {
203-
return nil
204-
}
205202

206-
guard let dependencyName = dependencyNames.first(where: { parentContainer.dependencies[$0] != nil }),
207-
let parentDependency = parentContainer.dependencies[dependencyName] else {
203+
guard let parent = parent,
204+
let parentDependency = parent.inspectingContainer.dependencies.orderedValues.first(where: { $0 === previousDependencyPointer }) else {
208205
return nil
209206
}
210207

211-
return TreeNode(inspectingContainer: parentContainer,
212-
dependencyChain: dependencyChain.dropLast(),
213-
dependencyGraph: dependencyGraph,
214-
dependencyPointer: parentDependency).nextByIterating(after: parentDependency)
208+
return parent.nextByIterating(after: parentDependency)
215209
}
216210
}

Sources/WeaverCommand/Configuration.swift

+10-2
Original file line numberDiff line numberDiff line change
@@ -240,15 +240,23 @@ extension Configuration {
240240

241241
func inputPaths() throws -> [Path] {
242242
var inputPaths = Set<Path>()
243-
243+
var fullIgnoredPaths = Set<Path>()
244+
244245
inputPaths.formUnion(inputPathStrings
245246
.lazy
246247
.map { self.projectPath + $0 }
247248
.flatMap { $0.isFile ? [$0] : self.recursivePathsByPattern(fromDirectory: $0) }
248249
.filter { $0.exists && $0.isFile && $0.extension == "swift" }
249250
.map { $0.absolute() })
250251

251-
inputPaths.subtract(try ignoredPathStrings
252+
fullIgnoredPaths.formUnion(ignoredPathStrings
253+
.lazy
254+
.map { self.projectPath + $0 }
255+
.flatMap { $0.isFile ? [$0] : self.recursivePathsByPattern(fromDirectory: $0) }
256+
.filter { $0.exists && $0.isFile && $0.extension == "swift" }
257+
.map { $0.absolute() })
258+
259+
inputPaths.subtract(try fullIgnoredPaths
252260
.lazy
253261
.map { self.projectPath + $0 }
254262
.flatMap { $0.isFile ? [$0] : try paths(fromDirectory: $0) }

Tests/WeaverCodeGenTests/RuntimeTreeInspectorTests.swift

+56
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,60 @@ final class RuntimeTreeInspectorTests: XCTestCase {
108108
XCTFail("Unexpected error: \(error).")
109109
}
110110
}
111+
112+
func test_tree_inspector_should_validate_a_dependency_graph_with_the_same_concrete_type_fulfilling_multiple_individual_abstractions() {
113+
114+
let file = File(contents: """
115+
protocol DataConfiguring { }
116+
117+
protocol DataProviding { }
118+
119+
final class Coordinator {
120+
// weaver: value <= Int
121+
}
122+
123+
final class AppDelegate {
124+
// weaver: coordinator = Coordinator
125+
// weaver: coordinator.scope = .transient
126+
127+
// weaver: dataConfiguration = DataManager <- DataConfiguring
128+
// weaver: dataConfiguration.scope = .container
129+
// weaver: dataConfiguration.builder = { _ in DataManager.shared }
130+
131+
// weaver: dataProvider = DataManager <- DataProviding
132+
// weaver: dataProvider.scope = .container
133+
// weaver: dataProvider.builder = { _ in DataManager.shared }
134+
135+
// weaver: viewController1 = ViewController1
136+
// weaver: viewController1.scope = .transient
137+
}
138+
139+
final class DataManager: DataConfiguring, DataProviding {
140+
static let shared = DataManager()
141+
}
142+
143+
final class ViewController1: UIViewController {
144+
// weaver: coordinator <= Coordinator
145+
146+
// weaver: viewController2 = ViewController2
147+
// weaver: viewController2.scope = .transient
148+
}
149+
150+
final class ViewController2: UIViewController {
151+
// weaver: coordinator <- Coordinator
152+
}
153+
""")
154+
155+
do {
156+
let lexer = Lexer(file, fileName: "test.swift")
157+
let tokens = try lexer.tokenize()
158+
let parser = Parser(tokens, fileName: "test.swift")
159+
let syntaxTree = try parser.parse()
160+
let linker = try Linker(syntaxTrees: [syntaxTree])
161+
let inspector = Inspector(dependencyGraph: linker.dependencyGraph)
162+
try inspector.validate()
163+
} catch {
164+
XCTFail("Unexpected error: \(error)")
165+
}
166+
}
111167
}

0 commit comments

Comments
 (0)