2021-11-14 20:01:54 +00:00
|
|
|
package ruleguard
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"go/ast"
|
|
|
|
"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
|
|
|
|
|
2021-11-16 20:07:53 +00:00
|
|
|
groups map[string]*GoRuleGroup // To handle redefinitions
|
2021-11-14 20:01:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type scopedGoRuleSet struct {
|
|
|
|
categorizedNum int
|
|
|
|
rulesByTag [nodetag.NumBuckets][]goRule
|
|
|
|
commentRules []goCommentRule
|
|
|
|
}
|
|
|
|
|
|
|
|
type goCommentRule struct {
|
|
|
|
base goRule
|
|
|
|
pat *regexp.Regexp
|
|
|
|
captureGroups bool
|
|
|
|
}
|
|
|
|
|
|
|
|
type goRule struct {
|
2021-11-16 20:07:53 +00:00
|
|
|
group *GoRuleGroup
|
2021-11-14 20:01:54 +00:00
|
|
|
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
|
|
|
|
|
2021-11-16 20:07:53 +00:00
|
|
|
match matchData
|
|
|
|
nodePath *nodePath
|
2021-11-14 20:01:54 +00:00
|
|
|
|
|
|
|
nodeText func(n ast.Node) []byte
|
|
|
|
|
2021-11-16 20:07:53 +00:00
|
|
|
deadcode bool
|
|
|
|
|
2021-11-14 20:01:54 +00:00
|
|
|
// 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 mergeRuleSets(toMerge []*goRuleSet) (*goRuleSet, error) {
|
|
|
|
out := &goRuleSet{
|
|
|
|
universal: &scopedGoRuleSet{},
|
2021-11-16 20:07:53 +00:00
|
|
|
groups: make(map[string]*GoRuleGroup),
|
2021-11-14 20:01:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, x := range toMerge {
|
|
|
|
out.universal = appendScopedRuleSet(out.universal, x.universal)
|
2021-11-16 20:07:53 +00:00
|
|
|
for groupName, group := range x.groups {
|
|
|
|
if prevGroup, ok := out.groups[groupName]; ok {
|
|
|
|
newRef := fmt.Sprintf("%s:%d", group.Filename, group.Line)
|
|
|
|
oldRef := fmt.Sprintf("%s:%d", prevGroup.Filename, prevGroup.Line)
|
|
|
|
return nil, fmt.Errorf("%s: redefinition of %s(), previously defined at %s", newRef, groupName, oldRef)
|
2021-11-14 20:01:54 +00:00
|
|
|
}
|
2021-11-16 20:07:53 +00:00
|
|
|
out.groups[groupName] = group
|
2021-11-14 20:01:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|