Repository: goplus/gop
Pull request #1172 (2022-05-16 16:30:28)

ParseFSDir: support mode = ParseGoFilesByGo


Patch:
diff --git a/parser/interface.go b/parser/interface.go
index be5e4a8c..c4bf7c9e 100644
--- a/parser/interface.go
+++ b/parser/interface.go
@@ -42,6 +42,9 @@ 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
 )
 
 // 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 4273085f..1bb39699 100644
--- a/parser/parser_gop.go
+++ b/parser/parser_gop.go
@@ -26,6 +26,9 @@ import (
 	"path/filepath"
 	"strings"
 
+	goast "go/ast"
+	goparser "go/parser"
+
 	"github.com/goplus/gop/ast"
 	"github.com/goplus/gop/token"
 )
@@ -141,13 +144,14 @@ func ParseFSDir(fset *token.FileSet, fs FileSystem, path string, conf Config) (p
 		}
 		fname := d.Name()
 		ext := filepath.Ext(fname)
-		var isProj, isClass bool
+		var isProj, isClass, useGoParser bool
 		switch ext {
 		case ".gop":
 		case ".go":
 			if strings.HasPrefix(fname, "gop_autogen") {
 				continue
 			}
+			useGoParser = (conf.Mode & ParseGoFilesByGo) != 0
 		default:
 			if isProj, isClass = conf.IsClass(ext); !isClass {
 				continue
@@ -155,22 +159,24 @@ func ParseFSDir(fset *token.FileSet, fs FileSystem, path string, conf Config) (p
 		}
 		if !strings.HasPrefix(fname, "_") && (conf.Filter == nil || conf.Filter(d)) {
 			filename := fs.Join(path, fname)
-			if filedata, err := fs.ReadFile(filename); err == nil {
-				if src, err := ParseFSFile(fset, fs, filename, filedata, conf.Mode); err == nil {
-					src.IsProj, src.IsClass = isProj, isClass
-					name := src.Name.Name
-					pkg, found := pkgs[name]
-					if !found {
-						pkg = &ast.Package{
-							Name:  name,
-							Files: make(map[string]*ast.File),
+			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 {
+						pkg := reqPkg(pkgs, src.Name.Name)
+						if pkg.GoFiles == nil {
+							pkg.GoFiles = make(map[string]*goast.File)
 						}
-						pkgs[name] = pkg
+						pkg.GoFiles[filename] = src
+					} else {
+						first = err
 					}
-					pkg.Files[filename] = src
-				} else if first == nil {
+				} else {
 					first = err
 				}
+			} else if src, err := ParseFSFile(fset, fs, filename, nil, conf.Mode); err == nil {
+				src.IsProj, src.IsClass = isProj, isClass
+				pkg := reqPkg(pkgs, src.Name.Name)
+				pkg.Files[filename] = src
 			} else if first == nil {
 				first = err
 			}
@@ -179,6 +185,18 @@ func ParseFSDir(fset *token.FileSet, fs FileSystem, path string, conf Config) (p
 	return
 }
 
+func reqPkg(pkgs map[string]*ast.Package, name string) *ast.Package {
+	pkg, found := pkgs[name]
+	if !found {
+		pkg = &ast.Package{
+			Name:  name,
+			Files: make(map[string]*ast.File),
+		}
+		pkgs[name] = pkg
+	}
+	return pkg
+}
+
 func defaultIsClass(ext string) (isProj bool, ok bool) {
 	switch ext {
 	case ".gmx":


Test patch:
diff --git a/parser/parserdir_test.go b/parser/parserdir_test.go
index 4875d4e8..503cb29c 100644
--- a/parser/parserdir_test.go
+++ b/parser/parserdir_test.go
@@ -23,6 +23,7 @@ import (
 	"path"
 	"reflect"
 	"strings"
+	"syscall"
 	"testing"
 
 	"github.com/qiniu/x/log"
@@ -130,6 +131,51 @@ func TestParseGoFiles(t *testing.T) {
 	}
 }
 
+func TestGopAutoGen(t *testing.T) {
+	fset := token.NewFileSet()
+	fs := parsertest.NewSingleFileFS("/foo", "gop_autogen.go", ``)
+	pkgs, err := ParseFSDir(fset, fs, "/foo", Config{})
+	if err != nil {
+		t.Fatal("ParseFSDir:", err)
+	}
+	if len(pkgs) != 0 {
+		t.Fatal("TestGopAutoGen:", len(pkgs))
+	}
+}
+
+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})
+	if err != nil {
+		t.Fatal("ParseFSDir:", err)
+	}
+	if len(pkgs) != 1 || pkgs["foo"].GoFiles["/foo/test.go"] == nil {
+		t.Fatal("TestGoFile:", len(pkgs))
+	}
+}
+
+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})
+	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})
+	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})
+	if err != syscall.ENOENT {
+		t.Fatal("ParseFSDir:", err)
+	}
+}
+
 func TestFromTestdata(t *testing.T) {
 	sel := ""
 	dir, err := os.Getwd()