github.com/goplus/gox@v1.14.13-0.20240308130321-6ff7f61cfae8/func_ext.go (about)

     1  /*
     2   Copyright 2023 The GoPlus Authors (goplus.org)
     3   Licensed under the Apache License, Version 2.0 (the "License");
     4   you may not use this file except in compliance with the License.
     5   You may obtain a copy of the License at
     6       http://www.apache.org/licenses/LICENSE-2.0
     7   Unless required by applicable law or agreed to in writing, software
     8   distributed under the License is distributed on an "AS IS" BASIS,
     9   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    10   See the License for the specific language governing permissions and
    11   limitations under the License.
    12  */
    13  
    14  package gox
    15  
    16  import (
    17  	"go/token"
    18  	"go/types"
    19  	"log"
    20  	"strings"
    21  )
    22  
    23  // ----------------------------------------------------------------------------
    24  
    25  // TyFuncEx is a FuncEx type.
    26  type TyFuncEx interface {
    27  	types.Type
    28  	funcEx()
    29  }
    30  
    31  // IsFunc returns if specified type is a function or not.
    32  func IsFunc(t types.Type) bool {
    33  	if _, ok := t.(*types.Signature); ok {
    34  		return true
    35  	}
    36  	_, ok := t.(*inferFuncType)
    37  	return ok
    38  }
    39  
    40  // CheckFuncEx returns if specified function is a FuncEx or not.
    41  func CheckFuncEx(sig *types.Signature) (ext TyFuncEx, ok bool) {
    42  	if typ, is := CheckSigFuncEx(sig); is {
    43  		ext, ok = typ.(TyFuncEx)
    44  	}
    45  	return
    46  }
    47  
    48  // CheckSigFuncExObjects retruns hide recv type and objects from func($overloadArgs ...interface{$overloadMethod()})
    49  // The return type can be OverloadType (*TyOverloadFunc, *TyOverloadMethod, *TyOverloadNamed) or
    50  // *TyTemplateRecvMethod.
    51  func CheckSigFuncExObjects(sig *types.Signature) (typ types.Type, objs []types.Object) {
    52  	if ext, ok := CheckSigFuncEx(sig); ok {
    53  		switch t := ext.(type) {
    54  		case *TyOverloadFunc:
    55  			typ, objs = t, t.Funcs
    56  		case *TyOverloadMethod:
    57  			typ, objs = t, t.Methods
    58  		case *TyTemplateRecvMethod:
    59  			typ = t
    60  			if tsig, ok := t.Func.Type().(*types.Signature); ok {
    61  				if ex, ok := CheckSigFuncEx(tsig); ok {
    62  					if t, ok := ex.(*TyOverloadFunc); ok {
    63  						objs = t.Funcs
    64  						break
    65  					}
    66  				}
    67  			}
    68  			objs = []types.Object{t.Func}
    69  		case *TyOverloadNamed:
    70  			typ = t
    71  			objs = make([]types.Object, len(t.Types))
    72  			for i, typ := range t.Types {
    73  				objs[i] = typ.Obj()
    74  			}
    75  		}
    76  	}
    77  	return
    78  }
    79  
    80  const (
    81  	overloadArgs   = "__gop_overload_args__"
    82  	overloadMethod = "_"
    83  )
    84  
    85  // CheckSigFuncEx retrun hide recv type from func($overloadArgs ...interface{$overloadMethod()})
    86  // The return type can be OverloadType (*TyOverloadFunc, *TyOverloadMethod, *TyOverloadNamed) or
    87  // *TyTemplateRecvMethod.
    88  func CheckSigFuncEx(sig *types.Signature) (types.Type, bool) {
    89  	if sig.Params().Len() == 1 {
    90  		if param := sig.Params().At(0); param.Name() == overloadArgs {
    91  			if typ, ok := param.Type().(*types.Interface); ok && typ.NumExplicitMethods() == 1 {
    92  				if sig, ok := typ.ExplicitMethod(0).Type().(*types.Signature); ok {
    93  					if recv := sig.Recv(); recv != nil {
    94  						return recv.Type(), true
    95  					}
    96  				}
    97  			}
    98  		}
    99  	}
   100  	return nil, false
   101  }
   102  
   103  func isSigFuncEx(sig *types.Signature) bool {
   104  	if sig.Params().Len() == 1 {
   105  		if param := sig.Params().At(0); param.Name() == overloadArgs {
   106  			return true
   107  		}
   108  	}
   109  	return false
   110  }
   111  
   112  // sigFuncEx return func type ($overloadArgs ...interface{$overloadMethod()})
   113  func sigFuncEx(pkg *types.Package, recv *types.Var, t types.Type) *types.Signature {
   114  	sig := types.NewSignatureType(types.NewVar(token.NoPos, nil, "", t), nil, nil, nil, nil, false)
   115  	typ := types.NewInterfaceType([]*types.Func{
   116  		types.NewFunc(token.NoPos, nil, overloadMethod, sig),
   117  	}, nil)
   118  	param := types.NewVar(token.NoPos, pkg, overloadArgs, typ)
   119  	return types.NewSignatureType(recv, nil, nil, types.NewTuple(param), nil, false)
   120  }
   121  
   122  func newFuncEx(pos token.Pos, pkg *types.Package, recv *types.Var, name string, t TyFuncEx) *types.Func {
   123  	sig := sigFuncEx(pkg, recv, t)
   124  	return types.NewFunc(pos, pkg, name, sig)
   125  }
   126  
   127  func newMethodEx(typ *types.Named, pos token.Pos, pkg *types.Package, name string, t TyFuncEx) *types.Func {
   128  	recv := types.NewVar(token.NoPos, pkg, "recv", typ)
   129  	ofn := newFuncEx(pos, pkg, recv, name, t)
   130  	typ.AddMethod(ofn)
   131  	if strings.HasPrefix(name, gopxPrefix) {
   132  		aname := name[len(gopxPrefix):]
   133  		ofnAlias := newFuncEx(pos, pkg, recv, aname, &tyTypeAsParams{ofn})
   134  		typ.AddMethod(ofnAlias)
   135  		if debugImport {
   136  			log.Println("==> AliasMethod", typ, name, "=>", aname)
   137  		}
   138  	}
   139  	return ofn
   140  }
   141  
   142  // ----------------------------------------------------------------------------
   143  
   144  // TyOverloadFunc: overload function type
   145  type TyOverloadFunc struct {
   146  	Funcs []types.Object
   147  }
   148  
   149  func (p *TyOverloadFunc) At(i int) types.Object { return p.Funcs[i] }
   150  func (p *TyOverloadFunc) Len() int              { return len(p.Funcs) }
   151  
   152  func (p *TyOverloadFunc) Underlying() types.Type { return p }
   153  func (p *TyOverloadFunc) String() string         { return "TyOverloadFunc" }
   154  func (p *TyOverloadFunc) funcEx()                {}
   155  
   156  // NewOverloadFunc creates an overload func.
   157  func NewOverloadFunc(pos token.Pos, pkg *types.Package, name string, funcs ...types.Object) *types.Func {
   158  	fn := newFuncEx(pos, pkg, nil, name, &TyOverloadFunc{funcs})
   159  	return fn
   160  }
   161  
   162  // CheckOverloadFunc checks a func is overload func or not.
   163  //
   164  // Deprecated: please use CheckFuncEx.
   165  func CheckOverloadFunc(sig *types.Signature) (funcs []types.Object, ok bool) {
   166  	if t, ok := CheckFuncEx(sig); ok {
   167  		if oft, ok := t.(*TyOverloadFunc); ok {
   168  			return oft.Funcs, true
   169  		}
   170  	}
   171  	return nil, false
   172  }
   173  
   174  // ----------------------------------------------------------------------------
   175  
   176  // TyOverloadMethod: overload function type
   177  type TyOverloadMethod struct {
   178  	Methods []types.Object
   179  }
   180  
   181  func (p *TyOverloadMethod) At(i int) types.Object { return p.Methods[i] }
   182  func (p *TyOverloadMethod) Len() int              { return len(p.Methods) }
   183  
   184  func (p *TyOverloadMethod) Underlying() types.Type { return p }
   185  func (p *TyOverloadMethod) String() string         { return "TyOverloadMethod" }
   186  func (p *TyOverloadMethod) funcEx()                {}
   187  
   188  // NewOverloadMethod creates an overload method.
   189  func NewOverloadMethod(typ *types.Named, pos token.Pos, pkg *types.Package, name string, methods ...types.Object) *types.Func {
   190  	return newMethodEx(typ, pos, pkg, name, &TyOverloadMethod{methods})
   191  }
   192  
   193  // CheckOverloadMethod checks a func is overload method or not.
   194  //
   195  // Deprecated: please use CheckFuncEx.
   196  func CheckOverloadMethod(sig *types.Signature) (methods []types.Object, ok bool) {
   197  	if t, ok := CheckFuncEx(sig); ok {
   198  		if oft, ok := t.(*TyOverloadMethod); ok {
   199  			return oft.Methods, true
   200  		}
   201  	}
   202  	return nil, false
   203  }
   204  
   205  // ----------------------------------------------------------------------------
   206  
   207  type tyTypeAsParams struct { // see TestTypeAsParamsFunc
   208  	obj types.Object
   209  }
   210  
   211  func (p *tyTypeAsParams) Obj() types.Object      { return p.obj }
   212  func (p *tyTypeAsParams) Underlying() types.Type { return p }
   213  func (p *tyTypeAsParams) String() string         { return "tyTypeAsParams" }
   214  func (p *tyTypeAsParams) funcEx()                {}
   215  
   216  // ----------------------------------------------------------------------------
   217  
   218  type TyTemplateRecvMethod struct {
   219  	Func types.Object
   220  }
   221  
   222  func (p *TyTemplateRecvMethod) Obj() types.Object      { return p.Func }
   223  func (p *TyTemplateRecvMethod) Underlying() types.Type { return p }
   224  func (p *TyTemplateRecvMethod) String() string         { return "TyTemplateRecvMethod" }
   225  func (p *TyTemplateRecvMethod) funcEx()                {}
   226  
   227  // NewTemplateRecvMethod - https://github.com/goplus/gop/issues/811
   228  func NewTemplateRecvMethod(typ *types.Named, pos token.Pos, pkg *types.Package, name string, fn types.Object) *types.Func {
   229  	return newMethodEx(typ, pos, pkg, name, &TyTemplateRecvMethod{fn})
   230  }
   231  
   232  // ----------------------------------------------------------------------------
   233  
   234  func overloadFnHasAutoProperty(fns []types.Object, n int) bool {
   235  	for _, fn := range fns {
   236  		if methodHasAutoProperty(fn.Type(), n) {
   237  			return true
   238  		}
   239  	}
   240  	return false
   241  }
   242  
   243  func methodHasAutoProperty(typ types.Type, n int) bool {
   244  	if sig, ok := typ.(*types.Signature); ok {
   245  		if t, ok := CheckFuncEx(sig); ok {
   246  			switch t := t.(type) {
   247  			case *TyOverloadMethod:
   248  				// is overload method
   249  				return overloadFnHasAutoProperty(t.Methods, n)
   250  			case *TyTemplateRecvMethod:
   251  				// is template recv method
   252  				return methodHasAutoProperty(t.Func.Type(), 1)
   253  			case *TyOverloadFunc:
   254  				// is overload func
   255  				return overloadFnHasAutoProperty(t.Funcs, n)
   256  			}
   257  		}
   258  		return sig.Params().Len() == n
   259  	}
   260  	return false
   261  }
   262  
   263  // HasAutoProperty checks if specified type is a function without parameters or not.
   264  func HasAutoProperty(typ types.Type) bool {
   265  	if sig, ok := typ.(*types.Signature); ok {
   266  		if t, ok := CheckFuncEx(sig); ok {
   267  			switch t := t.(type) {
   268  			case *TyOverloadFunc:
   269  				// is overload func
   270  				for _, fn := range t.Funcs {
   271  					if HasAutoProperty(fn.Type()) {
   272  						return true
   273  					}
   274  				}
   275  			}
   276  		} else {
   277  			return sig.Params().Len() == 0
   278  		}
   279  	}
   280  	return false
   281  }
   282  
   283  // ----------------------------------------------------------------------------