Skip to content
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

fix: Disable unused import check to prevent false positives in init function #3929

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
test
notJoon committed Mar 13, 2025
commit 2e12eab91474703daa54ae2bc1aeae07f1e5e7a8
392 changes: 392 additions & 0 deletions gnovm/pkg/gnolang/gotypecheck_test.go
Original file line number Diff line number Diff line change
@@ -357,3 +357,395 @@
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

Check failure on line 746 in gnovm/pkg/gnolang/gotypecheck_test.go

GitHub Actions / Run GnoVM suite / Go Lint / lint

Consider pre-allocating `pkgs` (prealloc)
for pkg := range d.importedPkgs {
pkgs = append(pkgs, pkg)
}
return pkgs
}