2018-08-13 11:37:58 -07:00
// Copyright 2018 The Go Cloud Authors
2018-05-01 14:46:39 -04:00
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2018-05-31 15:34:15 -07:00
package wire
2018-04-02 09:21:52 -07:00
import (
2018-05-08 16:26:38 -04:00
"errors"
2018-04-02 09:21:52 -07:00
"fmt"
"go/ast"
2018-04-04 10:58:07 -07:00
"go/build"
2018-04-02 09:21:52 -07:00
"go/token"
"go/types"
"strconv"
"strings"
2018-04-27 13:44:54 -04:00
"golang.org/x/tools/go/ast/astutil"
2018-04-02 09:21:52 -07:00
"golang.org/x/tools/go/loader"
2018-06-15 15:58:48 -07:00
"golang.org/x/tools/go/types/typeutil"
2018-04-02 09:21:52 -07:00
)
2018-08-02 09:31:50 -07:00
// A providerSetSrc captures the source for a type provided by a ProviderSet.
// Exactly one of the fields will be set.
type providerSetSrc struct {
Provider * Provider
Binding * IfaceBinding
Value * Value
Import * ProviderSet
}
2018-04-04 14:42:56 -07:00
// A ProviderSet describes a set of providers. The zero value is an empty
// ProviderSet.
type ProviderSet struct {
2018-05-31 15:34:15 -07:00
// Pos is the position of the call to wire.NewSet or wire.Build that
2018-04-27 13:44:54 -04:00
// created the set.
Pos token . Pos
// PkgPath is the import path of the package that declared this set.
PkgPath string
2018-08-02 14:00:17 -07:00
// VarName is the variable name of the set, if it came from a package
2018-04-27 13:44:54 -04:00
// variable.
2018-08-02 14:00:17 -07:00
VarName string
2018-04-27 13:44:54 -04:00
2018-04-04 14:42:56 -07:00
Providers [ ] * Provider
2018-04-27 13:44:54 -04:00
Bindings [ ] * IfaceBinding
2018-05-04 12:44:53 -04:00
Values [ ] * Value
2018-04-27 13:44:54 -04:00
Imports [ ] * ProviderSet
2018-06-15 15:58:48 -07:00
2018-08-16 15:36:53 -07:00
// providerMap maps from provided type to a *ProvidedType.
2018-06-15 15:58:48 -07:00
// It includes all of the imported types.
providerMap * typeutil . Map
2018-08-02 09:31:50 -07:00
// srcMap maps from provided type to a *providerSetSrc capturing the
// Provider, Binding, Value, or Import that provided the type.
srcMap * typeutil . Map
2018-06-15 15:58:48 -07:00
}
// Outputs returns a new slice containing the set of possible types the
// provider set can produce. The order is unspecified.
func ( set * ProviderSet ) Outputs ( ) [ ] types . Type {
return set . providerMap . Keys ( )
}
2018-08-16 15:36:53 -07:00
// For returns a ProvidedType for the given type, or the zero ProvidedType.
func ( set * ProviderSet ) For ( t types . Type ) ProvidedType {
pt := set . providerMap . At ( t )
if pt == nil {
return ProvidedType { }
2018-06-15 15:58:48 -07:00
}
2018-08-16 15:36:53 -07:00
return * pt . ( * ProvidedType )
2018-04-02 09:21:52 -07:00
}
2018-04-04 14:42:56 -07:00
// An IfaceBinding declares that a type should be used to satisfy inputs
2018-04-02 14:08:17 -07:00
// of the given interface type.
2018-04-04 14:42:56 -07:00
type IfaceBinding struct {
// Iface is the interface type, which is what can be injected.
Iface types . Type
2018-04-03 21:11:53 -07:00
2018-04-04 14:42:56 -07:00
// Provided is always a type that is assignable to Iface.
Provided types . Type
2018-04-03 21:11:53 -07:00
2018-04-04 14:42:56 -07:00
// Pos is the position where the binding was declared.
Pos token . Pos
2018-04-02 14:08:17 -07:00
}
2018-04-04 14:42:56 -07:00
// Provider records the signature of a provider. A provider is a
// single Go object, either a function or a named struct type.
type Provider struct {
// ImportPath is the package path that the Go object resides in.
ImportPath string
2018-04-03 21:11:53 -07:00
2018-04-04 14:42:56 -07:00
// Name is the name of the Go object.
Name string
2018-04-03 21:11:53 -07:00
2018-04-04 14:42:56 -07:00
// Pos is the source position of the func keyword or type spec
2018-04-03 21:11:53 -07:00
// defining this provider.
2018-04-04 14:42:56 -07:00
Pos token . Pos
2018-04-03 21:11:53 -07:00
2018-04-04 14:42:56 -07:00
// Args is the list of data dependencies this provider has.
Args [ ] ProviderInput
2018-04-03 21:11:53 -07:00
2018-04-04 14:42:56 -07:00
// IsStruct is true if this provider is a named struct type.
2018-04-03 21:11:53 -07:00
// Otherwise it's a function.
2018-04-04 14:42:56 -07:00
IsStruct bool
2018-04-03 21:11:53 -07:00
2018-04-04 14:42:56 -07:00
// Fields lists the field names to populate. This will map 1:1 with
2018-04-03 21:11:53 -07:00
// elements in Args.
2018-04-04 14:42:56 -07:00
Fields [ ] string
2018-04-03 21:11:53 -07:00
2018-08-16 15:36:53 -07:00
// Out is the set of types this provider produces. It will always
// contain at least one type.
Out [ ] types . Type
2018-04-03 21:11:53 -07:00
2018-04-04 14:42:56 -07:00
// HasCleanup reports whether the provider function returns a cleanup
2018-04-03 21:11:53 -07:00
// function. (Always false for structs.)
2018-04-04 14:42:56 -07:00
HasCleanup bool
2018-04-03 21:11:53 -07:00
2018-04-04 14:42:56 -07:00
// HasErr reports whether the provider function can return an error.
2018-04-03 21:11:53 -07:00
// (Always false for structs.)
2018-04-04 14:42:56 -07:00
HasErr bool
}
// ProviderInput describes an incoming edge in the provider graph.
type ProviderInput struct {
2018-04-26 14:23:06 -04:00
Type types . Type
// TODO(light): Move field name into this struct.
2018-04-04 14:42:56 -07:00
}
2018-05-04 12:44:53 -04:00
// Value describes a value expression.
type Value struct {
// Pos is the source position of the expression defining this value.
Pos token . Pos
// Out is the type this value produces.
Out types . Type
2018-05-31 15:34:15 -07:00
// expr is the expression passed to wire.Value.
2018-05-04 12:44:53 -04:00
expr ast . Expr
// info is the type info for the expression.
info * types . Info
}
2018-04-04 14:42:56 -07:00
// Load finds all the provider sets in the given packages, as well as
2018-07-17 10:39:30 -07:00
// the provider sets' transitive dependencies. It may return both errors
2018-06-25 10:30:34 -07:00
// and Info.
func Load ( bctx * build . Context , wd string , pkgs [ ] string ) ( * Info , [ ] error ) {
2018-07-19 16:04:26 -07:00
prog , errs := load ( bctx , wd , pkgs )
if len ( errs ) > 0 {
return nil , errs
2018-04-04 14:42:56 -07:00
}
info := & Info {
Fset : prog . Fset ,
Sets : make ( map [ ProviderSetID ] * ProviderSet ) ,
}
2018-04-27 13:44:54 -04:00
oc := newObjectCache ( prog )
2018-07-19 16:04:26 -07:00
ec := new ( errorCollector )
2018-04-27 13:44:54 -04:00
for _ , pkgInfo := range prog . InitialPackages ( ) {
2018-07-19 16:04:26 -07:00
if isWireImport ( pkgInfo . Pkg . Path ( ) ) {
// The marker function package confuses analysis.
continue
}
2018-04-27 13:44:54 -04:00
scope := pkgInfo . Pkg . Scope ( )
for _ , name := range scope . Names ( ) {
2018-07-17 10:39:30 -07:00
obj := scope . Lookup ( name )
if ! isProviderSetType ( obj . Type ( ) ) {
2018-04-27 13:44:54 -04:00
continue
2018-04-04 14:42:56 -07:00
}
2018-07-17 10:39:30 -07:00
item , errs := oc . get ( obj )
if len ( errs ) > 0 {
ec . add ( notePositionAll ( prog . Fset . Position ( obj . Pos ( ) ) , errs ) ... )
2018-04-27 13:44:54 -04:00
continue
2018-04-04 14:42:56 -07:00
}
2018-07-17 10:39:30 -07:00
pset := item . ( * ProviderSet )
2018-04-27 13:44:54 -04:00
// pset.Name may not equal name, since it could be an alias to
// another provider set.
id := ProviderSetID { ImportPath : pset . PkgPath , VarName : name }
info . Sets [ id ] = pset
2018-04-04 14:42:56 -07:00
}
2018-07-19 16:04:26 -07:00
for _ , f := range pkgInfo . Files {
for _ , decl := range f . Decls {
fn , ok := decl . ( * ast . FuncDecl )
if ! ok {
continue
}
2018-09-27 15:30:13 -07:00
buildCall , err := findInjectorBuild ( & pkgInfo . Info , fn )
if err != nil {
ec . add ( notePosition ( prog . Fset . Position ( fn . Pos ( ) ) , fmt . Errorf ( "inject %s: %v" , fn . Name . Name , err ) ) )
continue
}
2018-07-19 16:04:26 -07:00
if buildCall == nil {
continue
}
2018-08-02 14:00:17 -07:00
set , errs := oc . processNewSet ( pkgInfo , buildCall , "" )
2018-07-19 16:04:26 -07:00
if len ( errs ) > 0 {
ec . add ( notePositionAll ( prog . Fset . Position ( fn . Pos ( ) ) , errs ) ... )
continue
}
sig := pkgInfo . ObjectOf ( fn . Name ) . Type ( ) . ( * types . Signature )
ins , out , err := injectorFuncSignature ( sig )
if err != nil {
if w , ok := err . ( * wireErr ) ; ok {
ec . add ( notePosition ( w . position , fmt . Errorf ( "inject %s: %v" , fn . Name . Name , w . error ) ) )
} else {
ec . add ( notePosition ( prog . Fset . Position ( fn . Pos ( ) ) , fmt . Errorf ( "inject %s: %v" , fn . Name . Name , err ) ) )
}
continue
}
_ , errs = solve ( prog . Fset , out . out , ins , set )
if len ( errs ) > 0 {
ec . add ( mapErrors ( errs , func ( e error ) error {
if w , ok := e . ( * wireErr ) ; ok {
return notePosition ( w . position , fmt . Errorf ( "inject %s: %v" , fn . Name . Name , w . error ) )
}
return notePosition ( prog . Fset . Position ( fn . Pos ( ) ) , fmt . Errorf ( "inject %s: %v" , fn . Name . Name , e ) )
} ) ... )
continue
}
info . Injectors = append ( info . Injectors , & Injector {
ImportPath : pkgInfo . Pkg . Path ( ) ,
FuncName : fn . Name . Name ,
} )
}
}
2018-04-04 14:42:56 -07:00
}
2018-07-17 10:39:30 -07:00
return info , ec . errors
2018-04-04 14:42:56 -07:00
}
2018-07-19 16:04:26 -07:00
// load typechecks the packages, including function body type checking
// for the packages directly named.
func load ( bctx * build . Context , wd string , pkgs [ ] string ) ( * loader . Program , [ ] error ) {
var foundPkgs [ ] * build . Package
ec := new ( errorCollector )
for _ , name := range pkgs {
p , err := bctx . Import ( name , wd , build . FindOnly )
if err != nil {
ec . add ( err )
continue
}
foundPkgs = append ( foundPkgs , p )
}
if len ( ec . errors ) > 0 {
return nil , ec . errors
}
conf := & loader . Config {
Build : bctx ,
Cwd : wd ,
TypeChecker : types . Config {
Error : func ( err error ) {
ec . add ( err )
} ,
} ,
TypeCheckFuncBodies : func ( path string ) bool {
return importPathInPkgList ( foundPkgs , path )
} ,
FindPackage : func ( bctx * build . Context , importPath , fromDir string , mode build . ImportMode ) ( * build . Package , error ) {
// Optimistically try to load in the package with normal build tags.
pkg , err := bctx . Import ( importPath , fromDir , mode )
// If this is the generated package, then load it in with the
// wireinject build tag to pick up the injector template. Since
// the *build.Context is shared between calls to FindPackage, this
// uses a copy.
if pkg != nil && importPathInPkgList ( foundPkgs , pkg . ImportPath ) {
bctx2 := new ( build . Context )
* bctx2 = * bctx
n := len ( bctx2 . BuildTags )
bctx2 . BuildTags = append ( bctx2 . BuildTags [ : n : n ] , "wireinject" )
pkg , err = bctx2 . Import ( importPath , fromDir , mode )
}
return pkg , err
} ,
}
for _ , name := range pkgs {
conf . Import ( name )
}
prog , err := conf . Load ( )
if len ( ec . errors ) > 0 {
return nil , ec . errors
}
if err != nil {
return nil , [ ] error { err }
}
return prog , nil
}
func importPathInPkgList ( pkgs [ ] * build . Package , path string ) bool {
for _ , p := range pkgs {
if path == p . ImportPath {
return true
}
}
return false
}
2018-04-04 14:42:56 -07:00
// Info holds the result of Load.
type Info struct {
Fset * token . FileSet
// Sets contains all the provider sets in the initial packages.
Sets map [ ProviderSetID ] * ProviderSet
2018-07-19 16:04:26 -07:00
// Injectors contains all the injector functions in the initial packages.
// The order is undefined.
Injectors [ ] * Injector
2018-04-04 14:42:56 -07:00
}
2018-04-27 13:44:54 -04:00
// A ProviderSetID identifies a named provider set.
2018-04-04 14:42:56 -07:00
type ProviderSetID struct {
ImportPath string
2018-04-27 13:44:54 -04:00
VarName string
2018-04-04 14:42:56 -07:00
}
// String returns the ID as ""path/to/pkg".Foo".
func ( id ProviderSetID ) String ( ) string {
2018-04-27 13:44:54 -04:00
return strconv . Quote ( id . ImportPath ) + "." + id . VarName
2018-04-02 09:21:52 -07:00
}
2018-07-19 16:04:26 -07:00
// An Injector describes an injector function.
type Injector struct {
ImportPath string
FuncName string
}
// String returns the injector name as ""path/to/pkg".Foo".
func ( in * Injector ) String ( ) string {
return strconv . Quote ( in . ImportPath ) + "." + in . FuncName
}
2018-05-31 15:34:15 -07:00
// objectCache is a lazily evaluated mapping of objects to Wire structures.
2018-04-27 13:44:54 -04:00
type objectCache struct {
prog * loader . Program
2018-07-13 08:51:43 -07:00
objects map [ objRef ] objCacheEntry
2018-06-15 15:58:48 -07:00
hasher typeutil . Hasher
2018-03-30 21:34:08 -07:00
}
2018-04-27 13:44:54 -04:00
type objRef struct {
importPath string
name string
2018-03-30 21:34:08 -07:00
}
2018-07-13 08:51:43 -07:00
type objCacheEntry struct {
val interface { } // *Provider, *ProviderSet, *IfaceBinding, or *Value
errs [ ] error
}
2018-04-27 13:44:54 -04:00
func newObjectCache ( prog * loader . Program ) * objectCache {
return & objectCache {
prog : prog ,
2018-07-13 08:51:43 -07:00
objects : make ( map [ objRef ] objCacheEntry ) ,
2018-06-15 15:58:48 -07:00
hasher : typeutil . MakeHasher ( ) ,
2018-03-30 21:34:08 -07:00
}
}
2018-05-31 15:34:15 -07:00
// get converts a Go object into a Wire structure. It may return a
2018-08-16 15:36:53 -07:00
// *Provider, an *IfaceBinding, a *ProviderSet, or a *Value.
2018-07-13 08:51:43 -07:00
func ( oc * objectCache ) get ( obj types . Object ) ( val interface { } , errs [ ] error ) {
2018-04-27 13:44:54 -04:00
ref := objRef {
importPath : obj . Pkg ( ) . Path ( ) ,
name : obj . Name ( ) ,
}
2018-07-13 08:51:43 -07:00
if ent , cached := oc . objects [ ref ] ; cached {
return ent . val , append ( [ ] error ( nil ) , ent . errs ... )
2018-04-27 13:44:54 -04:00
}
2018-07-13 08:51:43 -07:00
defer func ( ) {
oc . objects [ ref ] = objCacheEntry {
val : val ,
errs : append ( [ ] error ( nil ) , errs ... ) ,
}
} ( )
2018-04-27 13:44:54 -04:00
switch obj := obj . ( type ) {
case * types . Var :
spec := oc . varDecl ( obj )
if len ( spec . Values ) == 0 {
2018-07-13 08:51:43 -07:00
return nil , [ ] error { fmt . Errorf ( "%v is not a provider or a provider set" , obj ) }
2018-04-02 14:08:17 -07:00
}
2018-04-27 13:44:54 -04:00
var i int
for i = range spec . Names {
if spec . Names [ i ] . Name == obj . Name ( ) {
break
2018-04-02 14:08:17 -07:00
}
}
2018-08-02 14:00:17 -07:00
return oc . processExpr ( oc . prog . Package ( obj . Pkg ( ) . Path ( ) ) , spec . Values [ i ] , obj . Name ( ) )
2018-04-27 13:44:54 -04:00
case * types . Func :
2018-07-13 08:51:43 -07:00
return processFuncProvider ( oc . prog . Fset , obj )
2018-04-27 13:44:54 -04:00
default :
2018-07-13 08:51:43 -07:00
return nil , [ ] error { fmt . Errorf ( "%v is not a provider or a provider set" , obj ) }
2018-04-27 13:44:54 -04:00
}
}
// varDecl finds the declaration that defines the given variable.
func ( oc * objectCache ) varDecl ( obj * types . Var ) * ast . ValueSpec {
// TODO(light): Walk files to build object -> declaration mapping, if more performant.
// Recommended by https://golang.org/s/types-tutorial
pkg := oc . prog . Package ( obj . Pkg ( ) . Path ( ) )
pos := obj . Pos ( )
for _ , f := range pkg . Files {
tokenFile := oc . prog . Fset . File ( f . Pos ( ) )
if base := tokenFile . Base ( ) ; base <= int ( pos ) && int ( pos ) < base + tokenFile . Size ( ) {
path , _ := astutil . PathEnclosingInterval ( f , pos , pos )
for _ , node := range path {
if spec , ok := node . ( * ast . ValueSpec ) ; ok {
return spec
2018-04-02 10:57:48 -07:00
}
2018-04-02 09:21:52 -07:00
}
2018-03-30 21:34:08 -07:00
}
}
return nil
}
2018-05-31 15:34:15 -07:00
// processExpr converts an expression into a Wire structure. It may
2018-08-16 15:36:53 -07:00
// return a *Provider, an *IfaceBinding, a *ProviderSet, or a *Value.
2018-08-02 14:00:17 -07:00
func ( oc * objectCache ) processExpr ( pkg * loader . PackageInfo , expr ast . Expr , varName string ) ( interface { } , [ ] error ) {
2018-04-27 13:44:54 -04:00
exprPos := oc . prog . Fset . Position ( expr . Pos ( ) )
expr = astutil . Unparen ( expr )
if obj := qualifiedIdentObject ( & pkg . Info , expr ) ; obj != nil {
2018-07-17 10:39:30 -07:00
item , errs := oc . get ( obj )
return item , mapErrors ( errs , func ( err error ) error {
return notePosition ( exprPos , err )
} )
2018-04-27 13:44:54 -04:00
}
if call , ok := expr . ( * ast . CallExpr ) ; ok {
fnObj := qualifiedIdentObject ( & pkg . Info , call . Fun )
2018-05-31 15:34:15 -07:00
if fnObj == nil || ! isWireImport ( fnObj . Pkg ( ) . Path ( ) ) {
2018-07-17 10:39:30 -07:00
return nil , [ ] error { notePosition ( exprPos , errors . New ( "unknown pattern" ) ) }
2018-04-03 21:11:53 -07:00
}
2018-04-27 13:44:54 -04:00
switch fnObj . Name ( ) {
case "NewSet" :
2018-08-02 14:00:17 -07:00
pset , errs := oc . processNewSet ( pkg , call , varName )
2018-07-17 10:39:30 -07:00
return pset , notePositionAll ( exprPos , errs )
2018-04-27 13:44:54 -04:00
case "Bind" :
b , err := processBind ( oc . prog . Fset , & pkg . Info , call )
if err != nil {
2018-07-17 10:39:30 -07:00
return nil , [ ] error { notePosition ( exprPos , err ) }
2018-04-03 21:11:53 -07:00
}
2018-04-27 13:44:54 -04:00
return b , nil
2018-05-04 12:44:53 -04:00
case "Value" :
v , err := processValue ( oc . prog . Fset , & pkg . Info , call )
if err != nil {
2018-07-17 10:39:30 -07:00
return nil , [ ] error { notePosition ( exprPos , err ) }
2018-05-04 12:44:53 -04:00
}
return v , nil
2018-08-14 14:55:28 -07:00
case "InterfaceValue" :
v , err := processInterfaceValue ( oc . prog . Fset , & pkg . Info , call )
if err != nil {
return nil , [ ] error { notePosition ( exprPos , err ) }
}
return v , nil
2018-04-27 13:44:54 -04:00
default :
2018-07-17 10:39:30 -07:00
return nil , [ ] error { notePosition ( exprPos , errors . New ( "unknown pattern" ) ) }
2018-04-03 21:11:53 -07:00
}
2018-04-27 13:44:54 -04:00
}
if tn := structArgType ( & pkg . Info , expr ) ; tn != nil {
2018-07-13 08:51:43 -07:00
p , errs := processStructProvider ( oc . prog . Fset , tn )
if len ( errs ) > 0 {
2018-07-17 10:39:30 -07:00
return nil , notePositionAll ( exprPos , errs )
2018-04-03 21:11:53 -07:00
}
2018-08-16 15:36:53 -07:00
return p , nil
2018-04-27 13:44:54 -04:00
}
2018-07-17 10:39:30 -07:00
return nil , [ ] error { notePosition ( exprPos , errors . New ( "unknown pattern" ) ) }
2018-04-27 13:44:54 -04:00
}
2018-08-02 14:00:17 -07:00
func ( oc * objectCache ) processNewSet ( pkg * loader . PackageInfo , call * ast . CallExpr , varName string ) ( * ProviderSet , [ ] error ) {
2018-05-31 15:34:15 -07:00
// Assumes that call.Fun is wire.NewSet or wire.Build.
2018-04-27 13:44:54 -04:00
pset := & ProviderSet {
Pos : call . Pos ( ) ,
PkgPath : pkg . Pkg . Path ( ) ,
2018-08-02 14:00:17 -07:00
VarName : varName ,
2018-04-27 13:44:54 -04:00
}
2018-07-17 10:39:30 -07:00
ec := new ( errorCollector )
2018-04-27 13:44:54 -04:00
for _ , arg := range call . Args {
2018-08-02 14:00:17 -07:00
item , errs := oc . processExpr ( pkg , arg , "" )
2018-07-17 10:39:30 -07:00
if len ( errs ) > 0 {
ec . add ( errs ... )
continue
2018-04-27 13:44:54 -04:00
}
switch item := item . ( type ) {
case * Provider :
pset . Providers = append ( pset . Providers , item )
case * ProviderSet :
pset . Imports = append ( pset . Imports , item )
case * IfaceBinding :
pset . Bindings = append ( pset . Bindings , item )
2018-05-04 12:44:53 -04:00
case * Value :
pset . Values = append ( pset . Values , item )
2018-04-27 13:44:54 -04:00
default :
panic ( "unknown item type" )
2018-04-03 21:11:53 -07:00
}
2018-04-27 13:44:54 -04:00
}
2018-07-17 10:39:30 -07:00
if len ( ec . errors ) > 0 {
return nil , ec . errors
}
2018-07-13 08:51:43 -07:00
var errs [ ] error
2018-08-02 09:31:50 -07:00
pset . providerMap , pset . srcMap , errs = buildProviderMap ( oc . prog . Fset , oc . hasher , pset )
2018-07-13 08:51:43 -07:00
if len ( errs ) > 0 {
return nil , errs
2018-06-15 15:58:48 -07:00
}
2018-07-13 08:51:43 -07:00
if errs := verifyAcyclic ( pset . providerMap , oc . hasher ) ; len ( errs ) > 0 {
return nil , errs
2018-06-20 11:21:59 -07:00
}
2018-04-27 13:44:54 -04:00
return pset , nil
}
// structArgType attempts to interpret an expression as a simple struct type.
// It assumes any parentheses have been stripped.
func structArgType ( info * types . Info , expr ast . Expr ) * types . TypeName {
lit , ok := expr . ( * ast . CompositeLit )
if ! ok {
return nil
}
tn , ok := qualifiedIdentObject ( info , lit . Type ) . ( * types . TypeName )
if ! ok {
return nil
}
if _ , isStruct := tn . Type ( ) . Underlying ( ) . ( * types . Struct ) ; ! isStruct {
return nil
}
return tn
}
// qualifiedIdentObject finds the object for an identifier or a
// qualified identifier, or nil if the object could not be found.
func qualifiedIdentObject ( info * types . Info , expr ast . Expr ) types . Object {
switch expr := expr . ( type ) {
case * ast . Ident :
return info . ObjectOf ( expr )
case * ast . SelectorExpr :
pkgName , ok := expr . X . ( * ast . Ident )
if ! ok {
return nil
2018-04-03 21:11:53 -07:00
}
2018-04-27 13:44:54 -04:00
if _ , ok := info . ObjectOf ( pkgName ) . ( * types . PkgName ) ; ! ok {
return nil
2018-04-02 09:21:52 -07:00
}
2018-04-27 13:44:54 -04:00
return info . ObjectOf ( expr . Sel )
2018-04-03 21:11:53 -07:00
default :
2018-04-27 13:44:54 -04:00
return nil
2018-04-03 21:11:53 -07:00
}
}
2018-04-27 13:44:54 -04:00
// processFuncProvider creates a provider for a function declaration.
2018-07-13 08:51:43 -07:00
func processFuncProvider ( fset * token . FileSet , fn * types . Func ) ( * Provider , [ ] error ) {
2018-04-03 21:11:53 -07:00
sig := fn . Type ( ) . ( * types . Signature )
2018-03-30 21:34:08 -07:00
fpos := fn . Pos ( )
2018-05-08 16:26:38 -04:00
providerSig , err := funcOutput ( sig )
if err != nil {
2018-07-17 10:39:30 -07:00
return nil , [ ] error { notePosition ( fset . Position ( fpos ) , fmt . Errorf ( "wrong signature for provider %s: %v" , fn . Name ( ) , err ) ) }
2018-03-30 21:34:08 -07:00
}
params := sig . Params ( )
2018-04-04 14:42:56 -07:00
provider := & Provider {
2018-04-27 13:44:54 -04:00
ImportPath : fn . Pkg ( ) . Path ( ) ,
2018-04-04 14:42:56 -07:00
Name : fn . Name ( ) ,
Pos : fn . Pos ( ) ,
Args : make ( [ ] ProviderInput , params . Len ( ) ) ,
2018-08-16 15:36:53 -07:00
Out : [ ] types . Type { providerSig . out } ,
2018-05-08 16:26:38 -04:00
HasCleanup : providerSig . cleanup ,
HasErr : providerSig . err ,
2018-03-30 21:34:08 -07:00
}
for i := 0 ; i < params . Len ( ) ; i ++ {
2018-04-04 14:42:56 -07:00
provider . Args [ i ] = ProviderInput {
2018-04-26 14:23:06 -04:00
Type : params . At ( i ) . Type ( ) ,
2018-03-30 21:34:08 -07:00
}
for j := 0 ; j < i ; j ++ {
2018-04-04 14:42:56 -07:00
if types . Identical ( provider . Args [ i ] . Type , provider . Args [ j ] . Type ) {
2018-07-17 10:39:30 -07:00
return nil , [ ] error { notePosition ( fset . Position ( fpos ) , fmt . Errorf ( "provider has multiple parameters of type %s" , types . TypeString ( provider . Args [ j ] . Type , nil ) ) ) }
2018-03-30 21:34:08 -07:00
}
}
}
2018-04-03 21:11:53 -07:00
return provider , nil
}
2018-07-19 16:04:26 -07:00
func injectorFuncSignature ( sig * types . Signature ) ( [ ] types . Type , outputSignature , error ) {
out , err := funcOutput ( sig )
if err != nil {
return nil , outputSignature { } , err
}
params := sig . Params ( )
given := make ( [ ] types . Type , params . Len ( ) )
for i := 0 ; i < params . Len ( ) ; i ++ {
given [ i ] = params . At ( i ) . Type ( )
}
return given , out , nil
}
2018-05-08 16:26:38 -04:00
type outputSignature struct {
out types . Type
cleanup bool
err bool
}
// funcOutput validates an injector or provider function's return signature.
func funcOutput ( sig * types . Signature ) ( outputSignature , error ) {
results := sig . Results ( )
switch results . Len ( ) {
case 0 :
return outputSignature { } , errors . New ( "no return values" )
case 1 :
return outputSignature { out : results . At ( 0 ) . Type ( ) } , nil
case 2 :
out := results . At ( 0 ) . Type ( )
switch t := results . At ( 1 ) . Type ( ) ; {
case types . Identical ( t , errorType ) :
return outputSignature { out : out , err : true } , nil
case types . Identical ( t , cleanupType ) :
return outputSignature { out : out , cleanup : true } , nil
default :
return outputSignature { } , fmt . Errorf ( "second return type is %s; must be error or func()" , types . TypeString ( t , nil ) )
}
case 3 :
if t := results . At ( 1 ) . Type ( ) ; ! types . Identical ( t , cleanupType ) {
return outputSignature { } , fmt . Errorf ( "second return type is %s; must be func()" , types . TypeString ( t , nil ) )
}
if t := results . At ( 2 ) . Type ( ) ; ! types . Identical ( t , errorType ) {
return outputSignature { } , fmt . Errorf ( "third return type is %s; must be error" , types . TypeString ( t , nil ) )
}
return outputSignature {
out : results . At ( 0 ) . Type ( ) ,
cleanup : true ,
err : true ,
} , nil
default :
return outputSignature { } , errors . New ( "too many return values" )
}
}
2018-04-27 13:44:54 -04:00
// processStructProvider creates a provider for a named struct type.
2018-08-16 15:36:53 -07:00
// It produces pointer and non-pointer variants via two values in Out.
2018-07-13 08:51:43 -07:00
func processStructProvider ( fset * token . FileSet , typeName * types . TypeName ) ( * Provider , [ ] error ) {
2018-04-03 21:11:53 -07:00
out := typeName . Type ( )
2018-04-27 13:44:54 -04:00
st , ok := out . Underlying ( ) . ( * types . Struct )
if ! ok {
2018-07-13 08:51:43 -07:00
return nil , [ ] error { fmt . Errorf ( "%v does not name a struct" , typeName ) }
2018-04-27 13:44:54 -04:00
}
2018-04-03 21:11:53 -07:00
pos := typeName . Pos ( )
2018-04-04 14:42:56 -07:00
provider := & Provider {
2018-04-27 13:44:54 -04:00
ImportPath : typeName . Pkg ( ) . Path ( ) ,
2018-04-04 14:42:56 -07:00
Name : typeName . Name ( ) ,
Pos : pos ,
Args : make ( [ ] ProviderInput , st . NumFields ( ) ) ,
Fields : make ( [ ] string , st . NumFields ( ) ) ,
IsStruct : true ,
2018-08-16 15:36:53 -07:00
Out : [ ] types . Type { out , types . NewPointer ( out ) } ,
2018-04-03 21:11:53 -07:00
}
for i := 0 ; i < st . NumFields ( ) ; i ++ {
f := st . Field ( i )
2018-04-04 14:42:56 -07:00
provider . Args [ i ] = ProviderInput {
2018-04-26 14:23:06 -04:00
Type : f . Type ( ) ,
2018-04-03 21:11:53 -07:00
}
2018-04-04 14:42:56 -07:00
provider . Fields [ i ] = f . Name ( )
2018-04-03 21:11:53 -07:00
for j := 0 ; j < i ; j ++ {
2018-04-04 14:42:56 -07:00
if types . Identical ( provider . Args [ i ] . Type , provider . Args [ j ] . Type ) {
2018-07-17 10:39:30 -07:00
return nil , [ ] error { notePosition ( fset . Position ( pos ) , fmt . Errorf ( "provider struct has multiple fields of type %s" , types . TypeString ( provider . Args [ j ] . Type , nil ) ) ) }
2018-04-03 21:11:53 -07:00
}
}
}
return provider , nil
2018-04-02 09:21:52 -07:00
}
2018-05-31 15:34:15 -07:00
// processBind creates an interface binding from a wire.Bind call.
2018-04-27 13:44:54 -04:00
func processBind ( fset * token . FileSet , info * types . Info , call * ast . CallExpr ) ( * IfaceBinding , error ) {
2018-05-31 15:34:15 -07:00
// Assumes that call.Fun is wire.Bind.
2018-04-02 09:21:52 -07:00
2018-04-27 13:44:54 -04:00
if len ( call . Args ) != 2 {
2018-07-17 10:39:30 -07:00
return nil , notePosition ( fset . Position ( call . Pos ( ) ) , errors . New ( "call to Bind takes exactly two arguments" ) )
2018-04-02 09:21:52 -07:00
}
2018-04-27 13:44:54 -04:00
// TODO(light): Verify that arguments are simple expressions.
2018-05-29 08:45:14 -07:00
ifaceArgType := info . TypeOf ( call . Args [ 0 ] )
ifacePtr , ok := ifaceArgType . ( * types . Pointer )
2018-04-02 09:21:52 -07:00
if ! ok {
2018-07-17 10:39:30 -07:00
return nil , notePosition ( fset . Position ( call . Pos ( ) ) , fmt . Errorf ( "first argument to bind must be a pointer to an interface type; found %s" , types . TypeString ( ifaceArgType , nil ) ) )
2018-05-29 08:45:14 -07:00
}
methodSet , ok := ifacePtr . Elem ( ) . Underlying ( ) . ( * types . Interface )
if ! ok {
2018-07-17 10:39:30 -07:00
return nil , notePosition ( fset . Position ( call . Pos ( ) ) , fmt . Errorf ( "first argument to bind must be a pointer to an interface type; found %s" , types . TypeString ( ifaceArgType , nil ) ) )
2018-04-02 09:21:52 -07:00
}
2018-04-27 13:44:54 -04:00
provided := info . TypeOf ( call . Args [ 1 ] )
2018-05-29 08:45:14 -07:00
if types . Identical ( ifacePtr . Elem ( ) , provided ) {
2018-07-17 10:39:30 -07:00
return nil , notePosition ( fset . Position ( call . Pos ( ) ) , errors . New ( "cannot bind interface to itself" ) )
2018-04-02 14:08:17 -07:00
}
2018-04-27 13:44:54 -04:00
if ! types . Implements ( provided , methodSet ) {
2018-07-17 10:39:30 -07:00
return nil , notePosition ( fset . Position ( call . Pos ( ) ) , fmt . Errorf ( "%s does not implement %s" , types . TypeString ( provided , nil ) , types . TypeString ( ifaceArgType , nil ) ) )
2018-04-04 10:58:07 -07:00
}
2018-04-27 13:44:54 -04:00
return & IfaceBinding {
Pos : call . Pos ( ) ,
2018-05-29 08:45:14 -07:00
Iface : ifacePtr . Elem ( ) ,
2018-04-27 13:44:54 -04:00
Provided : provided ,
} , nil
2018-04-04 10:58:07 -07:00
}
2018-05-31 15:34:15 -07:00
// processValue creates a value from a wire.Value call.
2018-05-04 12:44:53 -04:00
func processValue ( fset * token . FileSet , info * types . Info , call * ast . CallExpr ) ( * Value , error ) {
2018-05-31 15:34:15 -07:00
// Assumes that call.Fun is wire.Value.
2018-05-04 12:44:53 -04:00
if len ( call . Args ) != 1 {
2018-07-17 10:39:30 -07:00
return nil , notePosition ( fset . Position ( call . Pos ( ) ) , errors . New ( "call to Value takes exactly one argument" ) )
2018-05-04 12:44:53 -04:00
}
ok := true
ast . Inspect ( call . Args [ 0 ] , func ( node ast . Node ) bool {
2018-08-06 23:49:11 +02:00
switch expr := node . ( type ) {
2018-05-04 12:44:53 -04:00
case nil , * ast . ArrayType , * ast . BasicLit , * ast . BinaryExpr , * ast . ChanType , * ast . CompositeLit , * ast . FuncType , * ast . Ident , * ast . IndexExpr , * ast . InterfaceType , * ast . KeyValueExpr , * ast . MapType , * ast . ParenExpr , * ast . SelectorExpr , * ast . SliceExpr , * ast . StarExpr , * ast . StructType , * ast . TypeAssertExpr :
// Good!
case * ast . UnaryExpr :
if expr . Op == token . ARROW {
ok = false
return false
}
case * ast . CallExpr :
// Only acceptable if it's a type conversion.
2018-08-06 23:49:11 +02:00
if _ , isFunc := info . TypeOf ( expr . Fun ) . ( * types . Signature ) ; isFunc {
2018-05-04 12:44:53 -04:00
ok = false
return false
}
default :
ok = false
return false
}
return true
} )
if ! ok {
2018-07-17 10:39:30 -07:00
return nil , notePosition ( fset . Position ( call . Pos ( ) ) , errors . New ( "argument to Value is too complex" ) )
2018-05-04 12:44:53 -04:00
}
2018-08-14 14:55:28 -07:00
// Result type can't be an interface type; use wire.InterfaceValue for that.
argType := info . TypeOf ( call . Args [ 0 ] )
if _ , isInterfaceType := argType . Underlying ( ) . ( * types . Interface ) ; isInterfaceType {
return nil , notePosition ( fset . Position ( call . Pos ( ) ) , fmt . Errorf ( "argument to Value may not be an interface value (found %s); use InterfaceValue instead" , types . TypeString ( argType , nil ) ) )
}
2018-05-04 12:44:53 -04:00
return & Value {
Pos : call . Args [ 0 ] . Pos ( ) ,
Out : info . TypeOf ( call . Args [ 0 ] ) ,
expr : call . Args [ 0 ] ,
info : info ,
} , nil
}
2018-08-14 14:55:28 -07:00
// processInterfaceValue creates a value from a wire.InterfaceValue call.
func processInterfaceValue ( fset * token . FileSet , info * types . Info , call * ast . CallExpr ) ( * Value , error ) {
// Assumes that call.Fun is wire.InterfaceValue.
if len ( call . Args ) != 2 {
return nil , notePosition ( fset . Position ( call . Pos ( ) ) , errors . New ( "call to InterfaceValue takes exactly two arguments" ) )
}
ifaceArgType := info . TypeOf ( call . Args [ 0 ] )
ifacePtr , ok := ifaceArgType . ( * types . Pointer )
if ! ok {
return nil , notePosition ( fset . Position ( call . Pos ( ) ) , fmt . Errorf ( "first argument to InterfaceValue must be a pointer to an interface type; found %s" , types . TypeString ( ifaceArgType , nil ) ) )
}
iface := ifacePtr . Elem ( )
methodSet , ok := iface . Underlying ( ) . ( * types . Interface )
if ! ok {
return nil , notePosition ( fset . Position ( call . Pos ( ) ) , fmt . Errorf ( "first argument to InterfaceValue must be a pointer to an interface type; found %s" , types . TypeString ( ifaceArgType , nil ) ) )
}
provided := info . TypeOf ( call . Args [ 1 ] )
if ! types . Implements ( provided , methodSet ) {
return nil , notePosition ( fset . Position ( call . Pos ( ) ) , fmt . Errorf ( "%s does not implement %s" , types . TypeString ( provided , nil ) , types . TypeString ( ifaceArgType , nil ) ) )
}
return & Value {
Pos : call . Args [ 1 ] . Pos ( ) ,
Out : iface ,
expr : call . Args [ 1 ] ,
info : info ,
} , nil
}
2018-09-27 15:30:13 -07:00
// findInjectorBuild returns the wire.Build call if fn is an injector template.
// It returns nil if the function is not an injector template.
func findInjectorBuild ( info * types . Info , fn * ast . FuncDecl ) ( * ast . CallExpr , error ) {
2018-04-27 13:44:54 -04:00
if fn . Body == nil {
2018-09-27 15:30:13 -07:00
return nil , nil
2018-04-02 14:08:17 -07:00
}
2018-09-27 15:30:13 -07:00
numStatements := 0
invalid := false
var wireBuildCall * ast . CallExpr
2018-04-27 13:44:54 -04:00
for _ , stmt := range fn . Body . List {
switch stmt := stmt . ( type ) {
case * ast . ExprStmt :
2018-09-27 15:30:13 -07:00
numStatements ++
if numStatements > 1 {
invalid = true
}
call , ok := stmt . X . ( * ast . CallExpr )
if ! ok {
continue
}
if qualifiedIdentObject ( info , call . Fun ) == types . Universe . Lookup ( "panic" ) {
if len ( call . Args ) != 1 {
continue
}
call , ok = call . Args [ 0 ] . ( * ast . CallExpr )
if ! ok {
continue
}
}
buildObj := qualifiedIdentObject ( info , call . Fun )
if buildObj == nil || buildObj . Pkg ( ) == nil || ! isWireImport ( buildObj . Pkg ( ) . Path ( ) ) || buildObj . Name ( ) != "Build" {
continue
2018-03-30 21:34:08 -07:00
}
2018-09-27 15:30:13 -07:00
wireBuildCall = call
2018-04-27 13:44:54 -04:00
case * ast . EmptyStmt :
// Do nothing.
2018-06-15 15:47:43 -07:00
case * ast . ReturnStmt :
// Allow the function to end in a return.
2018-09-27 15:30:13 -07:00
if numStatements == 0 {
return nil , nil
2018-06-15 15:47:43 -07:00
}
2018-04-27 13:44:54 -04:00
default :
2018-09-27 15:30:13 -07:00
invalid = true
2018-03-30 21:34:08 -07:00
}
2018-09-27 15:30:13 -07:00
2018-03-30 21:34:08 -07:00
}
2018-09-27 15:30:13 -07:00
if wireBuildCall == nil {
return nil , nil
2018-05-01 14:46:39 -04:00
}
2018-09-27 15:30:13 -07:00
if invalid {
return nil , errors . New ( "a call to wire.Build indicates that this function is an injector, but injectors must consist of only the wire.Build call and an optional return" )
2018-04-02 10:57:48 -07:00
}
2018-09-27 15:30:13 -07:00
return wireBuildCall , nil
2018-04-02 10:57:48 -07:00
}
2018-05-31 15:34:15 -07:00
func isWireImport ( path string ) bool {
2018-04-27 13:44:54 -04:00
// TODO(light): This is depending on details of the current loader.
const vendorPart = "vendor/"
if i := strings . LastIndex ( path , vendorPart ) ; i != - 1 && ( i == 0 || path [ i - 1 ] == '/' ) {
path = path [ i + len ( vendorPart ) : ]
2018-04-02 09:21:52 -07:00
}
2018-07-02 11:50:01 -07:00
return path == "github.com/google/go-cloud/wire"
2018-04-02 09:21:52 -07:00
}
2018-06-15 15:58:48 -07:00
2018-07-17 10:39:30 -07:00
func isProviderSetType ( t types . Type ) bool {
n , ok := t . ( * types . Named )
if ! ok {
return false
}
obj := n . Obj ( )
return obj . Pkg ( ) != nil && isWireImport ( obj . Pkg ( ) . Path ( ) ) && obj . Name ( ) == "ProviderSet"
}
2018-08-16 15:36:53 -07:00
// ProvidedType is a pointer to a Provider or a Value. The zero value is
// a nil pointer. It also holds the concrete type that the Provider or Value
// provided.
type ProvidedType struct {
t types . Type
2018-06-15 15:58:48 -07:00
p * Provider
v * Value
}
// IsNil reports whether pv is the zero value.
2018-08-16 15:36:53 -07:00
func ( pv ProvidedType ) IsNil ( ) bool {
2018-06-15 15:58:48 -07:00
return pv . p == nil && pv . v == nil
}
2018-08-16 15:36:53 -07:00
// ConcreteType returns the concrete type that was provided.
func ( pv ProvidedType ) ConcreteType ( ) types . Type {
return pv . t
}
2018-06-15 15:58:48 -07:00
// IsProvider reports whether pv points to a Provider.
2018-08-16 15:36:53 -07:00
func ( pv ProvidedType ) IsProvider ( ) bool {
2018-06-15 15:58:48 -07:00
return pv . p != nil
}
// IsValue reports whether pv points to a Value.
2018-08-16 15:36:53 -07:00
func ( pv ProvidedType ) IsValue ( ) bool {
2018-06-15 15:58:48 -07:00
return pv . v != nil
}
// Provider returns pv as a Provider pointer. It panics if pv points to a
// Value.
2018-08-16 15:36:53 -07:00
func ( pv ProvidedType ) Provider ( ) * Provider {
2018-06-15 15:58:48 -07:00
if pv . v != nil {
panic ( "Value pointer converted to a Provider" )
}
return pv . p
}
2018-08-14 14:55:28 -07:00
// Value returns pv as a Value pointer. It panics if pv points to a
2018-06-15 15:58:48 -07:00
// Provider.
2018-08-16 15:36:53 -07:00
func ( pv ProvidedType ) Value ( ) * Value {
2018-06-15 15:58:48 -07:00
if pv . p != nil {
panic ( "Provider pointer converted to a Value" )
}
return pv . v
}