Repository: goplus/gop
Pull request #1049 (2021-12-09 18:21:24)

disable pkg.var/const; disable auto property of pkg.println


Patch:
diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index ea06a35a..5052546c 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -21,9 +21,6 @@ jobs:
       with:
         go-version: ${{ matrix.go-version }}
 
-    - name: Test Go+ installer
-      run: go test -v cmd/make_test.go
-
     - name: Compile gop and related tools
       run: go install ./...
 
diff --git a/README.md b/README.md
index d7afc11c..5fe28f26 100644
--- a/README.md
+++ b/README.md
@@ -87,10 +87,10 @@ It's for Go, and it's also for Go+.
 
 ## Playground
 
-Go+ Playground based on Docker:
+Go+ Playground based on Docker (recommended):
 * https://play.goplus.org/
 
-Go+ Playground based on GopherJS:
+Go+ Playground based on GopherJS (currently only available in v0.7.x):
 * https://jsplay.goplus.org/
 
 
diff --git a/cl/expr.go b/cl/expr.go
index 67551bce..9c68d4b2 100644
--- a/cl/expr.go
+++ b/cl/expr.go
@@ -114,10 +114,10 @@ func compileIdent(ctx *blockCtx, ident *ast.Ident, flags int) *gox.PkgRef {
 	}
 
 	// universe object
