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

checkers: add ruleguard checker #907

Merged
merged 1 commit into from Mar 18, 2020
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
95 changes: 95 additions & 0 deletions checkers/ruleguard_checker.go
@@ -0,0 +1,95 @@
package checkers

import (
"bytes"
"go/ast"
"go/token"
"io/ioutil"
"log"

"github.com/go-lintpack/lintpack"
"github.com/quasilyte/go-ruleguard/ruleguard"
)

func init() {
var info lintpack.CheckerInfo
info.Name = "ruleguard"
info.Tags = []string{"style", "experimental"}
info.Params = lintpack.CheckerParams{
"rules": {
Value: "",
Usage: "path to a gorules file",
},
}
info.Summary = "Runs user-defined rules using ruleguard linter"
info.Details = "Reads a rules file and turns them into go-critic checkers."
info.Before = `N/A`
info.After = `N/A`
info.Note = "See https://github.com/quasilyte/go-ruleguard."

collection.AddChecker(&info, func(ctx *lintpack.CheckerContext) lintpack.FileWalker {
return newRuleguardChecker(&info, ctx)
})
}

func newRuleguardChecker(info *lintpack.CheckerInfo, ctx *lintpack.CheckerContext) *ruleguardChecker {
c := &ruleguardChecker{ctx: ctx}
rulesFilename := info.Params.String("rules")
if rulesFilename == "" {
return c
}

// TODO(quasilyte): handle initialization errors better when we make
// a transition to the go/analysis framework.
//
// For now, we log error messages and return a ruleguard checker
// with an empty rules set.

data, err := ioutil.ReadFile(rulesFilename)
if err != nil {
log.Printf("ruleguard init error: %+v", err)
return c
}

fset := token.NewFileSet()
rset, err := ruleguard.ParseRules(rulesFilename, fset, bytes.NewReader(data))
if err != nil {
log.Printf("ruleguard init error: %+v", err)
return c
}

c.rset = rset
return c
}

type ruleguardChecker struct {
ctx *lintpack.CheckerContext

rset *ruleguard.GoRuleSet
}

func (c *ruleguardChecker) WalkFile(f *ast.File) {
if c.rset == nil {
return
}

ctx := &ruleguard.Context{
Pkg: c.ctx.Pkg,
Types: c.ctx.TypesInfo,
Sizes: c.ctx.SizesInfo,
Fset: c.ctx.FileSet,
Report: func(n ast.Node, msg string, _ *ruleguard.Suggestion) {
// TODO(quasilyte): investigate whether we should add a rule name as
Copy link
Member

Choose a reason for hiding this comment

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

imo - yes :) but it's a minor one

// a message prefix here.
c.ctx.Warn(n, msg)
},
}

err := ruleguard.RunRules(ctx, f, c.rset)
if err != nil {
// Normally this should never happen, but since
// we don't have a better mechanism to report errors,
// emit a warning.
c.ctx.Warn(f, "execution error: %v", err)
}
}
10 changes: 10 additions & 0 deletions checkers/testdata/_integration/ruleguard/f1.go
@@ -0,0 +1,10 @@
package foo

import "fmt"

type myError error

func _() {
var s1, s2 string
var _ = fmt.Sprintf("%s%s", s1, s2)
}
3 changes: 3 additions & 0 deletions checkers/testdata/_integration/ruleguard/linttest.golden
@@ -0,0 +1,3 @@
exit status 1
./f1.go:5:1: ruleguard: error as an underlying type is probably a mistake
./f1.go:9:10: ruleguard: suggestion: s1+s2
1 change: 1 addition & 0 deletions checkers/testdata/_integration/ruleguard/linttest.params
@@ -0,0 +1 @@
check -@ruleguard.rules ./rules.go -enable ruleguard ./... | linttest.golden
15 changes: 15 additions & 0 deletions checkers/testdata/_integration/ruleguard/rules.go
@@ -0,0 +1,15 @@
// +build ignore

package gorules

import "github.com/quasilyte/go-ruleguard/dsl/fluent"

func _(m fluent.Matcher) {
m.Match(`type $x error`).
Report(`error as an underlying type is probably a mistake`).
Suggest(`type $x struct { error }`)

m.Match(`fmt.Sprintf("%s%s", $a, $b)`).
Where(m["a"].Type.Is(`string`) && m["b"].Type.Is(`string`)).
Suggest(`$a+$b`)
}
1 change: 1 addition & 0 deletions go.mod
Expand Up @@ -14,5 +14,6 @@ require (
github.com/mattn/goveralls v0.0.2
github.com/pborman/uuid v1.2.0 // indirect
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c
github.com/quasilyte/go-ruleguard v0.1.2-0.20200318202121-b00d7a75d3d8
golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd
)
6 changes: 6 additions & 0 deletions go.sum
Expand Up @@ -35,6 +35,12 @@ github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c h1:JoUA0uz9U0FVFq5p4LjEq4C0VgQ0El320s3Ms0V4eww=
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
github.com/quasilyte/go-ruleguard v0.1.1 h1:L+vAaUaPT/iI7p6vl1691zTC5EAAOZE+Fous1k6e9IE=
github.com/quasilyte/go-ruleguard v0.1.1/go.mod h1:CGFX09Ci3pq9QZdj86B+VGIdNj4VyCo2iPOGS9esB/k=
github.com/quasilyte/go-ruleguard v0.1.2-0.20200318181856-dc8aae974065 h1:JViY6/0dUURSKOyIRZZo1wSn5fbQzB8Y4uSIrV8gIT0=
github.com/quasilyte/go-ruleguard v0.1.2-0.20200318181856-dc8aae974065/go.mod h1:CGFX09Ci3pq9QZdj86B+VGIdNj4VyCo2iPOGS9esB/k=
github.com/quasilyte/go-ruleguard v0.1.2-0.20200318202121-b00d7a75d3d8 h1:DvnesvLtRPQOvaUbfXfh0tpMHg29by0H7F2U+QIkSu8=
github.com/quasilyte/go-ruleguard v0.1.2-0.20200318202121-b00d7a75d3d8/go.mod h1:CGFX09Ci3pq9QZdj86B+VGIdNj4VyCo2iPOGS9esB/k=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand Down