Repository: goplus/gop
Pull request #1478 (2023-10-20 20:29:09)

typesutil: rec.Use; gox.Recorder


Patch:
diff --git a/ast/fromgo/gopast.go b/ast/fromgo/gopast.go
index 02220fb2..9ec50a0d 100644
--- a/ast/fromgo/gopast.go
+++ b/ast/fromgo/gopast.go
@@ -211,9 +211,19 @@ func gopIdent(v *ast.Ident) *gopast.Ident {
 	return &gopast.Ident{
 		NamePos: v.NamePos,
 		Name:    v.Name,
+		Obj:     &gopast.Object{Data: v},
 	}
 }
 
+// CheckIdent checks if a Go+ ast.Ident is converted from a Go ast.Ident or not.
+// If it is, CheckIdent returns the original Go ast.Ident object.
+func CheckIdent(v *gopast.Ident) (id *ast.Ident, ok bool) {
+	if o := v.Obj; o != nil && o.Kind == 0 && o.Data != nil {
+		id, ok = o.Data.(*ast.Ident)
+	}
+	return
+}
+
 func gopIdents(names []*ast.Ident) []*gopast.Ident {
 	ret := make([]*gopast.Ident, len(names))
 	for i, v := range names {
@@ -332,6 +342,7 @@ const (
 	KeepCgo
 )
 
+// ASTFile converts a Go ast.File into a Go+ ast.File object.
 func ASTFile(f *ast.File, mode int) *gopast.File {
 	if (mode & KeepFuncBody) != 0 {
 		log.Panicln("ASTFile: doesn't support keeping func body now")
diff --git a/cl/compile.go b/cl/compile.go
index 29400d25..c7fb665b 100644
--- a/cl/compile.go
+++ b/cl/compile.go
@@ -64,6 +64,7 @@ func SetDebug(flags dbgFlags) {
 
 // -----------------------------------------------------------------------------
 
+// Recorder represents a compiling event recorder.
 type Recorder interface {
 	// Type maps expressions to their types, and for constant
 	// expressions, also their values. Invalid expressions are
@@ -215,6 +216,24 @@ type Config struct {
 	Outline bool
 }
 
+type goxRecorder struct {
+	rec Recorder
+}
+
+// Member maps identifiers to the objects they denote.
+func (p *goxRecorder) Member(id ast.Node, obj types.Object) {
+	switch v := id.(type) {
+	case *ast.SelectorExpr:
+		sel := v.Sel
+		// TODO: record event for a Go ident
+		if _, ok := fromgo.CheckIdent(sel); !ok {
+			p.rec.Use(sel, obj)
+		}
+	case *ast.Ident: // it's impossible converted from Go
+		p.rec.Use(v, obj)
+	}
+}
+
 type nodeInterp struct {
 	fset       *token.FileSet
 	files      map[string]*ast.File
@@ -512,6 +531,9 @@ func NewPackage(pkgPath string, pkg *ast.Package, conf *Config) (p *gox.Package,
 		NoSkipConstant:  conf.NoSkipConstant,
 		PkgPathIox:      ioxPkgPath,
 	}
+	if conf.Recorder != nil {
+		confGox.Recorder = &goxRecorder{rec: conf.Recorder}
+	}
 	if enableRecover {
 		defer func() {
 			if e := recover(); e != nil {
diff --git a/cl/expr.go b/cl/expr.go
index cf2f8c1e..c23d4c9a 100644
--- a/cl/expr.go
+++ b/cl/expr.go
@@ -120,6 +120,10 @@ func compileIdent(ctx *blockCtx, ident *ast.Ident, flags int) (obj *gox.PkgRef,
 			return nil, objCPkgRef
 		}
 		if pr, ok := ctx.findImport(name); ok {
+			if rec := ctx.recorder(); rec != nil {
+				pkgName := types.NewPkgName(token.NoPos, ctx.pkg.Types, name, pr.Types)
+				rec.Use(ident, pkgName)
+			}
 			return pr, objPkgRef
 		}
 	}
@@ -149,6 +153,9 @@ find:
 	} else {
 		ctx.cb.VarRef(o, ident)
 	}
+	if rec := ctx.recorder(); rec != nil {
+		rec.Use(ident, o)
+	}
 	return
 }
 
@@ -424,6 +431,9 @@ func compilePkgRef(ctx *blockCtx, at *gox.PkgRef, x *ast.Ident, flags, pkgKind i
 				cb.Call(0)
 			}
 		}
+		if rec := ctx.recorder(); rec != nil {
+			rec.Use(x, v)
+		}
 		return true
 	}
 	return false
diff --git a/cl/func_type_and_var.go b/cl/func_type_and_var.go
index 32191348..1d3c0b8a 100644
--- a/cl/func_type_and_var.go
+++ b/cl/func_type_and_var.go
@@ -38,11 +38,22 @@ func toRecv(ctx *blockCtx, recv *ast.FieldList) *types.Var {
 		name = v.Names[0].Name
 	}
 	typ, star := getRecvType(v.Type)
-	t := toType(ctx, typ)
+	id, ok := typ.(*ast.Ident)
+	if !ok {
+		panic("TODO: getRecvType")
+	}
+	t := toIdentType(ctx, id)
 	if star {
 		t = types.NewPointer(t)
 	}
-	return ctx.pkg.NewParam(v.Pos(), name, t)
+	ret := ctx.pkg.NewParam(v.Pos(), name, t)
+	if rec := ctx.recorder(); rec != nil {
+		dRecv := recv.List[0]
+		if names := dRecv.Names; len(names) == 1 {
+			rec.Def(names[0], ret)
+		}
+	}
+	return ret
 }
 
 func getRecvTypeName(ctx *pkgCtx, recv *ast.FieldList, handleErr bool) (string, bool) {
@@ -90,7 +101,11 @@ func toParam(ctx *blockCtx, fld *ast.Field, args []*gox.Param) []*gox.Param {
 		return append(args, pkg.NewParam(fld.Pos(), "", typ))
 	}
 	for _, name := range fld.Names {
-		args = append(args, pkg.NewParam(name.Pos(), name.Name, typ))
+		param := pkg.NewParam(name.Pos(), name.Name, typ)
+		args = append(args, param)
+		if rec := ctx.recorder(); rec != nil {
+			rec.Def(name, param)
+		}
 	}
 	return args
 }
diff --git a/cl/typeparams.go b/cl/typeparams.go
index 5a1e9d32..a26b57b8 100644
--- a/cl/typeparams.go
+++ b/cl/typeparams.go
@@ -169,6 +169,9 @@ L:
 		case *ast.ParenExpr:
 			typ = t.X
 		case *ast.StarExpr:
+			if ptr {
+				panic("TODO: getRecvType")
+			}
 			ptr = true
 			typ = t.X
 		default:
diff --git a/go.mod b/go.mod
index 97821958..446d101b 100644
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,7 @@ go 1.18
 require (
 	github.com/fsnotify/fsnotify v1.6.0
 	github.com/goplus/c2go v0.7.16
-	github.com/goplus/gox v1.12.2-0.20231020050546-7a7af254a518
+	github.com/goplus/gox v1.12.2-0.20231020202641-5f657ff4e754
 	github.com/goplus/mod v0.11.8-0.20231019172744-da5848421263
 	github.com/qiniu/x v1.13.1
 	golang.org/x/tools v0.14.0
diff --git a/go.sum b/go.sum
index 64d2450a..d808e945 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.16 h1:B9jRNNytoq7yDBQOZSm2qENQhRQMfNhgOea5XHWNUnI=
 github.com/goplus/c2go v0.7.16/go.mod h1:XODEFX2PeEEJXNLLdykWeZgMSaKJ51fVm+C9IM3FxNQ=
 github.com/goplus/gox v1.12.1/go.mod h1:wymoQJ7ydd42cTlaXb4wNbvn4LlKjR+j8PZehI7v1zQ=
-github.com/goplus/gox v1.12.2-0.20231020050546-7a7af254a518 h1:s9CsA13IvxuCkOLrJkPmbS+Tilof5i2zIdQtyL5+USw=
-github.com/goplus/gox v1.12.2-0.20231020050546-7a7af254a518/go.mod h1:Ek1YIy3wRaZ1i0DD2XG29i3r5AFdhcOradK0/GGs1YQ=
+github.com/goplus/gox v1.12.2-0.20231020202641-5f657ff4e754 h1:uuXDqFfg4RhmHD7slw15hzwVQtJ51CsdcZn6gQtve/E=
+github.com/goplus/gox v1.12.2-0.20231020202641-5f657ff4e754/go.mod h1:Ek1YIy3wRaZ1i0DD2XG29i3r5AFdhcOradK0/GGs1YQ=
 github.com/goplus/mod v0.11.5/go.mod h1:NDC5E+XOT8vcJCMjqKhLDJHTHX7lyVN4Vbfi2U7dBhs=
 github.com/goplus/mod v0.11.8-0.20231019172744-da5848421263 h1:PE0HveOss5mai9pa52L4/ZvVqZtltogJ9rIUIsdlG/I=
 github.com/goplus/mod v0.11.8-0.20231019172744-da5848421263/go.mod h1:yl2QncBKTdXk+8UaNsdo4u2zSpGEJYA5JKjgD3K2h00=
diff --git a/x/typesutil/gopinfo.go b/x/typesutil/gopinfo.go
index 0648ec4d..77f77792 100644
--- a/x/typesutil/gopinfo.go
+++ b/x/typesutil/gopinfo.go
@@ -212,7 +212,7 @@ func (info gopRecorder) Instantiate(id *ast.Ident, inst types.Instance) {
 // Invariant: Defs[id] == nil || Defs[id].Pos() == id.Pos()
 func (info gopRecorder) Def(id *ast.Ident, obj types.Object) {
 	if debugVerbose {
-		log.Println("==> Def:", id, obj.Type())
+		log.Println("==> Def:", id, obj)
 	}
 	info.Defs[id] = obj
 }
@@ -224,7 +224,7 @@ func (info gopRecorder) Def(id *ast.Ident, obj types.Object) {
 // Invariant: Uses[id].Pos() != id.Pos()
 func (info gopRecorder) Use(id *ast.Ident, obj types.Object) {
 	if debugVerbose {
-		log.Println("==> Use:", id, obj.Type())
+		log.Println("==> Use:", id, obj)
 	}
 	info.Uses[id] = obj
 }


Test patch:
diff --git a/ast/fromgo/gopast_test.go b/ast/fromgo/gopast_test.go
index 5f748acf..46d51d70 100644
--- a/ast/fromgo/gopast_test.go
+++ b/ast/fromgo/gopast_test.go
@@ -23,6 +23,7 @@ import (
 	"go/token"
 	"testing"
 
+	gopast "github.com/goplus/gop/ast"
 	"github.com/goplus/gop/format"
 )
 
@@ -125,3 +126,14 @@ type foo int
 func (a foo) Str() (string) {}
 `)
 }
+
+func TestCheckIdent(t *testing.T) {
+	if _, ok := CheckIdent(&gopast.Ident{}); ok {
+		t.Fatal("CheckIdent: found?")
+	}
+	if _, ok := CheckIdent(&gopast.Ident{Obj: &gopast.Object{
+		Data: &ast.Ident{},
+	}}); !ok {
+		t.Fatal("CheckIdent: not found?")
+	}
+}
diff --git a/cl/builtin_test.go b/cl/builtin_test.go
index b20381db..bc15a433 100644
--- a/cl/builtin_test.go
+++ b/cl/builtin_test.go
@@ -39,6 +39,40 @@ func getGoxConf() *gox.Config {
 	return &gox.Config{Fset: fset, Importer: imp}
 }
 
+func TestErrMultiStarRecv(t *testing.T) {
+	defer func() {
+		if e := recover(); e == nil {
+			t.Fatal("TestErrMultiStarRecv: no panic?")
+		}
+	}()
+	getRecvType(&ast.StarExpr{
+		X: &ast.StarExpr{},
+	})
+}
+
+func TestErrPanicToRecv(t *testing.T) {
+	ctx := &blockCtx{
+		tlookup: &typeParamLookup{
+			[]*types.TypeParam{
+				types.NewTypeParam(types.NewTypeName(0, nil, "t", nil), nil),
+			},
+		},
+	}
+	recv := &ast.FieldList{
+		List: []*ast.Field{
+			{Type: &ast.SelectorExpr{}},
+		},
+	}
+	func() {
+		defer func() {
+			if e := recover(); e == nil {
+				t.Fatal("TestErrPanicToRecv: no panic?")
+			}
+		}()
+		toRecv(ctx, recv)
+	}()
+}
+
 func TestCompileErrWrapExpr(t *testing.T) {
 	defer func() {
 		if e := recover(); e != "TODO: can't use expr? in global" {