aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/mgechev/revive/lint/package.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/mgechev/revive/lint/package.go')
-rw-r--r--vendor/github.com/mgechev/revive/lint/package.go178
1 files changed, 178 insertions, 0 deletions
diff --git a/vendor/github.com/mgechev/revive/lint/package.go b/vendor/github.com/mgechev/revive/lint/package.go
new file mode 100644
index 0000000000..7b6046fd7e
--- /dev/null
+++ b/vendor/github.com/mgechev/revive/lint/package.go
@@ -0,0 +1,178 @@
+package lint
+
+import (
+ "go/ast"
+ "go/token"
+ "go/types"
+ "sync"
+
+ "golang.org/x/tools/go/gcexportdata"
+)
+
+// Package represents a package in the project.
+type Package struct {
+ fset *token.FileSet
+ files map[string]*File
+
+ TypesPkg *types.Package
+ TypesInfo *types.Info
+
+ // sortable is the set of types in the package that implement sort.Interface.
+ Sortable map[string]bool
+ // main is whether this is a "main" package.
+ main int
+ mu sync.Mutex
+}
+
+var newImporter = func(fset *token.FileSet) types.ImporterFrom {
+ return gcexportdata.NewImporter(fset, make(map[string]*types.Package))
+}
+
+var (
+ trueValue = 1
+ falseValue = 2
+ notSet = 3
+)
+
+// IsMain returns if that's the main package.
+func (p *Package) IsMain() bool {
+ if p.main == trueValue {
+ return true
+ } else if p.main == falseValue {
+ return false
+ }
+ for _, f := range p.files {
+ if f.isMain() {
+ p.main = trueValue
+ return true
+ }
+ }
+ p.main = falseValue
+ return false
+}
+
+// TypeCheck performs type checking for given package.
+func (p *Package) TypeCheck() error {
+ p.mu.Lock()
+ // If type checking has already been performed
+ // skip it.
+ if p.TypesInfo != nil || p.TypesPkg != nil {
+ p.mu.Unlock()
+ return nil
+ }
+ config := &types.Config{
+ // By setting a no-op error reporter, the type checker does as much work as possible.
+ Error: func(error) {},
+ Importer: newImporter(p.fset),
+ }
+ info := &types.Info{
+ Types: make(map[ast.Expr]types.TypeAndValue),
+ Defs: make(map[*ast.Ident]types.Object),
+ Uses: make(map[*ast.Ident]types.Object),
+ Scopes: make(map[ast.Node]*types.Scope),
+ }
+ var anyFile *File
+ var astFiles []*ast.File
+ for _, f := range p.files {
+ anyFile = f
+ astFiles = append(astFiles, f.AST)
+ }
+
+ typesPkg, err := check(config, anyFile.AST.Name.Name, p.fset, astFiles, info)
+
+ // Remember the typechecking info, even if config.Check failed,
+ // since we will get partial information.
+ p.TypesPkg = typesPkg
+ p.TypesInfo = info
+ p.mu.Unlock()
+ return err
+}
+
+// check function encapsulates the call to go/types.Config.Check method and
+// recovers if the called method panics (see issue #59)
+func check(config *types.Config, n string, fset *token.FileSet, astFiles []*ast.File, info *types.Info) (p *types.Package, err error) {
+ defer func() {
+ if r := recover(); r != nil {
+ err, _ = r.(error)
+ p = nil
+ return
+ }
+ }()
+
+ return config.Check(n, fset, astFiles, info)
+}
+
+// TypeOf returns the type of an expression.
+func (p *Package) TypeOf(expr ast.Expr) types.Type {
+ if p.TypesInfo == nil {
+ return nil
+ }
+ return p.TypesInfo.TypeOf(expr)
+}
+
+type walker struct {
+ nmap map[string]int
+ has map[string]int
+}
+
+func (w *walker) Visit(n ast.Node) ast.Visitor {
+ fn, ok := n.(*ast.FuncDecl)
+ if !ok || fn.Recv == nil || len(fn.Recv.List) == 0 {
+ return w
+ }
+ // TODO(dsymonds): We could check the signature to be more precise.
+ recv := receiverType(fn)
+ if i, ok := w.nmap[fn.Name.Name]; ok {
+ w.has[recv] |= i
+ }
+ return w
+}
+
+func (p *Package) scanSortable() {
+ p.Sortable = make(map[string]bool)
+
+ // bitfield for which methods exist on each type.
+ const (
+ Len = 1 << iota
+ Less
+ Swap
+ )
+ nmap := map[string]int{"Len": Len, "Less": Less, "Swap": Swap}
+ has := make(map[string]int)
+ for _, f := range p.files {
+ ast.Walk(&walker{nmap, has}, f.AST)
+ }
+ for typ, ms := range has {
+ if ms == Len|Less|Swap {
+ p.Sortable[typ] = true
+ }
+ }
+}
+
+// receiverType returns the named type of the method receiver, sans "*",
+// or "invalid-type" if fn.Recv is ill formed.
+func receiverType(fn *ast.FuncDecl) string {
+ switch e := fn.Recv.List[0].Type.(type) {
+ case *ast.Ident:
+ return e.Name
+ case *ast.StarExpr:
+ if id, ok := e.X.(*ast.Ident); ok {
+ return id.Name
+ }
+ }
+ // The parser accepts much more than just the legal forms.
+ return "invalid-type"
+}
+
+func (p *Package) lint(rules []Rule, config Config, failures chan Failure) {
+ p.scanSortable()
+ var wg sync.WaitGroup
+ for _, file := range p.files {
+ wg.Add(1)
+ go (func(file *File) {
+ file.lint(rules, config, failures)
+ defer wg.Done()
+ })(file)
+ }
+ wg.Wait()
+}