-	if (flags&clIdentAllowBuiltin) == 0 && isBuiltin(o) {
-		panic(ctx.newCodeErrorf(ident.Pos(), "use of builtin %s not in function call", name))
-	}
 	if obj := ctx.pkg.Builtin().TryRef(name); obj != nil {
+		if (flags&clIdentAllowBuiltin) == 0 && isBuiltin(o) && !strings.HasPrefix(o.Name(), "print") {
+			panic(ctx.newCodeErrorf(ident.Pos(), "use of builtin %s not in function call", name))
+		}
 		o = obj
 	} else if o == nil {
 		if (clIdentGoto & flags) != 0 {
@@ -144,24 +144,16 @@ func isBuiltin(o types.Object) bool {
 }
 
 func compileMember(ctx *blockCtx, v ast.Node, name string, flags int) error {
-	cb := ctx.cb
-	lhs := (flags & clIdentLHS) != 0
-	kind, err := cb.Member(name, lhs, v)
-	if kind != 0 {
-		return nil
-	}
-	if c := name[0]; c >= 'a' && c <= 'z' {
-		name = string(rune(c)+('A'-'a')) + name[1:]
-		switch kind, _ = cb.Member(name, lhs, v); kind {
-		case gox.MemberMethod:
-			if (flags & clIdentAutoCall) != 0 {
-				cb.Call(0)
-			}
-			return nil
-		case gox.MemberField:
-			return nil
-		}
+	var mflag gox.MemberFlag
+	switch {
+	case (flags & clIdentLHS) != 0:
+		mflag = gox.MemberFlagRef
+	case (flags & clIdentAutoCall) != 0:
+		mflag = gox.MemberFlagAutoProperty
+	default:
+		mflag = gox.MemberFlagMethodAlias
 	}
+	_, err := ctx.cb.Member(name, mflag, v)
 	return err
 }
 
@@ -330,39 +322,54 @@ func compileSelectorExpr(ctx *blockCtx, v *ast.SelectorExpr, flags int) {
 	}
 }
 
-func pkgRef(at *gox.PkgRef, name string) (o types.Object, canAutoCall bool) {
+func isFunc(typ types.Type) bool {
+	switch typ.(type) {
+	case *types.Signature:
+		return true
+	}
+	return false
+}
+
+func pkgRef(at *gox.PkgRef, name string) (o types.Object, alias bool) {
 	if c := name[0]; c >= 'a' && c <= 'z' {
 		name = string(rune(c)+('A'-'a')) + name[1:]
-		return at.TryRef(name), true
+		if v := at.TryRef(name); v != nil && isFunc(v.Type()) {
+			return v, true
+		}
+		return
 	}
 	return at.TryRef(name), false
 }
 
-func lookupPkgRef(ctx *blockCtx, pkg *gox.PkgRef, x *ast.Ident) (o types.Object, canAutoCall bool) {
+func lookupPkgRef(ctx *blockCtx, pkg *gox.PkgRef, x *ast.Ident) (o types.Object, alias bool) {
 	if pkg != nil {
 		return pkgRef(pkg, x.Name)
 	}
 	for _, at := range ctx.lookups {
-		if o2, canAutoCall2 := pkgRef(at, x.Name); o2 != nil {
+		if o2, alias2 := pkgRef(at, x.Name); o2 != nil {
 			if o != nil {
 				panic(ctx.newCodeErrorf(
 					x.Pos(), "confliction: %s declared both in \"%s\" and \"%s\"",
 					x.Name, at.Types.Path(), pkg.Types.Path()))
 			}
-			pkg, o, canAutoCall = at, o2, canAutoCall2
+			pkg, o, alias = at, o2, alias2
 		}
 	}
 	return
 }
 
 func compilePkgRef(ctx *blockCtx, at *gox.PkgRef, x *ast.Ident, flags int) bool {
-	if v, canAutoCall := lookupPkgRef(ctx, at, x); v != nil {
+	if v, alias := lookupPkgRef(ctx, at, x); v != nil {
 		cb := ctx.cb
 		if (flags & clIdentLHS) != 0 {
 			cb.VarRef(v, x)
 		} else {
+			autoprop := alias && (flags&clIdentAutoCall) != 0
+			if autoprop && !canAutoProperty(v.Type()) {
+				return false
+			}
 			cb.Val(v, x)
-			if canAutoCall && (flags&clIdentAutoCall) != 0 && isFunc(v.Type()) {
+			if autoprop {
 				cb.Call(0)
 			}
 		}
@@ -371,10 +378,10 @@ func compilePkgRef(ctx *blockCtx, at *gox.PkgRef, x *ast.Ident, flags int) bool
 	return false
 }
 
-func isFunc(typ types.Type) bool {
-	switch typ.(type) {
+func canAutoProperty(typ types.Type) bool {
+	switch v := typ.(type) {
 	case *types.Signature:
-		return true
+		return v.Params().Len() == 0
 	}
 	return false
 }
diff --git a/cmd/make.go b/cmd/make.go
index 7d8506d3..1760318f 100644
--- a/cmd/make.go
+++ b/cmd/make.go
@@ -83,8 +83,8 @@ const (
 )
 
 func init() {
-	if inWindows {
-		for index, file := range gopBinFiles {
+	for index, file := range gopBinFiles {
+		if inWindows {
 			file += ".exe"
 			gopBinFiles[index] = file
 		}
@@ -233,9 +233,6 @@ func buildGoplusTools(useGoProxy bool) {
 	}
 	print(buildOutput)
 
-	// Clear gop run cache
-	cleanGopRunCache()
-
 	installPath := linkGoplusToLocalBin()
 
 	println("\nGo+ tools installed successfully!")
@@ -296,22 +293,6 @@ func clean() {
 			log.Fatalln(err)
 		}
 	}
-
-	cleanGopRunCache()
-}
-
-func cleanGopRunCache() {
-	homeDir, _ := os.UserHomeDir()
-	runCacheDir := filepath.Join(homeDir, ".gop", "run")
-	files := []string{"go.mod", "go.sum"}
-	for _, file := range files {
-		fullPath := filepath.Join(runCacheDir, file)
-		if checkPathExist(fullPath, false) {
-			if err := os.Remove(fullPath); err != nil {
-				log.Fatalln(err)
-			}
-		}
-	}
 }
 
 func uninstall() {
diff --git a/go.mod b/go.mod
index 1b56ba14..9458a26c 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module github.com/goplus/gop
 go 1.16
 
 require (
-	github.com/goplus/gox v1.8.1
+	github.com/goplus/gox v1.8.5
 	github.com/qiniu/x v1.11.5
 	golang.org/x/mod v0.5.1
 	golang.org/x/tools v0.1.8
diff --git a/go.sum b/go.sum
index d0e06736..e204e55e 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,5 @@
-github.com/goplus/gox v1.8.1 h1:nMn6J3Jv6zNHr6Ul2GkUqhLCm6m0tWt8jGpI15iSaEw=
-github.com/goplus/gox v1.8.1/go.mod h1:XWqV75HImeysI8+Q9gAdYyvwlP8jds24xsgYRwTO3JI=
+github.com/goplus/gox v1.8.5 h1:2rvB6mwrwIxXHGisxA7HHbtrP8iWfazW+tL5Q8Fb4YM=
+github.com/goplus/gox v1.8.5/go.mod h1:XWqV75HImeysI8+Q9gAdYyvwlP8jds24xsgYRwTO3JI=
 github.com/qiniu/x v1.11.5 h1:TYr5cl4g2yoHAZeDK4MTjKF6CMoG+IHlCDvvM5qym6U=
 github.com/qiniu/x v1.11.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs=
 github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=


Test patch:
diff --git a/cl/builtin_test.go b/cl/builtin_test.go
index 8025f6f0..59cbf821 100644
--- a/cl/builtin_test.go
+++ b/cl/builtin_test.go
@@ -36,6 +36,12 @@ func TestCompileErrWrapExpr(t *testing.T) {
 	compileErrWrapExpr(ctx, &ast.ErrWrapExpr{Tok: token.QUESTION})
 }
 
+func TestCanAutoProperty(t *testing.T) {
+	if canAutoProperty(nil) {
+		t.Fatal("canAutoProperty?")
+	}
+}
+
 func TestToString(t *testing.T) {
 	defer func() {
 		if e := recover(); e == nil {
diff --git a/cl/compile_spx_test.go b/cl/compile_spx_test.go
index 04b20236..48a26f9a 100644
--- a/cl/compile_spx_test.go
+++ b/cl/compile_spx_test.go
@@ -203,7 +203,7 @@ func TestSpxMethod(t *testing.T) {
 func onInit() {
 	sched
 	broadcast "msg1"
-	testIntValue = 1
+	TestIntValue = 1
 	x := round(1.2)
 }
 `, `
diff --git a/cl/compile_test.go b/cl/compile_test.go
index 4fe15af4..d2b7e460 100644
--- a/cl/compile_test.go
+++ b/cl/compile_test.go
@@ -148,6 +148,21 @@ func main() {
 `)
 }
 
+func TestAssignPrintln(t *testing.T) {
+	gopClTest(t, `
+p := println
+p "Hello world"
+`, `package main
+
+import fmt "fmt"
+
+func main() {
+	p := fmt.Println
+	p("Hello world")
+}
+`)
+}
+
 func TestRedefineBuiltin(t *testing.T) {
 	gopClTest(t, `
 func main() {
diff --git a/cl/error_msg_test.go b/cl/error_msg_test.go
index 86138ac3..4906a553 100644
--- a/cl/error_msg_test.go
+++ b/cl/error_msg_test.go
@@ -171,8 +171,8 @@ var a = 1, 2
 }
 `)
 	codeErrorTest(t,
-		"./bar.gop:2:9: use of builtin len not in function call", `func main() {
-println(len.x)
+		"./bar.gop:2:10: use of builtin len not in function call", `func main() {
+	println(len.x)
 }
 `)
 	codeErrorTest(t,
@@ -215,7 +215,7 @@ func foo(v map[int]bar) {
 }
 `)
 	codeErrorTest(t,
-		"./bar.gop:2:2: use of builtin println not in function call", `func main() {
+		"./bar.gop:2:2: println is not a variable", `func main() {
 	println = "hello"
 }
 `)
@@ -798,3 +798,11 @@ switch n.(type) {
 }
 `)
 }
+
+func TestErrAutoProperty(t *testing.T) {
+	codeErrorTest(t, `./bar.gop:4:11: cannot refer to unexported name fmt.println`, `
+import "fmt"
+
+n, err := fmt.println
+`)
+}
diff --git a/cmd/make_test.go b/cmd/make_test.go
deleted file mode 100644
index b63bb311..00000000
--- a/cmd/make_test.go
+++ /dev/null
@@ -1,104 +0,0 @@
-//go:build ignore
-// +build ignore
-
-package make_test
-
-import (
-	"os"
-	"os/exec"
-	"path/filepath"
-	"runtime"
-	"testing"
-)
-
-const (
-	inWindows = (runtime.GOOS == "windows")
-)
-
-var script = "all.bash"
-var gopRoot = ""
-var gopBinFiles = []string{"gop", "gopfmt"}
-
-func checkPathExist(path string, isDir bool) bool {
-	stat, err := os.Stat(path)
-	isExists := !os.IsNotExist(err)
-	if isDir {
-		return isExists && stat.IsDir()
-	}
-	return isExists && !stat.IsDir()
-}
-
-func detectGoBinPath() string {
-	goBin, ok := os.LookupEnv("GOBIN")
-	if ok {
-		return goBin
-	}
-
-	goPath, ok := os.LookupEnv("GOPATH")
-	if ok {
-		list := filepath.SplitList(goPath)
-		if len(list) > 0 {
-			// Put in first directory of $GOPATH.
-			return filepath.Join(list[0], "bin")
-		}
-	}
-
-	homeDir, _ := os.UserHomeDir()
-	return filepath.Join(homeDir, "go", "bin")
-}
-
-func init() {
-	pwd, _ := os.Getwd()
-	gopRoot = filepath.Join(pwd, "..")
-	if inWindows {
-		script = "all.bat"
-		for index, file := range gopBinFiles {
-			file += ".exe"
-			gopBinFiles[index] = file
-		}
-	}
-}
-
-func TestAllScript(t *testing.T) {
-	os.Chdir(gopRoot)
-	cmd := exec.Command(filepath.Join(gopRoot, script))
-	if output, err := cmd.CombinedOutput(); err != nil {
-		t.Fatalf("Failed: %v:\nOut: %s\n", err, output)
-	}
-
-	goBinPath := detectGoBinPath()
-
-	for _, file := range gopBinFiles {
-		if !checkPathExist(filepath.Join(gopRoot, "bin", file), false) {
-			t.Fatalf("Failed: %s not found in ./bin directory\n", file)
-		}
-		if !checkPathExist(filepath.Join(goBinPath, file), false) {
-			t.Fatalf("Failed: %s not found in %s/bin directory\n", file, goBinPath)
-		}
-	}
-
-	cmd = exec.Command(filepath.Join(gopRoot, "bin", gopBinFiles[0]), "version")
-	if output, err := cmd.CombinedOutput(); err != nil {
-		t.Fatalf("Failed: %v:\nOut: %s\n", err, output)
-	}
-}
-
-func TestHandleMultiFlags(t *testing.T) {
-	os.Chdir(gopRoot)
-	cmd := exec.Command("go", "run", filepath.Join(gopRoot, "cmd/make.go"), "--install", "--test", "--uninstall")
-	if output, err := cmd.CombinedOutput(); err != nil {
-		t.Fatalf("Failed: %v:\nOut: %s\n", err, output)
-	}
-
-	goBinPath := detectGoBinPath()
-
-	// Uninstall will be the last action to run, test if all build assets being cleared.
-	for _, file := range gopBinFiles {
-		if checkPathExist(filepath.Join(gopRoot, "bin", file), false) {
-			t.Fatalf("Failed: %s found in ./bin directory\n", file)
-		}
-		if checkPathExist(filepath.Join(goBinPath, file), false) {
-			t.Fatalf("Failed: %s found in %s/bin directory\n", file, goBinPath)
-		}
-	}
-}