Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions Examples/Example Package Integration/Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 0 additions & 9 deletions Examples/ExamplePrebuiltPackageIntegration/Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 1 addition & 10 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ let package = Package(
],
dependencies: [
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.2.0"),
.package(url: "https://github.com/apple/swift-collections.git", from: "1.0.0"),
.package(url: "https://github.com/swiftlang/swift-syntax.git", "603.0.0"..<"605.0.0"),
],
targets: [
Expand Down Expand Up @@ -192,7 +191,6 @@ let package = Package(
.target(
name: "SafeDICore",
dependencies: [
.product(name: "Collections", package: "swift-collections"),
.product(name: "SwiftDiagnostics", package: "swift-syntax"),
.product(name: "SwiftSyntax", package: "swift-syntax"),
.product(name: "SwiftSyntaxBuilder", package: "swift-syntax"),
Expand Down
22 changes: 9 additions & 13 deletions Sources/SafeDICore/Generators/DependencyTreeGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import Collections

public actor DependencyTreeGenerator {
// MARK: Initialization

Expand Down Expand Up @@ -446,7 +444,7 @@ public actor DependencyTreeGenerator {
private func validateMockRootScopeForCycles(
_ scope: Scope,
typeDescriptionToScopeMap: [TypeDescription: Scope],
propertyStack: OrderedSet<Property> = [],
propertyStack: [Property] = [],
) throws {
// Check dependencies for cycles (catches @Received references back to ancestors).
for dependency in scope.instantiable.dependencies {
Expand All @@ -456,7 +454,7 @@ public actor DependencyTreeGenerator {
}
let typesInCycle = (
[propertyForDependency]
+ propertyStack.elements[0...cycleIndex],
+ propertyStack[0...cycleIndex],
).map(\.typeDescription)
try Self.throwIfInvalidCycle(
typesInCycle: typesInCycle,
Expand All @@ -471,8 +469,7 @@ public actor DependencyTreeGenerator {
switch childPropertyToGenerate {
case let .instantiated(childProperty, childScope, _):
guard !propertyStack.contains(childProperty) else { continue }
var nextStack = propertyStack
nextStack.insert(childProperty, at: 0)
let nextStack = [childProperty] + propertyStack
try validateMockRootScopeForCycles(
childScope,
typeDescriptionToScopeMap: typeDescriptionToScopeMap,
Expand Down Expand Up @@ -748,14 +745,14 @@ public actor DependencyTreeGenerator {
on scope: Scope,
receivableProperties: Set<Property>,
property: Property?,
propertyStack: OrderedSet<Property>,
propertyStack: [Property],
root: TypeDescription,
) throws {
if let property {
func validateNoCycleInReceivedProperties(
scope: Scope,
receivedPropertyStack: OrderedSet<Property>,
instantiationStack: OrderedSet<Property>,
receivedPropertyStack: [Property],
instantiationStack: [Property],
) throws {
for childProperty in scope.requiredReceivedProperties {
guard childProperty != property else {
Expand All @@ -769,7 +766,7 @@ public actor DependencyTreeGenerator {
return
}
if let cycleIndex = instantiationStack.firstIndex(of: childProperty) {
let instantiationProperties = Array(instantiationStack.elements[0...cycleIndex])
let instantiationProperties = Array(instantiationStack[0...cycleIndex])
let typesInCycle = (
[childProperty]
+ receivedPropertyStack.reversed()
Expand All @@ -792,8 +789,7 @@ public actor DependencyTreeGenerator {
}
}
}
var instantiationStack = propertyStack
instantiationStack.insert(property, at: 0)
let instantiationStack: [Property] = [property] + propertyStack
try validateNoCycleInReceivedProperties(
scope: scope,
receivedPropertyStack: [],
Expand Down Expand Up @@ -834,7 +830,7 @@ public actor DependencyTreeGenerator {
}
let typesInCycle = (
[propertyForDependency]
+ childPropertyStack.elements[0...cycleIndex],
+ childPropertyStack[0...cycleIndex],
).map(\.typeDescription)
try Self.throwIfInvalidCycle(
typesInCycle: typesInCycle,
Expand Down
22 changes: 13 additions & 9 deletions Sources/SafeDICore/Generators/ScopeGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import Collections
import Foundation

/// A model capable of generating code for a scope’s dependency tree.
Expand Down Expand Up @@ -339,7 +338,7 @@ actor ScopeGenerator: CustomStringConvertible, Sendable {
private lazy var orderedPropertiesToGenerate: [ScopeGenerator] = {
var orderedPropertiesToGenerate = [ScopeGenerator]()
var propertyToUnfulfilledScopeMap = propertiesToGenerate
.reduce(into: OrderedDictionary<Property, ScopeGenerator>()) { partialResult, scope in
.reduce(into: [Property: ScopeGenerator]()) { partialResult, scope in
if let property = scope.property {
partialResult[property] = scope
}
Expand All @@ -352,13 +351,18 @@ actor ScopeGenerator: CustomStringConvertible, Sendable {
}
// Mark as fulfilled before recursing to prevent cycles.
propertyToUnfulfilledScopeMap[property] = nil
let scopeDependencies = propertyToUnfulfilledScopeMap
.keys
.intersection(
scope.receivedProperties
.union(scope.onlyIfAvailableUnwrappedReceivedProperties),
)
.compactMap { propertyToUnfulfilledScopeMap[$0] }
let receivedAndOnlyIfAvailable = scope.receivedProperties
.union(scope.onlyIfAvailableUnwrappedReceivedProperties)
let scopeDependencies = propertiesToGenerate
.compactMap { childScope -> ScopeGenerator? in
guard let childProperty = childScope.property,
propertyToUnfulfilledScopeMap[childProperty] != nil,
receivedAndOnlyIfAvailable.contains(childProperty)
else {
return nil
}
return childScope
}
// Fulfill the scopes we depend upon.
for dependentScope in scopeDependencies {
fulfill(dependentScope)
Expand Down
3 changes: 1 addition & 2 deletions Sources/SafeDICore/Models/Initializer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import Collections
@preconcurrency import SwiftSyntax
import SwiftSyntaxBuilder

Expand Down Expand Up @@ -121,7 +120,7 @@ public struct Initializer: Codable, Hashable, Sendable {
let dependencyAndArgumentBinding = try createDependencyAndArgumentBinding(given: dependencies)

let initializerFulfulledDependencies = Set(dependencyAndArgumentBinding.map(\.dependency))
let missingArguments = OrderedSet(dependencies).subtracting(initializerFulfulledDependencies)
let missingArguments = dependencies.filter { !initializerFulfulledDependencies.contains($0) }

if !missingArguments.isEmpty {
try recordError(.missingArguments(missingArguments.map(\.property)))
Expand Down
4 changes: 1 addition & 3 deletions Sources/SafeDICore/Models/Scope.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import Collections

/// A model of the scoped dependencies required for an `@Instantiable` in the reachable dependency tree.
final class Scope: Hashable {
// MARK: Initialization
Expand Down Expand Up @@ -103,7 +101,7 @@ final class Scope: Hashable {

func createScopeGenerator(
for property: Property?,
propertyStack: OrderedSet<Property>,
propertyStack: [Property],
receivableProperties: Set<Property>,
erasedToConcreteExistential: Bool,
forMockGeneration: Bool = false,
Expand Down
Loading