github.com/goplus/gogen@v1.16.0/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 gogen 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 TyStaticMethod struct { 219 Func types.Object 220 } 221 222 func (p *TyStaticMethod) Obj() types.Object { return p.Func } 223 func (p *TyStaticMethod) Underlying() types.Type { return p } 224 func (p *TyStaticMethod) String() string { return "TyStaticMethod" } 225 func (p *TyStaticMethod) funcEx() {} 226 227 func NewStaticMethod(typ *types.Named, pos token.Pos, pkg *types.Package, name string, fn types.Object) *types.Func { 228 return newMethodEx(typ, pos, pkg, name, &TyStaticMethod{fn}) 229 } 230 231 // ---------------------------------------------------------------------------- 232 233 type TyTemplateRecvMethod struct { 234 Func types.Object 235 } 236 237 func (p *TyTemplateRecvMethod) Obj() types.Object { return p.Func } 238 func (p *TyTemplateRecvMethod) Underlying() types.Type { return p } 239 func (p *TyTemplateRecvMethod) String() string { return "TyTemplateRecvMethod" } 240 func (p *TyTemplateRecvMethod) funcEx() {} 241 242 // NewTemplateRecvMethod - https://github.com/goplus/gop/issues/811 243 func NewTemplateRecvMethod(typ *types.Named, pos token.Pos, pkg *types.Package, name string, fn types.Object) *types.Func { 244 return newMethodEx(typ, pos, pkg, name, &TyTemplateRecvMethod{fn}) 245 } 246 247 // ---------------------------------------------------------------------------- 248 249 func overloadFnHasAutoProperty(fns []types.Object, n int) bool { 250 for _, fn := range fns { 251 if methodHasAutoProperty(fn.Type(), n) { 252 return true 253 } 254 } 255 return false 256 } 257 258 func methodHasAutoProperty(typ types.Type, n int) bool { 259 if sig, ok := typ.(*types.Signature); ok { 260 if t, ok := CheckFuncEx(sig); ok { 261 switch t := t.(type) { 262 case *TyOverloadMethod: 263 // is overload method 264 return overloadFnHasAutoProperty(t.Methods, n) 265 case *TyTemplateRecvMethod: 266 // is template recv method 267 return methodHasAutoProperty(t.Func.Type(), 1) 268 case *TyOverloadFunc: 269 // is overload func 270 return overloadFnHasAutoProperty(t.Funcs, n) 271 } 272 } 273 return sig.Params().Len() == n 274 } 275 return false 276 } 277 278 // HasAutoProperty checks if specified type is a function without parameters or not. 279 func HasAutoProperty(typ types.Type) bool { 280 if sig, ok := typ.(*types.Signature); ok { 281 if t, ok := CheckFuncEx(sig); ok { 282 switch t := t.(type) { 283 case *TyOverloadFunc: 284 // is overload func 285 for _, fn := range t.Funcs { 286 if HasAutoProperty(fn.Type()) { 287 return true 288 } 289 } 290 } 291 } else { 292 return sig.Params().Len() == 0 293 } 294 } 295 return false 296 } 297 298 // ----------------------------------------------------------------------------