Skip to content

Commit

Permalink
dingo: support bindings of functions and structs (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
tessig authored Jul 1, 2021
1 parent da745b2 commit dd5ca7b
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 16 deletions.
25 changes: 19 additions & 6 deletions src/analyzers/dingo/checks/bind/binding_implements_interface.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package bind

import (
"fmt"
"go/ast"
"go/types"

Expand Down Expand Up @@ -77,18 +78,30 @@ func checkBlockStatmenetForCorrectBindings(block *ast.BlockStmt, pass *analysis.
if ok := bindCalls[firstFunc.Name()] && toCalls[secondFunc.Name()]; ok {
bindType := pass.TypesInfo.Types[firstCall.Args[0]].Type
toType := pass.TypesInfo.Types[secondCall.Args[0]].Type
iface := bindType.(*types.Pointer).Elem().(*types.Named).Underlying().(*types.Interface)

// If struct literal is used, get the toType into the correct format
_, ok := toType.(*types.Named)
if ok {
toType = types.NewPointer(toType)
}
if !types.Implements(toType, iface) {
message := "Incorrect Binding! `" + toType.Underlying().String()[1:] + "` must implement Interface `" + bindType.Underlying().String()[1:] + "`"
flanalysis.Report(pass, message, secondCall.Args[0])
switch what := bindType.(*types.Pointer).Elem().(*types.Named).Underlying().(type) {
case *types.Interface:
// in case of interface to interface binding
to := toType.(*types.Pointer).Elem().Underlying()
if !types.Implements(toType, what) && !types.Implements(to, what) {
message := fmt.Sprintf("Incorrect Binding! %q must implement Interface %q", toType.Underlying().String(), bindType.Underlying().String())
flanalysis.Report(pass, message, secondCall.Args[0])
}
case *types.Signature:
if !types.AssignableTo(toType, what) {
message := fmt.Sprintf("Incorrect Binding! %q must have Signature of %q", toType.String(), what.String())
flanalysis.Report(pass, message, secondCall.Args[0])
}
default:
if !types.AssignableTo(toType, bindType) {
message := fmt.Sprintf("Incorrect Binding! %q must be assignable to %q", toType.String(), bindType.String())
flanalysis.Report(pass, message, secondCall.Args[0])
}
}

}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
package correct_interface_to_instance_binding

import "flamingo.me/dingo"
import (
"flamingo.me/dingo"
)

type I interface {
funA()
funB()
}

type J interface {
funA()
}

type A struct{}
type B struct{}

Expand All @@ -18,6 +24,16 @@ func (a *A) funA() {}

func (a *B) funB() {}

type F func(a string) bool

func IsF(b string) bool {
return false
}

func IsNotF() bool {
return false
}

type otherType struct {
}

Expand All @@ -31,36 +47,54 @@ func (*otherType) To(what interface{}) {
func (*C) Configure(injector *dingo.Injector, otherType *otherType) {
// check different bind possibilities
injector.Bind(new(I)).To(new(A))
injector.Bind(new(I)).To(new(B)) // want "Incorrect Binding! `*correct_interface_to_instance_binding.B` must implement Interface `correct_interface_to_instance_binding.I`"
injector.Bind(new(I)).To(new(B)) // want "Incorrect Binding! \"\\*correct_interface_to_instance_binding.B\" must implement Interface \"\\*correct_interface_to_instance_binding.I\""

injector.Bind(new(J)).To(new(I))
injector.Bind(new(I)).To(new(J)) // want "Incorrect Binding! \"\\*correct_interface_to_instance_binding.J\" must implement Interface \"\\*correct_interface_to_instance_binding.I\""

injector.Bind(new(I)).ToInstance(new(A))
injector.Bind(new(I)).ToInstance(new(B)) // want "Incorrect Binding! `*correct_interface_to_instance_binding.B` must implement Interface `correct_interface_to_instance_binding.I`"
injector.Bind(new(I)).ToInstance(new(B)) // want "Incorrect Binding! \"\\*correct_interface_to_instance_binding.B\" must implement Interface \"\\*correct_interface_to_instance_binding.I\""

// check if string literals are allowed
injector.Bind(new(I)).ToInstance(A{})
injector.Bind(new(I)).ToInstance(B{}) // want "Incorrect Binding! `*correct_interface_to_instance_binding.B` must implement Interface `correct_interface_to_instance_binding.I`"
injector.Bind(new(I)).ToInstance(B{}) // want "Incorrect Binding! \"\\*correct_interface_to_instance_binding.B\" must implement Interface \"\\*correct_interface_to_instance_binding.I\""

injector.BindMulti(new(I)).To(new(A))
injector.BindMulti(new(I)).To(new(B)) // want "Incorrect Binding! `*correct_interface_to_instance_binding.B` must implement Interface `correct_interface_to_instance_binding.I`"
injector.BindMulti(new(I)).To(new(B)) // want "Incorrect Binding! \"\\*correct_interface_to_instance_binding.B\" must implement Interface \"\\*correct_interface_to_instance_binding.I\""
injector.BindMulti(new(I)).ToInstance(new(A))
injector.BindMulti(new(I)).ToInstance(new(B)) // want "Incorrect Binding! `*correct_interface_to_instance_binding.B` must implement Interface `correct_interface_to_instance_binding.I`"
injector.BindMulti(new(I)).ToInstance(new(B)) // want "Incorrect Binding! \"\\*correct_interface_to_instance_binding.B\" must implement Interface \"\\*correct_interface_to_instance_binding.I\""

// function binding
injector.Bind(new(F)).ToInstance(IsF)
injector.Bind(new(F)).ToInstance(IsNotF) // want "Incorrect Binding! \"func\\(\\) bool\" must have Signature of \"func\\(a string\\) bool\""
injector.Bind(new(F)).ToInstance(func(string) bool { return false })
injector.Bind(new(F)).ToInstance(func() bool { return false }) // want "Incorrect Binding! \"func\\(\\) bool\" must have Signature of \"func\\(a string\\) bool\""

otherType.Bind(new(I)).To(new(A))
otherType.Bind(new(I)).To(new(B))

// check struct binding
injector.Bind(new(A)).In(dingo.Singleton)
injector.Bind(new(A)).ToInstance(new(A))
injector.Bind(new(A)).ToInstance(new(B)) // want "Incorrect Binding! \"\\*correct_interface_to_instance_binding.B\" must be assignable to \"\\*correct_interface_to_instance_binding.A\""

// check uncommon bind arguments
injector.Bind((*I)(nil)).To(new(A))
injector.Bind((*I)(nil)).To(new(B)) // want "Incorrect Binding! \"\\*correct_interface_to_instance_binding.B\" must implement Interface \"\\*correct_interface_to_instance_binding.I\""
}

// check if it works in other functions than "configure"
func (*D) functionName(injector *dingo.Injector, otherType *otherType) {
injector.Bind(new(I)).To(new(A))
injector.Bind(new(I)).To(new(B)) // want "Incorrect Binding! `*correct_interface_to_instance_binding.B` must implement Interface `correct_interface_to_instance_binding.I`"
injector.Bind(new(I)).To(new(B)) // want "Incorrect Binding! \"\\*correct_interface_to_instance_binding.B\" must implement Interface \"\\*correct_interface_to_instance_binding.I\""
injector.Bind(new(I)).ToInstance(new(A))
injector.Bind(new(I)).ToInstance(new(B)) // want "Incorrect Binding! `*correct_interface_to_instance_binding.B` must implement Interface `correct_interface_to_instance_binding.I`"
injector.Bind(new(I)).ToInstance(new(B)) // want "Incorrect Binding! \"\\*correct_interface_to_instance_binding.B\" must implement Interface \"\\*correct_interface_to_instance_binding.I\""
}

// check if it works in functions that have no Receiver
func noReceiverFunc(injector *dingo.Injector, otherType *otherType) {
injector.Bind(new(I)).To(new(A))
injector.Bind(new(I)).To(new(B)) // want "Incorrect Binding! `*correct_interface_to_instance_binding.B` must implement Interface `correct_interface_to_instance_binding.I`"
injector.Bind(new(I)).To(new(B)) // want "Incorrect Binding! \"\\*correct_interface_to_instance_binding.B\" must implement Interface \"\\*correct_interface_to_instance_binding.I\""
injector.Bind(new(I)).ToInstance(new(A))
injector.Bind(new(I)).ToInstance(new(B)) // want "Incorrect Binding! `*correct_interface_to_instance_binding.B` must implement Interface `correct_interface_to_instance_binding.I`"
injector.Bind(new(I)).ToInstance(new(B)) // want "Incorrect Binding! \"\\*correct_interface_to_instance_binding.B\" must implement Interface \"\\*correct_interface_to_instance_binding.I\""
}

0 comments on commit dd5ca7b

Please sign in to comment.