diff --git a/.golangci.yml b/.golangci.yml index 1acdfc1..7ab5602 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,268 +1,233 @@ linters: # Disable all linters. # Default: false - disable-all: true + # disable-all: true # Enable specific linter # https://golangci-lint.run/usage/linters/#enabled-by-default enable: - - asasalint - - asciicheck - - bidichk + # - asasalint + # - asciicheck + # - bidichk - bodyclose - - canonicalheader - - containedctx - - contextcheck - - copyloopvar - - cyclop - - decorder - - depguard - - dogsled - - dupl - - dupword + # - containedctx + # - contextcheck + # - cyclop + # - decorder + # - depguard + # - dogsled + # - dupl + # - dupword - durationcheck - - err113 - errcheck - - errchkjson + # - errchkjson - errname - errorlint - - execinquery - - exhaustive - - exhaustruct - - exportloopref - - exptostd - - fatcontext - - forbidigo - - forcetypeassert - - funlen - - gci - - ginkgolinter - - gocheckcompilerdirectives - - gochecknoglobals - - gochecknoinits - - gochecksumtype - - gocognit + # - execinquery + # - exhaustive + # - exhaustruct + # - exportloopref + # - forbidigo + # - forcetypeassert + # - funlen + # - gci + # - ginkgolinter + # - gocheckcompilerdirectives + # - gochecknoglobals + # - gochecknoinits + # - gochecksumtype + # - gocognit - goconst - - gocritic - - gocyclo - - godot - - godox - - gofmt - - gofumpt - - goheader - - goimports - - gomoddirectives - - gomodguard - - goprintffuncname - - gosec - - gosimple - - gosmopolitan + # - gocritic + # - gocyclo + # - godot + # - godox + # - gofmt + # - gofumpt + # - goheader + # - goimports + # - gomoddirectives + # - gomodguard + # - goprintffuncname + # - gosec + # - gosimple + # - gosmopolitan - govet - - grouper - - iface - - importas - - inamedparam + # - grouper + # - importas + # - inamedparam - ineffassign - - interfacebloat - - intrange - - ireturn + # - interfacebloat + # - ireturn - lll - - loggercheck - - maintidx - - makezero - - mirror + # - loggercheck + # - maintidx + # - makezero + # - mirror - misspell - - mnd - - musttag - - nakedret - - nestif + # - musttag + # - nakedret + # - nestif - nilerr - - nilnesserr - nilnil - - nlreturn - - noctx - - nolintlint - - nonamedreturns - - nosprintfhostport - - paralleltest - - perfsprint + # - nlreturn + # - noctx + # - nolintlint + # - nonamedreturns + # - nosprintfhostport + # - paralleltest + # - perfsprint - prealloc - predeclared - - promlinter - - protogetter + # - promlinter + # - protogetter - reassign - - recvcheck - - revive - - rowserrcheck - - sloglint - - spancheck - - sqlclosecheck - - staticcheck - - stylecheck - - tagalign - - tagliatelle - - tenv - - testableexamples - - testifylint - - testpackage - - thelper - - tparallel - - unconvert - - unparam - - unused - - usestdlibvars - - usetesting - - varnamelen - - wastedassign + # - revive + # - rowserrcheck + # - sloglint + # - sqlclosecheck + # - staticcheck + # - stylecheck + # - tagalign + # - tagliatelle + # - tenv + # - testableexamples + # - testifylint + # - testpackage + # - thelper + # - tparallel + # - unconvert + # - unparam + # - unused + # - usestdlibvars + # - varnamelen + # - wastedassign - whitespace - - wrapcheck - - wsl - - zerologlint + # - wrapcheck + # - wsl + # - zerologlint # Enable all available linters. # Default: false - enable-all: true + # enable-all: true # Disable specific linter # https://golangci-lint.run/usage/linters/#disabled-by-default - disable: - - asasalint - - asciicheck - - bidichk - - bodyclose - - canonicalheader - - containedctx - - contextcheck - - copyloopvar - - cyclop - - decorder - - depguard - - dogsled - - dupl - - dupword - - durationcheck - - err113 - - errcheck - - errchkjson - - errname - - errorlint - - execinquery - - exhaustive - - exhaustruct - - exportloopref - - exptostd - - fatcontext - - forbidigo - - forcetypeassert - - funlen - - gci - - ginkgolinter - - gocheckcompilerdirectives - - gochecknoglobals - - gochecknoinits - - gochecksumtype - - gocognit - - goconst - - gocritic - - gocyclo - - godot - - godox - - gofmt - - gofumpt - - goheader - - goimports - - gomoddirectives - - gomodguard - - goprintffuncname - - gosec - - gosimple - - gosmopolitan - - govet - - grouper - - iface - - importas - - inamedparam - - ineffassign - - interfacebloat - - intrange - - ireturn - - lll - - loggercheck - - maintidx - - makezero - - mirror - - misspell - - mnd - - musttag - - nakedret - - nestif - - nilerr - - nilnesserr - - nilnil - - nlreturn - - noctx - - nolintlint - - nonamedreturns - - nosprintfhostport - - paralleltest - - perfsprint - - prealloc - - predeclared - - promlinter - - protogetter - - reassign - - recvcheck - - revive - - rowserrcheck - - sloglint - - spancheck - - sqlclosecheck - - staticcheck - - stylecheck - - tagalign - - tagliatelle - - tenv - - testableexamples - - testifylint - - testpackage - - thelper - - tparallel - - unconvert - - unparam - - unused - - usestdlibvars - - usetesting - - varnamelen - - wastedassign - - whitespace - - wrapcheck - - wsl - - zerologlint - - deadcode # Deprecated - - exhaustivestruct # Deprecated - - golint # Deprecated - - ifshort # Deprecated - - interfacer # Deprecated - - maligned # Deprecated - - gomnd # Deprecated - - nosnakecase # Deprecated - - scopelint # Deprecated - - structcheck # Deprecated - - varcheck # Deprecated + # disable: + # - asasalint + # - asciicheck + # - bidichk + # - bodyclose + # - containedctx + # - contextcheck + # - cyclop + # - decorder + # - depguard + # - dogsled + # - dupl + # - dupword + # - durationcheck + # - errcheck + # - errchkjson + # - errname + # - errorlint + # - execinquery + # - exhaustive + # - exhaustruct + # - exportloopref + # - forbidigo + # - forcetypeassert + # - funlen + # - gci + # - ginkgolinter + # - gocheckcompilerdirectives + # - gochecknoglobals + # - gochecknoinits + # - gochecksumtype + # - gocognit + # - goconst + # - gocritic + # - gocyclo + # - godot + # - godox + # - gofmt + # - gofumpt + # - goheader + # - goimports + # - gomoddirectives + # - gomodguard + # - goprintffuncname + # - gosec + # - gosimple + # - gosmopolitan + # - govet + # - grouper + # - importas + # - inamedparam + # - ineffassign + # - interfacebloat + # - ireturn + # - lll + # - loggercheck + # - maintidx + # - makezero + # - mirror + # - misspell + # - musttag + # - nakedret + # - nestif + # - nilerr + # - nilnil + # - nlreturn + # - noctx + # - nolintlint + # - nonamedreturns + # - nosprintfhostport + # - paralleltest + # - perfsprint + # - prealloc + # - predeclared + # - promlinter + # - protogetter + # - reassign + # - revive + # - rowserrcheck + # - sloglint + # - sqlclosecheck + # - staticcheck + # - stylecheck + # - tagalign + # - tagliatelle + # - tenv + # - testableexamples + # - testifylint + # - testpackage + # - thelper + # - tparallel + # - unconvert + # - unparam + # - unused + # - usestdlibvars + # - varnamelen + # - wastedassign + # - whitespace + # - wrapcheck + # - wsl + # - zerologlint # Enable presets. # https://golangci-lint.run/usage/linters # Default: [] - presets: - - bugs - - comment - - complexity - - error - - format - - import - - metalinter - - module - - performance - - sql - - style - - test - - unused + # presets: + # - bugs + # - comment + # - complexity + # - error + # - format + # - import + # - metalinter + # - module + # - performance + # - sql + # - style + # - test + # - unused # Enable only fast linters from enabled linters set (first run won't be fast) # Default: false fast: true diff --git a/README.md b/README.md index 4390239..9fb4e7c 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Inspired by Rust's [clippy](https://github.com/rust-lang/rust-clippy), tlin aims ## Installation - Requirements: - - Go: 1.22 or higher + - Go: 1.24 or higher - latest version of gno - GNU Make 3.81 or higher (for building) diff --git a/go.mod b/go.mod index 2060538..d2cbd2d 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/gnolang/tlin -go 1.23.5 +go 1.24.0 require ( github.com/fatih/color v1.18.0 @@ -21,14 +21,14 @@ require ( github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/stretchr/objx v0.5.2 // indirect - github.com/tetratelabs/wazero v1.8.2 // indirect + github.com/tetratelabs/wazero v1.9.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/image v0.24.0 // indirect + golang.org/x/image v0.26.0 // indirect golang.org/x/mod v0.24.0 // indirect golang.org/x/sync v0.13.0 // indirect golang.org/x/sys v0.32.0 // indirect - golang.org/x/term v0.30.0 // indirect - golang.org/x/text v0.22.0 // indirect + golang.org/x/term v0.31.0 // indirect + golang.org/x/text v0.24.0 // indirect ) require ( diff --git a/go.sum b/go.sum index 2a41974..4e73f69 100644 --- a/go.sum +++ b/go.sum @@ -42,6 +42,8 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tetratelabs/wazero v1.8.2 h1:yIgLR/b2bN31bjxwXHD8a3d+BogigR952csSDdLYEv4= github.com/tetratelabs/wazero v1.8.2/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs= +github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I= +github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -51,6 +53,8 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ= golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8= +golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY= +golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c= golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= @@ -60,9 +64,13 @@ golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= +golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= +golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= diff --git a/internal/lints/default_golangci.go b/internal/lints/default_golangci.go index e0193fe..309cfec 100644 --- a/internal/lints/default_golangci.go +++ b/internal/lints/default_golangci.go @@ -2,11 +2,13 @@ package lints import ( "encoding/json" - _ "fmt" + "fmt" "go/ast" "go/parser" "go/token" + "os" "os/exec" + "path/filepath" tt "github.com/gnolang/tlin/internal/types" ) @@ -40,20 +42,37 @@ type golangciOutput struct { } func RunGolangciLint(filename string, _ *ast.File, _ *token.FileSet, severity tt.Severity) ([]tt.Issue, error) { - cmd := exec.Command("golangci-lint", "run", "--config=./.golangci.yml", "--out-format=json", filename) - output, _ := cmd.CombinedOutput() + // find config file in current working directory + wd, err := os.Getwd() + if err != nil { + return nil, fmt.Errorf("failed to get working directory: %v", err) + } - var golangciResult golangciOutput + configPath := filepath.Join(wd, ".golangci.yml") + if _, err := os.Stat(configPath); os.IsNotExist(err) { + return nil, fmt.Errorf("failed to find .golangci.yml file: %v", err) + } + + cmd := exec.Command("golangci-lint", "run", "--out-format=json", "--config="+configPath, filename) + output, err := cmd.CombinedOutput() - // @notJoon: Ignore Unmarshal error. We cannot unmarshal the output of golangci-lint - // when source code contains gno package imports (i.e. p/demo, r/demo, std). [07/25/24] - json.Unmarshal(output, &golangciResult) + // If golangci-lint finds issues, it returns a non-zero exit code, + // so we ignore the error if there is json output. + if err != nil && len(output) == 0 { + return nil, fmt.Errorf("failed to run golangci-lint: %v", err) + } + + var golangciResult golangciOutput + err = json.Unmarshal(output, &golangciResult) + if err != nil { + return nil, fmt.Errorf("failed to parse golangci-lint output: %v", err) + } issues := make([]tt.Issue, 0, len(golangciResult.Issues)) for _, gi := range golangciResult.Issues { issues = append(issues, tt.Issue{ Rule: gi.FromLinter, - Filename: gi.Pos.Filename, // Use the filename from golangci-lint output + Filename: gi.Pos.Filename, Start: token.Position{Filename: gi.Pos.Filename, Line: gi.Pos.Line, Column: gi.Pos.Column}, End: token.Position{Filename: gi.Pos.Filename, Line: gi.Pos.Line, Column: gi.Pos.Column + 1}, Message: gi.Text, diff --git a/internal/lints/default_golangci_test.go b/internal/lints/default_golangci_test.go new file mode 100644 index 0000000..063b23e --- /dev/null +++ b/internal/lints/default_golangci_test.go @@ -0,0 +1,80 @@ +package lints + +import ( + "os" + "path/filepath" + "testing" + + tt "github.com/gnolang/tlin/internal/types" +) + +func TestRunGolangciLint(t *testing.T) { + tmpDir := t.TempDir() + testFile := filepath.Join(tmpDir, "test.go") + + code := `package main + +import "fmt" + +func main() { + var unused int + fmt.Println("Hello, World!") +} +` + + if err := os.WriteFile(testFile, []byte(code), 0644); err != nil { + t.Fatalf("failed to create test file: %v", err) + } + + // temporary .golangci.yml file for testing + golangciConfig := `linters: + enable: + - unused + - typecheck +` + golangciFile := filepath.Join(tmpDir, ".golangci.yml") + if err := os.WriteFile(golangciFile, []byte(golangciConfig), 0644); err != nil { + t.Fatalf("failed to create golangci.yml file: %v", err) + } + + // change working directory to temporary directory + oldDir, err := os.Getwd() + if err != nil { + t.Fatalf("failed to get working directory: %v", err) + } + defer os.Chdir(oldDir) + + if err := os.Chdir(tmpDir); err != nil { + t.Fatalf("failed to change working directory: %v", err) + } + + node, fset, err := ParseFile(testFile, nil) + if err != nil { + t.Fatalf("failed to parse file: %v", err) + } + + issues, err := RunGolangciLint(testFile, node, fset, tt.SeverityWarning) + if err != nil { + t.Fatalf("failed to run golangci-lint: %v", err) + } + + if len(issues) == 0 { + t.Error("golangci-lint did not find any issues") + } + + for _, issue := range issues { + t.Logf("issue: %+v", issue) + } + + foundUnused := false + for _, issue := range issues { + if issue.Rule == "unused" || issue.Rule == "typecheck" { + foundUnused = true + break + } + } + + if !foundUnused { + t.Error("unused variable issue was not found") + } +} diff --git a/internal/rule_set.go b/internal/rule_set.go index f86213d..57bdaa9 100644 --- a/internal/rule_set.go +++ b/internal/rule_set.go @@ -15,13 +15,8 @@ type LintRule struct { name string } -func (r LintRule) Severity() tt.Severity { - return r.severity -} - -func (r LintRule) Name() string { - return r.name -} +func (r LintRule) Severity() tt.Severity { return r.severity } +func (r LintRule) Name() string { return r.name } func (r LintRule) Check(filename string, node *ast.File, fset *token.FileSet) ([]tt.Issue, error) { return r.check(filename, node, fset, r.severity) diff --git a/testdata/basic/basic0.gno b/testdata/basic/basic0.gno new file mode 100644 index 0000000..98aa69b --- /dev/null +++ b/testdata/basic/basic0.gno @@ -0,0 +1,8 @@ +package main + +import "fmt" + +func main() { + var unused int + fmt.Println("Hello, World!") +}