Skip to content

Runtime error with "properly generated needle code" #456

@dbagwell

Description

@dbagwell

To my understanding, there are basically two steps the generator performs to ensure compile time safety.

  1. Try to ensure components are only instantiated from inside their parent components. This is so that pattern matching can be used to detect these instantiations and create the component graph.
  2. Ensure that all dependencies of a component are satisfied in one of it's parent components in the graph.

If one tries to instantiate a component outside of it's parent say like

final class SomeClass {

    private let component: ChildComponent 

    init(parentScope: Scope) {
        self.component = ChildComponent(parent: parentScope)
    }

}

The following error is produced at compile time

ChildComponent is instantiated incorrectly in {file_url}. All components must be instantiated by parent components, by passing `self` as the argument to the parent parameter.

However, because this uses pattern matching of the component name, .init syntax is not recognized. (This issue is also outlined here #394 )

The following code does not produce the previously mentioned error because the generator doesn't recognize it as a component instantiation:

final class SomeClass {

    private let component: ChildComponent 

    init(parentScope: Scope) {
        self.component = .init(parent: parentScope)
    }

}

This is fine as long as the parentScope satisfies all of ChildComponent's dependencies but the generator doesn't guarantee this.

For example, using the following components:

final class RootComponent: BootstrapComponent {
    var firstComponent: FirstComponent { return FirstComponent(parent: self) }
}

final class FirstComponent: Component<EmptyDependency> {
    var name: String { return "Some Name" }
    var secondComponent: SecondComponent { return SecondComponent(parent: self) }
}

protocol SecondDependency: Dependency {
    var name: String { get }
}

final class SecondComponent: Component<SecondDependency> {}

The following code crashes:

final class SomeClass {
    
    let secondComponent: SecondComponent
    
    init() {
        self.secondComponent = .init(parent: RootComponent())
    }
    
    func printName() {
        print(self.secondComponent.name) // Crashes here
    }
    
}

With the following error that is documented as This case should never occur with properly generated Needle code.:

Missing dependency provider factory for ["^", "RootComponent", "SecondComponent"]

I think in order to fix this issue, the generator would need to use the AST to detect component instantiations instead of just pattern matching, but I have no idea how feasible that is. If it can be done, it also shouldn't matter if components get instantiated outside of their parent at all because the AST would be used to detect them wherever they are in order to build the graph (it would be really nice if this were possible so that the boilerplate of defining child components in their parents is no longer necessary).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions