Repository: goplus/gop
Pull request #1688 (2024-01-29 13:36:03)

x/typesutil: info.Overloads
```
type Info struct {
        ...
	// Overloads maps identifiers to the overload objects.
	Overloads map[*ast.Ident][]types.Object
}
```

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/goplus/gop/pull/1688?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus) Report
Attention: `6 lines` in your changes are missing coverage. Please review.
> Comparison is base [(`9979a40`)](https://app.codecov.io/gh/goplus/gop/commit/9979a4072771cac95131b819a1a272e79612b232?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus) 91.88% compared to head [(`54e6365`)](https://app.codecov.io/gh/goplus/gop/pull/1688?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus) 91.84%.

| [Files](https://app.codecov.io/gh/goplus/gop/pull/1688?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus) | Patch % | Lines |
|---|---|---|
| [x/typesutil/gopinfo.go](https://app.codecov.io/gh/goplus/gop/pull/1688?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus#diff-eC90eXBlc3V0aWwvZ29waW5mby5nbw==) | 73.91% | [6 Missing :warning: ](https://app.codecov.io/gh/goplus/gop/pull/1688?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #1688      +/-   ##
==========================================
- Coverage   91.88%   91.84%   -0.04%     
==========================================
  Files          38       38              
  Lines       10718    10741      +23     
==========================================
+ Hits         9848     9865      +17     
- Misses        704      710       +6     
  Partials      166      166              
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/goplus/gop/pull/1688?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus).


Patch:
diff --git a/cl/compile.go b/cl/compile.go
index d38cf780..dc44f3c2 100644
--- a/cl/compile.go
+++ b/cl/compile.go
@@ -26,7 +26,6 @@ import (
 	"sort"
 	"strconv"
 	"strings"
-	_ "unsafe"
 
 	"github.com/goplus/gop/ast"
 	"github.com/goplus/gop/ast/fromgo"
diff --git a/go.mod b/go.mod
index cc237ce2..b375e151 100644
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,7 @@ go 1.18
 require (
 	github.com/fsnotify/fsnotify v1.7.0
 	github.com/goplus/c2go v0.7.20
-	github.com/goplus/gox v1.14.1-0.20240130050415-3bd8cee57731
+	github.com/goplus/gox v1.14.1-0.20240130100601-34b84d88ef29
 	github.com/goplus/mod v0.12.3
 	github.com/qiniu/x v1.13.2
 	golang.org/x/tools v0.17.0
diff --git a/go.sum b/go.sum
index 5b2b5572..9fef9bc4 100644
--- a/go.sum
+++ b/go.sum
@@ -7,8 +7,8 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
 github.com/goplus/c2go v0.7.20 h1:90HiO7tuX8hHfEnl7HZRmLsAPH9q2wsYIvHiJMpwtU8=
 github.com/goplus/c2go v0.7.20/go.mod h1:hToieb5V4BMY3c431DQ7DEg13JQ+q9qGmls4+GgymrA=
 github.com/goplus/gox v1.14.0/go.mod h1:G7Hz+cAOUyJyN9pPHrpqhfQPDUtiJNmoRVTwoaQ9nw0=
-github.com/goplus/gox v1.14.1-0.20240130050415-3bd8cee57731 h1:ZjWvtPmuf15J8dmcl1ILOmxi400Bf1y6mAMsVPUbg7I=
-github.com/goplus/gox v1.14.1-0.20240130050415-3bd8cee57731/go.mod h1:G7Hz+cAOUyJyN9pPHrpqhfQPDUtiJNmoRVTwoaQ9nw0=
+github.com/goplus/gox v1.14.1-0.20240130100601-34b84d88ef29 h1:Sv0zFjy7HzYQMShOY8dZfL/5O+MrKboQamRouM2w6l0=
+github.com/goplus/gox v1.14.1-0.20240130100601-34b84d88ef29/go.mod h1:G7Hz+cAOUyJyN9pPHrpqhfQPDUtiJNmoRVTwoaQ9nw0=
 github.com/goplus/mod v0.12.2/go.mod h1:ZtlS9wHOcAVxZ/zq7WLdKVes1HG/8Yn3KNuWZGcpeTs=
 github.com/goplus/mod v0.12.3 h1:qLU5/F27CzUTQhCQRN1WruiCepUn5GdLKQTB41OsYfk=
 github.com/goplus/mod v0.12.3/go.mod h1:ZtlS9wHOcAVxZ/zq7WLdKVes1HG/8Yn3KNuWZGcpeTs=
diff --git a/x/typesutil/gopinfo.go b/x/typesutil/gopinfo.go
index b9843279..8d91e1bb 100644
--- a/x/typesutil/gopinfo.go
+++ b/x/typesutil/gopinfo.go
@@ -20,6 +20,7 @@ import (
 	"go/types"
 
 	"github.com/goplus/gop/ast"
+	"github.com/goplus/gox"
 	"github.com/qiniu/x/log"
 )
 
@@ -128,6 +129,9 @@ type Info struct {
 	// in source order. Variables without an initialization expression do not
 	// appear in this list.
 	// InitOrder []*Initializer
+
+	// Overloads maps identifiers to the overload objects.
+	Overloads map[*ast.Ident][]types.Object
 }
 
 // ObjectOf returns the object denoted by the specified id,
@@ -238,6 +242,35 @@ func (info gopRecorder) Use(id *ast.Ident, obj types.Object) {
 	if info.Uses != nil {
 		info.Uses[id] = obj
 	}
+	if info.Overloads != nil {
+		if sig, ok := obj.Type().(*types.Signature); ok {
+			if ext, ok := gox.CheckSigFuncEx(sig); ok {
+				if debugVerbose {
+					log.Println("==> Overloads:", id, ext)
+				}
+				switch t := ext.(type) {
+				case *gox.TyOverloadFunc:
+					info.Overloads[id] = t.Funcs
+				case *gox.TyOverloadMethod:
+					info.Overloads[id] = t.Methods
+				case *gox.TyTemplateRecvMethod:
+					if tsig, ok := t.Func.Type().(*types.Signature); ok {
+						if ex, ok := gox.CheckSigFuncEx(tsig); ok {
+							if t, ok := ex.(*gox.TyOverloadFunc); ok {
+								info.Overloads[id] = t.Funcs
+							}
+						}
+					}
+				case *gox.TyOverloadNamed:
+					objs := make([]types.Object, len(t.Types))
+					for i, typ := range t.Types {
+						objs[i] = typ.Obj()
+					}
+					info.Overloads[id] = objs
+				}
+			}
+		}
+	}
 }
 
 // Implicit maps nodes to their implicitly declared objects, if any.


Test patch:
diff --git a/x/typesutil/check_test.go b/x/typesutil/check_test.go
index 703118c1..5ec8afe7 100644
--- a/x/typesutil/check_test.go
+++ b/x/typesutil/check_test.go
@@ -78,6 +78,7 @@ func checkInfo(fset *token.FileSet, files []*ast.File, gofiles []*goast.File) (*
 		Implicits:  make(map[ast.Node]types.Object),
 		Selections: make(map[*ast.SelectorExpr]*types.Selection),
 		Scopes:     make(map[ast.Node]*types.Scope),
+		Overloads:  make(map[*ast.Ident][]types.Object),
 	}
 	ginfo := &types.Info{
 		Types:      make(map[goast.Expr]types.TypeAndValue),
diff --git a/x/typesutil/info_test.go b/x/typesutil/info_test.go
index addddcaa..2bbbda6d 100644
--- a/x/typesutil/info_test.go
+++ b/x/typesutil/info_test.go
@@ -4,10 +4,10 @@ import (
 	"fmt"
 
 	goast "go/ast"
-	"go/constant"
 	goformat "go/format"
 	goparser "go/parser"
 
+	"go/constant"
 	"go/importer"
 	"go/types"
 	"sort"
@@ -23,18 +23,52 @@ import (
 	"github.com/goplus/gop/x/typesutil"
 	"github.com/goplus/mod/env"
 	"github.com/goplus/mod/gopmod"
+	"github.com/goplus/mod/modfile"
+	"github.com/goplus/mod/modload"
 )
 
-func parserMixedSource(fset *token.FileSet, src string, gosrc string) (*typesutil.Info, *types.Info, error) {
-	f, err := parser.ParseEntry(fset, "main.gop", src, parser.Config{
-		Mode: parser.ParseComments,
-	})
+var spxProject = &modfile.Project{
+	Ext: ".tgmx", Class: "*MyGame",
+	Works:    []*modfile.Class{{Ext: ".tspx", Class: "Sprite"}},
+	PkgPaths: []string{"github.com/goplus/gop/cl/internal/spx", "math"}}
+
+var spxMod *gopmod.Module
+
+func init() {
+	spxMod = gopmod.New(modload.Default)
+	spxMod.Opt.Projects = append(spxMod.Opt.Projects, spxProject)
+	spxMod.ImportClasses()
+}
+
+func lookupClass(ext string) (c *modfile.Project, ok bool) {
+	switch ext {
+	case ".tgmx", ".tspx":
+		return spxProject, true
+	}
+	return
+}
+
+func spxParserConf() parser.Config {
+	return parser.Config{
+		ClassKind: func(fname string) (isProj bool, ok bool) {
+			ext := modfile.ClassExt(fname)
+			c, ok := lookupClass(ext)
+			if ok {
+				isProj = c.IsProj(ext, fname)
+			}
+			return
+		},
+	}
+}
+
+func parserMixedSource(mod *gopmod.Module, fset *token.FileSet, name, src string, goname string, gosrc string, parserConf parser.Config) (*typesutil.Info, *types.Info, error) {
+	f, err := parser.ParseEntry(fset, name, src, parserConf)
 	if err != nil {
 		return nil, nil, err
 	}
 	var gofiles []*goast.File
 	if len(gosrc) > 0 {
-		f, err := goparser.ParseFile(fset, "main.go", gosrc, goparser.ParseComments)
+		f, err := goparser.ParseFile(fset, goname, gosrc, goparser.ParseComments)
 		if err != nil {
 			return nil, nil, err
 		}
@@ -46,7 +80,7 @@ func parserMixedSource(fset *token.FileSet, src string, gosrc string) (*typesuti
 	chkOpts := &typesutil.Config{
 		Types: types.NewPackage("main", "main"),
 		Fset:  fset,
-		Mod:   gopmod.Default,
+		Mod:   mod,
 	}
 	info := &typesutil.Info{
 		Types:      make(map[ast.Expr]types.TypeAndValue),
@@ -55,6 +89,7 @@ func parserMixedSource(fset *token.FileSet, src string, gosrc string) (*typesuti
 		Implicits:  make(map[ast.Node]types.Object),
 		Selections: make(map[*ast.SelectorExpr]*types.Selection),
 		Scopes:     make(map[ast.Node]*types.Scope),
+		Overloads:  make(map[*ast.Ident][]types.Object),
 	}
 	ginfo := &types.Info{
 		Types:      make(map[goast.Expr]types.TypeAndValue),
@@ -91,6 +126,7 @@ func parserSource(fset *token.FileSet, filename string, src interface{}, mode pa
 		Implicits:  make(map[ast.Node]types.Object),
 		Selections: make(map[*ast.SelectorExpr]*types.Selection),
 		Scopes:     make(map[ast.Node]*types.Scope),
+		Overloads:  make(map[*ast.Ident][]types.Object),
 	}
 	check := typesutil.NewChecker(conf, chkOpts, nil, info)
 	err = check.Files(nil, []*ast.File{f})
@@ -120,8 +156,16 @@ func parserGoSource(fset *token.FileSet, filename string, src interface{}, mode
 }
 
 func testGopInfo(t *testing.T, src string, gosrc string, expect string) {
+	testGopInfoEx(t, gopmod.Default, "main.gop", src, "main.go", gosrc, expect, parser.Config{})
+}
+
+func testSpxInfo(t *testing.T, name string, src string, expect string) {
+	testGopInfoEx(t, spxMod, name, src, "main.go", "", expect, spxParserConf())
+}
+
+func testGopInfoEx(t *testing.T, mod *gopmod.Module, name string, src string, goname string, gosrc string, expect string, parseConf parser.Config) {
 	fset := token.NewFileSet()
-	info, _, err := parserMixedSource(fset, src, gosrc)
+	info, _, err := parserMixedSource(mod, fset, name, src, goname, gosrc, parseConf)
 	if err != nil {
 		t.Fatal("parserMixedSource error", err)
 	}
@@ -1431,7 +1475,6 @@ d := bar.Var(bar.M)
 }
 
 func TestMixedOverloadNamed(t *testing.T) {
-	//gox.SetDebug(gox.DbgFlagAll)
 	testGopInfo(t, `
 var a Var[int]
 var b Var[M]
@@ -1494,7 +1537,6 @@ func Gopx_Var_Cast__1[T map[string]any]() *Var__1[T] {
 }
 
 func TestMixedRawNamed(t *testing.T) {
-	//gox.SetDebug(gox.DbgFlagAll)
 	testGopInfo(t, `
 var a Var__0[int]
 var b Var__1[M]
@@ -1555,3 +1597,74 @@ func Gopx_Var_Cast__1[T map[string]any]() *Var__1[T] {
 006:  5: 6 | Gopx_Var_Cast__1    | func main.Gopx_Var_Cast__1[T map[string]any]() *main.Var__1[T]
 007:  5:23 | M                   | type main.M = map[string]any`)
 }
+
+func TestSpxInfo(t *testing.T) {
+	testSpxInfo(t, "Kai.tspx", `
+var (
+	a int
+)
+
+type info struct {
+	x int
+	y int
+}
+
+func onInit() {
+	a = 1
+	clone
+	clone info{1,2}
+	clone &info{1,2}
+}
+
+func onCloned() {
+	say("Hi")
+}
+`, `== types ==
+000:  0: 0 | Kai                 *ast.Ident                     | type    : main.Kai | type
+001:  3: 4 | int                 *ast.Ident                     | type    : int | type
+002:  6:11 | struct {
+	x int
+	y int
+} *ast.StructType                | type    : struct{x int; y int} | type
+003:  7: 4 | int                 *ast.Ident                     | type    : int | type
+004:  8: 4 | int                 *ast.Ident                     | type    : int | type
+005: 12: 2 | a                   *ast.Ident                     | var     : int | variable
+006: 12: 6 | 1                   *ast.BasicLit                  | value   : untyped int = 1 | constant
+007: 13: 2 | clone               *ast.Ident                     | value   : func(sprite interface{}) | value
+008: 14: 2 | clone               *ast.Ident                     | value   : func(sprite interface{}, data interface{}) | value
+009: 14: 2 | clone info{1, 2}    *ast.CallExpr                  | void    : () | no value
+010: 14: 8 | info                *ast.Ident                     | type    : main.info | type
+011: 14: 8 | info{1, 2}          *ast.CompositeLit              | value   : main.info | value
+012: 14:13 | 1                   *ast.BasicLit                  | value   : untyped int = 1 | constant
+013: 14:15 | 2                   *ast.BasicLit                  | value   : untyped int = 2 | constant
+014: 15: 2 | clone               *ast.Ident                     | value   : func(sprite interface{}, data interface{}) | value
+015: 15: 2 | clone &info{1, 2}   *ast.CallExpr                  | void    : () | no value
+016: 15: 8 | &info{1, 2}         *ast.UnaryExpr                 | value   : *main.info | value
+017: 15: 9 | info                *ast.Ident                     | type    : main.info | type
+018: 15: 9 | info{1, 2}          *ast.CompositeLit              | value   : main.info | value
+019: 15:14 | 1                   *ast.BasicLit                  | value   : untyped int = 1 | constant
+020: 15:16 | 2                   *ast.BasicLit                  | value   : untyped int = 2 | constant
+021: 19: 2 | say                 *ast.Ident                     | value   : func(msg string, secs ...float64) | value
+022: 19: 2 | say("Hi")           *ast.CallExpr                  | void    : () | no value
+023: 19: 6 | "Hi"                *ast.BasicLit                  | value   : untyped string = "Hi" | constant
+== defs ==
+000:  0: 0 | this                | var this *main.Kai
+001:  3: 2 | a                   | field a int
+002:  6: 6 | info                | type main.info struct{x int; y int}
+003:  7: 2 | x                   | field x int
+004:  8: 2 | y                   | field y int
+005: 11: 6 | onInit              | func (*main.Kai).onInit()
+006: 18: 6 | onCloned            | func (*main.Kai).onCloned()
+== uses ==
+000:  0: 0 | Kai                 | type main.Kai struct{github.com/goplus/gop/cl/internal/spx.Sprite; a int}
+001:  3: 4 | int                 | type int
+002:  7: 4 | int                 | type int
+003:  8: 4 | int                 | type int
+004: 12: 2 | a                   | field a int
+005: 13: 2 | clone               | func github.com/goplus/gop/cl/internal/spx.Gopt_Sprite_Clone__0(sprite interface{})
+006: 14: 2 | clone               | func github.com/goplus/gop/cl/internal/spx.Gopt_Sprite_Clone__1(sprite interface{}, data interface{})
+007: 14: 8 | info                | type main.info struct{x int; y int}
+008: 15: 2 | clone               | func github.com/goplus/gop/cl/internal/spx.Gopt_Sprite_Clone__1(sprite interface{}, data interface{})
+009: 15: 9 | info                | type main.info struct{x int; y int}
+010: 19: 2 | say                 | func (*github.com/goplus/gop/cl/internal/spx.Sprite).Say(msg string, secs ...float64)`)
+}