-
Notifications
You must be signed in to change notification settings - Fork 385
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(gnovm): categorize imports #3323
base: master
Are you sure you want to change the base?
Changes from 15 commits
6c5d83e
d05c43b
702e3d9
1d17b9f
c4d82dc
27bb085
3e04787
a0dd0d8
d2d043e
efda22d
fcd66a5
37d96ce
e591002
315b37f
cd91edb
c0998d7
41ddaa6
4cfda1e
2100a57
0be8c69
1fcf4e3
0ebf4f5
0490b9e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,47 @@ | ||||||
package packages | ||||||
|
||||||
import ( | ||||||
"fmt" | ||||||
"go/parser" | ||||||
"go/token" | ||||||
"strings" | ||||||
) | ||||||
|
||||||
type FileKind uint | ||||||
|
||||||
const ( | ||||||
FileKindUnknown = FileKind(iota) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||
FileKindCompiled | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||
FileKindTest | ||||||
FileKindXtest | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Go uses XTest: https://pkg.go.dev/go/build There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||
FileKindFiletest | ||||||
) | ||||||
|
||||||
// GetFileKind analyzes a file's name and body to get it's [FileKind], fset is optional | ||||||
func GetFileKind(filename string, body string, fset *token.FileSet) (FileKind, error) { | ||||||
if !strings.HasSuffix(filename, ".gno") { | ||||||
return FileKindUnknown, fmt.Errorf("%s:1:1: not a gno file", filename) | ||||||
} | ||||||
|
||||||
if strings.HasSuffix(filename, "_filetest.gno") { | ||||||
return FileKindFiletest, nil | ||||||
} | ||||||
|
||||||
if !strings.HasSuffix(filename, "_test.gno") { | ||||||
return FileKindCompiled, nil | ||||||
} | ||||||
|
||||||
if fset == nil { | ||||||
fset = token.NewFileSet() | ||||||
} | ||||||
ast, err := parser.ParseFile(fset, filename, body, parser.PackageClauseOnly) | ||||||
if err != nil { | ||||||
return FileKindUnknown, err | ||||||
} | ||||||
packageName := ast.Name.Name | ||||||
|
||||||
if strings.HasSuffix(packageName, "_test") { | ||||||
return FileKindXtest, nil | ||||||
} | ||||||
return FileKindTest, nil | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package packages_test | ||
|
||
import ( | ||
"testing" | ||
|
||
. "github.com/gnolang/gno/gnovm/pkg/packages" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestGetFileKind(t *testing.T) { | ||
tcs := []struct { | ||
name string | ||
filename string | ||
body string | ||
fileKind FileKind | ||
errorContains string | ||
}{ | ||
{ | ||
name: "compiled", | ||
filename: "foo.gno", | ||
fileKind: FileKindCompiled, | ||
}, | ||
{ | ||
name: "test", | ||
filename: "foo_test.gno", | ||
body: "package foo", | ||
fileKind: FileKindTest, | ||
}, | ||
{ | ||
name: "xtest", | ||
filename: "foo_test.gno", | ||
body: "package foo_test", | ||
fileKind: FileKindXtest, | ||
}, | ||
{ | ||
name: "filetest", | ||
filename: "foo_filetest.gno", | ||
fileKind: FileKindFiletest, | ||
}, | ||
{ | ||
name: "err_badpkgclause", | ||
filename: "foo_test.gno", | ||
body: "pakage foo", | ||
errorContains: "foo_test.gno:1:1: expected 'package', found pakage", | ||
}, | ||
{ | ||
name: "err_notgnofile", | ||
filename: "foo.gno.bck", | ||
errorContains: `foo.gno.bck:1:1: not a gno file`, | ||
}, | ||
} | ||
|
||
for _, tc := range tcs { | ||
t.Run(tc.name, func(t *testing.T) { | ||
out, err := GetFileKind(tc.filename, tc.body, nil) | ||
if len(tc.errorContains) != 0 { | ||
require.ErrorContains(t, err, tc.errorContains) | ||
} else { | ||
require.NoError(t, err) | ||
} | ||
require.Equal(t, tc.fileKind, out) | ||
}) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,34 +13,40 @@ | |
) | ||
|
||
// Imports returns the list of gno imports from a [gnovm.MemPackage]. | ||
// fset is optional. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would still keep it as a comment There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. my bad, conflict resolution fail: 41ddaa6 |
||
func Imports(pkg *gnovm.MemPackage, fset *token.FileSet) ([]FileImport, error) { | ||
allImports := make([]FileImport, 0, 16) | ||
seen := make(map[string]struct{}, 16) | ||
func Imports(pkg *gnovm.MemPackage, fset *token.FileSet) (ImportsMap, error) { | ||
res := make(ImportsMap, 16) | ||
seen := make(map[FileKind]map[string]struct{}, 16) | ||
|
||
for _, file := range pkg.Files { | ||
if !strings.HasSuffix(file.Name, ".gno") { | ||
continue | ||
} | ||
if strings.HasSuffix(file.Name, "_filetest.gno") { | ||
continue | ||
|
||
fileKind, err := GetFileKind(file.Name, file.Body, fset) | ||
if err != nil { | ||
return nil, err | ||
} | ||
imports, err := FileImports(file.Name, file.Body, fset) | ||
if err != nil { | ||
return nil, err | ||
} | ||
for _, im := range imports { | ||
if _, ok := seen[im.PkgPath]; ok { | ||
if _, ok := seen[fileKind][im.PkgPath]; ok { | ||
continue | ||
} | ||
allImports = append(allImports, im) | ||
seen[im.PkgPath] = struct{}{} | ||
res[fileKind] = append(res[fileKind], im) | ||
if _, ok := seen[fileKind]; !ok { | ||
seen[fileKind] = make(map[string]struct{}, 16) | ||
} | ||
seen[fileKind][im.PkgPath] = struct{}{} | ||
} | ||
} | ||
sort.Slice(allImports, func(i, j int) bool { | ||
return allImports[i].PkgPath < allImports[j].PkgPath | ||
}) | ||
|
||
return allImports, nil | ||
for _, imports := range res { | ||
sortImports(imports) | ||
} | ||
|
||
return res, nil | ||
} | ||
|
||
// FileImport represents an import | ||
|
@@ -75,3 +81,31 @@ | |
} | ||
return res, nil | ||
} | ||
|
||
type ImportsMap map[FileKind][]FileImport | ||
|
||
// Merge merges imports, it removes duplicates and sorts the result | ||
func (imap ImportsMap) Merge(kinds ...FileKind) []FileImport { | ||
res := make([]FileImport, 0, 16) | ||
seen := make(map[string]struct{}, 16) | ||
|
||
for _, kind := range kinds { | ||
for _, im := range imap[kind] { | ||
if _, ok := seen[im.PkgPath]; ok { | ||
continue | ||
} | ||
seen[im.PkgPath] = struct{}{} | ||
|
||
res = append(res, im) | ||
} | ||
} | ||
|
||
sortImports(res) | ||
return res | ||
} | ||
|
||
func sortImports(imports []FileImport) { | ||
sort.Slice(imports, func(i, j int) bool { | ||
return imports[i].PkgPath < imports[j].PkgPath | ||
}) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,12 @@ | ||
package packages | ||
package packages_test | ||
|
||
import ( | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/gnolang/gno/gnovm/pkg/gnolang" | ||
. "github.com/gnolang/gno/gnovm/pkg/packages" | ||
"github.com/stretchr/testify/require" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I saw in the go standard libraries sources that they are using dot import for the current package in external tests, thus thought it was the idiomatic go way. I can use a normal import if you prefer |
||
) | ||
|
||
|
@@ -58,13 +59,26 @@ func TestImports(t *testing.T) { | |
) | ||
`, | ||
}, | ||
{ | ||
name: "file2_test.gno", | ||
data: ` | ||
package tmp_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"gno.land/p/demo/testpkg" | ||
"gno.land/p/demo/xtestdep" | ||
) | ||
`, | ||
}, | ||
{ | ||
name: "z_0_filetest.gno", | ||
data: ` | ||
package main | ||
|
||
import ( | ||
"gno.land/p/demo/filetestpkg" | ||
"gno.land/p/demo/filetestdep" | ||
) | ||
`, | ||
}, | ||
|
@@ -95,17 +109,28 @@ func TestImports(t *testing.T) { | |
}, | ||
} | ||
|
||
// Expected list of imports | ||
// Expected lists of imports | ||
// - ignore subdirs | ||
// - ignore duplicate | ||
// - ignore *_filetest.gno | ||
// - should be sorted | ||
expected := []string{ | ||
"gno.land/p/demo/pkg1", | ||
"gno.land/p/demo/pkg2", | ||
"gno.land/p/demo/testpkg", | ||
"std", | ||
"testing", | ||
expected := map[FileKind][]string{ | ||
FileKindCompiled: { | ||
"gno.land/p/demo/pkg1", | ||
"gno.land/p/demo/pkg2", | ||
"std", | ||
}, | ||
FileKindTest: { | ||
"gno.land/p/demo/testpkg", | ||
"testing", | ||
}, | ||
FileKindXtest: { | ||
"gno.land/p/demo/testpkg", | ||
"gno.land/p/demo/xtestdep", | ||
"testing", | ||
}, | ||
FileKindFiletest: { | ||
"gno.land/p/demo/filetestdep", | ||
}, | ||
} | ||
|
||
// Create subpkg dir | ||
|
@@ -120,12 +145,19 @@ func TestImports(t *testing.T) { | |
|
||
pkg, err := gnolang.ReadMemPackage(tmpDir, "test") | ||
require.NoError(t, err) | ||
imports, err := Imports(pkg, nil) | ||
|
||
importsMap, err := Imports(pkg, nil) | ||
require.NoError(t, err) | ||
importsStrings := make([]string, len(imports)) | ||
for idx, imp := range imports { | ||
importsStrings[idx] = imp.PkgPath | ||
|
||
// ignore specs | ||
got := map[FileKind][]string{} | ||
for key, vals := range importsMap { | ||
stringVals := make([]string, len(vals)) | ||
for i, val := range vals { | ||
stringVals[i] = val.PkgPath | ||
} | ||
got[key] = stringVals | ||
} | ||
|
||
require.Equal(t, expected, importsStrings) | ||
require.Equal(t, expected, got) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add godoc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
0be8c69