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

Filter out undefined issues in lint engine #3

Merged
merged 1 commit into from
Jul 16, 2024
Merged
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
21 changes: 13 additions & 8 deletions cmd/tlin/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,22 @@ func main() {

args := flag.Args()
if len(args) == 0 {
fmt.Println("Error: Please provide file or directory paths")
fmt.Println("error: Please provide file or directory paths")
os.Exit(1)
}

engine := lint.NewEngine()
var allIssues []lint.Issue
rootDir := "."
engine, err := lint.NewEngine(rootDir)
if err != nil {
fmt.Printf("error initializing lint engine: %v\n", err)
os.Exit(1)
}

var allIssues []lint.Issue
for _, path := range args {
info, err := os.Stat(path)
if err != nil {
fmt.Printf("Error accessing %s: %v\n", path, err)
fmt.Printf("error accessing %s: %v\n", path, err)
continue
}

Expand All @@ -39,26 +44,26 @@ func main() {
if !fileInfo.IsDir() && filepath.Ext(filePath) == ".go" || filepath.Ext(filePath) == ".gno" {
issues, err := processFile(engine, filePath)
if err != nil {
fmt.Printf("Error processing %s: %v\n", filePath, err)
fmt.Printf("error processing %s: %v\n", filePath, err)
} else {
allIssues = append(allIssues, issues...)
}
}
return nil
})
if err != nil {
fmt.Printf("Error walking directory %s: %v\n", path, err)
fmt.Printf("error walking directory %s: %v\n", path, err)
}
} else {
if filepath.Ext(path) == ".go" || filepath.Ext(path) == ".gno" {
issues, err := processFile(engine, path)
if err != nil {
fmt.Printf("Error processing %s: %v\n", path, err)
fmt.Printf("error processing %s: %v\n", path, err)
} else {
allIssues = append(allIssues, issues...)
}
} else {
fmt.Printf("Skipping non-.co file: %s\n", path)
fmt.Printf("skipping non-.co file: %s\n", path)
}
}
}
Expand Down
8 changes: 8 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,16 @@ go 1.22.2

require github.com/stretchr/testify v1.9.0

require (
github.com/BurntSushi/toml v1.2.1 // indirect
golang.org/x/mod v0.19.0 // indirect
golang.org/x/sync v0.7.0 // indirect
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/tools v0.23.0
gopkg.in/yaml.v3 v3.0.1 // indirect
honnef.co/go/tools v0.4.7
)
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.4.7 h1:9MDAWxMoSnB6QoSqiVr7P5mtkT9pOc1kSxchzPCnqJs=
honnef.co/go/tools v0.4.7/go.mod h1:+rnGS1THNh8zMwnd2oVOTL9QF6vmfyG6ZXBULae2uc0=
30 changes: 26 additions & 4 deletions internal/lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,17 @@ type Issue struct {
}

// Engine manages the linting process.
type Engine struct{}
type Engine struct {
SymbolTable *SymbolTable
}

