Repository: goplus/gop
Pull request #1177 (2022-05-17 09:38:50)

mix go/go+ code; TestMixedGo


Patch:
diff --git a/cl/compile.go b/cl/compile.go
index 65e1662b..a180760f 100644
--- a/cl/compile.go
+++ b/cl/compile.go
@@ -28,6 +28,7 @@ import (
 	"strings"
 
 	"github.com/goplus/gop/ast"
+	"github.com/goplus/gop/ast/fromgo"
 	"github.com/goplus/gop/token"
 	"github.com/goplus/gox"
 	"github.com/goplus/mod/modfile"
@@ -148,9 +149,11 @@ type baseLoader struct {
 	start token.Pos
 }
 
-func initLoader(ctx *pkgCtx, syms map[string]loader, start token.Pos, name string, fn func()) {
+func initLoader(ctx *pkgCtx, syms map[string]loader, start token.Pos, name string, fn func(), genCode bool) {
 	if name == "_" {
-		ctx.inits = append(ctx.inits, fn)
+		if genCode {
+			ctx.inits = append(ctx.inits, fn)
+		}
 		return
 	}
 	if old, ok := syms[name]; ok {
@@ -264,7 +267,7 @@ func (bc *blockCtx) findImport(name string) (pr *gox.PkgRef, ok bool) {
 	for k, v := range bc.imports {
 		v.EnsureImported()
 		if v.Types != nil {
-			if v.Types.Name() != k {
+			if v.Types.Name() != k { // if we guess pkgName failed
 				delete(bc.imports, k)
 				bc.imports[v.Types.Name()] = v
 			}
@@ -351,8 +354,9 @@ func (p *pkgCtx) handleRecover(e interface{}) {
 }
 
 const (
-	defaultGoFile = ""
-	testingGoFile = "_test"
+	defaultGoFile  = ""
+	skippingGoFile = "_skip"
+	testingGoFile  = "_test"
 )
 
 // NewPackage creates a Go+ package instance.
@@ -385,7 +389,21 @@ func NewPackage(pkgPath string, pkg *ast.Package, conf *Config) (p *gox.Package,
 		}
 	}
 	for fpath, f := range files {
-		preloadFile(p, ctx, fpath, f, targetDir, conf)
+		fileLine := !conf.NoFileLine
+		ctx := &blockCtx{
+			pkg: p, pkgCtx: ctx, cb: p.CB(), fset: p.Fset, targetDir: targetDir,
+			fileLine: fileLine, relativePath: conf.RelativePath, isClass: f.IsClass,
+			imports: make(map[string]*gox.PkgRef),
+		}
+		preloadGopFile(p, ctx, fpath, f, conf)
+	}
+	for fpath, gof := range pkg.GoFiles {
+		f := fromgo.ASTFile(gof, 0)
+		ctx := &blockCtx{
+			pkg: p, pkgCtx: ctx, cb: p.CB(), fset: p.Fset, targetDir: targetDir,
+			imports: make(map[string]*gox.PkgRef),
+		}
+		preloadFile(p, ctx, fpath, f, false)
 	}
 	for _, f := range files {
 		if f.IsProj {
@@ -474,22 +492,18 @@ func loadFile(ctx *pkgCtx, f *ast.File) {
 	}
 }
 
-func getGoFile(file string) string {
-	if strings.HasSuffix(file, "_test.gop") {
-		return testingGoFile
+func getGoFile(file string, genCode bool) string {
+	if genCode {
+		if strings.HasSuffix(file, "_test.gop") {
+			return testingGoFile
+		}
+		return defaultGoFile
 	}
-	return defaultGoFile
+	return skippingGoFile
 }
 
-func preloadFile(p *gox.Package, parent *pkgCtx, file string, f *ast.File, targetDir string, conf *Config) {
-	syms := parent.syms
-	fileLine := !conf.NoFileLine
-	goFile := getGoFile(file)
-	ctx := &blockCtx{
-		pkg: p, pkgCtx: parent, cb: p.CB(), fset: p.Fset, targetDir: targetDir,
-		fileLine: fileLine, relativePath: conf.RelativePath, isClass: f.IsClass,
-		imports: make(map[string]*gox.PkgRef),
-	}
+func preloadGopFile(p *gox.Package, ctx *blockCtx, file string, f *ast.File, conf *Config) {
+	var parent = ctx.pkgCtx
 	var classType string
 	var baseTypeName string
 	var baseType types.Type
@@ -518,6 +532,7 @@ func preloadFile(p *gox.Package, parent *pkgCtx, file string, f *ast.File, targe
 		for i, pkgPath := range parent.pkgPaths {
 			ctx.lookups[i] = p.Import(pkgPath)
 		}
+		syms := parent.syms
 		pos := f.Pos()
 		specs := getFields(ctx, f)
 		ld := getTypeLoader(parent, syms, pos, classType)
@@ -587,6 +602,14 @@ func preloadFile(p *gox.Package, parent *pkgCtx, file string, f *ast.File, targe
 			})
 		}
 	}
+	preloadFile(p, ctx, file, f, true)
+}
+
+func preloadFile(p *gox.Package, ctx *blockCtx, file string, f *ast.File, genCode bool) {
+	parent := ctx.pkgCtx
+	syms := parent.syms
+	goFile := getGoFile(file, genCode)
+	p.SetCurFile(goFile, true)
 	for _, decl := range f.Decls {
 		switch d := decl.(type) {
 		case *ast.FuncDecl:
@@ -599,42 +622,60 @@ func preloadFile(p *gox.Package, parent *pkgCtx, file string, f *ast.File, targe
 				}
 			}
 			if d.Recv == nil {
-				name := d.Name
-				fn := func() {
-					old, _ := p.SetCurFile(goFile, true)
-					defer p.RestoreCurFile(old)
-					loadFunc(ctx, nil, d)
+				var name = d.Name
+				var fn func()
+				if genCode {
+					fn = func() {
+						old, _ := p.SetCurFile(goFile, true)
+						defer p.RestoreCurFile(old)
+						loadFunc(ctx, nil, d)
+					}
+				} else {
+					fn = func() {
+						declFunc(ctx, nil, d)
+					}
 				}
 				if name.Name == "init" {
-					if debugLoad {
-						log.Println("==> Preload func init")
+					if genCode {
+						if debugLoad {
+							log.Println("==> Preload func init")
+						}
+						parent.inits = append(parent.inits, fn)
 					}
-					parent.inits = append(parent.inits, fn)
 				} else {
 					if debugLoad {
 						log.Println("==> Preload func", name.Name)
 					}
-					initLoader(parent, syms, name.Pos(), name.Name, fn)
+					initLoader(parent, syms, name.Pos(), name.Name, fn, genCode)
 				}
 			} else {
 				if name, ok := getRecvTypeName(parent, d.Recv, true); ok {
 					if debugLoad {
 						log.Printf("==> Preload method %s.%s\n", name, d.Name.Name)
 					}
-					ld := getTypeLoader(parent, syms, token.NoPos, name)
-					ld.methods = append(ld.methods, func() {
-						old, _ := p.SetCurFile(goFile, true)
-						defer p.RestoreCurFile(old)
-						doInitType(ld)
-						recv := toRecv(ctx, d.Recv)
-						loadFunc(ctx, recv, d)
-					})
+					var ld = getTypeLoader(parent, syms, token.NoPos, name)
+					var fn func()
+					if genCode {
+						fn = func() {
+							old, _ := p.SetCurFile(goFile, true)
+							defer p.RestoreCurFile(old)
+							doInitType(ld)
+							recv := toRecv(ctx, d.Recv)
+							loadFunc(ctx, recv, d)
+						}
+					} else {
+						fn = func() {
+							doInitType(ld)
+							recv := toRecv(ctx, d.Recv)
+							declFunc(ctx, recv, d)
+						}
+					}
+					ld.methods = append(ld.methods, fn)
 				}
 			}
 		case *ast.GenDecl:
 			switch d.Tok {
 			case token.IMPORT:
-				p.SetCurFile(goFile, true)
 				for _, item := range d.Specs {
 					loadImport(ctx, item.(*ast.ImportSpec))
 				}
@@ -646,30 +687,53 @@ func preloadFile(p *gox.Package, parent *pkgCtx, file string, f *ast.File, targe
 						log.Println("==> Preload type", name)
 					}
 					ld := getTypeLoader(parent, syms, t.Name.Pos(), name)
-					ld.typ = func() {
-						old, _ := p.SetCurFile(goFile, true)
-						defer p.RestoreCurFile(old)
-						if t.Assign != token.NoPos { // alias type
+					if genCode {
+						ld.typ = func() {
+							old, _ := p.SetCurFile(goFile, true)
+							defer p.RestoreCurFile(old)
+							if t.Assign != token.NoPos { // alias type
+								if debugLoad {
+									log.Println("==> Load > AliasType", name)
+								}
+								ctx.pkg.AliasType(name, toType(ctx, t.Type), t.Pos())
+								return
+							}
 							if debugLoad {
-								log.Println("==> Load > AliasType", name)
+								log.Println("==> Load > NewType", name)
+							}
+							decl := ctx.pkg.NewType(name)
+							if t.Doc != nil {
+								decl.SetComments(t.Doc)
+							} else if d.Doc != nil {
+								decl.SetComments(d.Doc)
+							}
+							ld.typInit = func() { // decycle
+								if debugLoad {
+									log.Println("==> Load > InitType", name)
+								}
+								decl.InitType(ctx.pkg, toType(ctx, t.Type))
 							}
-							ctx.pkg.AliasType(name, toType(ctx, t.Type))
-							return
-						}
-						if debugLoad {
-							log.Println("==> Load > NewType", name)
-						}
-						decl := ctx.pkg.NewType(name)
-						if t.Doc != nil {
-							decl.SetComments(t.Doc)
-						} else if d.Doc != nil {
-							decl.SetComments(d.Doc)
 						}
-						ld.typInit = func() { // decycle
+					} else {
+						ld.typ = func() {
+							pkg := ctx.pkg.Types
+							if t.Assign != token.NoPos { // alias type
+								if debugLoad {
+									log.Println("==> Load > AliasType", name)
+								}
+								aliasType(pkg, t.Pos(), name, toType(ctx, t.Type))
+								return
+							}
 							if debugLoad {
-								log.Println("==> Load > InitType", name)
+								log.Println("==> Load > NewType", name)
+							}
+							named := newType(pkg, t.Pos(), name)
+							ld.typInit = func() { // decycle
+								if debugLoad {
+									log.Println("==> Load > InitType", name)
+								}
+								initType(ctx, named, toType(ctx, t.Type))
 							}
-							decl.InitType(ctx.pkg, toType(ctx, t.Type))
 						}
 					}
 				}
@@ -683,8 +747,6 @@ func preloadFile(p *gox.Package, parent *pkgCtx, file string, f *ast.File, targe
 					}
 					setNamesLoader(parent, syms, vSpec.Names, func() {
 						if c := cdecl; c != nil {
-							old, _ := p.SetCurFile(goFile, true)
-							defer p.RestoreCurFile(old)
 							cdecl = nil
 							loadConstSpecs(ctx, c, d.Specs)
 							for _, s := range d.Specs {
@@ -702,9 +764,9 @@ func preloadFile(p *gox.Package, parent *pkgCtx, file string, f *ast.File, targe
 					}
 					setNamesLoader(parent, syms, vSpec.Names, func() {
 						if v := vSpec; v != nil { // only init once
+							vSpec = nil
 							old, _ := p.SetCurFile(goFile, true)
 							defer p.RestoreCurFile(old)
-							vSpec = nil
 							loadVars(ctx, v, true)
 							removeNames(syms, v.Names)
 						}
@@ -719,6 +781,33 @@ func preloadFile(p *gox.Package, parent *pkgCtx, file string, f *ast.File, targe
 	}
 }
 
+func newType(pkg *types.Package, pos token.Pos, name string) *types.Named {
+	typName := types.NewTypeName(pos, pkg, name, nil)
+	if old := pkg.Scope().Insert(typName); old != nil {
+		log.Panicf("%s redeclared in this block\n\tprevious declaration at %v", name, "<TODO>")
+	}
+	return types.NewNamed(typName, nil, nil)
+}
+
+func initType(ctx *blockCtx, named *types.Named, typ types.Type) {
+	if named, ok := typ.(*types.Named); ok {
+		typ = getUnderlying(ctx, named)
+	}
+	named.SetUnderlying(typ)
+}
+
+func aliasType(pkg *types.Package, pos token.Pos, name string, typ types.Type) {
+	o := types.NewTypeName(pos, pkg, name, typ)
+	pkg.Scope().Insert(o)
+}
+
+func declFunc(ctx *blockCtx, recv *types.Var, d *ast.FuncDecl) {
+	name := d.Name.Name
+	pkg := ctx.pkg.Types
+	sig := toFuncType(ctx, d.Type, recv)
+	pkg.Scope().Insert(types.NewFunc(d.Pos(), pkg, name, sig))
+}
+
 func loadFunc(ctx *blockCtx, recv *types.Var, d *ast.FuncDecl) {
 	name := d.Name.Name
 	if debugLoad {
@@ -936,7 +1025,7 @@ func removeNames(syms map[string]loader, names []*ast.Ident) {
 
 func setNamesLoader(ctx *pkgCtx, syms map[string]loader, names []*ast.Ident, load func()) {
 	for _, name := range names {
-		initLoader(ctx, syms, name.Pos(), name.Name, load)
+		initLoader(ctx, syms, name.Pos(), name.Name, load, true)
 	}
 }
 


Test patch:
diff --git a/ast/fromgo/gopast_test.go b/ast/fromgo/gopast_test.go
index 0539331e..5f748acf 100644
--- a/ast/fromgo/gopast_test.go
+++ b/ast/fromgo/gopast_test.go
@@ -116,3 +116,12 @@ var f = a.i.(func() (int))()
 func foo(v ...interface{}) {}
 `)
 }
+
+func TestMethod(t *testing.T) {
+	test(t, `package main
+
+type foo int
+
+func (a foo) Str() (string) {}
+`)
+}
diff --git a/cl/builtin_test.go b/cl/builtin_test.go
index 7ca59162..1cd9e9a7 100644
--- a/cl/builtin_test.go
+++ b/cl/builtin_test.go
@@ -130,7 +130,10 @@ func lookupClass(ext string) (c *gopmod.Class, ok bool) {
 }
 
 func TestGetGoFile(t *testing.T) {
-	if f := getGoFile("a_test.gop"); f != testingGoFile {
+	if f := getGoFile("a_test.gop", true); f != testingGoFile {
+		t.Fatal("TestGetGoFile:", f)
+	}
+	if f := getGoFile("a_test.gop", false); f != skippingGoFile {
 		t.Fatal("TestGetGoFile:", f)
 	}
 }
diff --git a/cl/compile_test.go b/cl/compile_test.go
index e2567228..2f81b312 100644
--- a/cl/compile_test.go
+++ b/cl/compile_test.go
@@ -62,10 +62,19 @@ func gopClTest(t *testing.T, gopcode, expected string) {
 }
 
 func gopClTestEx(t *testing.T, conf *cl.Config, pkgname, gopcode, expected string) {
+	fs := parsertest.NewSingleFileFS("/foo", "bar.gop", gopcode)
+	gopClTestFS(t, conf, fs, pkgname, expected)
+}
+
+func gopMixedClTest(t *testing.T, pkgname, gocode, gopcode, expected string) {
+	fs := parsertest.NewTwoFilesFS("/foo", "a.go", gocode, "b.gop", gopcode)
+	gopClTestFS(t, gblConf, fs, pkgname, expected)
+}
+
+func gopClTestFS(t *testing.T, conf *cl.Config, fs parser.FileSystem, pkgname, expected string) {
 	cl.SetDisableRecover(true)
 	defer cl.SetDisableRecover(false)
 
-	fs := parsertest.NewSingleFileFS("/foo", "bar.gop", gopcode)
 	pkgs, err := parser.ParseFSDir(gblFset, fs, "/foo", parser.Config{Mode: parser.ParseComments})
 	if err != nil {
 		scanner.PrintError(os.Stderr, err)
@@ -87,6 +96,42 @@ func gopClTestEx(t *testing.T, conf *cl.Config, pkgname, gopcode, expected strin
 	}
 }
 
+func TestMixedGo(t *testing.T) {
+	gopMixedClTest(t, "main", `package main
+
+import "strconv"
+
+const n = 10
+
+func f(v int) string {
+	return strconv.Itoa(v)
+}
+
+type foo struct {
+	v int
+}
+
+func (a foo) Str() string {
+	return f(a.v)
+}
+
+type foo2 = foo
+
+var x string = c.str
+`, `
+var a [n]int
+var b string = f(n)
+var c foo2
+var d int = c.v
+`, `package main
+
+var a [10]int
+var b string = f(n)
+var c foo
+var d int = c.v
+`)
+}
+
 func TestInitFunc(t *testing.T) {
 	gopClTest(t, `
 
diff --git a/parser/parsertest/memfs.go b/parser/parsertest/memfs.go
index dbe7e402..09e7b9ef 100644
--- a/parser/parsertest/memfs.go
+++ b/parser/parsertest/memfs.go
@@ -109,4 +109,14 @@ func NewSingleFileFS(dir string, fname string, data string) *MemFS {
 	})
 }
 
+// NewTwoFilesFS creates a file system that contains two files.
+func NewTwoFilesFS(dir string, fname1, data1, fname2, data2 string) *MemFS {
+	return NewMemFS(map[string][]string{
+		dir: {fname1, fname2},
+	}, map[string]string{
+		path.Join(dir, fname1): data1,
+		path.Join(dir, fname2): data2,
+	})
+}
+
 // -----------------------------------------------------------------------------