2018-11-12 14:37:53 -08:00
// Copyright 2018 The Wire 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-11-06 08:44:51 -08:00
"context"
2018-05-08 16:26:38 -04:00
"errors"
2018-04-02 09:21:52 -07:00
"fmt"
"go/ast"
"go/token"
"go/types"
"strconv"
"strings"
2018-04-27 13:44:54 -04:00
"golang.org/x/tools/go/ast/astutil"
2018-11-07 14:44:15 -08:00
"golang.org/x/tools/go/packages"
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 {
2018-11-16 10:26:10 -08:00
Provider * Provider
Binding * IfaceBinding
Value * Value
Import * ProviderSet
InjectorArg * InjectorArg
2018-08-02 09:31:50 -07:00
}
2018-10-17 14:14:08 -07:00
// description returns a string describing the source of p, including line numbers.
func ( p * providerSetSrc ) description ( fset * token . FileSet , typ types . Type ) string {
2018-10-04 14:19:25 -07:00
quoted := func ( s string ) string {
if s == "" {
return ""
}
return fmt . Sprintf ( "%q " , s )
}
2018-10-17 14:14:08 -07:00
switch {
case p . Provider != nil :
2018-10-04 14:19:25 -07:00
kind := "provider"
if p . Provider . IsStruct {
kind = "struct provider"
}
2018-10-17 14:14:08 -07:00
return fmt . Sprintf ( "%s %s(%s)" , kind , quoted ( p . Provider . Name ) , fset . Position ( p . Provider . Pos ) )
case p . Binding != nil :
return fmt . Sprintf ( "wire.Bind (%s)" , fset . Position ( p . Binding . Pos ) )
case p . Value != nil :
return fmt . Sprintf ( "wire.Value (%s)" , fset . Position ( p . Value . Pos ) )
case p . Import != nil :
return fmt . Sprintf ( "provider set %s(%s)" , quoted ( p . Import . VarName ) , fset . Position ( p . Import . Pos ) )
2018-11-16 10:26:10 -08:00
case p . InjectorArg != nil :
args := p . InjectorArg . Args
return fmt . Sprintf ( "argument %s to injector function %s (%s)" , args . Tuple . At ( p . InjectorArg . Index ) . Name ( ) , args . Name , fset . Position ( args . Pos ) )
2018-10-17 14:14:08 -07:00
}
panic ( "providerSetSrc with no fields set" )
}
// trace returns a slice of strings describing the (possibly recursive) source
// of p, including line numbers.
func ( p * providerSetSrc ) trace ( fset * token . FileSet , typ types . Type ) [ ] string {
var retval [ ] string
// Only Imports need recursion.
if p . Import != nil {
2018-10-04 14:19:25 -07:00
if parent := p . Import . srcMap . At ( typ ) ; parent != nil {
retval = append ( retval , parent . ( * providerSetSrc ) . trace ( fset , typ ) ... )
}
}
2018-10-17 14:14:08 -07:00
retval = append ( retval , p . description ( fset , typ ) )
2018-10-04 14:19:25 -07:00
return retval
}
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-11-16 10:26:10 -08:00
// InjectorArgs is only filled in for wire.Build.
InjectorArgs * InjectorArgs
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 {
2018-11-07 14:44:15 -08:00
// Pkg is the package that the Go object resides in.
Pkg * types . Package
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-12-03 08:30:42 -08:00
// Varargs is true if the provider function is variadic.
Varargs bool
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-11-16 10:26:10 -08:00
// InjectorArg describes a specific argument passed to an injector function.
type InjectorArg struct {
// Args is the full set of arguments.
Args * InjectorArgs
// Index is the index into Args.Tuple for this argument.
Index int
}
// InjectorArgs describes the arguments passed to an injector function.
type InjectorArgs struct {
// Name is the name of the injector function.
Name string
// Tuple represents the arguments.
Tuple * types . Tuple
// Pos is the source position of the injector function.
Pos token . Pos
}
2018-11-06 08:44:51 -08:00
// Load finds all the provider sets in the packages that match the given
// patterns, as well as the provider sets' transitive dependencies. It
// may return both errors and Info. The patterns are defined by the
// underlying build system. For the go tool, this is described at
// https://golang.org/cmd/go/#hdr-Package_lists_and_patterns
//
// wd is the working directory and env is the set of environment
// variables to use when loading the packages specified by patterns. If
// env is nil or empty, it is interpreted as an empty set of variables.
// In case of duplicate environment variables, the last one in the list
// takes precedence.
func Load ( ctx context . Context , wd string , env [ ] string , patterns [ ] string ) ( * Info , [ ] error ) {
2018-11-07 14:44:15 -08:00
pkgs , errs := load ( ctx , wd , env , patterns )
2018-07-19 16:04:26 -07:00
if len ( errs ) > 0 {
return nil , errs
2018-04-04 14:42:56 -07:00
}
2018-11-07 14:44:15 -08:00
if len ( pkgs ) == 0 {
return new ( Info ) , nil
}
fset := pkgs [ 0 ] . Fset
2018-04-04 14:42:56 -07:00
info := & Info {
2018-11-07 14:44:15 -08:00
Fset : fset ,
2018-04-04 14:42:56 -07:00
Sets : make ( map [ ProviderSetID ] * ProviderSet ) ,
}
2018-11-07 14:44:15 -08:00
oc := newObjectCache ( pkgs )
2018-07-19 16:04:26 -07:00
ec := new ( errorCollector )
2018-11-07 14:44:15 -08:00
for _ , pkg := range pkgs {
if isWireImport ( pkg . PkgPath ) {
2018-07-19 16:04:26 -07:00
// The marker function package confuses analysis.
continue
}
2018-11-07 14:44:15 -08:00
scope := pkg . Types . Scope ( )
2018-04-27 13:44:54 -04:00
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 {
2018-11-07 14:44:15 -08:00
ec . add ( notePositionAll ( 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-11-07 14:44:15 -08:00
for _ , f := range pkg . Syntax {
2018-07-19 16:04:26 -07:00
for _ , decl := range f . Decls {
fn , ok := decl . ( * ast . FuncDecl )
if ! ok {
continue
}
2018-11-07 14:44:15 -08:00
buildCall , err := findInjectorBuild ( pkg . TypesInfo , fn )
2018-09-27 15:30:13 -07:00
if err != nil {
2018-11-07 14:44:15 -08:00
ec . add ( notePosition ( fset . Position ( fn . Pos ( ) ) , fmt . Errorf ( "inject %s: %v" , fn . Name . Name , err ) ) )
2018-09-27 15:30:13 -07:00
continue
}
2018-07-19 16:04:26 -07:00
if buildCall == nil {
continue
}
2018-11-07 14:44:15 -08:00
sig := pkg . TypesInfo . ObjectOf ( fn . Name ) . Type ( ) . ( * types . Signature )
2018-07-19 16:04:26 -07:00
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 {
2018-11-07 14:44:15 -08:00
ec . add ( notePosition ( fset . Position ( fn . Pos ( ) ) , fmt . Errorf ( "inject %s: %v" , fn . Name . Name , err ) ) )
2018-07-19 16:04:26 -07:00
}
continue
}
2018-11-16 10:26:10 -08:00
injectorArgs := & InjectorArgs {
Name : fn . Name . Name ,
Tuple : ins ,
Pos : fn . Pos ( ) ,
}
set , errs := oc . processNewSet ( pkg . TypesInfo , pkg . PkgPath , buildCall , injectorArgs , "" )
if len ( errs ) > 0 {
ec . add ( notePositionAll ( fset . Position ( fn . Pos ( ) ) , errs ) ... )
continue
}
2018-11-07 14:44:15 -08:00
_ , errs = solve ( fset , out . out , ins , set )
2018-07-19 16:04:26 -07:00
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 ) )
}
2018-11-07 14:44:15 -08:00
return notePosition ( fset . Position ( fn . Pos ( ) ) , fmt . Errorf ( "inject %s: %v" , fn . Name . Name , e ) )
2018-07-19 16:04:26 -07:00
} ) ... )
continue
}
info . Injectors = append ( info . Injectors , & Injector {
2018-11-07 14:44:15 -08:00
ImportPath : pkg . PkgPath ,
2018-07-19 16:04:26 -07:00
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-11-07 14:44:15 -08:00
// load typechecks the packages that match the given patterns and
// includes source for all transitive dependencies. The patterns are
// defined by the underlying build system. For the go tool, this is
// described at https://golang.org/cmd/go/#hdr-Package_lists_and_patterns
2018-11-06 08:44:51 -08:00
//
// wd is the working directory and env is the set of environment
// variables to use when loading the packages specified by patterns. If
// env is nil or empty, it is interpreted as an empty set of variables.
// In case of duplicate environment variables, the last one in the list
// takes precedence.
2018-11-07 14:44:15 -08:00
func load ( ctx context . Context , wd string , env [ ] string , patterns [ ] string ) ( [ ] * packages . Package , [ ] error ) {
cfg := & packages . Config {
Context : ctx ,
Mode : packages . LoadAllSyntax ,
Dir : wd ,
Env : env ,
BuildFlags : [ ] string { "-tags=wireinject" } ,
// TODO(light): Use ParseFile to skip function bodies and comments in indirect packages.
}
escaped := make ( [ ] string , len ( patterns ) )
for i := range patterns {
escaped [ i ] = "pattern=" + patterns [ i ]
}
pkgs , err := packages . Load ( cfg , escaped ... )
2018-07-19 16:04:26 -07:00
if err != nil {
return nil , [ ] error { err }
}
2018-11-07 14:44:15 -08:00
var errs [ ] error
2018-07-19 16:04:26 -07:00
for _ , p := range pkgs {
2018-11-07 14:44:15 -08:00
for _ , e := range p . Errors {
errs = append ( errs , e )
2018-07-19 16:04:26 -07:00
}
}
2018-11-07 14:44:15 -08:00
if len ( errs ) > 0 {
return nil , errs
}
return pkgs , nil
2018-07-19 16:04:26 -07:00
}
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 {
2018-11-07 14:44:15 -08:00
fset * token . FileSet
packages map [ string ] * packages . Package
objects map [ objRef ] objCacheEntry
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-11-07 14:44:15 -08:00
func newObjectCache ( pkgs [ ] * packages . Package ) * objectCache {
if len ( pkgs ) == 0 {
panic ( "object cache must have packages to draw from" )
}
oc := & objectCache {
fset : pkgs [ 0 ] . Fset ,
packages : make ( map [ string ] * packages . Package ) ,
objects : make ( map [ objRef ] objCacheEntry ) ,
hasher : typeutil . MakeHasher ( ) ,
}
// Depth-first search of all dependencies to gather import path to
// packages.Package mapping. go/packages guarantees that for a single
// call to packages.Load and an import path X, there will exist only
// one *packages.Package value with PkgPath X.
stk := append ( [ ] * packages . Package ( nil ) , pkgs ... )
for len ( stk ) > 0 {
p := stk [ len ( stk ) - 1 ]
stk = stk [ : len ( stk ) - 1 ]
if oc . packages [ p . PkgPath ] != nil {
continue
}
oc . packages [ p . PkgPath ] = p
for _ , imp := range p . Imports {
stk = append ( stk , imp )
}
2018-03-30 21:34:08 -07:00
}
2018-11-07 14:44:15 -08:00
return oc
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 )
2018-11-16 13:27:30 -08:00
if spec == nil || 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-11-07 14:44:15 -08:00
pkgPath := obj . Pkg ( ) . Path ( )
return oc . processExpr ( oc . packages [ pkgPath ] . TypesInfo , pkgPath , spec . Values [ i ] , obj . Name ( ) )
2018-04-27 13:44:54 -04:00
case * types . Func :
2018-11-07 14:44:15 -08:00
return processFuncProvider ( oc . 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
2018-11-07 14:44:15 -08:00
pkg := oc . packages [ obj . Pkg ( ) . Path ( ) ]
2018-04-27 13:44:54 -04:00
pos := obj . Pos ( )
2018-11-07 14:44:15 -08:00
for _ , f := range pkg . Syntax {
tokenFile := oc . fset . File ( f . Pos ( ) )
2018-04-27 13:44:54 -04:00
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-11-07 14:44:15 -08:00
func ( oc * objectCache ) processExpr ( info * types . Info , pkgPath string , expr ast . Expr , varName string ) ( interface { } , [ ] error ) {
exprPos := oc . fset . Position ( expr . Pos ( ) )
2018-04-27 13:44:54 -04:00
expr = astutil . Unparen ( expr )
2018-11-07 14:44:15 -08:00
if obj := qualifiedIdentObject ( 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 {
2018-11-07 14:44:15 -08:00
fnObj := qualifiedIdentObject ( 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-11-16 10:26:10 -08:00
pset , errs := oc . processNewSet ( info , pkgPath , call , nil , varName )
2018-07-17 10:39:30 -07:00
return pset , notePositionAll ( exprPos , errs )
2018-04-27 13:44:54 -04:00
case "Bind" :
2018-11-07 14:44:15 -08:00
b , err := processBind ( oc . fset , info , call )
2018-04-27 13:44:54 -04:00
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" :
2018-11-07 14:44:15 -08:00
v , err := processValue ( oc . fset , info , call )
2018-05-04 12:44:53 -04:00
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" :
2018-11-07 14:44:15 -08:00
v , err := processInterfaceValue ( oc . fset , info , call )
2018-08-14 14:55:28 -07:00
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
}
2018-11-07 14:44:15 -08:00
if tn := structArgType ( info , expr ) ; tn != nil {
p , errs := processStructProvider ( oc . fset , tn )
2018-07-13 08:51:43 -07:00
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-11-16 10:26:10 -08:00
func ( oc * objectCache ) processNewSet ( info * types . Info , pkgPath string , call * ast . CallExpr , args * InjectorArgs , 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 {
2018-11-16 10:26:10 -08:00
Pos : call . Pos ( ) ,
InjectorArgs : args ,
PkgPath : pkgPath ,
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-11-07 14:44:15 -08:00
item , errs := oc . processExpr ( info , pkgPath , 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-11-07 14:44:15 -08:00
pset . providerMap , pset . srcMap , errs = buildProviderMap ( oc . 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-11-07 14:44:15 -08:00
Pkg : fn . Pkg ( ) ,
2018-04-04 14:42:56 -07:00
Name : fn . Name ( ) ,
Pos : fn . Pos ( ) ,
Args : make ( [ ] ProviderInput , params . Len ( ) ) ,
2018-12-03 08:30:42 -08:00
Varargs : sig . Variadic ( ) ,
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-11-16 10:26:10 -08:00
func injectorFuncSignature ( sig * types . Signature ) ( * types . Tuple , outputSignature , error ) {
2018-07-19 16:04:26 -07:00
out , err := funcOutput ( sig )
if err != nil {
return nil , outputSignature { } , err
}
2018-11-16 10:26:10 -08:00
return sig . Params ( ) , out , nil
2018-07-19 16:04:26 -07:00
}
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-11-07 14:44:15 -08:00
Pkg : typeName . Pkg ( ) ,
Name : typeName . Name ( ) ,
Pos : pos ,
Args : make ( [ ] ProviderInput , st . NumFields ( ) ) ,
Fields : make ( [ ] string , st . NumFields ( ) ) ,
IsStruct : true ,
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-09-28 12:37:21 -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
}
2018-09-28 12:37:21 -07:00
iface := ifacePtr . Elem ( )
methodSet , ok := iface . Underlying ( ) . ( * types . Interface )
2018-05-29 08:45:14 -07:00
if ! ok {
2018-09-28 12:37:21 -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-09-28 12:37:21 -07:00
if types . Identical ( iface , 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-09-28 12:37:21 -07:00
return nil , notePosition ( fset . Position ( call . Pos ( ) ) , fmt . Errorf ( "%s does not implement %s" , types . TypeString ( provided , nil ) , types . TypeString ( iface , nil ) ) )
2018-04-04 10:58:07 -07:00
}
2018-04-27 13:44:54 -04:00
return & IfaceBinding {
Pos : call . Pos ( ) ,
2018-09-28 12:37:21 -07:00
Iface : iface ,
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 ) {
2018-09-28 12:37:21 -07:00
return nil , notePosition ( fset . Position ( call . Pos ( ) ) , fmt . Errorf ( "%s does not implement %s" , types . TypeString ( provided , nil ) , types . TypeString ( iface , nil ) ) )
2018-08-14 14:55:28 -07:00
}
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-11-12 14:37:53 -08:00
return path == "github.com/google/wire" || 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-11-16 10:26:10 -08:00
// ProvidedType represents a type provided from a source. The source
// can be a *Provider (a provider function), a *Value (wire.Value), or an
// *InjectorArgs (arguments to the injector function). The zero value has
// none of the above, and returns true for IsNil.
2018-08-16 15:36:53 -07:00
type ProvidedType struct {
2018-11-16 10:26:10 -08:00
// t is the provided concrete type.
2018-08-16 15:36:53 -07:00
t types . Type
2018-06-15 15:58:48 -07:00
p * Provider
v * Value
2018-11-16 10:26:10 -08:00
a * InjectorArg
2018-06-15 15:58:48 -07:00
}
2018-11-16 10:26:10 -08:00
// IsNil reports whether pt is the zero value.
func ( pt ProvidedType ) IsNil ( ) bool {
return pt . p == nil && pt . v == nil && pt . a == nil
2018-06-15 15:58:48 -07:00
}
2018-12-14 12:56:01 -08:00
// Type returns the output type.
//
// - For a function provider, this is the first return value type.
// - For a struct provider, this is either the struct type or the pointer type
// whose element type is the struct type.
// - For a value, this is the type of the expression.
// - For an argument, this is the type of the argument.
func ( pt ProvidedType ) Type ( ) types . Type {
2018-11-16 10:26:10 -08:00
return pt . t
2018-08-16 15:36:53 -07:00
}
2018-11-16 10:26:10 -08:00
// IsProvider reports whether pt points to a Provider.
func ( pt ProvidedType ) IsProvider ( ) bool {
return pt . p != nil
2018-06-15 15:58:48 -07:00
}
2018-11-16 10:26:10 -08:00
// IsValue reports whether pt points to a Value.
func ( pt ProvidedType ) IsValue ( ) bool {
return pt . v != nil
}
// IsArg reports whether pt points to an injector argument.
func ( pt ProvidedType ) IsArg ( ) bool {
return pt . a != nil
}
// Provider returns pt as a Provider pointer. It panics if pt does not point
// to a Provider.
func ( pt ProvidedType ) Provider ( ) * Provider {
if pt . p == nil {
panic ( "ProvidedType does not hold a Provider" )
}
return pt . p
2018-06-15 15:58:48 -07:00
}
2018-11-16 10:26:10 -08:00
// Value returns pt as a Value pointer. It panics if pt does not point
// to a Value.
func ( pt ProvidedType ) Value ( ) * Value {
if pt . v == nil {
panic ( "ProvidedType does not hold a Value" )
2018-06-15 15:58:48 -07:00
}
2018-11-16 10:26:10 -08:00
return pt . v
2018-06-15 15:58:48 -07:00
}
2018-11-16 10:26:10 -08:00
// Arg returns pt as an *InjectorArg representing an injector argument. It
// panics if pt does not point to an arg.
func ( pt ProvidedType ) Arg ( ) * InjectorArg {
if pt . a == nil {
panic ( "ProvidedType does not hold an Arg" )
2018-06-15 15:58:48 -07:00
}
2018-11-16 10:26:10 -08:00
return pt . a
2018-06-15 15:58:48 -07:00
}