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

249 lines
6.8 KiB
Go

package lintpack
import (
"fmt"
"go/ast"
"go/token"
"go/types"
"github.com/go-toolsmith/astfmt"
)
// CheckerCollection provides additional information for a group of checkers.
type CheckerCollection struct {
// URL is a link for a main source of information on the collection.
URL string
}
// AddChecker registers a new checker into a checkers pool.
// Constructor is used to create a new checker instance.
// Checker name (defined in CheckerInfo.Name) must be unique.
//
// CheckerInfo.Collection is automatically set to the coll (the receiver).
//
// If checker is never needed, for example if it is disabled,
// constructor will not be called.
func (coll *CheckerCollection) AddChecker(info *CheckerInfo, constructor func(*CheckerContext) FileWalker) {
if coll == nil {
panic(fmt.Sprintf("adding checker to a nil collection"))
}
info.Collection = coll
addChecker(info, constructor)
}
// CheckerParam describes a single checker customizable parameter.
type CheckerParam struct {
// Value holds parameter bound value.
// It might be overwritten by the integrating linter.
//
// Permitted types include:
// - int
// - bool
// - string
Value interface{}
// Usage gives an overview about what parameter does.
Usage string
}
// CheckerParams holds all checker-specific parameters.
//
// Provides convenient access to the loosely typed underlying map.
type CheckerParams map[string]*CheckerParam
// Int lookups pname key in underlying map and type-asserts it to int.
func (params CheckerParams) Int(pname string) int { return params[pname].Value.(int) }
// Bool lookups pname key in underlying map and type-asserts it to bool.
func (params CheckerParams) Bool(pname string) bool { return params[pname].Value.(bool) }
// String lookups pname key in underlying map and type-asserts it to string.
func (params CheckerParams) String(pname string) string { return params[pname].Value.(string) }
// CheckerInfo holds checker metadata and structured documentation.
type CheckerInfo struct {
// Name is a checker name.
Name string
// Tags is a list of labels that can be used to enable or disable checker.
// Common tags are "experimental" and "performance".
Tags []string
// Params declares checker-specific parameters. Optional.
Params CheckerParams
// Summary is a short one sentence description.
// Should not end with a period.
Summary string
// Details extends summary with additional info. Optional.
Details string
// Before is a code snippet of code that will violate rule.
Before string
// After is a code snippet of fixed code that complies to the rule.
After string
// Note is an optional caution message or advice.
Note string
// Collection establishes a checker-to-collection relationship.
Collection *CheckerCollection
}
// GetCheckersInfo returns a checkers info list for all registered checkers.
// The slice is sorted by a checker name.
//
// Info objects can be used to instantiate checkers with NewChecker function.
func GetCheckersInfo() []*CheckerInfo {
return getCheckersInfo()
}
// HasTag reports whether checker described by the info has specified tag.
func (info *CheckerInfo) HasTag(tag string) bool {
for i := range info.Tags {
if info.Tags[i] == tag {
return true
}
}
return false
}
// Checker is an implementation of a check that is described by the associated info.
type Checker struct {
// Info is an info object that was used to instantiate this checker.
Info *CheckerInfo
ctx CheckerContext
fileWalker FileWalker
}
// Check runs rule checker over file f.
func (c *Checker) Check(f *ast.File) []Warning {
c.ctx.warnings = c.ctx.warnings[:0]
c.fileWalker.WalkFile(f)
return c.ctx.warnings
}
// Warning represents issue that is found by checker.
type Warning struct {
// Node is an AST node that caused warning to trigger.
// Can be used to obtain proper error location.
Node ast.Node
// Text is warning message without source location info.
Text string
}
// NewChecker returns initialized checker identified by an info.
// info must be non-nil.
// Panics if info describes a checker that was not properly registered.
func NewChecker(ctx *Context, info *CheckerInfo) *Checker {
return newChecker(ctx, info)
}
// Context is a readonly state shared among every checker.
type Context struct {
// TypesInfo carries parsed packages types information.
TypesInfo *types.Info
// SizesInfo carries alignment and type size information.
// Arch-dependent.
SizesInfo types.Sizes
// FileSet is a file set that was used during the program loading.
FileSet *token.FileSet
// Pkg describes package that is being checked.
Pkg *types.Package
// Filename is a currently checked file name.
Filename string
// Require records what optional resources are required
// by the checkers set that use this context.
//
// Every require fields makes associated context field
// to be properly initialized.
// For example, Context.require.PkgObjects => Context.PkgObjects.
Require struct {
PkgObjects bool
PkgRenames bool
}
// PkgObjects stores all imported packages and their local names.
PkgObjects map[*types.PkgName]string
// PkgRenames maps package path to its local renaming.
// Contains no entries for packages that were imported without
// explicit local names.
PkgRenames map[string]string
}
// NewContext returns new shared context to be used by every checker.
//
// All data carried by the context is readonly for checkers,
// but can be modified by the integrating application.
func NewContext(fset *token.FileSet, sizes types.Sizes) *Context {
return &Context{
FileSet: fset,
SizesInfo: sizes,
TypesInfo: &types.Info{},
}
}
// SetPackageInfo sets package-related metadata.
//
// Must be called for every package being checked.
func (c *Context) SetPackageInfo(info *types.Info, pkg *types.Package) {
if info != nil {
// We do this kind of assignment to avoid
// changing c.typesInfo field address after
// every re-assignment.
*c.TypesInfo = *info
}
c.Pkg = pkg
}
// SetFileInfo sets file-related metadata.
//
// Must be called for every source code file being checked.
func (c *Context) SetFileInfo(name string, f *ast.File) {
c.Filename = name
if c.Require.PkgObjects {
resolvePkgObjects(c, f)
}
if c.Require.PkgRenames {
resolvePkgRenames(c, f)
}
}
// CheckerContext is checker-local context copy.
// Fields that are not from Context itself are writeable.
type CheckerContext struct {
*Context
// printer used to format warning text.
printer *astfmt.Printer
warnings []Warning
}
// Warn adds a Warning to checker output.
func (ctx *CheckerContext) Warn(node ast.Node, format string, args ...interface{}) {
ctx.warnings = append(ctx.warnings, Warning{
Text: ctx.printer.Sprintf(format, args...),
Node: node,
})
}
// FileWalker is an interface every checker should implement.
//
// The WalkFile method is executed for every Go file inside the
// package that is being checked.
type FileWalker interface {
WalkFile(*ast.File)
}