github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/compiler/compiler_test.go (about)

     1  package compiler_test
     2  
     3  import (
     4  	"fmt"
     5  	"math/big"
     6  	"os"
     7  	"path/filepath"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/nspcc-dev/neo-go/internal/random"
    12  	"github.com/nspcc-dev/neo-go/internal/versionutil"
    13  	"github.com/nspcc-dev/neo-go/pkg/compiler"
    14  	"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
    15  	"github.com/nspcc-dev/neo-go/pkg/interop/native/neo"
    16  	"github.com/nspcc-dev/neo-go/pkg/smartcontract"
    17  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
    18  	"github.com/nspcc-dev/neo-go/pkg/util"
    19  	"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
    20  	"github.com/stretchr/testify/require"
    21  )
    22  
    23  const examplePath = "../../examples"
    24  const exampleCompilePath = "testdata/compile"
    25  const exampleSavePath = exampleCompilePath + "/save"
    26  
    27  // Keep contract NEFs consistent between runs.
    28  const _ = versionutil.TestVersion
    29  
    30  type compilerTestCase struct {
    31  	name     string
    32  	function func(*testing.T)
    33  }
    34  
    35  func TestCompiler(t *testing.T) {
    36  	testCases := []compilerTestCase{
    37  		{
    38  			name: "TestCompileDirectory",
    39  			function: func(t *testing.T) {
    40  				const multiMainDir = "testdata/multi"
    41  				_, di, err := compiler.CompileWithOptions(multiMainDir, nil, nil)
    42  				require.NoError(t, err)
    43  				m := map[string]bool{}
    44  				for i := range di.Methods {
    45  					m[di.Methods[i].ID] = true
    46  				}
    47  				require.Contains(t, m, "Func1")
    48  				require.Contains(t, m, "Func2")
    49  			},
    50  		},
    51  		{
    52  			name: "TestCompile",
    53  			function: func(t *testing.T) {
    54  				infos, err := os.ReadDir(examplePath)
    55  				require.NoError(t, err)
    56  				for _, info := range infos {
    57  					if !info.IsDir() {
    58  						// example smart contracts are located in the `examplePath` subdirectories, but
    59  						// there is also a couple of files inside the `examplePath` which don't need to be compiled
    60  						continue
    61  					}
    62  					if info.Name() == "zkp" {
    63  						// A set of special ZKP-related examples, they have their own tests.
    64  						continue
    65  					}
    66  
    67  					targetPath := filepath.Join(examplePath, info.Name())
    68  					require.NoError(t, compileFile(targetPath), info.Name())
    69  				}
    70  			},
    71  		},
    72  		{
    73  			name: "TestCompileAndSave",
    74  			function: func(t *testing.T) {
    75  				infos, err := os.ReadDir(exampleCompilePath)
    76  				require.NoError(t, err)
    77  				err = os.MkdirAll(exampleSavePath, os.ModePerm)
    78  				require.NoError(t, err)
    79  				t.Cleanup(func() {
    80  					err := os.RemoveAll(exampleSavePath)
    81  					require.NoError(t, err)
    82  				})
    83  				outfile := exampleSavePath + "/test.nef"
    84  				_, err = compiler.CompileAndSave(exampleCompilePath+"/"+infos[0].Name(), &compiler.Options{Outfile: outfile})
    85  				require.NoError(t, err)
    86  			},
    87  		},
    88  		{
    89  			name: "TestCompileAndSave_NEF_constraints",
    90  			function: func(t *testing.T) {
    91  				tmp := t.TempDir()
    92  				src := `package nefconstraints
    93  	var data = "%s"
    94  
    95  	func Main() string {
    96  		return data
    97  	}
    98  `
    99  				data := make([]byte, stackitem.MaxSize-10)
   100  				for i := range data {
   101  					data[i] = byte('a')
   102  				}
   103  				in := filepath.Join(tmp, "src.go")
   104  				require.NoError(t, os.WriteFile(in, []byte(fmt.Sprintf(src, data)), os.ModePerm))
   105  				out := filepath.Join(tmp, "test.nef")
   106  				_, err := compiler.CompileAndSave(in, &compiler.Options{Outfile: out})
   107  				require.Error(t, err)
   108  				require.Contains(t, err.Error(), "serialized NEF size exceeds VM stackitem limits")
   109  			},
   110  		},
   111  	}
   112  
   113  	for _, tcase := range testCases {
   114  		t.Run(tcase.name, tcase.function)
   115  	}
   116  }
   117  
   118  func compileFile(src string) error {
   119  	_, err := compiler.Compile(src, nil)
   120  	return err
   121  }
   122  
   123  func TestOnPayableChecks(t *testing.T) {
   124  	compileAndCheck := func(t *testing.T, src string) error {
   125  		_, di, err := compiler.CompileWithOptions("payable.go", strings.NewReader(src), nil)
   126  		require.NoError(t, err)
   127  		_, err = compiler.CreateManifest(di, &compiler.Options{Name: "payable"})
   128  		return err
   129  	}
   130  
   131  	t.Run("NEP-11, good", func(t *testing.T) {
   132  		src := `package payable
   133  		import "github.com/nspcc-dev/neo-go/pkg/interop"
   134  		func OnNEP11Payment(from interop.Hash160, amount int, tokenID []byte, data any) {}`
   135  		require.NoError(t, compileAndCheck(t, src))
   136  	})
   137  	t.Run("NEP-11, bad", func(t *testing.T) {
   138  		src := `package payable
   139  		import "github.com/nspcc-dev/neo-go/pkg/interop"
   140  		func OnNEP11Payment(from interop.Hash160, amount int, oldParam string, tokenID []byte, data any) {}`
   141  		require.Error(t, compileAndCheck(t, src))
   142  	})
   143  	t.Run("NEP-17, good", func(t *testing.T) {
   144  		src := `package payable
   145  		import "github.com/nspcc-dev/neo-go/pkg/interop"
   146  		func OnNEP17Payment(from interop.Hash160, amount int, data any) {}`
   147  		require.NoError(t, compileAndCheck(t, src))
   148  	})
   149  	t.Run("NEP-17, bad", func(t *testing.T) {
   150  		src := `package payable
   151  		import "github.com/nspcc-dev/neo-go/pkg/interop"
   152  		func OnNEP17Payment(from interop.Hash160, amount int, data any, extra int) {}`
   153  		require.Error(t, compileAndCheck(t, src))
   154  	})
   155  }
   156  
   157  func TestSafeMethodWarnings(t *testing.T) {
   158  	src := `package payable
   159  		func Main() int { return 1 }`
   160  
   161  	_, di, err := compiler.CompileWithOptions("eventTest.go", strings.NewReader(src),
   162  		&compiler.Options{Name: "eventTest"})
   163  	require.NoError(t, err)
   164  
   165  	_, err = compiler.CreateManifest(di, &compiler.Options{SafeMethods: []string{"main"}, Name: "eventTest"})
   166  	require.NoError(t, err)
   167  
   168  	_, err = compiler.CreateManifest(di, &compiler.Options{SafeMethods: []string{"main", "mississippi"}, Name: "eventTest"})
   169  	require.Error(t, err)
   170  }
   171  
   172  func TestEventWarnings(t *testing.T) {
   173  	src := `package payable
   174  		import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
   175  		func Main() { runtime.Notify("Event", 1) }`
   176  
   177  	_, di, err := compiler.CompileWithOptions("eventTest.go", strings.NewReader(src), nil)
   178  	require.NoError(t, err)
   179  
   180  	t.Run("event it missing from config", func(t *testing.T) {
   181  		_, err = compiler.CreateManifest(di, &compiler.Options{Name: "payable"})
   182  		require.Error(t, err)
   183  
   184  		t.Run("suppress", func(t *testing.T) {
   185  			_, err = compiler.CreateManifest(di, &compiler.Options{NoEventsCheck: true, Name: "payable"})
   186  			require.NoError(t, err)
   187  		})
   188  	})
   189  	t.Run("wrong parameter number", func(t *testing.T) {
   190  		_, err = compiler.CreateManifest(di, &compiler.Options{
   191  			ContractEvents: []compiler.HybridEvent{{Name: "Event"}},
   192  			Name:           "payable",
   193  		})
   194  		require.Error(t, err)
   195  	})
   196  	t.Run("wrong parameter type", func(t *testing.T) {
   197  		_, err = compiler.CreateManifest(di, &compiler.Options{
   198  			ContractEvents: []compiler.HybridEvent{{
   199  				Name:       "Event",
   200  				Parameters: []compiler.HybridParameter{{Parameter: manifest.NewParameter("number", smartcontract.StringType)}},
   201  			}},
   202  			Name: "payable",
   203  		})
   204  		require.Error(t, err)
   205  	})
   206  	t.Run("any parameter type", func(t *testing.T) {
   207  		_, err = compiler.CreateManifest(di, &compiler.Options{
   208  			ContractEvents: []compiler.HybridEvent{{
   209  				Name:       "Event",
   210  				Parameters: []compiler.HybridParameter{{Parameter: manifest.NewParameter("number", smartcontract.AnyType)}},
   211  			}},
   212  			Name: "payable",
   213  		})
   214  		require.NoError(t, err)
   215  	})
   216  	t.Run("good", func(t *testing.T) {
   217  		_, err = compiler.CreateManifest(di, &compiler.Options{
   218  			ContractEvents: []compiler.HybridEvent{{
   219  				Name:       "Event",
   220  				Parameters: []compiler.HybridParameter{{Parameter: manifest.NewParameter("number", smartcontract.IntegerType)}},
   221  			}},
   222  			Name: "payable",
   223  		})
   224  		require.NoError(t, err)
   225  	})
   226  	t.Run("event in imported package", func(t *testing.T) {
   227  		t.Run("unused", func(t *testing.T) {
   228  			src := `package foo
   229  			import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/notify"
   230  			func Main() int {
   231  				return notify.Value
   232  			}`
   233  
   234  			_, di, err := compiler.CompileWithOptions("eventTest.go", strings.NewReader(src), &compiler.Options{Name: "eventTest"})
   235  			require.NoError(t, err)
   236  
   237  			_, err = compiler.CreateManifest(di, &compiler.Options{NoEventsCheck: true, Name: "eventTest"})
   238  			require.NoError(t, err)
   239  		})
   240  		t.Run("used", func(t *testing.T) {
   241  			src := `package foo
   242  			import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/notify"
   243  			func Main() int {
   244  				notify.EmitEvent()
   245  				return 42
   246  			}`
   247  
   248  			_, di, err := compiler.CompileWithOptions("eventTest.go",
   249  				strings.NewReader(src), &compiler.Options{Name: "eventTest"})
   250  			require.NoError(t, err)
   251  
   252  			_, err = compiler.CreateManifest(di, &compiler.Options{Name: "eventTest"})
   253  			require.Error(t, err)
   254  
   255  			_, err = compiler.CreateManifest(di, &compiler.Options{
   256  				ContractEvents: []compiler.HybridEvent{{Name: "Event"}},
   257  				Name:           "eventTest",
   258  			})
   259  			require.NoError(t, err)
   260  		})
   261  	})
   262  	t.Run("variadic event args via ellipsis", func(t *testing.T) {
   263  		src := `package payable
   264  		import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
   265  		func Main() {
   266  			runtime.Notify("Event", []any{1}...)
   267  		}`
   268  
   269  		_, di, err := compiler.CompileWithOptions("eventTest.go", strings.NewReader(src), nil)
   270  		require.NoError(t, err)
   271  
   272  		_, err = compiler.CreateManifest(di, &compiler.Options{
   273  			Name: "eventTest",
   274  			ContractEvents: []compiler.HybridEvent{{
   275  				Name:       "Event",
   276  				Parameters: []compiler.HybridParameter{{Parameter: manifest.NewParameter("number", smartcontract.IntegerType)}},
   277  			}},
   278  		})
   279  		require.NoError(t, err)
   280  	})
   281  }
   282  
   283  func TestNotifyInVerify(t *testing.T) {
   284  	srcTmpl := `package payable
   285  		import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
   286  		func Verify() bool { runtime.%s("Event"); return true }`
   287  
   288  	for _, name := range []string{"Notify", "Log"} {
   289  		t.Run(name, func(t *testing.T) {
   290  			src := fmt.Sprintf(srcTmpl, name)
   291  			_, _, err := compiler.CompileWithOptions("eventTest.go", strings.NewReader(src),
   292  				&compiler.Options{ContractEvents: []compiler.HybridEvent{{Name: "Event"}}})
   293  			require.Error(t, err)
   294  
   295  			t.Run("suppress", func(t *testing.T) {
   296  				_, _, err := compiler.CompileWithOptions("eventTest.go", strings.NewReader(src),
   297  					&compiler.Options{NoEventsCheck: true})
   298  				require.NoError(t, err)
   299  			})
   300  		})
   301  	}
   302  }
   303  
   304  func TestInvokedContractsPermissons(t *testing.T) {
   305  	testCompile := func(t *testing.T, di *compiler.DebugInfo, disable bool, ps ...manifest.Permission) error {
   306  		o := &compiler.Options{
   307  			NoPermissionsCheck: disable,
   308  			Permissions:        ps,
   309  			Name:               "test",
   310  		}
   311  
   312  		_, err := compiler.CreateManifest(di, o)
   313  		return err
   314  	}
   315  
   316  	t.Run("native", func(t *testing.T) {
   317  		src := `package test
   318  			import "github.com/nspcc-dev/neo-go/pkg/interop/native/neo"
   319  			import "github.com/nspcc-dev/neo-go/pkg/interop/native/management"
   320  			func Main() int {
   321  				neo.Transfer(nil, nil, 10, nil)
   322  				management.GetContract(nil) // skip read-only
   323  				return 0
   324  			}`
   325  
   326  		_, di, err := compiler.CompileWithOptions("permissionTest.go", strings.NewReader(src), nil)
   327  		require.NoError(t, err)
   328  
   329  		var nh util.Uint160
   330  
   331  		p := manifest.NewPermission(manifest.PermissionHash, nh)
   332  		require.Error(t, testCompile(t, di, false, *p))
   333  		require.NoError(t, testCompile(t, di, true, *p))
   334  
   335  		copy(nh[:], neo.Hash)
   336  		p.Contract.Value = nh
   337  		require.NoError(t, testCompile(t, di, false, *p))
   338  
   339  		p.Methods.Restrict()
   340  		require.Error(t, testCompile(t, di, false, *p))
   341  		require.NoError(t, testCompile(t, di, true, *p))
   342  	})
   343  
   344  	t.Run("custom", func(t *testing.T) {
   345  		hashStr := "aaaaaaaaaaaaaaaaaaaa"
   346  		src := fmt.Sprintf(`package test
   347  			import "github.com/nspcc-dev/neo-go/pkg/interop/contract"
   348  			import "github.com/nspcc-dev/neo-go/pkg/interop"
   349  			import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/runh"
   350  
   351  			const hash = "%s"
   352  			var runtimeHash interop.Hash160
   353  			var runtimeMethod string
   354  			func invoke(h string) interop.Hash160 { return nil }
   355  			func Main() {
   356  				contract.Call(interop.Hash160(hash), "method1", contract.All)
   357  				contract.Call(interop.Hash160(hash), "method2", contract.All)
   358  				contract.Call(interop.Hash160(hash), "method2", contract.All)
   359  
   360  				// skip read-only
   361  				contract.Call(interop.Hash160(hash), "method3", contract.ReadStates)
   362  
   363  				// skip this
   364  				contract.Call(interop.Hash160(hash), runtimeMethod, contract.All)
   365  				contract.Call(runtimeHash, "someMethod", contract.All)
   366  				contract.Call(interop.Hash160(runtimeHash), "someMethod", contract.All)
   367  				contract.Call(runh.RuntimeHash(), "method4", contract.All)
   368  			}`, hashStr)
   369  
   370  		_, di, err := compiler.CompileWithOptions("permissionTest.go", strings.NewReader(src), nil)
   371  		require.NoError(t, err)
   372  
   373  		var h util.Uint160
   374  		copy(h[:], hashStr)
   375  
   376  		p := manifest.NewPermission(manifest.PermissionHash, h)
   377  		require.NoError(t, testCompile(t, di, false, *p))
   378  
   379  		p.Methods.Add("method1")
   380  		require.Error(t, testCompile(t, di, false, *p))
   381  		require.NoError(t, testCompile(t, di, true, *p))
   382  
   383  		pr := manifest.NewPermission(manifest.PermissionHash, random.Uint160())
   384  		pr.Methods.Add("someMethod")
   385  		pr.Methods.Add("method4")
   386  
   387  		t.Run("wildcard", func(t *testing.T) {
   388  			pw := manifest.NewPermission(manifest.PermissionWildcard)
   389  			require.NoError(t, testCompile(t, di, false, *p, *pw))
   390  
   391  			pw.Methods.Add("method2")
   392  			require.Error(t, testCompile(t, di, false, *p, *pw))
   393  			require.NoError(t, testCompile(t, di, false, *p, *pw, *pr))
   394  		})
   395  
   396  		t.Run("group", func(t *testing.T) {
   397  			priv, _ := keys.NewPrivateKey()
   398  			pw := manifest.NewPermission(manifest.PermissionGroup, priv.PublicKey())
   399  			require.NoError(t, testCompile(t, di, false, *p, *pw))
   400  
   401  			pw.Methods.Add("invalid")
   402  			require.Error(t, testCompile(t, di, false, *p, *pw, *pr))
   403  
   404  			pw.Methods.Add("method2")
   405  			require.Error(t, testCompile(t, di, false, *p, *pw))
   406  			require.NoError(t, testCompile(t, di, false, *p, *pw, *pr))
   407  		})
   408  	})
   409  }
   410  
   411  func TestUnnamedParameterCheck(t *testing.T) {
   412  	t.Run("single argument", func(t *testing.T) {
   413  		src := `
   414  		package testcase
   415  		func Main(_ int) int {
   416  			x := 10
   417  			return x
   418  		}
   419  	`
   420  		_, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil)
   421  		require.Error(t, err)
   422  		require.ErrorIs(t, err, compiler.ErrMissingExportedParamName)
   423  	})
   424  	t.Run("several arguments", func(t *testing.T) {
   425  		src := `
   426  		package testcase
   427  		func Main(a int, b string, _ int) int {
   428  			x := 10
   429  			return x
   430  		}
   431  	`
   432  		_, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil)
   433  		require.Error(t, err)
   434  		require.ErrorIs(t, err, compiler.ErrMissingExportedParamName)
   435  	})
   436  	t.Run("interface", func(t *testing.T) {
   437  		src := `
   438  		package testcase
   439  		func OnNEP17Payment(h string, i int, _ any){}
   440  	`
   441  		_, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil)
   442  		require.Error(t, err)
   443  		require.ErrorIs(t, err, compiler.ErrMissingExportedParamName)
   444  	})
   445  	t.Run("a set of unnamed params", func(t *testing.T) {
   446  		src := `
   447  		package testcase
   448  		func OnNEP17Payment(_ string, _ int, _ any){}
   449  	`
   450  		_, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil)
   451  		require.Error(t, err)
   452  		require.ErrorIs(t, err, compiler.ErrMissingExportedParamName)
   453  	})
   454  	t.Run("mixed named and unnamed params", func(t *testing.T) {
   455  		src := `
   456  		package testcase
   457  		func OnNEP17Payment(s0, _, s2 string){}
   458  	`
   459  		_, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil)
   460  		require.Error(t, err)
   461  		require.ErrorIs(t, err, compiler.ErrMissingExportedParamName)
   462  	})
   463  	t.Run("empty args", func(t *testing.T) {
   464  		src := `
   465  		package testcase
   466  		func OnNEP17Payment(){}
   467  	`
   468  		_, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil)
   469  		require.NoError(t, err)
   470  	})
   471  	t.Run("good", func(t *testing.T) {
   472  		src := `
   473  		package testcase
   474  		func OnNEP17Payment(s string, i int, iface interface{}){}
   475  	`
   476  		_, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil)
   477  		require.NoError(t, err)
   478  	})
   479  	t.Run("good, use any keyword", func(t *testing.T) {
   480  		src := `
   481  		package testcase
   482  		func OnNEP17Payment(s string, i int, iface any){}
   483  	`
   484  		_, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil)
   485  		require.NoError(t, err)
   486  	})
   487  	t.Run("method with unnamed params", func(t *testing.T) {
   488  		src := `
   489  		package testcase
   490  		type A int
   491  		func (rsv A) OnNEP17Payment(_ string, _ int, iface any){}
   492  	`
   493  		_, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil)
   494  		require.NoError(t, err) // it's OK for exported method to have unnamed params as it won't be included into manifest
   495  	})
   496  }
   497  
   498  func TestReturnValuesCountCheck(t *testing.T) {
   499  	t.Run("void", func(t *testing.T) {
   500  		t.Run("exported", func(t *testing.T) {
   501  			t.Run("func", func(t *testing.T) {
   502  				src := `package testcase
   503  					var a int
   504  					func Main() {
   505  						a = 5
   506  					}`
   507  				_, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil)
   508  				require.NoError(t, err)
   509  			})
   510  			t.Run("method", func(t *testing.T) {
   511  				src := `package testcase
   512  					type A int
   513  					var a int
   514  					func (rcv A) Main() {
   515  						a = 5
   516  					}`
   517  				_, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil)
   518  				require.NoError(t, err)
   519  			})
   520  		})
   521  		t.Run("unexported", func(t *testing.T) {
   522  			src := `package testcase
   523  					var a int
   524  					func main() {
   525  						a = 5
   526  					}`
   527  			_, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil)
   528  			require.NoError(t, err)
   529  		})
   530  	})
   531  	t.Run("single return", func(t *testing.T) {
   532  		t.Run("exported", func(t *testing.T) {
   533  			t.Run("func", func(t *testing.T) {
   534  				src := `package testcase
   535  					var a int
   536  					func Main() int {
   537  						a = 5
   538  						return a
   539  					}`
   540  				eval(t, src, big.NewInt(5))
   541  			})
   542  			t.Run("method", func(t *testing.T) {
   543  				src := `package testcase
   544  					type A int
   545  					var a int
   546  					func (rcv A) Main() int {
   547  						a = 5
   548  						return a
   549  					}`
   550  				_, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil)
   551  				require.NoError(t, err)
   552  			})
   553  		})
   554  		t.Run("unexported", func(t *testing.T) {
   555  			src := `package testcase
   556  					var a int
   557  					func main() int {
   558  						a = 5
   559  						return a
   560  					}`
   561  			_, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil)
   562  			require.NoError(t, err)
   563  		})
   564  	})
   565  	t.Run("multiple unnamed return vals", func(t *testing.T) {
   566  		t.Run("exported", func(t *testing.T) {
   567  			t.Run("func", func(t *testing.T) {
   568  				src := `package testcase
   569  					var a int
   570  					func Main() (int, int) {
   571  						a = 5
   572  						return a, a
   573  					}`
   574  				_, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil)
   575  				require.Error(t, err)
   576  				require.ErrorIs(t, err, compiler.ErrInvalidExportedRetCount)
   577  			})
   578  			t.Run("method", func(t *testing.T) {
   579  				src := `package testcase
   580  					type A int
   581  					var a int
   582  					func (rcv A) Main() (int, int) {
   583  						a = 5
   584  						return a, a
   585  					}`
   586  				_, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil)
   587  				require.NoError(t, err) // OK for method to have multiple return values as it won't be included into manifest
   588  			})
   589  		})
   590  		t.Run("unexported", func(t *testing.T) {
   591  			src := `package testcase
   592  					var a int
   593  					func main() (int, int) {
   594  						a = 5
   595  						return a, a
   596  					}`
   597  			_, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil)
   598  			require.NoError(t, err) // OK for unexported function to have multiple return values as it won't be included into manifest
   599  		})
   600  	})
   601  	t.Run("multiple named return vals", func(t *testing.T) {
   602  		t.Run("exported", func(t *testing.T) {
   603  			t.Run("func", func(t *testing.T) {
   604  				src := `package testcase
   605  					var a int
   606  					func Main() (a int, b int) {
   607  						a = 5
   608  						b = 2
   609  						return
   610  					}`
   611  				_, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil)
   612  				require.Error(t, err)
   613  				require.ErrorIs(t, err, compiler.ErrInvalidExportedRetCount)
   614  			})
   615  			t.Run("method", func(t *testing.T) {
   616  				src := `package testcase
   617  					type A int
   618  					var a int
   619  					func (rcv A) Main() (a int, b int) {
   620  						a = 5
   621  						b = 2
   622  						return
   623  					}`
   624  				_, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil)
   625  				require.NoError(t, err) // OK for method to have multiple return values as it won't be included into manifest
   626  			})
   627  		})
   628  		t.Run("unexported", func(t *testing.T) {
   629  			src := `package testcase
   630  					var a int
   631  					func main() (a int, b int) {
   632  						a = 5
   633  						b = 2
   634  						return
   635  					}`
   636  			_, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil)
   637  			require.NoError(t, err) // OK for unexported function to have multiple return values as it won't be included into manifest
   638  		})
   639  	})
   640  }