From 2e12eab91474703daa54ae2bc1aeae07f1e5e7a8 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Thu, 13 Mar 2025 12:36:56 +0900 Subject: [PATCH 1/3] test --- gnovm/pkg/gnolang/gotypecheck_test.go | 392 ++++++++++++++++++++++++++ 1 file changed, 392 insertions(+) diff --git a/gnovm/pkg/gnolang/gotypecheck_test.go b/gnovm/pkg/gnolang/gotypecheck_test.go index 259a1bc3e78..81bf28c91de 100644 --- a/gnovm/pkg/gnolang/gotypecheck_test.go +++ b/gnovm/pkg/gnolang/gotypecheck_test.go @@ -357,3 +357,395 @@ func Hello(name string) string { assert.NotEqual(t, input, pkg.Files[0].Body) assert.Equal(t, expected, pkg.Files[0].Body) } + +func TestTypeCheckMemPackage_RealmImports(t *testing.T) { + t.Parallel() + + type testCase struct { + name string + pkg *gnovm.MemPackage + getter MemPackageGetter + check func(*testing.T, error) + } + + tests := []testCase{ + { + name: "realm package with init-only imports", + pkg: &gnovm.MemPackage{ + Name: "gns", + Path: "gno.land/r/demo/gns", + Files: []*gnovm.MemFile{ + { + Name: "gns.gno", + Body: ` + package gns + import ( + "std" + "gno.land/r/demo/registry" + ) + + var ( + adminAddr std.Address + ) + + func init() { + registry.Register("gns") + } + + func GetAdmin() string { + return string(adminAddr) + }`, + }, + }, + }, + getter: mockPackageGetter{ + &gnovm.MemPackage{ + Name: "std", + Path: "std", + Files: []*gnovm.MemFile{ + { + Name: "std.gno", + Body: ` + package std + type Address string`, + }, + }, + }, + &gnovm.MemPackage{ + Name: "registry", + Path: "gno.land/r/demo/registry", + Files: []*gnovm.MemFile{ + { + Name: "registry.gno", + Body: ` + package registry + func Register(name string) {}`, + }, + }, + }, + }, + check: func(t *testing.T, err error) { + t.Helper() + require.NoError(t, err, "should not report unused imports in realm packages") + }, + }, + { + name: "realm package with cross-realm imports", + pkg: &gnovm.MemPackage{ + Name: "gns", + Path: "gno.land/r/demo/gns", + Files: []*gnovm.MemFile{ + { + Name: "gns.gno", + Body: ` + package gns + import ( + "gno.land/r/demo/token" + "gno.land/r/demo/registry" + ) + + func init() { + registry.Register() + } + + func Transfer(t token.Token) { + t.Transfer() + }`, + }, + }, + }, + getter: mockPackageGetter{ + &gnovm.MemPackage{ + Name: "token", + Path: "gno.land/r/demo/token", + Files: []*gnovm.MemFile{ + { + Name: "token.gno", + Body: ` + package token + type Token interface { + Transfer() + }`, + }, + }, + }, + &gnovm.MemPackage{ + Name: "registry", + Path: "gno.land/r/demo/registry", + Files: []*gnovm.MemFile{ + { + Name: "registry.gno", + Body: ` + package registry + func Register() {}`, + }, + }, + }, + }, + check: func(t *testing.T, err error) { + t.Helper() + require.NoError(t, err, "should handle cross-realm imports correctly") + }, + }, + { + name: "debug realm package import scope", + pkg: &gnovm.MemPackage{ + Name: "gns", + Path: "gno.land/r/demo/gns", + Files: []*gnovm.MemFile{ + { + Name: "gns.gno", + Body: ` + package gns + import "gno.land/r/demo/registry" + + func init() { + registry.Register("test") + }`, + }, + }, + }, + getter: &debugPackageGetter{ + mockPackageGetter: mockPackageGetter{ + &gnovm.MemPackage{ + Name: "registry", + Path: "gno.land/r/demo/registry", + Files: []*gnovm.MemFile{ + { + Name: "registry.gno", + Body: ` + package registry + func Register(name string) {}`, + }, + }, + }, + }, + }, + check: func(t *testing.T, err error) { + t.Helper() + require.NoError(t, err) + }, + }, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + err := TypeCheckMemPackage(tc.pkg, tc.getter, false) + if tc.check != nil { + tc.check(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestTypeCheckMemPackage_InitAndCallbacks(t *testing.T) { + t.Parallel() + + type testCase struct { + name string + pkg *gnovm.MemPackage + getter MemPackageGetter + check func(*testing.T, error) + } + + tests := []testCase{ + { + name: "callback registration in init", + pkg: &gnovm.MemPackage{ + Name: "callback", + Path: "gno.land/r/demo/callback", + Files: []*gnovm.MemFile{ + { + Name: "callback.gno", + Body: ` + package callback + import "gno.land/r/demo/events" + + func MintCallback(amount uint64) { + // will be called at runtime + } + + func init() { + events.RegisterCallback("mint", MintCallback) + }`, + }, + }, + }, + getter: mockPackageGetter{ + &gnovm.MemPackage{ + Name: "events", + Path: "gno.land/r/demo/events", + Files: []*gnovm.MemFile{ + { + Name: "events.gno", + Body: ` + package events + + type CallbackFn func(uint64) + + func RegisterCallback(event string, fn CallbackFn) {}`, + }, + }, + }, + }, + check: func(t *testing.T, err error) { + t.Helper() + require.NoError(t, err, "callback registration should be recognized as package usage") + }, + }, + { + name: "multiple aliases with init usage", + pkg: &gnovm.MemPackage{ + Name: "main", + Path: "gno.land/r/demo/main", + Files: []*gnovm.MemFile{ + { + Name: "main.gno", + Body: ` + package main + + import ( + ev "gno.land/r/demo/events" + cb "gno.land/r/demo/callback" + ) + + func ProcessEvent(amount uint64) { + // will be called at runtime + } + + func init() { + ev.RegisterCallback("process", ProcessEvent) + cb.SetHandler(ProcessEvent) + }`, + }, + }, + }, + getter: mockPackageGetter{ + &gnovm.MemPackage{ + Name: "events", + Path: "gno.land/r/demo/events", + Files: []*gnovm.MemFile{ + { + Name: "events.gno", + Body: ` + package events + type CallbackFn func(uint64) + func RegisterCallback(event string, fn CallbackFn) {}`, + }, + }, + }, + &gnovm.MemPackage{ + Name: "callback", + Path: "gno.land/r/demo/callback", + Files: []*gnovm.MemFile{ + { + Name: "callback.gno", + Body: ` + package callback + type HandlerFn func(uint64) + func SetHandler(fn HandlerFn) {}`, + }, + }, + }, + }, + check: func(t *testing.T, err error) { + t.Helper() + require.NoError(t, err, "aliased imports in init should be recognized") + }, + }, + { + name: "init with deferred callback setup", + pkg: &gnovm.MemPackage{ + Name: "deferred", + Path: "gno.land/r/demo/deferred", + Files: []*gnovm.MemFile{ + { + Name: "deferred.gno", + Body: ` + package deferred + + import handler "gno.land/r/demo/handler" + + var setupCallback func() + + func init() { + setupCallback = func() { + handler.Register("deferred", processEvent) + } + setupCallback() + } + + func processEvent() {}`, + }, + }, + }, + getter: mockPackageGetter{ + &gnovm.MemPackage{ + Name: "handler", + Path: "gno.land/r/demo/handler", + Files: []*gnovm.MemFile{ + { + Name: "handler.gno", + Body: ` + package handler + func Register(name string, fn func()) {}`, + }, + }, + }, + }, + check: func(t *testing.T, err error) { + t.Helper() + require.NoError(t, err, "deferred callback setup should be recognized") + }, + }, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + debugGetter := &debugPackageGetter{ + mockPackageGetter: tc.getter.(mockPackageGetter), + } + + err := TypeCheckMemPackage(tc.pkg, debugGetter, false) + + t.Logf("Imported packages: %v", debugGetter.GetImportedPackages()) + + if tc.check != nil { + tc.check(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +type debugPackageGetter struct { + mockPackageGetter + importedPkgs map[string]bool +} + +func (d *debugPackageGetter) GetMemPackage(path string) *gnovm.MemPackage { + if d.importedPkgs == nil { + d.importedPkgs = make(map[string]bool) + } + pkg := d.mockPackageGetter.GetMemPackage(path) + if pkg != nil { + d.importedPkgs[path] = true + } + return pkg +} + +func (d *debugPackageGetter) GetImportedPackages() []string { + var pkgs []string + for pkg := range d.importedPkgs { + pkgs = append(pkgs, pkg) + } + return pkgs +} From 1773c3b592de0665ca5ac72a1fca67138de58b3a Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Tue, 18 Mar 2025 17:58:18 +0900 Subject: [PATCH 2/3] disable check --- gnovm/pkg/gnolang/gotypecheck.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gnovm/pkg/gnolang/gotypecheck.go b/gnovm/pkg/gnolang/gotypecheck.go index 8f86deb3dc5..90501431d88 100644 --- a/gnovm/pkg/gnolang/gotypecheck.go +++ b/gnovm/pkg/gnolang/gotypecheck.go @@ -51,6 +51,9 @@ func typeCheckMemPackage(mempkg *gnovm.MemPackage, getter MemPackageGetter, test Error: func(err error) { errs = multierr.Append(errs, err) }, + // TODO: tempory solution to avoid false-positive unused import check + // TODO: remove this once the root cause is found + DisableUnusedImportCheck: true, }, allowRedefinitions: testing, } From d4582318082dfa88eada8b8e034873a6ec0eedf2 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Tue, 18 Mar 2025 18:13:50 +0900 Subject: [PATCH 3/3] lint --- gnovm/pkg/gnolang/gotypecheck_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnovm/pkg/gnolang/gotypecheck_test.go b/gnovm/pkg/gnolang/gotypecheck_test.go index 81bf28c91de..c4f2fcd0923 100644 --- a/gnovm/pkg/gnolang/gotypecheck_test.go +++ b/gnovm/pkg/gnolang/gotypecheck_test.go @@ -743,7 +743,7 @@ func (d *debugPackageGetter) GetMemPackage(path string) *gnovm.MemPackage { } func (d *debugPackageGetter) GetImportedPackages() []string { - var pkgs []string + pkgs := make([]string, 0, len(d.importedPkgs)) for pkg := range d.importedPkgs { pkgs = append(pkgs, pkg) }