Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 2 additions & 0 deletions examples/gno.land/r/demo/lintest/gnomod.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module = "gno.land/r/demo/lintest"
gno = "0.9"
41 changes: 41 additions & 0 deletions examples/gno.land/r/demo/lintest/lintest.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package lintest

import "gno.land/p/nt/avl"

var lintest *avl.Tree

type whatever struct{}

func (w whatever) Iterate(start, end string)

func init() {
lintest = avl.NewTree()
lintest.Set("key1", "value1")
lintest.Set("key2", "value2")
lintest.Set("key3", "value3")
}

func JoinValues() string {
result := ""
lintest.Iterate("", "", func(key string, value any) bool {
result += value.(string) + ","
return false
})
w := whatever{}
w.Iterate("", "")
return result
}

func JoinValuesReverse() string {
result := ""
//nolint
lintest.ReverseIterate("", "", func(key string, value any) bool {
result += value.(string) + ","
return false
})
return result
}

func Render(path string) string {
return "Rendering AVL Tree at path: " + path
}
72 changes: 72 additions & 0 deletions gnovm/cmd/gno/lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
"path/filepath"

"github.com/gnolang/gno/gnovm/cmd/gno/internal/cmdutil"
"github.com/gnolang/gno/gnovm/cmd/gno/lintrules"
"github.com/gnolang/gno/gnovm/pkg/gnoenv"
"github.com/gnolang/gno/gnovm/pkg/gnolang"

Check failure on line 15 in gnovm/cmd/gno/lint.go

View workflow job for this annotation

GitHub Actions / Run GnoVM suite / Go Lint / lint

ST1019: package "github.com/gnolang/gno/gnovm/pkg/gnolang" is being imported more than once (staticcheck)
gno "github.com/gnolang/gno/gnovm/pkg/gnolang"

Check failure on line 16 in gnovm/cmd/gno/lint.go

View workflow job for this annotation

GitHub Actions / Run GnoVM suite / Go Lint / lint

ST1019(related information): other import of "github.com/gnolang/gno/gnovm/pkg/gnolang" (staticcheck)
"github.com/gnolang/gno/gnovm/pkg/gnomod"
"github.com/gnolang/gno/gnovm/pkg/packages"
"github.com/gnolang/gno/gnovm/pkg/test"
Expand Down Expand Up @@ -286,6 +288,33 @@
pn, _ := tm.PreprocessFiles(
mpkg.Name, mpkg.Path, fset, false, false, "")
ppkg.AddNormal(pn, fset)

rules := []lintrules.LintRule{
lintrules.AvlLimitRule{},
}

// TODO: Should i recreate a new store ?
tm.Store = newProdGnoStore()
pn = gno.NewPackageNode(gno.Name(mpkg.Name), pkgPath, fset)
tm.Store.SetBlockNode(pn)
gno.PredefineFileSet(tm.Store, pn, fset)
for _, fn := range fset.Files {
mf := mpkg.GetFile(fn.FileName)
src := string(mf.Body)

Check failure on line 303 in gnovm/cmd/gno/lint.go

View workflow job for this annotation

GitHub Actions / Run GnoVM suite / Go Lint / lint

unnecessary conversion (unconvert)
runLintExtensions(
tm.Store,
pn,
fn,
src,
rules,
func(err error, pos gnolang.Pos) {
fmt.Printf("%s:%d:%d: %s\n",
fn.FileName, pos.Line, pos.Column,
err.Error(),
)
},
)
}
}
{
// LINT STEP 5: PreprocessFiles()
Expand Down Expand Up @@ -390,3 +419,46 @@

return tryRelativizePath(pkg.Dir)
}

func runLintExtensions(
store gnolang.Store,
pn *gnolang.PackageNode,
fn *gnolang.FileNode,
source string,
rules []lintrules.LintRule,
report func(error, gnolang.Pos),
) {
ctx := &lintrules.RuleContext{
Store: store,
File: pn.GetFileByName(pn.FileNames()[0]),
Source: source,
}

var currentFile *gnolang.FileNode
gnolang.TranscribeB(pn, fn, func(
ns []gnolang.Node,
stack []gnolang.BlockNode,
last gnolang.BlockNode,
ftype gnolang.TransField,
index int,
n gnolang.Node,
stage gnolang.TransStage,
) (gnolang.Node, gnolang.TransCtrl) {

Check failure on line 446 in gnovm/cmd/gno/lint.go

View workflow job for this annotation

GitHub Actions / Run GnoVM suite / Go Lint / lint

unnecessary leading newline (whitespace)

if stage == gnolang.TRANS_ENTER {

Check failure on line 448 in gnovm/cmd/gno/lint.go

View workflow job for this annotation

GitHub Actions / Run GnoVM suite / Go Lint / lint

unnecessary leading newline (whitespace)

if fn, ok := n.(*gnolang.FileNode); ok {
currentFile = fn
}
ctx.File = currentFile

for _, rule := range rules {
if err := rule.Run(ctx, n); err != nil {
report(err, n.GetPos())
}
}
}

return n, gnolang.TRANS_CONTINUE
})
}
86 changes: 86 additions & 0 deletions gnovm/cmd/gno/lintrules/avl-limit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package lintrules