// NewEngine creates a new lint engine.
func NewEngine() *Engine {
return &Engine{}
func NewEngine(rootDir string) (*Engine, error) {
st, err := BuildSymbolTable(rootDir)
if err != nil {
return nil, fmt.Errorf("error building symbol table: %w", err)
}
return &Engine{SymbolTable: st}, nil
}

// Run applies golangci-lint to the given file and returns a slice of issues.
Expand All @@ -47,7 +53,23 @@ func (e *Engine) Run(filename string) ([]Issue, error) {
if err != nil {
return nil, fmt.Errorf("error running golangci-lint: %w", err)
}
return issues, nil
filtered := e.filterUndefinedIssues(issues)
return filtered, nil
}

func (e *Engine) filterUndefinedIssues(issues []Issue) []Issue {
var filtered []Issue
for _, issue := range issues {
if issue.Rule == "typecheck" && strings.Contains(issue.Message, "undefined:") {
symbol := strings.TrimSpace(strings.TrimPrefix(issue.Message, "undefined:"))
if e.SymbolTable.IsDefined(symbol) {
// ignore issues if the symbol is defined in the symbol table
continue
}
}
filtered = append(filtered, issue)
}
return filtered
}

type golangciOutput struct {
Expand Down
7 changes: 6 additions & 1 deletion internal/lint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
)

func TestIntegratedLintEngine(t *testing.T) {
t.Skip("skipping integrated lint engine test")
tests := []struct {
name string
code string
Expand Down Expand Up @@ -68,7 +69,11 @@ func main() {
err = os.WriteFile(tmpfile, []byte(tt.code), 0o644)
require.NoError(t, err)

engine := NewEngine()
rootDir := "."
engine, err := NewEngine(rootDir)
if err != nil {
t.Fatalf("unexpected error initializing lint engine: %v", err)
}

issues, err := engine.Run(tmpfile)
require.NoError(t, err)
Expand Down
64 changes: 64 additions & 0 deletions internal/symbol_table.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package internal

import (
"go/ast"
"go/parser"
"go/token"
"os"
"path/filepath"
"strings"
)

type SymbolTable struct {
symbols map[string]string // symbol name -> file path
}

func BuildSymbolTable(rootDir string) (*SymbolTable, error) {
st := &SymbolTable{symbols: make(map[string]string)}
err := filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && (strings.HasSuffix(path, ".go") || strings.HasSuffix(path, ".gno")) {
if err := st.parseFile(path); err != nil {
return err
}
}
return err
})
return st, err
}

func (st *SymbolTable) parseFile(filepath string) error {
fset := token.NewFileSet()
node, err := parser.ParseFile(fset, filepath, nil, parser.AllErrors)
if err != nil {
return err
}

ast.Inspect(node, func(n ast.Node) bool {
switch x := n.(type) {
case *ast.TypeSpec:
st.symbols[x.Name.Name] = filepath
case *ast.FuncDecl:
st.symbols[x.Name.Name] = filepath
case *ast.ValueSpec:
for _, ident := range x.Names {
st.symbols[ident.Name] = filepath
}
}
return true
})

return nil
}

func (st *SymbolTable) IsDefined(symbol string) bool {
_, exists := st.symbols[symbol]
return exists
}

func (st *SymbolTable) GetSymbolPath(symbol string) (string, bool) {
path, exists := st.symbols[symbol]
return path, exists
}
55 changes: 55 additions & 0 deletions internal/symbol_table_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package internal

import (
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestSymbolTable(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "symboltable-test")
require.NoError(t, err)
defer os.RemoveAll(tmpDir)

// generate test files
file1Content := `package test
type TestStruct struct {}
func TestFunc() {}
var TestVar int
`
err = os.WriteFile(filepath.Join(tmpDir, "file1.go"), []byte(file1Content), 0o644)
require.NoError(t, err)

file2Content := `package test
type AnotherStruct struct {}
func AnotherFunc() {}
`
err = os.WriteFile(filepath.Join(tmpDir, "file2.go"), []byte(file2Content), 0o644)
require.NoError(t, err)

// create symbol table
st, err := BuildSymbolTable(tmpDir)
require.NoError(t, err)

assert.True(t, st.IsDefined("TestStruct"))
assert.True(t, st.IsDefined("TestFunc"))
assert.True(t, st.IsDefined("TestVar"))
assert.True(t, st.IsDefined("AnotherStruct"))
assert.True(t, st.IsDefined("AnotherFunc"))
assert.False(t, st.IsDefined("NonExistentSymbol"))

// validate symbol file paths
path, exists := st.GetSymbolPath("TestStruct")
assert.True(t, exists)
assert.Equal(t, filepath.Join(tmpDir, "file1.go"), path)

path, exists = st.GetSymbolPath("AnotherFunc")
assert.True(t, exists)
assert.Equal(t, filepath.Join(tmpDir, "file2.go"), path)

_, exists = st.GetSymbolPath("NonExistentSymbol")
assert.False(t, exists)
}
Loading