woodpecker/vendor/github.com/quasilyte/go-ruleguard/ruleguard/gorule.go

147 lines
3.1 KiB
Go
Raw Normal View History

package ruleguard
import (
"fmt"
"go/ast"
"go/token"
"go/types"
"regexp"
"github.com/quasilyte/go-ruleguard/internal/gogrep"
"github.com/quasilyte/go-ruleguard/nodetag"
"github.com/quasilyte/go-ruleguard/ruleguard/quasigo"
)
type goRuleSet struct {
universal *scopedGoRuleSet
groups map[string]token.Position // To handle redefinitions
}
type scopedGoRuleSet struct {
categorizedNum int
rulesByTag [nodetag.NumBuckets][]goRule
commentRules []goCommentRule
}
type goCommentRule struct {
base goRule
pat *regexp.Regexp
captureGroups bool
}
type goRule struct {
group string
filename string
line int
pat *gogrep.Pattern
msg string
location string
suggestion string
filter matchFilter
}
type matchFilterResult string
func (s matchFilterResult) Matched() bool { return s == "" }
func (s matchFilterResult) RejectReason() string { return string(s) }
type filterFunc func(*filterParams) matchFilterResult
type matchFilter struct {
src string
fn func(*filterParams) matchFilterResult
}
type filterParams struct {
ctx *RunContext
filename string
imports map[string]struct{}
env *quasigo.EvalEnv
importer *goImporter
match matchData
nodeText func(n ast.Node) []byte
// varname is set only for custom filters before bytecode function is called.
varname string
}
func (params *filterParams) subNode(name string) ast.Node {
n, _ := params.match.CapturedByName(name)
return n
}
func (params *filterParams) subExpr(name string) ast.Expr {
n, _ := params.match.CapturedByName(name)
switch n := n.(type) {
case ast.Expr:
return n
case *ast.ExprStmt:
return n.X
default:
return nil
}
}
func (params *filterParams) typeofNode(n ast.Node) types.Type {
if e, ok := n.(ast.Expr); ok {
if typ := params.ctx.Types.TypeOf(e); typ != nil {
return typ
}
}
return types.Typ[types.Invalid]
}
func cloneRuleSet(rset *goRuleSet) *goRuleSet {
out, err := mergeRuleSets([]*goRuleSet{rset})
if err != nil {
panic(err) // Should never happen
}
return out
}
func mergeRuleSets(toMerge []*goRuleSet) (*goRuleSet, error) {
out := &goRuleSet{
universal: &scopedGoRuleSet{},
groups: make(map[string]token.Position),
}
for _, x := range toMerge {
out.universal = appendScopedRuleSet(out.universal, x.universal)
for group, pos := range x.groups {
if prevPos, ok := out.groups[group]; ok {
newRef := fmt.Sprintf("%s:%d", pos.Filename, pos.Line)
oldRef := fmt.Sprintf("%s:%d", prevPos.Filename, prevPos.Line)
return nil, fmt.Errorf("%s: redefinition of %s(), previously defined at %s", newRef, group, oldRef)
}
out.groups[group] = pos
}
}
return out, nil
}
func appendScopedRuleSet(dst, src *scopedGoRuleSet) *scopedGoRuleSet {
for tag, rules := range src.rulesByTag {
dst.rulesByTag[tag] = append(dst.rulesByTag[tag], cloneRuleSlice(rules)...)
dst.categorizedNum += len(rules)
}
dst.commentRules = append(dst.commentRules, src.commentRules...)
return dst
}
func cloneRuleSlice(slice []goRule) []goRule {
out := make([]goRule, len(slice))
for i, rule := range slice {
clone := rule
clone.pat = rule.pat.Clone()
out[i] = clone
}
return out
}