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

feat(cmd/gno): perform type checking when calling linter #1730

Open
wants to merge 70 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
85f62ba
refactor(gnovm): rename precompiler to transpiler, move to own package
thehowl Feb 22, 2024
9409285
move transpiler to pkg
thehowl Feb 22, 2024
cb63cf5
Merge branch 'master' into dev/morgan/precompile-refactor
thehowl Feb 22, 2024
3710ded
Merge branch 'dev/morgan/precompile-refactor' of github.com:gnolang/g…
thehowl Feb 22, 2024
37c9e66
fixup
thehowl Feb 22, 2024
59c5bb1
Merge branch 'master' of github.com:gnolang/gno into dev/morgan/preco…
thehowl Feb 26, 2024
d39292b
Merge branch 'master' into dev/morgan/precompile-refactor
thehowl Feb 27, 2024
a25d076
Merge branch 'master' of github.com:gnolang/gno into dev/morgan/preco…
thehowl Feb 28, 2024
a889dea
begin removing support for linked identifiers
thehowl Feb 22, 2024
aae89db
convert native bindings to never use linked types
thehowl Feb 23, 2024
fd2722e
remove InjectNativeMappings
thehowl Feb 23, 2024
8856225
fix errors in genstd
thehowl Feb 27, 2024
d6955d0
remove AddGo2GnoMapping, nolint on btReadonly
thehowl Feb 28, 2024
c3e8ff8
Merge branch 'master' of github.com:gnolang/gno into dev/morgan/preco…
thehowl Feb 28, 2024
1de00fa
Merge branch 'dev/morgan/precompile-refactor' into dev/morgan/natbind…
thehowl Feb 28, 2024
c0581dd
Merge branch 'master' of github.com:gnolang/gno into dev/morgan/natbi…
thehowl Feb 28, 2024
b3c7adb
fmt + bugfix
thehowl Feb 28, 2024
250e292
feat(gno.land): add go type checking to keeper
thehowl Feb 28, 2024
0d8596d
lint + test fixes
thehowl Feb 28, 2024
10c0434
Merge branch 'master' of github.com:gnolang/gno into dev/morgan/natbi…
thehowl Mar 3, 2024
f641c58
Merge branch 'dev/morgan/natbind-no-linkedtype' into dev/morgan/go-ty…
thehowl Mar 3, 2024
9779ff3
use custom type instead of any in importer
thehowl Mar 3, 2024
7b239df
support multiple errors in type checker
thehowl Mar 3, 2024
1b323a2
correctly return error in vm keeper
thehowl Mar 3, 2024
df1679c
add tests to type checker
thehowl Mar 3, 2024
ee0448e
fixup
thehowl Mar 3, 2024
a169800
add integration test for gno.land
thehowl Mar 3, 2024
d2c3264
feat(cmd/gno): perform type checking when calling linter
thehowl Mar 4, 2024
b320155
fix testscript tests, remove duplicate code;
thehowl Mar 4, 2024
e53aa22
Merge branch 'master' into dev/morgan/go-types-typecheck
thehowl Mar 20, 2024
172e4ae
Merge branch 'master' of github.com:gnolang/gno into dev/morgan/go-ty…
thehowl Mar 27, 2024
0fa34fa
remove newMemPackage
thehowl Mar 27, 2024
8688167
Merge branch 'dev/morgan/go-types-typecheck' of github.com:gnolang/gn…
thehowl Mar 27, 2024
89d9887
remove unused var
thehowl Mar 27, 2024
ccf0ba9
Merge branch 'master' into dev/morgan/go-types-typecheck
thehowl Apr 15, 2024
338454f
remove other usage in gnoclient
thehowl Apr 15, 2024
1c5c14f
Merge branch 'dev/morgan/go-types-typecheck' into dev/morgan/typechec…
thehowl Apr 15, 2024
c2917c6
s/--verbose/-v/g
thehowl Apr 15, 2024
6b4ed9e
remove unused import in test
thehowl Apr 15, 2024
593defa
Merge branch 'dev/morgan/go-types-typecheck' into dev/morgan/typechec…
thehowl Apr 15, 2024
0007e73
Merge branch 'master' of github.com:gnolang/gno into dev/morgan/go-ty…
thehowl Apr 17, 2024
9ccb5ee
bump limits for issue-1786 test
thehowl Apr 17, 2024
e20ebbe
bump up to 4M
thehowl Apr 17, 2024
ad2f0b1
Merge branch 'master' of github.com:gnolang/gno into dev/morgan/go-ty…
thehowl May 6, 2024
66fb953
changes from code review
thehowl May 6, 2024
38cba04
fix gas numbers
thehowl May 7, 2024
bee0f30
add transaction simulation in gnokey
thehowl May 7, 2024
3a82a98
fixup lint
thehowl May 7, 2024
a112b4a
Merge branch 'dev/morgan/go-types-typecheck' into dev/morgan/typechec…
thehowl May 7, 2024
39ae304
update docs
thehowl May 7, 2024
dbde296
partial revert of store changes
thehowl May 9, 2024
9ee3cc6
Merge branch 'master' of github.com:gnolang/gno into dev/morgan/go-ty…
thehowl May 9, 2024
f843acb
fixup
thehowl May 9, 2024
2558b09
Revert "fixup"
thehowl May 10, 2024
cc9d45e
Revert "partial revert of store changes"
thehowl May 10, 2024
09f081c
fixup
thehowl May 10, 2024
38719cd
update docs
thehowl May 10, 2024
815c573
Merge branch 'master' of github.com:gnolang/gno into dev/morgan/go-ty…
thehowl May 13, 2024
6154240
Merge branch 'dev/morgan/go-types-typecheck' into dev/morgan/typechec…
thehowl May 13, 2024
10992d2
please, ci, work
thehowl May 13, 2024
b0dbfe7
Merge branch 'master' of github.com:gnolang/gno into dev/morgan/typec…
thehowl May 13, 2024
c5af2fe
mv
thehowl May 13, 2024
dd64ec1
mv
thehowl May 13, 2024
7d93ab1
codereview changes
thehowl May 13, 2024
0ffda3b
Merge branch 'master' of github.com:gnolang/gno into dev/morgan/typec…
thehowl May 21, 2024
dcb9930
review comments
thehowl May 21, 2024
d5d166c
ignore errorlint on fmt.Errorf
thehowl May 21, 2024
b21a7f5
Merge branch 'master' of github.com:gnolang/gno into dev/morgan/typec…
thehowl May 29, 2024
6530717
Merge branch 'master' of github.com:gnolang/gno into dev/morgan/typec…
thehowl Jun 3, 2024
733fa73
adapt to new master changes
thehowl Jun 3, 2024
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
2 changes: 1 addition & 1 deletion examples/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ test:

