Skip to content

Commit

Permalink
Add linter
Browse files Browse the repository at this point in the history
  • Loading branch information
josephschorr committed Mar 1, 2024
1 parent 78eb98b commit de2e2da
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 0 deletions.
2 changes: 2 additions & 0 deletions tools/analyzers/cmd/analyzers/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"github.com/authzed/spicedb/tools/analyzers/closeafterusagecheck"
"github.com/authzed/spicedb/tools/analyzers/exprstatementcheck"
"github.com/authzed/spicedb/tools/analyzers/lendowncastcheck"
"github.com/authzed/spicedb/tools/analyzers/nilvaluecheck"
"github.com/authzed/spicedb/tools/analyzers/paniccheck"
"golang.org/x/tools/go/analysis/multichecker"
Expand All @@ -14,5 +15,6 @@ func main() {
exprstatementcheck.Analyzer(),
closeafterusagecheck.Analyzer(),
paniccheck.Analyzer(),
lendowncastcheck.Analyzer(),
)
}
126 changes: 126 additions & 0 deletions tools/analyzers/lendowncastcheck/lendowncastcheck.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package lendowncastcheck

import (
"flag"
"fmt"
"go/ast"
"regexp"
"strings"

"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
"golang.org/x/tools/go/ast/inspector"
)

func sliceMap(s []string, f func(value string) string) []string {
mapped := make([]string, 0, len(s))
for _, value := range s {
mapped = append(mapped, f(value))
}
return mapped
}

var disallowedDowncastTypes = map[string]bool{
"int8": true,
"int16": true,
"int32": true,
"int64": true,
"uint": true,
"uint8": true,
"uint16": true,
"uint32": true,
"float32": true,
"float64": true,
}

func Analyzer() *analysis.Analyzer {
flagSet := flag.NewFlagSet("lendowncastcheck", flag.ExitOnError)
skipPkg := flagSet.String("skip-pkg", "", "package(s) to skip for linting")
skipFiles := flagSet.String("skip-files", "", "patterns of files to skip for linting")

return &analysis.Analyzer{
Name: "lendowncastcheck",
Doc: "reports downcasting of len() calls",
Run: func(pass *analysis.Pass) (any, error) {
// Check for a skipped package.
if len(*skipPkg) > 0 {
skipped := sliceMap(strings.Split(*skipPkg, ","), strings.TrimSpace)
for _, s := range skipped {
if strings.Contains(pass.Pkg.Path(), s) {
return nil, nil
}
}
}

// Check for a skipped file.
skipFilePatterns := make([]string, 0)
if len(*skipFiles) > 0 {
skipFilePatterns = sliceMap(strings.Split(*skipFiles, ","), strings.TrimSpace)
}
for _, pattern := range skipFilePatterns {
_, err := regexp.Compile(pattern)
if err != nil {
return nil, fmt.Errorf("invalid skip-files pattern `%s`: %w", pattern, err)
}
}

inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)

nodeFilter := []ast.Node{
(*ast.File)(nil),
(*ast.CallExpr)(nil),
}

inspect.WithStack(nodeFilter, func(n ast.Node, push bool, stack []ast.Node) bool {
switch s := n.(type) {
case *ast.File:
for _, pattern := range skipFilePatterns {
isMatch, _ := regexp.MatchString(pattern, pass.Fset.Position(s.Package).Filename)
if isMatch {
return false
}
}
return true

case *ast.CallExpr:
identExpr, ok := s.Fun.(*ast.Ident)
if !ok {
return false
}

if _, ok := disallowedDowncastTypes[identExpr.Name]; !ok {
return false
}

if len(s.Args) != 1 {
return false
}

childExpr, ok := s.Args[0].(*ast.CallExpr)
if !ok {
return false
}

childIdentExpr, ok := childExpr.Fun.(*ast.Ident)
if !ok {
return false
}

if childIdentExpr.Name != "len" {
return false
}

pass.Reportf(s.Pos(), "In package %s: found downcast of `len` call to %s", pass.Pkg.Path(), identExpr.Name)
return false

default:
return true
}
})

return nil, nil
},
Requires: []*analysis.Analyzer{inspect.Analyzer},
Flags: *flagSet,
}
}
14 changes: 14 additions & 0 deletions tools/analyzers/lendowncastcheck/lendowncastcheck_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package lendowncastcheck

import (
"testing"

"golang.org/x/tools/go/analysis/analysistest"
)

func TestAnalyzer(t *testing.T) {
analyzer := Analyzer()

testdata := analysistest.TestData()
analysistest.Run(t, testdata, analyzer, "lenexamples")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package lenexamples

import "fmt"

func DoSomething(someSlice []string) uint64 {
return uint64(len(someSlice))
}

func DoSomethingBad(someSlice []string) uint32 {
v := uint32(len(someSlice)) // want "found downcast of `len` call to uint32"
return v
}

func DoSomethingBad16(someSlice []string) uint16 {
v := uint16(len(someSlice)) // want "found downcast of `len` call to uint16"
return v
}

func DoSomeLoop(someSlice []string) {
for i := 0; i < len(someSlice); i++ {
fmt.Println(someSlice[i])
}
}

0 comments on commit de2e2da

Please sign in to comment.