David López 37e6995b45 lintpack/gocritic: update lintpack & gocritic versions
update lintpack & gocritic versions to support all the new gocritic checks
2018-12-22 15:34:16 +03:00

136 lines
3.0 KiB
Go

package lintpack
import (
"fmt"
"regexp"
"sort"
"strings"
"github.com/go-toolsmith/astfmt"
)
type checkerProto struct {
info *CheckerInfo
constructor func(*Context) *Checker
}
// prototypes is a set of registered checkers that are not yet instantiated.
// Registration should be done with AddChecker function.
// Initialized checkers can be obtained with NewChecker function.
var prototypes = make(map[string]checkerProto)
func getCheckersInfo() []*CheckerInfo {
infoList := make([]*CheckerInfo, 0, len(prototypes))
for _, proto := range prototypes {
infoCopy := *proto.info
infoList = append(infoList, &infoCopy)
}
sort.Slice(infoList, func(i, j int) bool {
return infoList[i].Name < infoList[j].Name
})
return infoList
}
func addChecker(info *CheckerInfo, constructor func(*CheckerContext) FileWalker) {
if _, ok := prototypes[info.Name]; ok {
panic(fmt.Sprintf("checker with name %q already registered", info.Name))
}
// Validate param value type.
for pname, param := range info.Params {
switch param.Value.(type) {
case string, int, bool:
// OK.
default:
panic(fmt.Sprintf("unsupported %q param type value: %T",
pname, param.Value))
}
}
trimDocumentation := func(info *CheckerInfo) {
fields := []*string{
&info.Summary,
&info.Details,
&info.Before,
&info.After,
&info.Note,
}
for _, f := range fields {
*f = strings.TrimSpace(*f)
}
}
trimDocumentation(info)
if err := validateCheckerInfo(info); err != nil {
panic(err)
}
proto := checkerProto{
info: info,
constructor: func(ctx *Context) *Checker {
var c Checker
c.Info = info
c.ctx = CheckerContext{
Context: ctx,
printer: astfmt.NewPrinter(ctx.FileSet),
}
c.fileWalker = constructor(&c.ctx)
return &c
},
}
prototypes[info.Name] = proto
}
func newChecker(ctx *Context, info *CheckerInfo) *Checker {
proto, ok := prototypes[info.Name]
if !ok {
panic(fmt.Sprintf("checker with name %q not registered", info.Name))
}
return proto.constructor(ctx)
}
func validateCheckerInfo(info *CheckerInfo) error {
steps := []func(*CheckerInfo) error{
validateCheckerName,
validateCheckerDocumentation,
validateCheckerTags,
}
for _, step := range steps {
if err := step(info); err != nil {
return fmt.Errorf("%q validation error: %v", info.Name, err)
}
}
return nil
}
var validIdentRE = regexp.MustCompile(`^\w+$`)
func validateCheckerName(info *CheckerInfo) error {
if !validIdentRE.MatchString(info.Name) {
return fmt.Errorf("checker name contains illegal chars")
}
return nil
}
func validateCheckerDocumentation(info *CheckerInfo) error {
// TODO(Quasilyte): validate documentation.
return nil
}
func validateCheckerTags(info *CheckerInfo) error {
tagSet := make(map[string]bool)
for _, tag := range info.Tags {
if tagSet[tag] {
return fmt.Errorf("duplicated tag %q", tag)
}
if !validIdentRE.MatchString(tag) {
return fmt.Errorf("checker tag %q contains illegal chars", tag)
}
tagSet[tag] = true
}
return nil
}