.PHONY: lint
lint:
go run ../gnovm/cmd/gno lint $(OFFICIAL_PACKAGES)
go run ../gnovm/cmd/gno lint --verbose $(OFFICIAL_PACKAGES)
thehowl marked this conversation as resolved.
Show resolved Hide resolved

.PHONY: test.sync
test.sync:
Expand Down
2 changes: 2 additions & 0 deletions examples/gno.land/r/demo/art/gnoface/gno.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Draft
zivkovicmilos marked this conversation as resolved.
Show resolved Hide resolved

module gno.land/r/demo/art/gnoface

require (
Expand Down
2 changes: 1 addition & 1 deletion gno.land/cmd/gnoland/testdata/addpkg.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
## start a new node
gnoland start

## add bar.gno package located in $WORK directory as gno.land/r/foobar/bar
## add bar package located in $WORK directory as gno.land/r/foobar/bar
gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/foobar/bar -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1

## execute Render
Expand Down
21 changes: 21 additions & 0 deletions gno.land/cmd/gnoland/testdata/addpkg_invalid.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# test for add package; ensuring type checker catches invalid code.

# start a new node
gnoland start

# add bar package located in $WORK directory as gno.land/r/foobar/bar
! gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/foobar/bar -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1

# compare render
! stdout .+
stderr 'as string value in return statement'
stderr '"std" imported and not used'

-- bar.gno --
package bar

import "std"

func Render(path string) string {
return 89
}
6 changes: 0 additions & 6 deletions gno.land/pkg/gnoclient/client_txs.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package gnoclient

import (
"github.com/gnolang/gno/gno.land/pkg/sdk/vm"
"github.com/gnolang/gno/gnovm/pkg/transpiler"
"github.com/gnolang/gno/tm2/pkg/amino"
ctypes "github.com/gnolang/gno/tm2/pkg/bft/rpc/core/types"
"github.com/gnolang/gno/tm2/pkg/crypto"
Expand Down Expand Up @@ -138,11 +137,6 @@ func (c *Client) Run(cfg BaseTxCfg, msgs ...MsgRun) (*ctypes.ResultBroadcastTxCo

caller := c.Signer.Info().GetAddress()

// Transpile and validate Gno syntax
if err = transpiler.TranspileAndCheckMempkg(msg.Package); err != nil {
return nil, err
}

msg.Package.Name = "main"
msg.Package.Path = ""

Expand Down
3 changes: 0 additions & 3 deletions gno.land/pkg/gnoclient/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,6 @@ func TestRunSingle_Integration(t *testing.T) {

fileBody := `package main
import (
"std"
"gno.land/p/demo/ufmt"
"gno.land/r/demo/tests"
)
Expand Down Expand Up @@ -297,7 +296,6 @@ func TestRunMultiple_Integration(t *testing.T) {

fileBody1 := `package main
import (
"std"
"gno.land/p/demo/ufmt"
"gno.land/r/demo/tests"
)
Expand All @@ -311,7 +309,6 @@ func main() {

fileBody2 := `package main
import (
"std"
"gno.land/p/demo/ufmt"
"gno.land/r/demo/deep/very/deep"
)
Expand Down
7 changes: 0 additions & 7 deletions gno.land/pkg/keyscli/addpkg.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (

"github.com/gnolang/gno/gno.land/pkg/sdk/vm"
gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
"github.com/gnolang/gno/gnovm/pkg/transpiler"
"github.com/gnolang/gno/tm2/pkg/amino"
"github.com/gnolang/gno/tm2/pkg/commands"
"github.com/gnolang/gno/tm2/pkg/crypto/keys"
Expand Down Expand Up @@ -102,12 +101,6 @@ func execMakeAddPkg(cfg *MakeAddPkgCfg, args []string, io commands.IO) error {
panic(fmt.Sprintf("found an empty package %q", cfg.PkgPath))
}

// transpile and validate syntax
err = transpiler.TranspileAndCheckMempkg(memPkg)
if err != nil {
panic(err)
}

// parse gas wanted & fee.
gaswanted := cfg.RootCfg.GasWanted
gasfee, err := std.ParseCoin(cfg.RootCfg.GasFee)
Expand Down
7 changes: 1 addition & 6 deletions gno.land/pkg/keyscli/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (

"github.com/gnolang/gno/gno.land/pkg/sdk/vm"
gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
"github.com/gnolang/gno/gnovm/pkg/transpiler"
"github.com/gnolang/gno/tm2/pkg/amino"
"github.com/gnolang/gno/tm2/pkg/commands"
"github.com/gnolang/gno/tm2/pkg/crypto/keys"
Expand Down Expand Up @@ -109,11 +108,7 @@ func execMakeRun(cfg *MakeRunCfg, args []string, cmdio commands.IO) error {
if memPkg.IsEmpty() {
panic(fmt.Sprintf("found an empty package %q", memPkg.Path))
}
// transpile and validate syntax
err = transpiler.TranspileAndCheckMempkg(memPkg)
if err != nil {
panic(err)
}

memPkg.Name = "main"
// Set to empty; this will be automatically set by the VM keeper.
memPkg.Path = ""
Expand Down
5 changes: 2 additions & 3 deletions gno.land/pkg/sdk/vm/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func (vm *VMKeeper) initBuiltinPackagesAndTypes(store gno.Store) {
// NOTE: native functions/methods added here must be quick operations,
// or account for gas before operation.
// TODO: define criteria for inclusion, and solve gas calculations.
getPackage := func(pkgPath string) (pn *gno.PackageNode, pv *gno.PackageValue) {
getPackage := func(pkgPath string, newStore gno.Store) (pn *gno.PackageNode, pv *gno.PackageValue) {
// otherwise, built-in package value.
// first, load from filepath.
stdlibPath := filepath.Join(vm.stdlibsDir, pkgPath)
Expand All @@ -34,14 +34,13 @@ func (vm *VMKeeper) initBuiltinPackagesAndTypes(store gno.Store) {
PkgPath: "gno.land/r/stdlibs/" + pkgPath,
// PkgPath: pkgPath,
Output: os.Stdout,
Store: store,
Store: newStore,
})
defer m2.Release()
return m2.RunMemPackage(memPkg, true)
}
store.SetPackageGetter(getPackage)
store.SetNativeStore(stdlibs.NativeStore)
stdlibs.InjectNativeMappings(store)
}

// ----------------------------------------
Expand Down
26 changes: 25 additions & 1 deletion gno.land/pkg/sdk/vm/errors.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package vm

import "github.com/gnolang/gno/tm2/pkg/errors"
import (
"strings"

"github.com/gnolang/gno/tm2/pkg/errors"
"go.uber.org/multierr"
)

// for convenience:
type abciError struct{}
Expand All @@ -13,11 +18,21 @@ type (
InvalidPkgPathError struct{ abciError }
InvalidStmtError struct{ abciError }
InvalidExprError struct{ abciError }
TypeCheckError struct {
abciError
Errors []string
}
)

func (e InvalidPkgPathError) Error() string { return "invalid package path" }
func (e InvalidStmtError) Error() string { return "invalid statement" }
func (e InvalidExprError) Error() string { return "invalid expression" }
func (e TypeCheckError) Error() string {
var bld strings.Builder
bld.WriteString("invalid gno package; type check errors:\n")
bld.WriteString(strings.Join(e.Errors, "\n"))
return bld.String()
}

func ErrInvalidPkgPath(msg string) error {
return errors.Wrap(InvalidPkgPathError{}, msg)
Expand All @@ -30,3 +45,12 @@ func ErrInvalidStmt(msg string) error {
func ErrInvalidExpr(msg string) error {
return errors.Wrap(InvalidExprError{}, msg)
}

func ErrTypeCheck(err error) error {
var tce TypeCheckError
errs := multierr.Errors(err)
for _, err := range errs {
tce.Errors = append(tce.Errors, err.Error())
}
return errors.NewWithData(tce).Stacktrace()
}
13 changes: 13 additions & 0 deletions gno.land/pkg/sdk/vm/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,11 @@ func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) error {
return ErrInvalidPkgPath("reserved package name: " + pkgPath)
}

// Validate Gno syntax and type check.
if err := gno.TypeCheckMemPackage(memPkg, store); err != nil {
return ErrTypeCheck(err)
}

// Pay deposit from creator.
pkgAddr := gno.DerivePkgAddr(pkgPath)

Expand Down Expand Up @@ -317,6 +322,11 @@ func (vm *VMKeeper) Run(ctx sdk.Context, msg MsgRun) (res string, err error) {
return "", ErrInvalidPkgPath(err.Error())
}

// Validate Gno syntax and type check.
if err = gno.TypeCheckMemPackage(memPkg, store); err != nil {
return "", ErrTypeCheck(err)
}

// Send send-coins to pkg from caller.
err = vm.bank.SendCoins(ctx, caller, pkgAddr, send)
if err != nil {
Expand Down Expand Up @@ -565,6 +575,9 @@ func (vm *VMKeeper) QueryFile(ctx sdk.Context, filepath string) (res string, err
return memFile.Body, nil
} else {
memPkg := store.GetMemPackage(dirpath)
if memPkg == nil {
return "", fmt.Errorf("package %q is not available", dirpath) // TODO: XSS protection
}
for i, memfile := range memPkg.Files {
if i > 0 {
res += "\n"
Expand Down
4 changes: 0 additions & 4 deletions gno.land/pkg/sdk/vm/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ func TestVMKeeperAddPackage(t *testing.T) {
Name: "test.gno",
Body: `package test

import "std"

func Echo() string {
return "hello world"
}`,
Expand Down Expand Up @@ -413,8 +411,6 @@ func TestNumberOfArgsError(t *testing.T) {
Name: "test.gno",
Body: `package test

import "std"

func Echo(msg string) string {
return "echo:"+msg
}`,
Expand Down
1 change: 1 addition & 0 deletions gno.land/pkg/sdk/vm/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ var Package = amino.RegisterPackage(amino.NewPackage(
InvalidPkgPathError{}, "InvalidPkgPathError",
InvalidStmtError{}, "InvalidStmtError",
InvalidExprError{}, "InvalidExprError",
TypeCheckError{}, "TypeCheckError",
))
70 changes: 57 additions & 13 deletions gnovm/cmd/gno/lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,19 @@ import (
"errors"
"flag"
"fmt"
"go/scanner"
"go/types"
"os"
"path/filepath"
"regexp"
"strings"

"github.com/gnolang/gno/gnovm/pkg/gnoenv"
gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
"github.com/gnolang/gno/gnovm/pkg/gnomod"
"github.com/gnolang/gno/gnovm/tests"
"github.com/gnolang/gno/tm2/pkg/commands"
osm "github.com/gnolang/gno/tm2/pkg/os"
"go.uber.org/multierr"
)

type lintCfg struct {
Expand Down Expand Up @@ -75,36 +78,75 @@ func execLint(cfg *lintCfg, args []string, io commands.IO) error {

for _, pkgPath := range pkgPaths {
if verbose {
fmt.Fprintf(io.Err(), "Linting %q...\n", pkgPath)
fmt.Fprintf(io.Err(), "%s\n", pkgPath)
thehowl marked this conversation as resolved.
Show resolved Hide resolved
}

info, err := os.Stat(pkgPath)
if err == nil && !info.IsDir() {
pkgPath = filepath.Dir(pkgPath)
}

// Check if 'gno.mod' exists
gnoModPath := filepath.Join(pkgPath, "gno.mod")
if !osm.FileExists(gnoModPath) {
gmFile, err := gnomod.ParseAt(pkgPath)
if err != nil {
addIssue(lintIssue{
Code: lintNoGnoMod,
Code: lintGnoMod,
Confidence: 1,
Location: pkgPath,
Msg: "missing 'gno.mod' file",
Msg: err.Error(),
})
}

// Handle runtime errors
catchRuntimeError(pkgPath, addIssue, func() {
stdout, stdin, stderr := io.Out(), io.In(), io.Err()

testStore := tests.TestStore(
rootDir, "",
stdin, stdout, stderr,
tests.ImportModeStdlibsOnly,
)

targetPath := pkgPath
info, err := os.Stat(pkgPath)
if err == nil && !info.IsDir() {
targetPath = filepath.Dir(pkgPath)
memPkg := gno.ReadMemPackage(pkgPath, pkgPath)

if gmFile == nil || !gmFile.Draft {
tcErr := gno.TypeCheckMemPackageTest(memPkg, testStore)
if tcErr != nil {
thehowl marked this conversation as resolved.
Show resolved Hide resolved
errs := multierr.Errors(tcErr)
for _, err := range errs {
switch err := err.(type) {
case types.Error:
addIssue(lintIssue{
Code: lintTypeCheckError,
Msg: err.Msg,
Confidence: 1,
Location: err.Fset.Position(err.Pos).String(),
})
case scanner.ErrorList:
for _, scErr := range err {
addIssue(lintIssue{
Code: lintParserError,
Msg: scErr.Msg,
Confidence: 1,
Location: scErr.Pos.String(),
})
}
case scanner.Error:
addIssue(lintIssue{
Code: lintParserError,
Msg: err.Msg,
Confidence: 1,
Location: err.Pos.String(),
})
default:
fmt.Fprintf(os.Stderr, "unexpected error type: %T\n", err)
thehowl marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
} else if verbose {
zivkovicmilos marked this conversation as resolved.
Show resolved Hide resolved
fmt.Fprintf(stderr, "%s: module is draft, skipping type check\n", pkgPath)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would vote for making gmFile == nil not a draft.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's already the case!

}

memPkg := gno.ReadMemPackage(targetPath, targetPath)
tm := tests.TestMachine(testStore, stdout, memPkg.Name)

// Check package
Expand Down Expand Up @@ -210,9 +252,11 @@ func catchRuntimeError(pkgPath string, addIssue func(issue lintIssue), action fu
type lintCode int

const (
thehowl marked this conversation as resolved.
Show resolved Hide resolved
lintUnknown lintCode = 0
lintNoGnoMod lintCode = iota
lintUnknown lintCode = 0
lintGnoMod lintCode = iota
thehowl marked this conversation as resolved.
Show resolved Hide resolved
lintGnoError
lintParserError
lintTypeCheckError

// TODO: add new linter codes here.
)
Expand Down
Loading
Loading