import (
"errors"
"fmt"
"strings"

"github.com/gnolang/gno/gnovm/pkg/gnolang"
)

type AvlLimitRule struct{}

// XXX: this is a PoC not a production ready code
func (AvlLimitRule) Run(ctx *RuleContext, node gnolang.Node) error {
call, ok := node.(*gnolang.CallExpr)
if !ok {
return nil
}

sel, ok := call.Func.(*gnolang.SelectorExpr)
if !ok {
return nil
}

m := string(sel.Sel)
if m != "Iterate" && m != "ReverseIterate" {
return nil
}

recvT := gnolang.EvalStaticTypeOf(ctx.Store, ctx.File, sel.X)
if !isAVLTree(recvT) {
// DEBUG:
// fmt.Printf("receiver is not AVL tree for %s\n", method)
return nil
}

if len(call.Args) < 2 {
return nil
}

if !isEmptyConstString(call.Args[0]) ||
!isEmptyConstString(call.Args[1]) {
return nil
}

if hasNoLintDirective(ctx, node.GetPos()) {
return nil
}

return errors.New("avl tree Iterate/Reverse iterate")
}

func isEmptyConstString(expr gnolang.Expr) bool {
cs, ok := expr.(*gnolang.ConstExpr)
if !ok {
return false
}
if cs.T.Kind() != gnolang.StringKind {
return false
}
return string(cs.V.(gnolang.StringValue)) == ""
}

func hasNoLintDirective(ctx *RuleContext, pos gnolang.Pos) bool {
if ctx.Source == "" {
return false
}

lines := strings.Split(ctx.Source, "\n")
line := pos.Line - 2

if line <= 0 || line > len(lines) {
return false
}
prev := strings.TrimSpace(lines[line])
return strings.HasPrefix(prev, "//nolint")
}

func isAVLTree(t gnolang.Type) bool {
dt, ok := gnolang.UnwrapPointerType(t).(*gnolang.DeclaredType)
if !ok {
fmt.Printf("DEBUG: not declared type %T \n", t)
return false
}
return dt.PkgPath == "gno.land/p/nt/avl" && dt.Name == "Tree"
}
19 changes: 19 additions & 0 deletions gnovm/cmd/gno/lintrules/lintrule.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package lintrules

import (
"github.com/gnolang/gno/gnovm/pkg/gnolang"
)

// The PoC aim to implement a way where gnolang pkg does not about linter
// We use TranscribeB to go through the AST built with gnolang.Nodes
// And we apply all the LintRules activated on all nodes.

type RuleContext struct {
Store gnolang.Store
File *gnolang.FileNode
Source string
}

type LintRule interface {
Run(ctx *RuleContext, node gnolang.Node) error
}
11 changes: 11 additions & 0 deletions gnovm/pkg/gnolang/preprocess.go
Original file line number Diff line number Diff line change
Expand Up @@ -3586,6 +3586,17 @@ func evalStaticTypeOf(store Store, last BlockNode, x Expr) Type {
return t
}

// EXPERTIMENT
func EvalStaticTypeOf(store Store, last BlockNode, x Expr) Type {
t := evalStaticTypeOfRaw(store, last, x)

if tt, ok := t.(*tupleType); ok && len(tt.Elts) == 1 {
return tt.Elts[0]
}

return t
}

// like evalStaticTypeOf() but returns the raw *tupleType for *CallExpr.
func evalStaticTypeOfRaw(store Store, last BlockNode, x Expr) (t Type) {
if t, ok := x.GetAttribute(ATTR_TYPEOF_VALUE).(Type); ok {
Expand Down
8 changes: 8 additions & 0 deletions gnovm/pkg/gnolang/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -1474,6 +1474,14 @@ func unwrapPointerType(t Type) Type {
return t
}

// XXX: TEMPORARY FOR EXPERIMENTATION
func UnwrapPointerType(t Type) Type {
if pt, ok := t.(*PointerType); ok {
return pt.Elem()
}
return t
}

// NOTE: it may be faster to switch on baseOf().
func (dt *DeclaredType) Kind() Kind {
return dt.Base.Kind()
Expand Down
Loading