Repository: goplus/gop
Pull request #1173 (2022-05-16 17:33:19)

gop/x/types


Patch:
diff --git a/cmd/gopdecl/goenv.go b/cmd/gopdecl/goenv.go
deleted file mode 100644
index f8429603..00000000
--- a/cmd/gopdecl/goenv.go
+++ /dev/null
@@ -1,16 +0,0 @@
-package main
-
-import (
-	"os"
-	"path/filepath"
-	"runtime"
-)
-
-func initGoModCache() string {
-	val := os.Getenv("GOMODCACHE")
-	if val == "" {
-		val = filepath.Join(runtime.GOROOT(), "pkg/mod")
-		os.Setenv("GOMODCACHE", val)
-	}
-	return val
-}
diff --git a/cmd/gopdecl/gopdecl.go b/cmd/gopdecl/gopdecl.go
index cd5772de..7a82918c 100644
--- a/cmd/gopdecl/gopdecl.go
+++ b/cmd/gopdecl/gopdecl.go
@@ -1,28 +1,43 @@
+/*
+ * Copyright (c) 2022 The GoPlus Authors (goplus.org). All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package main
 
 import (
 	"flag"
 	"fmt"
-	"go/ast"
-	"go/importer"
-	"go/parser"
 	"go/token"
-	"go/types"
-	"io/fs"
 	"log"
 	"os"
 	"path/filepath"
 	"strings"
 	"unicode"
 
-	"github.com/goplus/gop/ast/togo"
-	gopp "github.com/goplus/gop/parser"
-	"golang.org/x/tools/go/gcexportdata"
+	goast "go/ast"
+	goparser "go/parser"
+
+	"github.com/goplus/gop/ast"
+	"github.com/goplus/gop/parser"
+	"github.com/goplus/gop/x/types"
+	"github.com/goplus/gox/packages"
 )
 
 var (
-	compiler = flag.String("c", "source", `compiler to use: source, gc or gccgo`)
-	internal = flag.Bool("i", false, "print internal declarations")
+	verboseFlag = flag.Bool("v", false, "print verbose information")
+	internal    = flag.Bool("i", false, "print internal declarations")
 )
 
 func usage() {
@@ -44,78 +59,67 @@ func isPublic(name string) bool {
 	return false
 }
 
-var (
-	goModCache string
-)
-
 func main() {
 	flag.Parse()
 	if flag.NArg() < 1 {
 		usage()
 		return
 	}
-	goModCache = initGoModCache()
-
-	var fset = token.NewFileSet()
-	var files []*ast.File
-	var imp types.Importer
-	switch *compiler {
-	case "gc":
-		packages := make(map[string]*types.Package)
-		imp = gcexportdata.NewImporter(fset, packages)
-	case "source":
-		imp = importer.ForCompiler(fset, "source", nil)
-	default:
-		log.Panicln("Unsupport compiler:", *compiler)
+	verbose := *verboseFlag
+	if verbose {
+		log.Println("==> Parsing ...")
 	}
 
+	var fset = token.NewFileSet()
+	var in *ast.Package
 	if infile := flag.Arg(0); isDir(infile) {
-		pkgs, first := gopp.ParseDir(fset, infile, func(fi fs.FileInfo) bool {
-			name := fi.Name()
-			if strings.HasPrefix(name, "_") {
-				return false
-			}
-			return !strings.HasSuffix(strings.TrimSuffix(name, filepath.Ext(name)), "_test")
-		}, 0)
+		pkgs, first := parser.ParseDir(fset, infile, nil, 0)
 		check(first)
 		for name, pkg := range pkgs {
 			if !strings.HasSuffix(name, "_test") {
-				for _, f := range pkg.Files {
-					files = append(files, togo.ASTFile(f, 0))
-				}
+				in = pkg
 				break
 			}
 		}
 	} else {
+		in = &ast.Package{
+			Files:   make(map[string]*ast.File),
+			GoFiles: make(map[string]*goast.File),
+		}
 		for i, n := 0, flag.NArg(); i < n; i++ {
 			infile = flag.Arg(i)
 			switch filepath.Ext(infile) {
 			case ".gop":
-				f, err := gopp.ParseFile(fset, infile, nil, 0)
+				f, err := parser.ParseFile(fset, infile, nil, 0)
 				check(err)
-				files = append(files, togo.ASTFile(f, 0))
+				in.Files[infile] = f
 			case ".go":
-				f, err := parser.ParseFile(fset, infile, nil, 0)
+				f, err := goparser.ParseFile(fset, infile, nil, 0)
 				check(err)
-				files = append(files, f)
+				in.GoFiles[infile] = f
 			default:
 				log.Panicln("Unknown support file:", infile)
 			}
 		}
 	}
 
+	if verbose {
+		log.Println("==> Loading ...")
+	}
 	conf := &types.Config{
-		Importer:         imp,
+		Importer:         packages.NewImporter(fset),
 		IgnoreFuncBodies: true,
 		Error: func(err error) {
 			log.Println(err)
 		},
 		DisableUnusedImportCheck: true,
 	}
-
-	pkg, err := conf.Check("", fset, files, nil)
+	pkg, err := types.Load(fset, in, conf)
 	check(err)
 
+	if verbose {
+		log.Println("==> Printing ...")
+	}
 	scope := pkg.Scope()
 	names := scope.Names()
 	for _, name := range names {
diff --git a/parser/interface.go b/parser/interface.go
index c4bf7c9e..a0fd6bff 100644
--- a/parser/interface.go
+++ b/parser/interface.go
@@ -42,9 +42,8 @@ const (
 	DeclarationErrors
 	// AllErrors - report all errors (not just the first 10 on different lines)
 	AllErrors
-	// ParseGoFilesByGo - parse Go files by go/parser, not gop/parser
-	// Result will save to gop/ast.Package.GoFiles
-	ParseGoFilesByGo
+	// ParseGoAsGoPlus - parse Go files by gop/parser
+	ParseGoAsGoPlus
 )
 
 // ParseFile parses the source code of a single Go source file and returns
diff --git a/parser/parser_gop.go b/parser/parser_gop.go
index 1bb39699..5b80f460 100644
--- a/parser/parser_gop.go
+++ b/parser/parser_gop.go
@@ -151,7 +151,7 @@ func ParseFSDir(fset *token.FileSet, fs FileSystem, path string, conf Config) (p
 			if strings.HasPrefix(fname, "gop_autogen") {
 				continue
 			}
-			useGoParser = (conf.Mode & ParseGoFilesByGo) != 0
+			useGoParser = (conf.Mode & ParseGoAsGoPlus) == 0
 		default:
 			if isProj, isClass = conf.IsClass(ext); !isClass {
 				continue
@@ -161,7 +161,7 @@ func ParseFSDir(fset *token.FileSet, fs FileSystem, path string, conf Config) (p
 			filename := fs.Join(path, fname)
 			if useGoParser {
 				if filedata, err := fs.ReadFile(filename); err == nil {
-					if src, err := goparser.ParseFile(fset, filename, filedata, goparser.Mode(conf.Mode & ^ParseGoFilesByGo)); err == nil {
+					if src, err := goparser.ParseFile(fset, filename, filedata, goparser.Mode(conf.Mode)); err == nil {
 						pkg := reqPkg(pkgs, src.Name.Name)
 						if pkg.GoFiles == nil {
 							pkg.GoFiles = make(map[string]*goast.File)
diff --git a/x/types/load.go b/x/types/load.go
new file mode 100644
index 00000000..69a5c4fb
--- /dev/null
+++ b/x/types/load.go
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2022 The GoPlus Authors (goplus.org). All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package types
+
+import (
+	"go/token"
+	"go/types"
+	"strings"
+
+	goast "go/ast"
+
+	"github.com/goplus/gop/ast"
+	"github.com/goplus/gop/ast/togo"
+)
+
+// A Config specifies the configuration for type checking.
+// The zero value for Config is a ready-to-use default configuration.
+type Config = types.Config
+
+// A Package describes a Go/Go+ package.
+type Package = types.Package
+
+// Load loads a package and returns the resulting package object and
+// the first error if any.
+//
+// The package is marked as complete if no errors occurred, otherwise it is
+// incomplete. See Config.Error for controlling behavior in the presence of
+// errors.
+func Load(fset *token.FileSet, in *ast.Package, conf *Config) (pkg *Package, err error) {
+	n := len(in.Files) + len(in.GoFiles)
+	files := make([]*goast.File, 0, n)
+	for filename, f := range in.Files {
+		if !strings.HasSuffix(filename, "_test.gop") {
+			files = append(files, togo.ASTFile(f, 0))
+		}
+	}
+	for filename, f := range in.GoFiles {
+		if !strings.HasSuffix(filename, "_test.go") {
+			files = append(files, f)
+		}
+	}
+	return conf.Check(in.Name, fset, files, nil)
+}


Test patch:
diff --git a/parser/parserdir_test.go b/parser/parserdir_test.go
index 503cb29c..9fb0aa53 100644
--- a/parser/parserdir_test.go
+++ b/parser/parserdir_test.go
@@ -90,7 +90,7 @@ func testFrom(t *testing.T, pkgDir, sel string, exclude Mode) {
 	}
 	log.Println("Parsing", pkgDir)
 	fset := token.NewFileSet()
-	pkgs, err := ParseDir(fset, pkgDir, nil, (Trace|ParseComments)&^exclude)
+	pkgs, err := ParseDir(fset, pkgDir, nil, (Trace|ParseComments|ParseGoAsGoPlus)&^exclude)
 	if err != nil || len(pkgs) != 1 {
 		if errs, ok := err.(scanner.ErrorList); ok {
 			for _, e := range errs {
@@ -111,7 +111,7 @@ func testFrom(t *testing.T, pkgDir, sel string, exclude Mode) {
 
 func TestParseGo(t *testing.T) {
 	fset := token.NewFileSet()
-	pkgs, err := ParseDirEx(fset, "./_testdata/functype", Config{Mode: Trace})
+	pkgs, err := ParseDirEx(fset, "./_testdata/functype", Config{Mode: Trace | ParseGoAsGoPlus})
 	if err != nil {
 		t.Fatal("TestParseGo: ParseDir failed -", err)
 	}
@@ -122,7 +122,7 @@ func TestParseGo(t *testing.T) {
 
 func TestParseGoFiles(t *testing.T) {
 	fset := token.NewFileSet()
-	pkgs, err := ParseFiles(fset, []string{"./_testdata/functype/functype.go"}, Trace)
+	pkgs, err := ParseFiles(fset, []string{"./_testdata/functype/functype.go"}, Trace|ParseGoAsGoPlus)
 	if err != nil {
 		t.Fatal("TestParseGoFiles: ParseFiles failed -", err)
 	}
@@ -146,7 +146,7 @@ func TestGopAutoGen(t *testing.T) {
 func TestGoFile(t *testing.T) {
 	fset := token.NewFileSet()
 	fs := parsertest.NewSingleFileFS("/foo", "test.go", `package foo`)
-	pkgs, err := ParseFSDir(fset, fs, "/foo", Config{Mode: ParseGoFilesByGo})
+	pkgs, err := ParseFSDir(fset, fs, "/foo", Config{})
 	if err != nil {
 		t.Fatal("ParseFSDir:", err)
 	}
@@ -158,19 +158,19 @@ func TestGoFile(t *testing.T) {
 func TestErrParse(t *testing.T) {
 	fset := token.NewFileSet()
 	fs := parsertest.NewSingleFileFS("/foo", "test.go", `package foo bar`)
-	_, err := ParseFSDir(fset, fs, "/foo", Config{Mode: ParseGoFilesByGo})
+	_, err := ParseFSDir(fset, fs, "/foo", Config{})
 	if err == nil {
 		t.Fatal("ParseFSDir test.go: no error?")
 	}
 
 	fs = parsertest.NewSingleFileFS("/foo", "test.gop", `package foo bar`)
-	_, err = ParseFSDir(fset, fs, "/foo", Config{Mode: ParseGoFilesByGo})
+	_, err = ParseFSDir(fset, fs, "/foo", Config{})
 	if err == nil {
 		t.Fatal("ParseFSDir test.gop: no error?")
 	}
 
 	fs = parsertest.NewMemFS(map[string][]string{"/foo": {"test.go"}}, map[string]string{})
-	_, err = ParseFSDir(fset, fs, "/foo", Config{Mode: ParseGoFilesByGo})
+	_, err = ParseFSDir(fset, fs, "/foo", Config{})
 	if err != syscall.ENOENT {
 		t.Fatal("ParseFSDir:", err)
 	}